bouncycastle: Android tree with upstream code for version 1.61

Test: no tests needed, this branch is only for diffing against upstream
Change-Id: I229752afcbb6c113248155b5dd969bd73b697e42
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
index fac9b44..082f582 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
@@ -1,7 +1,10 @@
 package org.bouncycastle.cert;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Date;
@@ -26,12 +29,14 @@
  * Holding class for an X.509 AttributeCertificate structure.
  */
 public class X509AttributeCertificateHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
+    private static final long serialVersionUID = 20170722001L;
+
     private static Attribute[] EMPTY_ARRAY = new Attribute[0];
     
-    private AttributeCertificate attrCert;
-    private Extensions extensions;
+    private transient AttributeCertificate attrCert;
+    private transient Extensions extensions;
 
     private static AttributeCertificate parseBytes(byte[] certEncoding)
         throws IOException
@@ -69,6 +74,11 @@
      */
     public X509AttributeCertificateHolder(AttributeCertificate attrCert)
     {
+        init(attrCert);
+    }
+
+    private void init(AttributeCertificate attrCert)
+    {
         this.attrCert = attrCert;
         this.extensions = attrCert.getAcinfo().getExtensions();
     }
@@ -364,4 +374,22 @@
     {
         return this.attrCert.hashCode();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(AttributeCertificate.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
index 67abd31..ef89601 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
@@ -3,7 +3,10 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,12 +34,14 @@
  * Holding class for an X.509 CRL structure.
  */
 public class X509CRLHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
-    private CertificateList x509CRL;
-    private boolean isIndirect;
-    private Extensions extensions;
-    private GeneralNames issuerName;
+    private static final long serialVersionUID = 20170722001L;
+    
+    private transient CertificateList x509CRL;
+    private transient boolean isIndirect;
+    private transient Extensions extensions;
+    private transient GeneralNames issuerName;
 
     private static CertificateList parseStream(InputStream stream)
         throws IOException
@@ -103,6 +108,11 @@
      */
     public X509CRLHolder(CertificateList x509CRL)
     {
+        init(x509CRL);
+    }
+
+    private void init(CertificateList x509CRL)
+    {
         this.x509CRL = x509CRL;
         this.extensions = x509CRL.getTBSCertList().getExtensions();
         this.isIndirect = isIndirectCRL(extensions);
@@ -322,4 +332,22 @@
     {
         return this.x509CRL.hashCode();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(CertificateList.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
index 2396aac..0fb8673 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
@@ -1,7 +1,10 @@
 package org.bouncycastle.cert;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
@@ -24,10 +27,12 @@
  * Holding class for an X.509 Certificate structure.
  */
 public class X509CertificateHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
-    private Certificate x509Certificate;
-    private Extensions  extensions;
+    private static final long serialVersionUID = 20170722001L;
+
+    private transient Certificate x509Certificate;
+    private transient Extensions  extensions;
 
     private static Certificate parseBytes(byte[] certEncoding)
         throws IOException
@@ -65,6 +70,11 @@
      */
     public X509CertificateHolder(Certificate x509Certificate)
     {
+        init(x509Certificate);
+    }
+
+    private void init(Certificate x509Certificate)
+    {
         this.x509Certificate = x509Certificate;
         this.extensions = x509Certificate.getTBSCertificate().getExtensions();
     }
@@ -325,4 +335,22 @@
     {
         return x509Certificate.getEncoded();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(Certificate.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
index 2763083..05d5946 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
@@ -15,16 +15,26 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.CMPCertificate;
 import org.bouncycastle.asn1.cmp.CertConfirmContent;
+import org.bouncycastle.asn1.cmp.CertOrEncCert;
 import org.bouncycastle.asn1.cmp.CertRepMessage;
+import org.bouncycastle.asn1.cmp.CertResponse;
+import org.bouncycastle.asn1.cmp.CertifiedKeyPair;
 import org.bouncycastle.asn1.cmp.PKIBody;
 import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.cmp.PKIStatus;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
 import org.bouncycastle.asn1.crmf.CertReqMessages;
 import org.bouncycastle.asn1.crmf.CertReqMsg;
+import org.bouncycastle.asn1.crmf.EncryptedValue;
 import org.bouncycastle.asn1.crmf.ProofOfPossession;
 import org.bouncycastle.asn1.crmf.SubsequentMessage;
+import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.cert.CertException;
@@ -40,16 +50,25 @@
 import org.bouncycastle.cert.crmf.CertificateRequestMessageBuilder;
 import org.bouncycastle.cert.crmf.PKMACBuilder;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessageBuilder;
+import org.bouncycastle.cert.crmf.jcajce.JcaEncryptedValueBuilder;
+import org.bouncycastle.cert.crmf.jcajce.JceCRMFEncryptorBuilder;
 import org.bouncycastle.cert.crmf.jcajce.JcePKMACValuesCalculator;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.AsymmetricKeyUnwrapper;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper;
+import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
+import org.bouncycastle.operator.jcajce.JceInputDecryptorProviderBuilder;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.io.Streams;
 
 public class AllTests
@@ -225,6 +244,77 @@
         assertEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.getPopo().getType());
     }
 
+    public void testServerSideKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+        X509CertificateHolder cert = makeV3Certificate(kp, "CN=Test", kp, "CN=Test");
+
+        JcaEncryptedValueBuilder encBldr = new JcaEncryptedValueBuilder(
+            new JceAsymmetricKeyWrapper(kp.getPublic()).setProvider(BC),
+            new JceCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        GeneralName sender = new GeneralName(new X500Name("CN=Sender"));
+        GeneralName recipient = new GeneralName(new X500Name("CN=Recip"));
+
+        CertRepMessage msg = new CertRepMessage(null, new CertResponse[] {
+            new CertResponse(
+                new ASN1Integer(2),
+                new PKIStatusInfo(PKIStatus.granted),
+                new CertifiedKeyPair(
+                    new CertOrEncCert(CMPCertificate.getInstance(cert.getEncoded())),
+                    encBldr.build(kp.getPrivate()),
+                    null), null) });
+
+        ContentSigner signer = new JcaContentSignerBuilder("MD5WithRSAEncryption").setProvider(BC).build(kp.getPrivate());
+        ProtectedPKIMessage message = new ProtectedPKIMessageBuilder(sender, recipient)
+                                                  .setBody(new PKIBody(PKIBody.TYPE_INIT_REP, msg))
+                                                  .addCMPCertificate(cert)
+                                                  .build(signer);
+
+        X509Certificate jcaCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(message.getCertificates()[0]);
+        ContentVerifierProvider verifierProvider = new JcaContentVerifierProviderBuilder().setProvider(BC).build(jcaCert.getPublicKey());
+
+        assertTrue(message.verify(verifierProvider));
+
+        assertEquals(sender, message.getHeader().getSender());
+        assertEquals(recipient, message.getHeader().getRecipient());
+
+        CertRepMessage content = CertRepMessage.getInstance(message.getBody().getContent());
+
+        CertResponse[] responseList = content.getResponse();
+
+        assertEquals(1, responseList.length);
+
+        CertResponse response = responseList[0];
+
+        assertEquals(PKIStatus.granted.getValue(), response.getStatus().getStatus());
+
+        CertifiedKeyPair certKp = response.getCertifiedKeyPair();
+
+        // steps to unwrap private key
+        EncryptedValue encValue = certKp.getPrivateKey();
+
+        // recover symmetric key
+        AsymmetricKeyUnwrapper unwrapper = new JceAsymmetricKeyUnwrapper(encValue.getKeyAlg(), kp.getPrivate());
+        
+        byte[] secKeyBytes = (byte[])unwrapper.generateUnwrappedKey(encValue.getKeyAlg(), encValue.getEncSymmKey().getBytes()).getRepresentation();
+
+        // recover private key
+        PKCS8EncryptedPrivateKeyInfo respInfo = new PKCS8EncryptedPrivateKeyInfo(
+            new EncryptedPrivateKeyInfo(encValue.getSymmAlg(), encValue.getEncValue().getBytes()));
+
+        PrivateKeyInfo keyInfo = respInfo.decryptPrivateKeyInfo(new JceInputDecryptorProviderBuilder().setProvider("BC").build(secKeyBytes));
+
+        assertEquals(keyInfo.getPrivateKeyAlgorithm(), encValue.getIntendedAlg());
+        assertTrue(Arrays.areEqual(kp.getPrivate().getEncoded(), keyInfo.getEncoded()));
+    }
+
+
     public void testNotBeforeNotAfter()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
index 8ea6ecd..04673d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
@@ -5,6 +5,11 @@
 {
     private Throwable cause;
 
+    public CRMFException(String msg)
+    {
+        this(msg, null);
+    }
+
     public CRMFException(String msg, Throwable cause)
     {
         super(msg);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
index aa48235..3294285 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
@@ -13,12 +13,14 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.crmf.AttributeTypeAndValue;
 import org.bouncycastle.asn1.crmf.CertReqMsg;
 import org.bouncycastle.asn1.crmf.CertRequest;
 import org.bouncycastle.asn1.crmf.CertTemplate;
 import org.bouncycastle.asn1.crmf.CertTemplateBuilder;
 import org.bouncycastle.asn1.crmf.OptionalValidity;
+import org.bouncycastle.asn1.crmf.PKMACValue;
 import org.bouncycastle.asn1.crmf.POPOPrivKey;
 import org.bouncycastle.asn1.crmf.ProofOfPossession;
 import org.bouncycastle.asn1.crmf.SubsequentMessage;
@@ -41,8 +43,10 @@
     private PKMACBuilder pkmacBuilder;
     private char[] password;
     private GeneralName sender;
+    private int popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
     private POPOPrivKey popoPrivKey;
     private ASN1Null popRaVerified;
+    private PKMACValue agreeMAC;
 
     public CertificateRequestMessageBuilder(BigInteger certReqId)
     {
@@ -148,7 +152,7 @@
 
     public CertificateRequestMessageBuilder setProofOfPossessionSigningKeySigner(ContentSigner popSigner)
     {
-        if (popoPrivKey != null || popRaVerified != null)
+        if (popoPrivKey != null || popRaVerified != null || agreeMAC != null)
         {
             throw new IllegalStateException("only one proof of possession allowed");
         }
@@ -160,16 +164,46 @@
 
     public CertificateRequestMessageBuilder setProofOfPossessionSubsequentMessage(SubsequentMessage msg)
     {
-        if (popSigner != null || popRaVerified != null)
+        if (popSigner != null || popRaVerified != null || agreeMAC != null)
         {
             throw new IllegalStateException("only one proof of possession allowed");
         }
 
+        this.popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
         this.popoPrivKey = new POPOPrivKey(msg);
 
         return this;
     }
 
+    public CertificateRequestMessageBuilder setProofOfPossessionSubsequentMessage(int type, SubsequentMessage msg)
+    {
+        if (popSigner != null || popRaVerified != null || agreeMAC != null)
+        {
+            throw new IllegalStateException("only one proof of possession allowed");
+        }
+        if (type != ProofOfPossession.TYPE_KEY_ENCIPHERMENT && type != ProofOfPossession.TYPE_KEY_AGREEMENT)
+        {
+            throw new IllegalArgumentException("type must be ProofOfPossession.TYPE_KEY_ENCIPHERMENT || ProofOfPossession.TYPE_KEY_AGREEMENT");
+        }
+
+        this.popoType = type;
+        this.popoPrivKey = new POPOPrivKey(msg);
+
+        return this;
+    }
+
+    public CertificateRequestMessageBuilder setProofOfPossessionAgreeMAC(PKMACValue macValue)
+    {
+        if (popSigner != null || popRaVerified != null || popoPrivKey != null)
+        {
+            throw new IllegalStateException("only one proof of possession allowed");
+        }
+
+        this.agreeMAC = macValue;
+
+        return this;
+    }
+
     public CertificateRequestMessageBuilder setProofOfPossessionRaVerified()
     {
         if (popSigner != null || popoPrivKey != null)
@@ -267,7 +301,13 @@
         }
         else if (popoPrivKey != null)
         {
-            v.add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, popoPrivKey));
+            v.add(new ProofOfPossession(popoType, popoPrivKey));
+        }
+        else if (agreeMAC != null)
+        {
+            v.add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_AGREEMENT,
+                    POPOPrivKey.getInstance(new DERTaggedObject(false, POPOPrivKey.agreeMAC, agreeMAC))));
+
         }
         else if (popRaVerified != null)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
index 55187b5..c9aacc5 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
@@ -7,11 +7,14 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.KeyWrapper;
 import org.bouncycastle.operator.OperatorException;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder;
 import org.bouncycastle.util.Strings;
 
 /**
@@ -82,6 +85,44 @@
         }
     }
 
+    /**
+     * Build an EncryptedValue structure containing the private key contained in
+     * the passed info structure.
+     *
+     * @param privateKeyInfo  a PKCS#8 private key info structure.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(PrivateKeyInfo privateKeyInfo)
+        throws CRMFException
+    {
+        PKCS8EncryptedPrivateKeyInfoBuilder encInfoBldr = new PKCS8EncryptedPrivateKeyInfoBuilder(privateKeyInfo);
+
+        AlgorithmIdentifier intendedAlg = privateKeyInfo.getPrivateKeyAlgorithm();
+        AlgorithmIdentifier symmAlg = encryptor.getAlgorithmIdentifier();
+        DERBitString encSymmKey;
+
+        try
+        {
+            PKCS8EncryptedPrivateKeyInfo encInfo = encInfoBldr.build(encryptor);
+            
+            encSymmKey = new DERBitString(wrapper.generateWrappedKey(encryptor.getKey()));
+
+            AlgorithmIdentifier keyAlg = wrapper.getAlgorithmIdentifier();
+            ASN1OctetString valueHint = null;
+
+            return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, new DERBitString(encInfo.getEncryptedData()));
+        }
+        catch (IllegalStateException e)
+        {
+            throw new CRMFException("cannot encode key: " + e.getMessage(), e);
+        }
+        catch (OperatorException e)
+        {
+            throw new CRMFException("cannot wrap key: " + e.getMessage(), e);
+        }
+    }
+
     private EncryptedValue encryptData(byte[] data)
        throws CRMFException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
index 6c0aa87..3390064 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
@@ -5,6 +5,8 @@
 import java.io.InputStream;
 
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Certificate;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.InputDecryptor;
@@ -42,13 +44,14 @@
         this.padder = padder;
     }
 
+    public AlgorithmIdentifier getIntendedAlg()
+    {
+        return value.getIntendedAlg();
+    }
+
     private byte[] decryptValue(ValueDecryptorGenerator decGen)
         throws CRMFException
     {
-        if (value.getIntendedAlg() != null)
-        {
-            throw new UnsupportedOperationException();
-        }
         if (value.getValueHint() != null)
         {
             throw new UnsupportedOperationException();
@@ -89,6 +92,19 @@
     }
 
     /**
+     * Read a PKCS#8 PrivateKeyInfo.
+     *
+     * @param decGen the decryptor generator to decrypt the encrypted value.
+     * @return an PrivateKeyInfo containing the private key that was read.
+     * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+     */
+    public PrivateKeyInfo readPrivateKeyInfo(ValueDecryptorGenerator decGen)
+        throws CRMFException
+    {
+        return PrivateKeyInfo.getInstance(decryptValue(decGen));
+    }
+
+    /**
      * Read a pass phrase.
      *
      * @param decGen the decryptor generator to decrypt the encrypted value.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
index 1d8c369..02073b3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
@@ -38,6 +38,6 @@
             throw new CRMFException("exception encoding mac input: " + e.getMessage(), e);
         }
 
-        return Arrays.areEqual(calculator.getMac(), value.getValue().getBytes());
+        return Arrays.constantTimeAreEqual(calculator.getMac(), value.getValue().getBytes());
     }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java
new file mode 100644
index 0000000..df40133
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.OutputEncryptor;
+
+/**
+ * Lightweight CRMFOutputEncryptor builder.
+ */
+public class BcCRMFEncryptorBuilder
+{
+    private final ASN1ObjectIdentifier encryptionOID;
+    private final int                  keySize;
+
+    private CRMFHelper helper = new CRMFHelper();
+    private SecureRandom random;
+
+    public BcCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
+    {
+        this(encryptionOID, -1);
+    }
+
+    public BcCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
+    {
+        this.encryptionOID = encryptionOID;
+        this.keySize = keySize;
+    }
+
+    public BcCRMFEncryptorBuilder setSecureRandom(SecureRandom random)
+    {
+        this.random = random;
+
+        return this;
+    }
+
+    public OutputEncryptor build()
+        throws CRMFException
+    {
+        return new CRMFOutputEncryptor(encryptionOID, keySize, random);
+    }
+
+    private class CRMFOutputEncryptor
+        implements OutputEncryptor
+    {
+        private KeyParameter encKey;
+        private AlgorithmIdentifier algorithmIdentifier;
+        private Object cipher;
+
+        CRMFOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
+            throws CRMFException
+        {
+            if (random == null)
+            {
+                random = CryptoServicesRegistrar.getSecureRandom();
+            }
+
+            CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, random);
+
+            encKey = new KeyParameter(keyGen.generateKey());
+            algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random);
+            cipher = helper.createContentCipher(true, encKey, algorithmIdentifier);
+        }
+
+        public AlgorithmIdentifier getAlgorithmIdentifier()
+        {
+            return algorithmIdentifier;
+        }
+
+        public OutputStream getOutputStream(OutputStream dOut)
+        {
+            return CipherFactory.createOutputStream(dOut, cipher);
+        }
+
+        public GenericKey getKey()
+        {
+            return new GenericKey(algorithmIdentifier, encKey.getKey());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java
new file mode 100644
index 0000000..a39215c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.cert.crmf.EncryptedValueBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.operator.KeyWrapper;
+import org.bouncycastle.operator.OutputEncryptor;
+
+/**
+ * Lightweight convenience class for EncryptedValueBuilder
+ */
+public class BcEncryptedValueBuilder
+    extends EncryptedValueBuilder
+{
+    public BcEncryptedValueBuilder(KeyWrapper wrapper, OutputEncryptor encryptor)
+    {
+        super(wrapper, encryptor);
+    }
+
+    /**
+     * Build an EncryptedValue structure containing the passed in certificate.
+     *
+     * @param certificate the certificate to be encrypted.
+     * @return an EncryptedValue containing the encrypted certificate.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(X509Certificate certificate)
+        throws CertificateEncodingException, CRMFException
+    {
+        return build(new JcaX509CertificateHolder(certificate));
+    }
+
+    /**
+     * Build an EncryptedValue structure containing the private key details contained in
+     * the passed PrivateKey.
+     *
+     * @param privateKey a private key parameter.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(AsymmetricKeyParameter privateKey)
+        throws CRMFException, IOException
+    {
+        return build(PrivateKeyInfoFactory.createPrivateKeyInfo(privateKey));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java
new file mode 100644
index 0000000..eb36b97
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.AlgorithmIdentifierFactory;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.crypto.util.CipherKeyGeneratorFactory;
+
+class CRMFHelper
+{
+    CRMFHelper()
+    {
+    }
+
+    CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRandom random)
+        throws CRMFException
+    {
+        try
+        {
+            return CipherKeyGeneratorFactory.createKeyGenerator(algorithm, random);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+
+    static Object createContentCipher(boolean forEncryption, CipherParameters encKey, AlgorithmIdentifier encryptionAlgID)
+        throws CRMFException
+    {
+        try
+        {
+            return CipherFactory.createContentCipher(forEncryption, encKey, encryptionAlgID);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+
+    AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random)
+        throws CRMFException
+    {
+        try
+        {
+            return AlgorithmIdentifierFactory.generateEncryptionAlgID(encryptionOID, encKey.getKey().length * 8, random);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
index 91d22a0..31f1b89 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
@@ -1,15 +1,20 @@
 package org.bouncycastle.cert.crmf.jcajce;
 
+import java.security.PrivateKey;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.cert.crmf.CRMFException;
 import org.bouncycastle.cert.crmf.EncryptedValueBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.operator.KeyWrapper;
 import org.bouncycastle.operator.OutputEncryptor;
 
+/**
+ * JCA convenience class for EncryptedValueBuilder
+ */
 public class JcaEncryptedValueBuilder
     extends EncryptedValueBuilder
 {
@@ -18,9 +23,30 @@
         super(wrapper, encryptor);
     }
 
+    /**
+     * Build an EncryptedValue structure containing the passed in certificate.
+     *
+     * @param certificate the certificate to be encrypted.
+     * @return an EncryptedValue containing the encrypted certificate.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
     public EncryptedValue build(X509Certificate certificate)
         throws CertificateEncodingException, CRMFException
     {
         return build(new JcaX509CertificateHolder(certificate));
     }
+
+    /**
+     * Build an EncryptedValue structure containing the private key details contained in
+     * the passed PrivateKey.
+     *
+     * @param privateKey the asymmetric private key.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(PrivateKey privateKey)
+        throws CertificateEncodingException, CRMFException
+    {
+        return build(PrivateKeyInfo.getInstance(privateKey.getEncoded()));
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
index 2173a92..8c4e2ec 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
@@ -19,7 +19,6 @@
 import javax.crypto.spec.PSource;
 import javax.security.auth.x500.X500Principal;
 
-import junit.framework.Assert;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -34,10 +33,12 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.X509v1CertificateBuilder;
 import org.bouncycastle.cert.crmf.CRMFException;
@@ -47,6 +48,8 @@
 import org.bouncycastle.cert.crmf.PKIArchiveControl;
 import org.bouncycastle.cert.crmf.PKMACBuilder;
 import org.bouncycastle.cert.crmf.ValueDecryptorGenerator;
+import org.bouncycastle.cert.crmf.bc.BcCRMFEncryptorBuilder;
+import org.bouncycastle.cert.crmf.bc.BcEncryptedValueBuilder;
 import org.bouncycastle.cert.crmf.bc.BcFixedLengthMGF1Padder;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessage;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessageBuilder;
@@ -67,9 +70,12 @@
 import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.operator.bc.BcRSAAsymmetricKeyWrapper;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
@@ -256,6 +262,58 @@
         TestCase.assertEquals(kp.getPublic(), certReqMsg.getPublicKey());
     }
 
+    public void testEncryptedValueWithKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+        
+        JcaEncryptedValueBuilder build = new JcaEncryptedValueBuilder(new JceAsymmetricKeyWrapper(kp.getPublic()).setProvider(BC), new JceCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        EncryptedValue value = build.build(kp.getPrivate());
+
+        ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
+
+        EncryptedValueParser  parser = new EncryptedValueParser(value);
+
+        PrivateKeyInfo privInfo = parser.readPrivateKeyInfo(decGen);
+
+        TestCase.assertEquals(privInfo.getPrivateKeyAlgorithm(), parser.getIntendedAlg());
+
+        TestCase.assertTrue(Arrays.areEqual(privInfo.getEncoded(), kp.getPrivate().getEncoded()));
+    }
+
+    public void testBcEncryptedValueWithKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+
+        BcEncryptedValueBuilder build = new BcEncryptedValueBuilder(new BcRSAAsymmetricKeyWrapper(
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE),
+            PublicKeyFactory.createKey(SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()))),
+            new BcCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).build());
+
+        EncryptedValue value = build.build(
+            PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded())));
+
+        ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
+
+        EncryptedValueParser  parser = new EncryptedValueParser(value);
+
+        PrivateKeyInfo privInfo = parser.readPrivateKeyInfo(decGen);
+
+        TestCase.assertEquals(privInfo.getPrivateKeyAlgorithm(), parser.getIntendedAlg());
+
+        TestCase.assertTrue(Arrays.areEqual(privInfo.getEncoded(), kp.getPrivate().getEncoded()));
+    }
+
     public void testProofOfPossessionWithSender()
         throws Exception
     {
@@ -345,7 +403,7 @@
 
         OutputEncryptor outputEncryptor = encryptorBuilder.build();
 
-        Assert.assertEquals(keySize / 8, ((byte[])(outputEncryptor.getKey().getRepresentation())).length);
+        assertEquals(keySize / 8, ((byte[])(outputEncryptor.getKey().getRepresentation())).length);
     }
 
     public void testEncryptedValue()
@@ -415,9 +473,9 @@
 
         EncryptedValue value = build.build(cert);
 
-        Assert.assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, value.getKeyAlg().getAlgorithm());
-        Assert.assertEquals(NISTObjectIdentifiers.id_sha256, RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getHashAlgorithm().getAlgorithm());
-        Assert.assertEquals(new DEROctetString(new byte[2]), RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getPSourceAlgorithm().getParameters());
+        assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, value.getKeyAlg().getAlgorithm());
+        assertEquals(NISTObjectIdentifiers.id_sha256, RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getHashAlgorithm().getAlgorithm());
+        assertEquals(new DEROctetString(new byte[2]), RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getPSourceAlgorithm().getParameters());
 
         ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
index 507775b..603dc2a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
@@ -12,7 +12,6 @@
  *     91d23d115b68072e7a38afeb7e295bd6392a19f25f8328b4ecae4778._smimecert.test.org
  * </pre>
  * In the case of the later ideally just returning a list containing the single entry.
- * </p>
  */
 public interface DANEEntryFetcherFactory
 {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
index 577ea22..38fa9fb 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
@@ -22,7 +22,6 @@
      *     new DANEEntrySelectorFactory(new TruncatingDigestCalculator(new SHA256DigestCalculator()));
      * </pre>
      * or some equivalent.
-     * </p>
      *
      * @param digestCalculator a calculator for the message digest to filter email addresses currently truncated SHA-256 (originally SHA-224).
      */
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
index 0838f08..63c5d57 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
@@ -8,22 +8,34 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
 
 import javax.security.auth.x500.X500Principal;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509ExtensionUtils;
 import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Integers;
 
 public class JcaX509ExtensionUtils
     extends X509ExtensionUtils
@@ -112,6 +124,73 @@
         return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(encExtValue).getOctets());
     }
 
+    public static Collection getIssuerAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {
+        byte[] extVal = cert.getExtensionValue(Extension.issuerAlternativeName.getId());
+
+        return getAlternativeNames(extVal);
+    }
+
+    public static Collection getSubjectAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {
+        byte[] extVal = cert.getExtensionValue(Extension.subjectAlternativeName.getId());
+
+        return getAlternativeNames(extVal);
+    }
+
+    private static Collection getAlternativeNames(byte[] extVal)
+        throws CertificateParsingException
+    {
+        if (extVal == null)
+        {
+            return Collections.EMPTY_LIST;
+        }
+        try
+        {
+            Collection temp = new ArrayList();
+            Enumeration it = DERSequence.getInstance(parseExtensionValue(extVal)).getObjects();
+            while (it.hasMoreElements())
+            {
+                GeneralName genName = GeneralName.getInstance(it.nextElement());
+                List list = new ArrayList();
+                list.add(Integers.valueOf(genName.getTagNo()));
+                switch (genName.getTagNo())
+                {
+                case GeneralName.ediPartyName:
+                case GeneralName.x400Address:
+                case GeneralName.otherName:
+                    list.add(genName.getName().toASN1Primitive());
+                    break;
+                case GeneralName.directoryName:
+                    list.add(X500Name.getInstance(genName.getName()).toString());
+                    break;
+                case GeneralName.dNSName:
+                case GeneralName.rfc822Name:
+                case GeneralName.uniformResourceIdentifier:
+                    list.add(((ASN1String)genName.getName()).getString());
+                    break;
+                case GeneralName.registeredID:
+                    list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId());
+                    break;
+                case GeneralName.iPAddress:
+                    list.add(DEROctetString.getInstance(genName.getName()).getOctets());
+                    break;
+                default:
+                    throw new IOException("Bad tag number: " + genName.getTagNo());
+                }
+
+                temp.add(list);
+            }
+            return Collections.unmodifiableCollection(temp);
+        }
+        catch (Exception e)
+        {
+            throw new CertificateParsingException(e.getMessage());
+        }
+    }
+
     private static class SHA1DigestCalculator
         implements DigestCalculator
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
index f91b3a8..1c1d93a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
@@ -43,7 +43,7 @@
     {
         CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
 
-        CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder();
+        CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder(context);
 
         for (int i = 0; i != ruleSet.length; i++)
         {
@@ -56,7 +56,7 @@
                 }
                 catch (CertPathValidationException e)
                 {
-                   builder.addException(e);
+                   builder.addException(j, i, e);
                 }
             }
         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
index facefb4..0a4e618 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
@@ -3,18 +3,26 @@
 import java.util.Collections;
 import java.util.Set;
 
+import org.bouncycastle.util.Arrays;
+
 public class CertPathValidationResult
 {
     private final boolean isValid;
     private final CertPathValidationException cause;
     private final Set unhandledCriticalExtensionOIDs;
+    private final int certIndex;
+    private final int ruleIndex;
 
+    private CertPathValidationException[] causes;
     private int[] certIndexes;
+    private int[] ruleIndexes;
 
     public CertPathValidationResult(CertPathValidationContext context)
     {
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = this.unhandledCriticalExtensionOIDs.isEmpty();
+        this.certIndex = -1;
+        this.ruleIndex = -1;
         cause = null;
     }
 
@@ -22,16 +30,21 @@
     {
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = false;
+        this.certIndex = certIndex;
+        this.ruleIndex = ruleIndex;
         this.cause = cause;
     }
 
-    public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] cause)
+    public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] causes)
     {
-        // TODO
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = false;
-        this.cause = cause[0];
+        this.cause = causes[0];
+        this.certIndex = certIndexes[0];
+        this.ruleIndex = ruleIndexes[0];
+        this.causes = causes;
         this.certIndexes = certIndexes;
+        this.ruleIndexes = ruleIndexes;
     }
 
     public boolean isValid()
@@ -39,7 +52,7 @@
         return isValid;
     }
 
-    public Exception getCause()
+    public CertPathValidationException getCause()
     {
         if (cause != null)
         {
@@ -54,6 +67,16 @@
         return null;
     }
 
+    public int getFailingCertIndex()
+    {
+        return certIndex;
+    }
+
+    public int getFailingRuleIndex()
+    {
+        return ruleIndex;
+    }
+
     public Set getUnhandledCriticalExtensionOIDs()
     {
         return unhandledCriticalExtensionOIDs;
@@ -63,4 +86,34 @@
     {
         return this.certIndexes != null;
     }
+
+    public CertPathValidationException[] getCauses()
+    {
+        if (causes != null)
+        {
+            CertPathValidationException[] rv = new CertPathValidationException[causes.length];
+
+            System.arraycopy(causes, 0, rv, 0, causes.length);
+
+            return rv;
+        }
+
+        if (!unhandledCriticalExtensionOIDs.isEmpty())
+        {
+            return new CertPathValidationException[]
+                { new CertPathValidationException("Unhandled Critical Extensions") };
+        }
+
+        return null;
+    }
+
+    public int[] getFailingCertIndexes()
+    {
+        return Arrays.clone(certIndexes);
+    }
+
+    public int[] getFailingRuleIndexes()
+    {
+        return Arrays.clone(ruleIndexes);
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
index 9e81339..c00d5f7 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
@@ -1,14 +1,51 @@
 package org.bouncycastle.cert.path;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.util.Integers;
+
 class CertPathValidationResultBuilder
 {
-    public CertPathValidationResult build()
+    private final CertPathValidationContext context;
+    private final List<Integer> certIndexes = new ArrayList<Integer>();
+    private final List<Integer> ruleIndexes = new ArrayList<Integer>();
+    private final List<CertPathValidationException> exceptions = new ArrayList<CertPathValidationException>();
+
+    CertPathValidationResultBuilder(CertPathValidationContext context)
     {
-        return new CertPathValidationResult(null, 0, 0, null);
+        this.context = context;
     }
 
-    public void addException(CertPathValidationException exception)
+    public CertPathValidationResult build()
     {
-        //To change body of created methods use File | Settings | File Templates.
+        if (exceptions.isEmpty())
+        {
+            return new CertPathValidationResult(context);
+        }
+        else
+        {
+            return new CertPathValidationResult(context,
+                toInts(certIndexes), toInts(ruleIndexes), (CertPathValidationException[])exceptions.toArray(new CertPathValidationException[exceptions.size()]));
+        }
+    }
+
+    public void addException(int certIndex, int ruleIndex, CertPathValidationException exception)
+    {
+        this.certIndexes.add(Integers.valueOf(certIndex));
+        this.ruleIndexes.add(Integers.valueOf(ruleIndex));
+        this.exceptions.add(exception);
+    }
+
+    private int[] toInts(List<Integer> values)
+    {
+        int[] rv = new int[values.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = ((Integer)values.get(i)).intValue();
+        }
+
+        return rv;
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
index 80da034..48cd306 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
@@ -265,6 +265,13 @@
             fail("basic validation (1) not working");
         }
 
+        result = path.evaluate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new BasicConstraintsValidation(), new KeyUsageValidation()});
+
+        if (!result.isValid())
+        {
+            fail("basic evaluation (1) not working");
+        }
+
         List crlList = new ArrayList();
 
         crlList.add(rootCrl);
@@ -305,8 +312,15 @@
             fail("incorrect path validated!!");
         }
 
+        result = path.evaluate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier)});
 
+        if (result.isValid())
+        {
+            fail("incorrect path validated!!");
+        }
 
+        isTrue(result.isDetailed());
+        
 //        List list = new ArrayList();
 //        list.add(rootCert);
 //        list.add(interCert);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
index f970734..ee73302 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
@@ -157,8 +157,8 @@
      * Adds a collection with target groups criteria. If <code>null</code> is
      * given any will do.
      * <p>
-     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code representing DER
-     * encoded GeneralNames.
+     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code>
+     * representing DER encoded GeneralNames.
      *
      * @param names A collection of target groups.
      * @throws java.io.IOException if a parsing error occurs.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
index 6b85e8e..5f0e9b5 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
@@ -14,7 +14,7 @@
 {
     public void testSimpleTests()
     {
-        org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertTest(), new DANETest(), new PKCS10Test(), new AttrCertSelectorTest(), new AttrCertTest(), new X509ExtensionUtilsTest(), new CertPathLoopTest() };
+        org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertTest(), new DANETest(), new PKCS10Test(), new AttrCertSelectorTest(), new AttrCertTest(), new X509ExtensionUtilsTest(), new CertPathLoopTest(), new GOST3410_2012CMSTest() };
 
         for (int i = 0; i != tests.length; i++)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
index e47fed7..e289fd0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
@@ -40,6 +40,8 @@
 import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.asn1.x509.KeyUsage;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.CertException;
 import org.bouncycastle.cert.X509CRLEntryHolder;
@@ -807,16 +809,9 @@
       */
      public void checkCreation3()
      {
-         ECCurve curve = new ECCurve.Fp(
-             new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-             new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-             new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-         ECDomainParameters params = new ECDomainParameters(
-             curve,
-             curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-             new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-
+         X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+         ECCurve curve = x9.getCurve();
+         ECDomainParameters params = new ECDomainParameters(curve, x9.getG(), x9.getN(), x9.getH());
 
          ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
              new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
index 2b0af04..fc2aa23 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
@@ -1,7 +1,11 @@
 package org.bouncycastle.cert.test;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.security.KeyFactory;
@@ -22,7 +26,11 @@
 import java.security.cert.X509CRL;
 import java.security.cert.X509CRLEntry;
 import java.security.cert.X509Certificate;
+import java.security.spec.DSAParameterSpec;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.PSSParameterSpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 import java.util.Collection;
@@ -44,6 +52,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
 import org.bouncycastle.asn1.x500.X500Name;
@@ -60,6 +69,8 @@
 import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLEntryHolder;
@@ -76,6 +87,8 @@
 import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 import org.bouncycastle.jce.X509KeyUsage;
@@ -97,7 +110,11 @@
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
 import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+import org.bouncycastle.util.Encodable;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
@@ -1135,7 +1152,7 @@
             + "RRsRsjse3i2/KClFVd6YLZ+7K1BE0WxFyY2bbytkwQJSxvv3vLSuweFUbhNxutb68wl/yW4GLy4b"
             + "1QdyswNxrNDXTuu5ILKhRDDuWeocz83aG2KGtr3JlFyr3biWGEyn5WUOE6tbONoQDJ0oPYgI6CAc"
             + "EHdUp0lioOCt6UOw7Cs=");
-
+    
     private final byte[] gostRFC4491_94 = Base64.decode(
         "MIICCzCCAboCECMO42BGlSTOxwvklBgufuswCAYGKoUDAgIEMGkxHTAbBgNVBAMM" +
             "FEdvc3RSMzQxMC05NCBleGFtcGxlMRIwEAYDVQQKDAlDcnlwdG9Qcm8xCzAJBgNV" +
@@ -1163,28 +1180,28 @@
 
     private final byte[] sha3Cert = Base64.decode(
         "MIID8jCCAqagAwIBAgIICfBykpzUT+IwQQYJKoZIhvcNAQEKMDSgDzANBglg"
-      + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
-      + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
-      + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
-      + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
-      + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
-      + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
-      + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
-      + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
-      + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
-      + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
-      + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
-      + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
-      + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
-      + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
-      + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
-      + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
-      + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
-      + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
-      + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
-      + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
-      + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
-      + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
+            + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
+            + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
+            + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
+            + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
+            + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
+            + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
+            + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
+            + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
+            + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
+            + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
+            + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
+            + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
+            + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
+            + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
+            + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
+            + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
+            + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
+            + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
+            + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
+            + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
+            + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
+            + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
 
     private static byte[] sm_root = Base64.decode(
         "MIICwzCCAmmgAwIBAgIIIBQGIgAAAAMwCgYIKoEcz1UBg3UwgdgxCzAJBgNVBAYT" +
@@ -1205,27 +1222,73 @@
 
     private static byte[] sm_sign = Base64.decode(
         "MIID9zCCA5ygAwIBAgIIIBcEJwKSCCMwCgYIKoEcz1UBg3UwgccxCzAJBgNVBAYT" +
-        "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
-        "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
-        "5Lu75YWs5Y+4MTwwOgYDVQQLDDPmsZ/oi4/nnIHnlLXlrZDllYbliqHmnI3liqHk" +
-        "uK3lv4PmnInpmZDotKPku7vlhazlj7gxFDASBgNVBAMMC0pTQ0FfQ0FfU00yMB4X" +
-        "DTE3MDQyNzAwMzkwNVoXDTE4MDQyNzAwMzkwNVowggEdMQ4wDAYDVQRYDAUwMDAw" +
-        "MTESMBAGA1UEGgwJ5biC6L6W5Yy6MRswGQYDVQQBDBIzMjAxMTIxOTgxMDUxMTAw" +
-        "MTQxDTALBgRVBIhYDAM0NTYxDTALBgRVBIhXDAMxMjMxEjAQBgNVBC0MCXVzZXJD" +
-        "ZXJ0MjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCeaxn+iLj+ecgTESMBAGA1UEBwwJ" +
-        "5Y2X5Lqs5biCMQwwCgYDVQQLDAMwMDgxHzAdBgkqhkiG9w0BCQEWEDMyNzMyMTU2" +
-        "OEBxcS5jb20xITAfBgNVBCoMGOa1i+ivlee9keWFs1NNMueul+azlTEyMzEhMB8G" +
-        "A1UEAwwY5rWL6K+V572R5YWzU00y566X5rOVMTIzMFkwEwYHKoZIzj0CAQYIKoEc" +
-        "z1UBgi0DQgAEdbrBzy2y8Gz4grOF23iaDipPhRPQRApAMIAP0cAuL1tATFjFuWJs" +
-        "pBc1cnCZmsOJnVpV4W7VF8hNOaqv3Tq4NqOCARcwggETMAkGA1UdEwQCMAAwCwYD" +
-        "VR0PBAQDAgbAMB0GA1UdDgQWBBRsWSOQDniy75t7UEvTXugwfq0HpzAfBgNVHSME" +
-        "GDAWgBT/02hyCI7lesT55ixTRU6RpLF6AzAxBgNVHSUEKjAoBggrBgEFBQcDAgYI" +
-        "KwYBBQUHAwgGCCsGAQUFBwMEBggrBgEFBQcDCDA9BgNVHR8ENjA0MDKgMKAuhixo" +
-        "dHRwOi8vY3JsLmpzY2EuY29tLmNuL2NybC9TTTJDUkxfRU5USVRZLmNybDBHBggr" +
-        "BgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAKBK2h0dHA6Ly8xMC4xMDguNS4yOjg4ODAv" +
-        "ZG93bmxvYWQvSlNDQV9DQS5jZXIwCgYIKoEcz1UBg3UDSQAwRgIhALFoMoA1+uO4" +
-        "tXfmoyePz1pmv0CWPBgEP1EfDeS6FPitAiEAjHJYq7ryHKULqpRg6ph9r+xUDoWd" +
-        "0TPMOQ9jj4XJPO4=");
+            "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
+            "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
+            "5Lu75YWs5Y+4MTwwOgYDVQQLDDPmsZ/oi4/nnIHnlLXlrZDllYbliqHmnI3liqHk" +
+            "uK3lv4PmnInpmZDotKPku7vlhazlj7gxFDASBgNVBAMMC0pTQ0FfQ0FfU00yMB4X" +
+            "DTE3MDQyNzAwMzkwNVoXDTE4MDQyNzAwMzkwNVowggEdMQ4wDAYDVQRYDAUwMDAw" +
+            "MTESMBAGA1UEGgwJ5biC6L6W5Yy6MRswGQYDVQQBDBIzMjAxMTIxOTgxMDUxMTAw" +
+            "MTQxDTALBgRVBIhYDAM0NTYxDTALBgRVBIhXDAMxMjMxEjAQBgNVBC0MCXVzZXJD" +
+            "ZXJ0MjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCeaxn+iLj+ecgTESMBAGA1UEBwwJ" +
+            "5Y2X5Lqs5biCMQwwCgYDVQQLDAMwMDgxHzAdBgkqhkiG9w0BCQEWEDMyNzMyMTU2" +
+            "OEBxcS5jb20xITAfBgNVBCoMGOa1i+ivlee9keWFs1NNMueul+azlTEyMzEhMB8G" +
+            "A1UEAwwY5rWL6K+V572R5YWzU00y566X5rOVMTIzMFkwEwYHKoZIzj0CAQYIKoEc" +
+            "z1UBgi0DQgAEdbrBzy2y8Gz4grOF23iaDipPhRPQRApAMIAP0cAuL1tATFjFuWJs" +
+            "pBc1cnCZmsOJnVpV4W7VF8hNOaqv3Tq4NqOCARcwggETMAkGA1UdEwQCMAAwCwYD" +
+            "VR0PBAQDAgbAMB0GA1UdDgQWBBRsWSOQDniy75t7UEvTXugwfq0HpzAfBgNVHSME" +
+            "GDAWgBT/02hyCI7lesT55ixTRU6RpLF6AzAxBgNVHSUEKjAoBggrBgEFBQcDAgYI" +
+            "KwYBBQUHAwgGCCsGAQUFBwMEBggrBgEFBQcDCDA9BgNVHR8ENjA0MDKgMKAuhixo" +
+            "dHRwOi8vY3JsLmpzY2EuY29tLmNuL2NybC9TTTJDUkxfRU5USVRZLmNybDBHBggr" +
+            "BgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAKBK2h0dHA6Ly8xMC4xMDguNS4yOjg4ODAv" +
+            "ZG93bmxvYWQvSlNDQV9DQS5jZXIwCgYIKoEcz1UBg3UDSQAwRgIhALFoMoA1+uO4" +
+            "tXfmoyePz1pmv0CWPBgEP1EfDeS6FPitAiEAjHJYq7ryHKULqpRg6ph9r+xUDoWd" +
+            "0TPMOQ9jj4XJPO4=");
+
+    private static byte[] gost_2012_cert = Base64.decode(
+        "MIIEfDCCBCmgAwIBAgIECiew2zAKBggqhQMHAQEDAjCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Q" +
+            "n9C10YLQtdGA0LHRg9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0G" +
+            "A1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7" +
+            "MDkGA1UEAwwy0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L0wHhcNMTcwNzAyMTQw" +
+            "MDAwWhcNMzcwNzAyMTQwMDAwWjCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Qn9C10YLQtdGA0LHR" +
+            "g9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0GA1UECwwW0KDRg9C6" +
+            "0L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0JDQ" +
+            "u9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L0wZjAfBggqhQMHAQEBATATBgcqhQMCAiQA" +
+            "BggqhQMHAQECAgNDAARA6UpRcgr4pVAKuYkEQ0XKicUxjhd8jbCEz3OFYQ/wSQnuXR5RquASztlnnmnb5W/PKEAnElAUxW0j" +
+            "ROLOGZrDWaOCAZ4wggGaMA4GA1UdDwEB/wQEAwIB/jAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMD" +
+            "BggrBgEFBQcDBDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQLWqbXBTDJanrRowAoQbAaXbKRDTCCASMGA1UdIwSCARow" +
+            "ggEWgBQLWqbXBTDJanrRowAoQbAaXbKRDaGB96SB9DCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Q" +
+            "n9C10YLQtdGA0LHRg9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0G" +
+            "A1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7" +
+            "MDkGA1UEAwwy0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L2CBAonsNswCgYIKoUD" +
+            "BwEBAwIDQQDL2un6Wxn0frStAheZsd34ANDFwAb0rCOInrXsi6HOAxgIuS+9iICiQNTRlQ6x9LSWOUf+aa7kDDU5P4Ovd5od");
+
+    private static byte[] gost_2012_privateKey = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQg0MVlKYHb5/AwO1ZjNW8nhjyX3IgHo7nPSKuvKf87" +
+            "tTU=");
+
+    private static DSAParameters def2048Params = new DSAParameters(
+        new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
+                        "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                        "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                        "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                        "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                        "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                        "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                        "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                        "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                        "a406cb0b", 16),
+        new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
+        new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
+                        "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                        "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                        "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                        "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                        "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                        "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                        "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                        "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                        "78693c7a", 16),
+        new DSAValidationParameters(Hex.decode("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
 
     private PublicKey dudPublicKey = new PublicKey()
     {
@@ -1267,7 +1330,8 @@
             Certificate cert = fact.generateCertificate(bIn);
 
             PublicKey k = cert.getPublicKey();
-            // System.out.println(cert);
+//            System.out.println("****** " + id + " ******");
+//            System.out.println(cert);
         }
         catch (Exception e)
         {
@@ -1356,7 +1420,7 @@
 
             X509CertificateHolder certHldr = new X509CertificateHolder(bytes);
 
-            certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k));
+            isTrue(certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k)));
             // System.out.println(cert);
         }
         catch (Exception e)
@@ -1366,6 +1430,53 @@
 
     }
 
+    public void checkSelfSignedCertificateAndKey(
+        int id,
+        byte[] certBytes,
+        String sigAlgorithm,
+        byte[] keyBytes)
+    {
+        ByteArrayInputStream bIn;
+        String dump = "";
+
+        try
+        {
+            bIn = new ByteArrayInputStream(certBytes);
+
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
+
+            Certificate cert = fact.generateCertificate(bIn);
+
+            PublicKey k = cert.getPublicKey();
+
+            X509CertificateHolder certHldr = new X509CertificateHolder(certBytes);
+
+            isTrue(certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k)));
+            // System.out.println(cert);
+
+            KeyFactory keyFactory = KeyFactory.getInstance(k.getAlgorithm(), "BC");
+
+            PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
+
+            Signature signer = Signature.getInstance(sigAlgorithm, "BC");
+
+            signer.initSign(privKey);
+
+            signer.update(certBytes);
+
+            byte[] sig = signer.sign();
+
+            signer.initVerify(cert);
+
+            signer.update(certBytes);
+
+            isTrue(signer.verify(sig));
+        }
+        catch (Exception e)
+        {
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
+        }
+    }
 
     /**
      * Test a generated certificate with the sun provider
@@ -1378,7 +1489,7 @@
         certFact.generateCertificate(new ByteArrayInputStream(encoding));
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - RSA
      */
     public void checkCreation1()
@@ -1604,7 +1715,7 @@
         }
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - DSA
      */
     public void checkCreation2()
@@ -1699,8 +1810,8 @@
         }
     }
 
-    /**
-     * we generate a self signed certificate for the sake of testing - DSA
+    /*
+     * we generate a self signed certificate for the sake of testing - SM3withSM2
      */
     public void checkSm3WithSm2Creation()
         throws Exception
@@ -1762,7 +1873,7 @@
 
         cert = (X509Certificate)fact.generateCertificate(bIn);
 
-        // System.out.println(cert);
+        cert.getEncoded();
     }
 
     private void checkComparison(byte[] encCert)
@@ -1798,21 +1909,14 @@
         return builder;
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - ECDSA
      */
     public void checkCreation3()
     {
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
@@ -1928,21 +2032,15 @@
 
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - SHA224withECDSA
      */
     private void createECCert(String algorithm, ASN1ObjectIdentifier algOid)
         throws Exception
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
@@ -2155,6 +2253,8 @@
 
         X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
 
+        crl.verify(pair.getPublic(), "BC");
+
         if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
         {
             fail("failed CRL issuer test");
@@ -2356,6 +2456,8 @@
 
         X509CRL readCrl = (X509CRL)cFact.generateCRL(new ByteArrayInputStream(crlHolder.getEncoded()));
 
+        readCrl.verify(pair.getPublic(), "BC");
+
         if (readCrl == null)
         {
             fail("crl not returned!");
@@ -2369,7 +2471,187 @@
         }
     }
 
-    /**
+    public void checkCRLCreation4()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        Date now = new Date();
+        KeyPair pair = kpGen.generateKeyPair();
+        X509v2CRLBuilder crlGen = new JcaX509v2CRLBuilder(new X500Principal("CN=Test CA"), now);
+
+        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
+
+        Vector extOids = new Vector();
+        Vector extValues = new Vector();
+
+        CRLReason crlReason = CRLReason.lookup(CRLReason.privilegeWithdrawn);
+
+        try
+        {
+            extOids.addElement(Extension.reasonCode);
+            extValues.addElement(new Extension(Extension.reasonCode, false, new DEROctetString(crlReason.getEncoded())));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error encoding reason: " + e);
+        }
+
+        Extensions entryExtensions = generateExtensions(extOids, extValues);
+
+        crlGen.addCRLEntry(BigInteger.ONE, now, entryExtensions);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(pair.getPublic()));
+
+        X509CRLHolder crlHolder = crlGen.build(new JcaContentSignerBuilder("SHA256withRSAandMGF1").setProvider(BC).build(pair.getPrivate()));
+
+        X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
+
+        crl.verify(pair.getPublic(), "BC");
+
+        if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
+        {
+            fail("failed CRL issuer test");
+        }
+
+        byte[] authExt = crl.getExtensionValue(Extension.authorityKeyIdentifier.getId());
+
+        if (authExt == null)
+        {
+            fail("failed to find CRL extension");
+        }
+
+        AuthorityKeyIdentifier authId = AuthorityKeyIdentifier.getInstance(ASN1OctetString.getInstance(authExt).getOctets());
+
+        X509CRLEntry entry = crl.getRevokedCertificate(BigInteger.ONE);
+
+        if (entry == null)
+        {
+            fail("failed to find CRL entry");
+        }
+
+        if (!entry.getSerialNumber().equals(BigInteger.ONE))
+        {
+            fail("CRL cert serial number does not match");
+        }
+
+        if (!entry.hasExtensions())
+        {
+            fail("CRL entry extension not found");
+        }
+
+        byte[] ext = entry.getExtensionValue(Extension.reasonCode.getId());
+
+        if (ext != null)
+        {
+            ASN1Enumerated reasonCode = (ASN1Enumerated)fromExtensionValue(ext);
+
+            if (reasonCode.getValue().intValue() != CRLReason.privilegeWithdrawn)
+            {
+                fail("CRL entry reasonCode wrong");
+            }
+        }
+        else
+        {
+            fail("CRL entry reasonCode not found");
+        }
+    }
+
+    public void checkCRLCreation5()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        Date now = new Date();
+        KeyPair pair = kpGen.generateKeyPair();
+        X509v2CRLBuilder crlGen = new JcaX509v2CRLBuilder(new X500Principal("CN=Test CA"), now);
+
+        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
+
+        Vector extOids = new Vector();
+        Vector extValues = new Vector();
+
+        CRLReason crlReason = CRLReason.lookup(CRLReason.privilegeWithdrawn);
+
+        try
+        {
+            extOids.addElement(Extension.reasonCode);
+            extValues.addElement(new Extension(Extension.reasonCode, false, new DEROctetString(crlReason.getEncoded())));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error encoding reason: " + e);
+        }
+
+        Extensions entryExtensions = generateExtensions(extOids, extValues);
+
+        crlGen.addCRLEntry(BigInteger.ONE, now, entryExtensions);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(pair.getPublic()));
+
+        ContentSigner signer = new JcaContentSignerBuilder(
+            "RSAPSS",
+            new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 20, 1))
+                                        .setProvider(BC).build(pair.getPrivate());
+        X509CRLHolder crlHolder = crlGen.build(signer);
+
+        X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
+
+        crl.verify(pair.getPublic(), "BC");
+
+        if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
+        {
+            fail("failed CRL issuer test");
+        }
+
+        byte[] authExt = crl.getExtensionValue(Extension.authorityKeyIdentifier.getId());
+
+        if (authExt == null)
+        {
+            fail("failed to find CRL extension");
+        }
+
+        AuthorityKeyIdentifier authId = AuthorityKeyIdentifier.getInstance(ASN1OctetString.getInstance(authExt).getOctets());
+
+        X509CRLEntry entry = crl.getRevokedCertificate(BigInteger.ONE);
+
+        if (entry == null)
+        {
+            fail("failed to find CRL entry");
+        }
+
+        if (!entry.getSerialNumber().equals(BigInteger.ONE))
+        {
+            fail("CRL cert serial number does not match");
+        }
+
+        if (!entry.hasExtensions())
+        {
+            fail("CRL entry extension not found");
+        }
+
+        byte[] ext = entry.getExtensionValue(Extension.reasonCode.getId());
+
+        if (ext != null)
+        {
+            ASN1Enumerated reasonCode = (ASN1Enumerated)fromExtensionValue(ext);
+
+            if (reasonCode.getValue().intValue() != CRLReason.privilegeWithdrawn)
+            {
+                fail("CRL entry reasonCode wrong");
+            }
+        }
+        else
+        {
+            fail("CRL entry reasonCode not found");
+        }
+    }
+
+    /*
      * we generate a self signed certificate for the sake of testing - GOST3410
      */
     public void checkCreation4()
@@ -2576,7 +2858,10 @@
 
         cert.checkValidity(new Date());
 
-        cert.verify(pubKey, "BCPQC");
+        cert.verify(cert.getPublicKey());
+
+        // check encoded works
+        cert.getEncoded();
 
         if (!areEqual(baseCert.getExtensionValue("2.5.29.15"), cert.getExtensionValue("2.5.29.15")))
         {
@@ -2602,6 +2887,487 @@
         {
             // expected
         }
+
+        // certificate with NewHope key
+        kpGen = KeyPairGenerator.getInstance("NH", "BCPQC");
+
+        kpGen.initialize(1024, new SecureRandom());
+
+        KeyPair nhKp = kpGen.generateKeyPair();
+
+        certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), nhKp.getPublic())
+            .copyAndAddExtension(new ASN1ObjectIdentifier("2.5.29.15"), true, baseCert)
+            .copyAndAddExtension(new ASN1ObjectIdentifier("2.5.29.37"), false, baseCert);
+
+        cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        cert.verify(pubKey);
+
+        isTrue(nhKp.getPublic().equals(cert.getPublicKey()));
+
+        // check encoded works
+        cert.getEncoded();
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - ECGOST3410-2012
+     */
+    public void checkCreation7()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator g = KeyPairGenerator.getInstance("ECGOST3410-2012", BC);
+
+        g.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-512-paramSetA"), new SecureRandom());
+
+        KeyPair p = g.generateKeyPair();
+
+        PrivateKey privKey = p.getPrivate();
+        PublicKey pubKey = p.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("GOST3411-2012-512WITHECGOST3410-2012-512").setProvider(BC).build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - XMSS
+     */
+    public void checkCreation8()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("SHA256withXMSS").setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - XMSS^MT
+     */
+    public void checkCreation9()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(10, 5, XMSSMTParameterSpec.SHAKE256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("SHAKE256withXMSSMT").setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - qTESLA
+     */
+    public void checkCreationQTESLA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpg.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.HEURISTIC_I), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder(pubKey.getAlgorithm()).setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of DSA algorithms
+     */
+    public void checkCreationDSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", "BC");
+
+        kpg.initialize(new DSAParameterSpec(def2048Params.getP(), def2048Params.getQ(), def2048Params.getG()), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHDSA",
+                "DSAWITHSHA1",
+                "SHA224WITHDSA",
+                "SHA256WITHDSA",
+                "SHA384WITHDSA",
+                "SHA512WITHDSA",
+                "SHA3-224WITHDSA",
+                "SHA3-256WITHDSA",
+                "SHA3-384WITHDSA",
+                "SHA3-512WITHDSA"
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                X9ObjectIdentifiers.id_dsa_with_sha1,
+                X9ObjectIdentifiers.id_dsa_with_sha1,
+                NISTObjectIdentifiers.dsa_with_sha224,
+                NISTObjectIdentifiers.dsa_with_sha256,
+                NISTObjectIdentifiers.dsa_with_sha384,
+                NISTObjectIdentifiers.dsa_with_sha512,
+                NISTObjectIdentifiers.id_dsa_with_sha3_224,
+                NISTObjectIdentifiers.id_dsa_with_sha3_256,
+                NISTObjectIdentifiers.id_dsa_with_sha3_384,
+                NISTObjectIdentifiers.id_dsa_with_sha3_512
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of RSA algorithms
+     */
+    public void checkCreationRSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpg.initialize(2048, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHRSA",
+                "SHA1WITHRSAENCRYPTION",
+                "SHA224WITHRSA",
+                "SHA224WITHRSAENCRYPTION",
+                "SHA256WITHRSA",
+                "SHA256WITHRSAENCRYPTION",
+                "SHA384WITHRSA",
+                "SHA384WITHRSAENCRYPTION",
+                "SHA512WITHRSA",
+                "SHA512WITHRSAENCRYPTION",
+                "SHA3-224WITHRSA",
+                "SHA3-224WITHRSAENCRYPTION",
+                "SHA3-256WITHRSA",
+                "SHA3-256WITHRSAENCRYPTION",
+                "SHA3-384WITHRSA",
+                "SHA3-384WITHRSAENCRYPTION",
+                "SHA3-512WITHRSA",
+                "SHA3-512WITHRSAENCRYPTION",
+                "SHA1WITHRSAANDMGF1",
+                "SHA224WITHRSAANDMGF1",
+                "SHA256WITHRSAANDMGF1",
+                "SHA384WITHRSAANDMGF1",
+                "SHA512WITHRSAANDMGF1",
+                "SHA3-224WITHRSAANDMGF1",
+                "SHA3-256WITHRSAANDMGF1",
+                "SHA3-384WITHRSAANDMGF1",
+                "SHA3-512WITHRSAANDMGF1",
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                PKCSObjectIdentifiers.sha1WithRSAEncryption,
+                PKCSObjectIdentifiers.sha1WithRSAEncryption,
+                PKCSObjectIdentifiers.sha224WithRSAEncryption,
+                PKCSObjectIdentifiers.sha224WithRSAEncryption,
+                PKCSObjectIdentifiers.sha256WithRSAEncryption,
+                PKCSObjectIdentifiers.sha256WithRSAEncryption,
+                PKCSObjectIdentifiers.sha384WithRSAEncryption,
+                PKCSObjectIdentifiers.sha384WithRSAEncryption,
+                PKCSObjectIdentifiers.sha512WithRSAEncryption,
+                PKCSObjectIdentifiers.sha512WithRSAEncryption,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("RSAPSS",
+            new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 20, 1))
+            .setProvider("BC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+        
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of ECDSA algorithms
+     */
+    public void checkCreationECDSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
+
+        kpg.initialize(256, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHECDSA",
+                "ECDSAWITHSHA1",
+                "SHA224WITHECDSA",
+                "SHA256WITHECDSA",
+                "SHA384WITHECDSA",
+                "SHA512WITHECDSA",
+                "SHA3-224WITHECDSA",
+                "SHA3-256WITHECDSA",
+                "SHA3-384WITHECDSA",
+                "SHA3-512WITHECDSA"
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                X9ObjectIdentifiers.ecdsa_with_SHA1,
+                X9ObjectIdentifiers.ecdsa_with_SHA1,
+                X9ObjectIdentifiers.ecdsa_with_SHA224,
+                X9ObjectIdentifiers.ecdsa_with_SHA256,
+                X9ObjectIdentifiers.ecdsa_with_SHA384,
+                X9ObjectIdentifiers.ecdsa_with_SHA512,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_224,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_256,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_384,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_512
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+    }
+
+    private void doGenSelfSignedCert(PrivateKey privKey, PublicKey pubKey, String[] algs, ASN1ObjectIdentifier[] oids)
+        throws Exception
+    {
+        X500NameBuilder builder = createStdBuilder();
+
+        for (int i = 0; i != algs.length; i++)
+        {
+            //
+            // create the certificate - version 3
+            //
+            ContentSigner sigGen = new JcaContentSignerBuilder(algs[i]).setProvider("BC").build(privKey);
+            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+            isEquals("oid mismatch", sigGen.getAlgorithmIdentifier().getAlgorithm(), oids[i]);
+
+            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+            cert.checkValidity(new Date());
+
+            //
+            // check verifies in general
+            //
+            cert.verify(pubKey);
+
+            //
+            // check verifies with contained key
+            //
+            cert.verify(cert.getPublicKey());
+
+            ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+            cert = (X509Certificate)fact.generateCertificate(bIn);
+
+//            System.out.println(cert);
+        }
     }
 
     private void testForgedSignature()
@@ -3182,9 +3948,48 @@
         }
     }
 
+    private void checkSerialisation()
+        throws Exception
+    {
+        X509CertificateHolder crtHolder = new X509CertificateHolder(cert1);
+
+        doSerialize(crtHolder);
+
+        X509CRLHolder crlHolder = new X509CRLHolder(crl1);
+
+        doSerialize(crlHolder);
+
+        X509AttributeCertificateHolder attrHolder = new X509AttributeCertificateHolder(AttrCertTest.attrCert);
+
+        doSerialize(attrHolder);
+    }
+
+    private void doSerialize(Serializable encodable)
+        throws Exception
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(encodable);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        Encodable obj = (Encodable)oIn.readObject();
+
+        isEquals(encodable, obj);
+        isEquals(encodable.hashCode(), obj.hashCode());
+    }
+
     public void performTest()
         throws Exception
     {
+        if (Security.getProvider("BCPQC") == null)
+        {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+        
         testDirect();
         testIndirect();
         testIndirect2();
@@ -3215,6 +4020,7 @@
         checkSelfSignedCertificate(18, gost34102001A);
         checkSelfSignedCertificate(19, sha3Cert);
 
+        checkSelfSignedCertificateAndKey(20, gost_2012_cert, "ECGOST3410-2012-256", gost_2012_privateKey);
         checkCRL(1, crl1);
 
         checkCreation1();
@@ -3224,6 +4030,14 @@
         checkCreation5();
 
         checkCreation6();
+        checkCreation7();
+        checkCreation8();
+        checkCreation9();
+
+        checkCreationQTESLA();
+        checkCreationDSA();
+        checkCreationECDSA();
+        checkCreationRSA();
 
         checkSm3WithSm2Creation();
 
@@ -3241,6 +4055,8 @@
         checkCRLCreation1();
         checkCRLCreation2();
         checkCRLCreation3();
+        checkCRLCreation4();
+        checkCRLCreation5();
 
         pemTest();
         pkcs7Test();
@@ -3253,6 +4069,8 @@
         checkCertificate(18, emptyDNCert);
 
         zeroDataTest();
+
+        checkSerialisation();
     }
 
     private Extensions generateExtensions(Vector oids, Vector values)
@@ -3283,7 +4101,7 @@
         String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
-
+        
         runTest(new CertTest());
     }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java
new file mode 100644
index 0000000..6839bdf
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java
@@ -0,0 +1,241 @@
+package org.bouncycastle.cert.test;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerId;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.SignerInformationVerifier;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcContentSignerBuilder;
+import org.bouncycastle.operator.bc.BcECContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+
+public class GOST3410_2012CMSTest
+    extends SimpleTest
+{
+
+    public String getName()
+    {
+        return "GOST3410 2012 CMS TEST";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        if (Security.getProvider("BC").containsKey("KeyFactory.ECGOST3410-2012"))
+        {
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetA", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetB", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetC", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-256-paramSetA", "GOST3411-2012-256WITHECGOST3410-2012-256",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.getId());
+        }
+    }
+
+    public void cmsTest(String keyAlgorithm, String paramName, String signAlgorithm, String digestId)
+    {
+        try
+        {
+            KeyPairGenerator keyPairGenerator =
+                KeyPairGenerator.getInstance(keyAlgorithm, "BC");
+            keyPairGenerator.initialize(new ECNamedCurveGenParameterSpec(paramName), new SecureRandom());
+            KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+            X509CertificateHolder signingCertificate = selfSignedCertificate(keyPair, signAlgorithm);
+
+            // CMS
+            byte[] dataContent = new byte[]{1, 2, 3, 4, 33, 22, 11, 33, 52, 21, 23};
+            CMSTypedData cmsTypedData = new CMSProcessableByteArray(dataContent);
+
+
+            final JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(
+                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
+
+            final ContentSigner contentSigner = new
+                JcaContentSignerBuilder(signAlgorithm).setProvider("BC")
+                .build(keyPair.getPrivate());
+
+            final SignerInfoGenerator signerInfoGenerator = signerInfoGeneratorBuilder.build(contentSigner, signingCertificate);
+
+            CMSSignedDataGenerator cmsSignedDataGenerator = new CMSSignedDataGenerator();
+
+            cmsSignedDataGenerator.addCertificate(signingCertificate);
+            cmsSignedDataGenerator.addSignerInfoGenerator(signerInfoGenerator);
+
+            CMSSignedData cmsSignedData = cmsSignedDataGenerator.generate(cmsTypedData, false);
+            if (cmsSignedData == null)
+            {
+                fail("Cant create CMS");
+            }
+
+            boolean algIdContains = false;
+            for (Iterator it = cmsSignedData.getDigestAlgorithmIDs().iterator(); it.hasNext();)
+            {
+                AlgorithmIdentifier algorithmIdentifier = (AlgorithmIdentifier)it.next();
+                if (algorithmIdentifier.getAlgorithm().getId().equals(digestId))
+                {
+                    algIdContains = true;
+                    break;
+                }
+            }
+            if (!algIdContains)
+            {
+                fail("identifier not valid");
+            }
+            boolean result = verify(cmsSignedData, cmsTypedData);
+            if (!result)
+            {
+                fail("Verification fails ");
+            }
+
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+            fail("fail with exception:", ex);
+        }
+    }
+
+    private boolean verify(CMSSignedData signature, CMSTypedData typedData)
+        throws CertificateException, OperatorCreationException, IOException, CMSException
+    {
+        CMSSignedData signedDataToVerify = new CMSSignedData(typedData, signature.getEncoded());
+        Store certs = signedDataToVerify.getCertificates();
+        SignerInformationStore signers = signedDataToVerify.getSignerInfos();
+        Collection<SignerInformation> c = signers.getSigners();
+        for (Iterator it = c.iterator(); it.hasNext();)
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            SignerId signerId = signer.getSID();
+            Collection certCollection = certs.getMatches(signerId);
+
+            Iterator certIt = certCollection.iterator();
+            Object certificate = certIt.next();
+            SignerInformationVerifier verifier =
+                new JcaSimpleSignerInfoVerifierBuilder()
+                    .setProvider("BC").build((X509CertificateHolder)certificate);
+
+
+            boolean result = signer.verify(verifier);
+            if (result)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private X509CertificateHolder selfSignedCertificate(KeyPair keyPair, String signatureAlgName)
+        throws IOException, OperatorCreationException
+    {
+
+        X500Name name = new X500Name("CN=BB, C=aa");
+        ECPublicKey k = (ECPublicKey)keyPair.getPublic();
+        ECParameterSpec s = k.getParameters();
+        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(
+            k.getQ(),
+            new ECDomainParameters(s.getCurve(), s.getG(), s.getN()));
+
+        ECPrivateKey kk = (ECPrivateKey)keyPair.getPrivate();
+        ECParameterSpec ss = kk.getParameters();
+
+        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(
+            kk.getD(),
+            new ECDomainParameters(ss.getCurve(), ss.getG(), ss.getN()));
+
+        AsymmetricKeyParameter publicKey = ecPublicKeyParameters;
+        AsymmetricKeyParameter privateKey = ecPrivateKeyParameters;
+        X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
+            name,
+            BigInteger.ONE,
+            new Date(),
+            new Date(new Date().getTime() + 364 * 50 * 3600),
+            name,
+            SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+
+        DefaultSignatureAlgorithmIdentifierFinder signatureAlgorithmIdentifierFinder = new DefaultSignatureAlgorithmIdentifierFinder();
+        DefaultDigestAlgorithmIdentifierFinder digestAlgorithmIdentifierFinder = new DefaultDigestAlgorithmIdentifierFinder();
+
+        AlgorithmIdentifier signAlgId = signatureAlgorithmIdentifierFinder.find(signatureAlgName);
+        AlgorithmIdentifier digestAlgId = digestAlgorithmIdentifierFinder.find(signAlgId);
+
+        BcContentSignerBuilder signerBuilder = new BcECContentSignerBuilder(signAlgId, digestAlgId);
+
+        int val = KeyUsage.cRLSign;
+        val = val | KeyUsage.dataEncipherment;
+        val = val | KeyUsage.decipherOnly;
+        val = val | KeyUsage.digitalSignature;
+        val = val | KeyUsage.encipherOnly;
+        val = val | KeyUsage.keyAgreement;
+        val = val | KeyUsage.keyEncipherment;
+        val = val | KeyUsage.nonRepudiation;
+        myCertificateGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(val));
+
+        myCertificateGenerator.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
+
+        myCertificateGenerator.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping));
+
+
+        X509CertificateHolder holder = myCertificateGenerator.build(signerBuilder.build(privateKey));
+
+        return holder;
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+        Test test = new GOST3410_2012CMSTest();
+        TestResult result = test.perform();
+        System.out.println(result);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
index cd3e8af..135716c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
@@ -26,6 +26,7 @@
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
 import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
@@ -265,15 +266,9 @@
     private void createECRequest(String algorithm, ASN1ObjectIdentifier algOid)
         throws Exception
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
@@ -567,15 +562,9 @@
         // elliptic curve openSSL
         KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         g.initialize(ecSpec, new SecureRandom());
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
index 23f70d6..63717f6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
@@ -40,7 +40,7 @@
      * Create a SimplePKIResponse from the passed in bytes.
      *
      * @param responseEncoding BER/DER encoding of the certificate.
-     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     * @throws CMCException in the event of corrupted data, or an incorrect structure.
      */
     public SimplePKIResponse(byte[] responseEncoding)
         throws CMCException
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
index 17731c1..bb251f6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
@@ -1,12 +1,14 @@
 package org.bouncycastle.cms;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -29,9 +31,14 @@
     public static final ASN1ObjectIdentifier  AES192_GCM      = NISTObjectIdentifiers.id_aes192_GCM.intern();
     public static final ASN1ObjectIdentifier  AES256_GCM      = NISTObjectIdentifiers.id_aes256_GCM.intern();
 
+//	public static final ASN1ObjectIdentifier  AES128_CBC_CMAC      = BSIObjectIdentifiers.id_aes128_CBC_CMAC;
+//	public static final ASN1ObjectIdentifier  AES192_CBC_CMAC      = BSIObjectIdentifiers.id_aes192_CBC_CMAC;
+//	public static final ASN1ObjectIdentifier  AES256_CBC_CMAC      = BSIObjectIdentifiers.id_aes256_CBC_CMAC;
+
     public static final ASN1ObjectIdentifier  CAMELLIA128_CBC = NTTObjectIdentifiers.id_camellia128_cbc.intern();
     public static final ASN1ObjectIdentifier  CAMELLIA192_CBC = NTTObjectIdentifiers.id_camellia192_cbc.intern();
     public static final ASN1ObjectIdentifier  CAMELLIA256_CBC = NTTObjectIdentifiers.id_camellia256_cbc.intern();
+    public static final ASN1ObjectIdentifier  GOST28147_GCFB  = CryptoProObjectIdentifiers.gostR28147_gcfb.intern();
     public static final ASN1ObjectIdentifier  SEED_CBC        = KISAObjectIdentifiers.id_seedCBC.intern();
 
     public static final ASN1ObjectIdentifier  DES_EDE3_WRAP   = PKCSObjectIdentifiers.id_alg_CMS3DESwrap.intern();
@@ -43,6 +50,9 @@
     public static final ASN1ObjectIdentifier  CAMELLIA256_WRAP = NTTObjectIdentifiers.id_camellia256_wrap.intern();
     public static final ASN1ObjectIdentifier  SEED_WRAP       = KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.intern();
 
+    public static final ASN1ObjectIdentifier  GOST28147_WRAP  = CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap.intern();
+    public static final ASN1ObjectIdentifier  GOST28147_CRYPTOPRO_WRAP  = CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.intern();
+
     public static final ASN1ObjectIdentifier  ECDH_SHA1KDF    = X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECCDH_SHA1KDF    = X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECMQV_SHA1KDF   = X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme.intern();
@@ -63,6 +73,15 @@
     public static final ASN1ObjectIdentifier  ECCDH_SHA512KDF    = SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECMQV_SHA512KDF   = SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme.intern();
 
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2001    = CryptoProObjectIdentifiers.gostR3410_2001.intern();
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.intern();
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.intern();
+
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF  = BSIObjectIdentifiers.ecka_eg_X963kdf;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA256  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA384  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA512  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512;
+
     public static final ASN1ObjectIdentifier  SHA1 = OIWObjectIdentifiers.idSHA1.intern();
     public static final ASN1ObjectIdentifier  SHA224 = NISTObjectIdentifiers.id_sha224.intern();
     public static final ASN1ObjectIdentifier  SHA256 = NISTObjectIdentifiers.id_sha256.intern();
@@ -70,6 +89,8 @@
     public static final ASN1ObjectIdentifier  SHA512 = NISTObjectIdentifiers.id_sha512.intern();
     public static final ASN1ObjectIdentifier  MD5 = PKCSObjectIdentifiers.md5.intern();
     public static final ASN1ObjectIdentifier  GOST3411 = CryptoProObjectIdentifiers.gostR3411.intern();
+    public static final ASN1ObjectIdentifier  GOST3411_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.intern();
+    public static final ASN1ObjectIdentifier  GOST3411_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.intern();
     public static final ASN1ObjectIdentifier  RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.intern();
     public static final ASN1ObjectIdentifier  RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.intern();
     public static final ASN1ObjectIdentifier  RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256.intern();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
index fd6782d..6a45155 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
@@ -24,11 +24,12 @@
      *
      * @param oid object identifier to map.
      * @param algorithmName algorithm name to use.
+     * @deprecated no longer required.
      */
     public static void setSigningDigestAlgorithmMapping(String oid, String algorithmName)
     {
         ASN1ObjectIdentifier id = new ASN1ObjectIdentifier(oid);
 
-        CMSSignedHelper.INSTANCE.setSigningDigestAlgorithmMapping(id, algorithmName);
+        //CMSSignedHelper.INSTANCE.setSigningDigestAlgorithmMapping(id, algorithmName);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
index 1c79a94..d24608e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
@@ -21,7 +21,7 @@
     public CMSProcessableByteArray(
         byte[]  bytes)
     {
-        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), bytes);
+        this(CMSObjectIdentifiers.data, bytes);
     }
 
     public CMSProcessableByteArray(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
index b1e4527..10b5d55 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
@@ -32,7 +32,7 @@
         File file,
         int  bufSize)
     {
-        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), file, bufSize);
+        this(CMSObjectIdentifiers.data, file, bufSize);
     }
 
     public CMSProcessableFile(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
index 1e09b48..04dbf52 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
@@ -2,6 +2,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -15,6 +16,7 @@
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
  * General class for generating a pkcs7-signature message stream.
@@ -188,14 +190,16 @@
         sigGen.addObject(calculateVersion(eContentType));
         
         ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
-        
+
         //
         // add the precalculated SignerInfo digest algorithms.
         //
         for (Iterator it = _signers.iterator(); it.hasNext();)
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+            AlgorithmIdentifier digAlg = CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID());
+
+            digestAlgs.add(digAlg);
         }
         
         //
@@ -228,6 +232,40 @@
         return new CmsSignedDataOutputStream(sigStream, eContentType, sGen, sigGen, eiGen);
     }
 
+    /**
+     * Return a list of the current Digest AlgorithmIdentifiers applying to the next signature.
+     *
+     * @return a list of the Digest AlgorithmIdentifiers
+     */
+    public List<AlgorithmIdentifier> getDigestAlgorithms()
+    {
+        List  digestAlorithms = new ArrayList();
+
+        //
+        // add the precalculated SignerInfo digest algorithms.
+        //
+        for (Iterator it = _signers.iterator(); it.hasNext();)
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            AlgorithmIdentifier digAlg = CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID());
+
+            digestAlorithms.add(digAlg);
+        }
+
+        //
+        // add the new digests
+        //
+
+        for (Iterator it = signerGens.iterator(); it.hasNext();)
+        {
+            SignerInfoGenerator signerGen = (SignerInfoGenerator)it.next();
+
+            digestAlorithms.add(signerGen.getDigestAlgorithm());
+        }
+
+       return digestAlorithms;
+    }
+
     // RFC3852, section 5.1:
     // IF ((certificates is present) AND
     //    (any certificates with a type of other are present)) OR
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
index 9fe6779..37a697f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -17,6 +17,7 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -50,6 +51,8 @@
     public static final String  ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS.getId();
     public static final String  ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94.getId();
     public static final String  ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001.getId();
+    public static final String  ENCRYPTION_ECGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256.getId();
+    public static final String  ENCRYPTION_ECGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512.getId();
 
     private static final String  ENCRYPTION_ECDSA_WITH_SHA1 = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
     private static final String  ENCRYPTION_ECDSA_WITH_SHA224 = X9ObjectIdentifiers.ecdsa_with_SHA224.getId();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
index 2f98e69..1933fd4 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
@@ -19,6 +19,7 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AttributeCertificate;
@@ -37,81 +38,74 @@
     static final CMSSignedHelper INSTANCE = new CMSSignedHelper();
 
     private static final Map     encryptionAlgs = new HashMap();
-    private static final Map     digestAlgs = new HashMap();
-    private static final Map     digestAliases = new HashMap();
 
-    private static void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
+    private static void addEntries(ASN1ObjectIdentifier alias, String encryption)
     {
-        digestAlgs.put(alias.getId(), digest);
         encryptionAlgs.put(alias.getId(), encryption);
     }
 
     static
     {
-        addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
-        addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
-        addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
-        addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
-        addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
-        addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
-        addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
-        addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
-        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
-        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha224, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha512,  "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_224, "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_256, "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_384,  "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_512,  "DSA");
+        addEntries(OIWObjectIdentifiers.dsaWithSHA1,  "DSA");
+        addEntries(OIWObjectIdentifiers.md4WithRSA, "RSA");
+        addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "RSA");
+        addEntries(OIWObjectIdentifiers.md5WithRSA,  "RSA");
+        addEntries(OIWObjectIdentifiers.sha1WithRSA,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "RSA");
+        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,  "RSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_224,  "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256,  "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1,  "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1,  "RSAandMGF1");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "RSAandMGF1");
 
-        encryptionAlgs.put(X9ObjectIdentifiers.id_dsa.getId(), "DSA");
-        encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption.getId(), "RSA");
-        encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
-        encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa.getId(), "RSA");
-        encryptionAlgs.put(CMSSignedDataGenerator.ENCRYPTION_RSA_PSS, "RSAandMGF1");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94.getId(), "GOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001.getId(), "ECGOST3410");
-        encryptionAlgs.put("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
-        encryptionAlgs.put("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001.getId(), "ECGOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94.getId(), "GOST3410");
-
-        digestAlgs.put(PKCSObjectIdentifiers.md2.getId(), "MD2");
-        digestAlgs.put(PKCSObjectIdentifiers.md4.getId(), "MD4");
-        digestAlgs.put(PKCSObjectIdentifiers.md5.getId(), "MD5");
-        digestAlgs.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha224.getId(), "SHA224");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha256.getId(), "SHA256");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha384.getId(), "SHA384");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha512.getId(), "SHA512");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), "RIPEMD128");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256");
-        digestAlgs.put(CryptoProObjectIdentifiers.gostR3411.getId(),  "GOST3411");
-        digestAlgs.put("1.3.6.1.4.1.5849.1.2.1",  "GOST3411");
-
-        digestAliases.put("SHA1", new String[] { "SHA-1" });
-        digestAliases.put("SHA224", new String[] { "SHA-224" });
-        digestAliases.put("SHA256", new String[] { "SHA-256" });
-        digestAliases.put("SHA384", new String[] { "SHA-384" });
-        digestAliases.put("SHA512", new String[] { "SHA-512" });
+        addEntries(X9ObjectIdentifiers.id_dsa, "DSA");
+        addEntries(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+        addEntries(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
+        addEntries(X509ObjectIdentifiers.id_ea_rsa, "RSA");
+        addEntries(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSAandMGF1");
+        addEntries(CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
+        addEntries(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+        addEntries(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
+        addEntries(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012-256");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012-512");
+        addEntries(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
+        addEntries(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
     }
 
 
@@ -145,12 +139,7 @@
 
     void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
     {
-        encryptionAlgs.put(oid.getId(), algorithmName);
-    }
-
-    void setSigningDigestAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
-    {
-        digestAlgs.put(oid.getId(), algorithmName);
+        addEntries(oid, algorithmName);
     }
 
     Store getCertificates(ASN1Set certSet)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
index f63bf3c..9204b5b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -24,11 +24,15 @@
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.ocsp.OCSPResponse;
 import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
@@ -42,6 +46,9 @@
 class CMSUtils
 {
     private static final Set<String> des = new HashSet<String>();
+    private static final Set mqvAlgs = new HashSet();
+    private static final Set ecAlgs = new HashSet();
+    private static final Set gostAlgs = new HashSet();
 
     static
     {
@@ -51,6 +58,47 @@
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
+
+        mqvAlgs.add(X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme);
+
+        ecAlgs.add(X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme);
+        ecAlgs.add(X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme);
+
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+    }
+
+    static boolean isMQV(ASN1ObjectIdentifier algorithm)
+    {
+        return mqvAlgs.contains(algorithm);
+    }
+
+    static boolean isEC(ASN1ObjectIdentifier algorithm)
+    {
+        return ecAlgs.contains(algorithm);
+    }
+
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
+    static boolean isRFC2631(ASN1ObjectIdentifier algorithm)
+    {
+        return algorithm.equals(PKCSObjectIdentifiers.id_alg_ESDH) || algorithm.equals(PKCSObjectIdentifiers.id_alg_SSDH);
     }
 
     static boolean isDES(String algorithmID)
@@ -96,7 +144,7 @@
     {
         // enforce some limit checking
         return readContentInfo(new ASN1InputStream(input));
-    } 
+    }
 
     static List getCertificatesFromStore(Store certStore)
         throws CMSException
@@ -105,7 +153,7 @@
 
         try
         {
-            for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 X509CertificateHolder c = (X509CertificateHolder)it.next();
 
@@ -127,7 +175,7 @@
 
         try
         {
-            for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 X509AttributeCertificateHolder attrCert = (X509AttributeCertificateHolder)it.next();
 
@@ -150,7 +198,7 @@
 
         try
         {
-            for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 Object rev = it.next();
 
@@ -199,7 +247,7 @@
     {
         List others = new ArrayList();
 
-        for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext();)
+        for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext(); )
         {
             ASN1Encodable info = (ASN1Encodable)it.next();
             OtherRevocationInfoFormat infoFormat = new OtherRevocationInfoFormat(otherRevocationInfoFormat, info);
@@ -216,7 +264,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
 
-        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
         {
             v.add((ASN1Encodable)it.next());
         }
@@ -228,7 +276,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
 
-        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
         {
             v.add((ASN1Encodable)it.next());
         }
@@ -237,7 +285,8 @@
     }
 
     static OutputStream createBEROctetOutputStream(OutputStream s,
-            int tagNo, boolean isExplicit, int bufferSize) throws IOException
+                                                   int tagNo, boolean isExplicit, int bufferSize)
+        throws IOException
     {
         BEROctetStringGenerator octGen = new BEROctetStringGenerator(s, tagNo, isExplicit);
 
@@ -278,7 +327,7 @@
     }
 
     public static byte[] streamToByteArray(
-        InputStream in) 
+        InputStream in)
         throws IOException
     {
         return Streams.readAll(in);
@@ -286,7 +335,7 @@
 
     public static byte[] streamToByteArray(
         InputStream in,
-        int         limit)
+        int limit)
         throws IOException
     {
         return Streams.readAllLimited(in, limit);
@@ -322,10 +371,10 @@
     }
 
     static OutputStream getSafeTeeOutputStream(OutputStream s1,
-            OutputStream s2)
+                                               OutputStream s2)
     {
         return s1 == null ? getSafeOutputStream(s2)
-                : s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
-                        s1, s2);
+            : s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
+            s1, s2);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
index 20e1cb5..db45ec4 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
@@ -7,9 +7,11 @@
 import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
@@ -33,6 +35,18 @@
         addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
         addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
         addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_224, "SHA3-224", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_256, "SHA3-256", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_384, "SHA3-384", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_512, "SHA3-512", "DSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512", "RSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, "SHA3-224", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, "SHA3-256", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "SHA3-384", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, "SHA3-512", "ECDSA");
         addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
         addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
         addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
@@ -82,8 +96,13 @@
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
         encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
         encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012-256");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012-512");
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
+        encryptionAlgs.put(GMObjectIdentifiers.sm2sign_with_sm3, "SM2");
 
         digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
         digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
@@ -93,11 +112,18 @@
         digestAlgs.put(NISTObjectIdentifiers.id_sha256, "SHA256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha384, "SHA384");
         digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
         digestAlgs.put(CryptoProObjectIdentifiers.gostR3411,  "GOST3411");
         digestAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.2.1"),  "GOST3411");
+        digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256,  "GOST3411-2012-256");
+        digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,  "GOST3411-2012-512");
+        digestAlgs.put(GMObjectIdentifiers.sm3,  "SM3");
     }
 
     /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
index 23b9680..8ea8089 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
@@ -8,6 +8,8 @@
 import org.bouncycastle.asn1.cms.OriginatorIdentifierOrKey;
 import org.bouncycastle.asn1.cms.OriginatorPublicKey;
 import org.bouncycastle.asn1.cms.RecipientInfo;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789KeyWrapParameters;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -31,13 +33,17 @@
         throws CMSException
     {
         OriginatorIdentifierOrKey originator = new OriginatorIdentifierOrKey(
-                createOriginatorPublicKey(originatorKeyInfo));
+            createOriginatorPublicKey(originatorKeyInfo));
 
         AlgorithmIdentifier keyEncAlg;
         if (CMSUtils.isDES(keyEncryptionOID.getId()) || keyEncryptionOID.equals(PKCSObjectIdentifiers.id_alg_CMSRC2wrap))
         {
             keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID, DERNull.INSTANCE);
         }
+        else if (CMSUtils.isGOST(keyAgreementOID))
+        {
+            keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID, new Gost2814789KeyWrapParameters(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet));
+        }
         else
         {
             keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID);
@@ -51,8 +57,7 @@
         if (userKeyingMaterial != null)
         {
             return new RecipientInfo(new KeyAgreeRecipientInfo(originator, new DEROctetString(userKeyingMaterial),
-                    keyAgreeAlg, recipients));
-
+                keyAgreeAlg, recipients));
         }
         else
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
index ac6dd0f..40edbfc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
@@ -111,6 +111,8 @@
         this.signature = info.getEncryptedDigest().getOctets();
         this.content = baseInfo.content;
         this.resultDigest = baseInfo.resultDigest;
+        this.signedAttributeValues = baseInfo.signedAttributeValues;
+        this.unsignedAttributeValues = baseInfo.unsignedAttributeValues;
     }
 
     public boolean isCounterSignature()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
index a12c66b..6395ac0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
@@ -9,11 +9,9 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
-import org.bouncycastle.crypto.StreamCipher;
-import org.bouncycastle.crypto.io.CipherOutputStream;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.CipherFactory;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OutputEncryptor;
 import org.bouncycastle.util.Integers;
@@ -94,9 +92,9 @@
 
             encKey = new KeyParameter(keyGen.generateKey());
 
-            algorithmIdentifier = helper.generateAlgorithmIdentifier(encryptionOID, encKey, random);
+            algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random);
 
-            cipher = helper.createContentCipher(true, encKey, algorithmIdentifier);
+            cipher = EnvelopedDataHelper.createContentCipher(true, encKey, algorithmIdentifier);
         }
 
         public AlgorithmIdentifier getAlgorithmIdentifier()
@@ -106,14 +104,7 @@
 
         public OutputStream getOutputStream(OutputStream dOut)
         {
-            if (cipher instanceof BufferedBlockCipher)
-            {
-                return new CipherOutputStream(dOut, (BufferedBlockCipher)cipher);
-            }
-            else
-            {
-                return new CipherOutputStream(dOut, (StreamCipher)cipher);
-            }
+            return CipherFactory.createOutputStream(dOut, cipher);
         }
 
         public GenericKey getKey()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
index b571b9a..92cfd16 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
@@ -18,6 +18,6 @@
     public BcRSAKeyTransRecipientInfoGenerator(X509CertificateHolder recipientCert)
         throws IOException
     {
-        super(recipientCert, new BcRSAAsymmetricKeyWrapper(recipientCert.getSubjectPublicKeyInfo().getAlgorithmId(), recipientCert.getSubjectPublicKeyInfo()));
+        super(recipientCert, new BcRSAAsymmetricKeyWrapper(recipientCert.getSubjectPublicKeyInfo().getAlgorithm(), recipientCert.getSubjectPublicKeyInfo()));
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
index d2c3996..3eebc95 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
@@ -5,30 +5,16 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.bouncycastle.asn1.ASN1Null;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
-import org.bouncycastle.asn1.misc.CAST5CBCParameters;
-import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
-import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.ExtendedDigest;
-import org.bouncycastle.crypto.KeyGenerationParameters;
-import org.bouncycastle.crypto.StreamCipher;
 import org.bouncycastle.crypto.Wrapper;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.crypto.digests.SHA224Digest;
@@ -36,20 +22,14 @@
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.engines.CAST5Engine;
 import org.bouncycastle.crypto.engines.DESEngine;
 import org.bouncycastle.crypto.engines.DESedeEngine;
 import org.bouncycastle.crypto.engines.RC2Engine;
-import org.bouncycastle.crypto.engines.RC4Engine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
-import org.bouncycastle.crypto.generators.DESKeyGenerator;
-import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.paddings.PKCS7Padding;
-import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
 import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.crypto.util.AlgorithmIdentifierFactory;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.crypto.util.CipherKeyGeneratorFactory;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.bc.BcDigestProvider;
 
@@ -117,101 +97,16 @@
         MAC_ALG_NAMES.put(CMSAlgorithm.RC2_CBC, "RC2Mac");
     }
 
-    private static final short[] rc2Table = {
-        0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
-        0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
-        0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
-        0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
-        0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
-        0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
-        0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
-        0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
-        0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
-        0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
-        0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
-        0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
-        0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
-        0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
-        0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
-        0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
-    };
-
-    private static final short[] rc2Ekb = {
-        0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
-        0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
-        0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
-        0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
-        0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
-        0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
-        0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
-        0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
-        0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
-        0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
-        0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
-        0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
-        0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
-        0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
-        0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
-        0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd
-    };
-
     EnvelopedDataHelper()
     {
     }
 
-    String getBaseCipherName(ASN1ObjectIdentifier algorithm)
-    {
-        String name = (String)BASE_CIPHER_NAMES.get(algorithm);
-
-        if (name == null)
-        {
-            return algorithm.getId();
-        }
-
-        return name;
-    }
-
     static ExtendedDigest getPRF(AlgorithmIdentifier algID)
         throws OperatorCreationException
     {
         return ((BcDigestProvider)prfs.get(algID.getAlgorithm())).get(null);
     }
 
-    static BufferedBlockCipher createCipher(ASN1ObjectIdentifier algorithm)
-        throws CMSException
-    {
-        BlockCipher cipher;
-
-        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm)
-            || NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm)
-            || NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new AESEngine());
-        }
-        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new DESedeEngine());
-        }
-        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new DESEngine());
-        }
-        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new RC2Engine());
-        }
-        else if (MiscObjectIdentifiers.cast5CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new CAST5Engine());
-        }
-        else
-        {
-            throw new CMSException("cannot recognise cipher: " + algorithm);
-        }
-
-        return new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
-    }
-
     static Wrapper createRFC3211Wrapper(ASN1ObjectIdentifier algorithm)
         throws CMSException
     {
@@ -242,201 +137,39 @@
     static Object createContentCipher(boolean forEncryption, CipherParameters encKey, AlgorithmIdentifier encryptionAlgID)
         throws CMSException
     {
-        ASN1ObjectIdentifier encAlg = encryptionAlgID.getAlgorithm();
-
-        if (encAlg.equals(PKCSObjectIdentifiers.rc4))
+        try
         {
-            StreamCipher cipher = new RC4Engine();
-
-            cipher.init(forEncryption, encKey);
-
-            return cipher;
+            return CipherFactory.createContentCipher(forEncryption, encKey, encryptionAlgID);
         }
-        else
+        catch (IllegalArgumentException e)
         {
-            BufferedBlockCipher cipher = createCipher(encryptionAlgID.getAlgorithm());
-            ASN1Primitive sParams = encryptionAlgID.getParameters().toASN1Primitive();
-
-            if (sParams != null && !(sParams instanceof ASN1Null))
-            {
-                if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
-                    || encAlg.equals(CMSAlgorithm.IDEA_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES128_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES192_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES256_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA128_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA192_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA256_CBC)
-                    || encAlg.equals(CMSAlgorithm.SEED_CBC)
-                    || encAlg.equals(OIWObjectIdentifiers.desCBC))
-                {
-                    cipher.init(forEncryption, new ParametersWithIV(encKey,
-                        ASN1OctetString.getInstance(sParams).getOctets()));
-                }
-                else if (encAlg.equals(CMSAlgorithm.CAST5_CBC))
-                {
-                    CAST5CBCParameters cbcParams = CAST5CBCParameters.getInstance(sParams);
-
-                    cipher.init(forEncryption, new ParametersWithIV(encKey, cbcParams.getIV()));
-                }
-                else if (encAlg.equals(CMSAlgorithm.RC2_CBC))
-                {
-                    RC2CBCParameter cbcParams = RC2CBCParameter.getInstance(sParams);
-
-                    cipher.init(forEncryption, new ParametersWithIV(new RC2Parameters(((KeyParameter)encKey).getKey(), rc2Ekb[cbcParams.getRC2ParameterVersion().intValue()]), cbcParams.getIV()));
-                }
-                else
-                {
-                    throw new CMSException("cannot match parameters");
-                }
-            }
-            else
-            {
-                if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
-                    || encAlg.equals(CMSAlgorithm.IDEA_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAST5_CBC))
-                {
-                    cipher.init(forEncryption, new ParametersWithIV(encKey, new byte[8]));
-                }
-                else
-                {
-                    cipher.init(forEncryption, encKey);
-                }
-            }
-
-            return cipher;
+            throw new CMSException(e.getMessage(), e);
         }
     }
 
-    AlgorithmIdentifier generateAlgorithmIdentifier(ASN1ObjectIdentifier encryptionOID, CipherParameters encKey, SecureRandom random)
+    AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random)
         throws CMSException
     {
-        if (encryptionOID.equals(CMSAlgorithm.AES128_CBC)
-                || encryptionOID.equals(CMSAlgorithm.AES192_CBC)
-                || encryptionOID.equals(CMSAlgorithm.AES256_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA128_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA192_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA256_CBC)
-                || encryptionOID.equals(CMSAlgorithm.SEED_CBC))
+        try
         {
-            byte[] iv = new byte[16];
-
-            random.nextBytes(iv);
-
-            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
+            return AlgorithmIdentifierFactory.generateEncryptionAlgID(encryptionOID, encKey.getKey().length * 8, random);
         }
-        else if (encryptionOID.equals(CMSAlgorithm.DES_EDE3_CBC)
-                || encryptionOID.equals(CMSAlgorithm.IDEA_CBC)
-                || encryptionOID.equals(OIWObjectIdentifiers.desCBC))
+        catch (IllegalArgumentException e)
         {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
-        }
-        else if (encryptionOID.equals(CMSAlgorithm.CAST5_CBC))
-        {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            CAST5CBCParameters cbcParams = new CAST5CBCParameters(iv, ((KeyParameter)encKey).getKey().length * 8);
-
-            return new AlgorithmIdentifier(encryptionOID, cbcParams);
-        }
-        else if (encryptionOID.equals(PKCSObjectIdentifiers.rc4))
-        {
-            return new AlgorithmIdentifier(encryptionOID, DERNull.INSTANCE);
-        }
-        else if (encryptionOID.equals(PKCSObjectIdentifiers.RC2_CBC))
-        {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            RC2CBCParameter cbcParams = new RC2CBCParameter(rc2Table[128], iv);
-
-            return new AlgorithmIdentifier(encryptionOID, cbcParams);
-        }
-        else
-        {
-            throw new CMSException("unable to match algorithm");
+            throw new CMSException(e.getMessage(), e);
         }
     }
 
     CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRandom random)
         throws CMSException
     {
-        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm))
+        try
         {
-            return createCipherKeyGenerator(random, 128);
+            return CipherKeyGeneratorFactory.createKeyGenerator(algorithm, random);
         }
-        else if (NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm))
+        catch (IllegalArgumentException e)
         {
-            return createCipherKeyGenerator(random, 192);
+            throw new CMSException(e.getMessage(), e);
         }
-        else if (NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 256);
-        }
-        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
-        {
-            DESedeKeyGenerator keyGen = new DESedeKeyGenerator();
-
-            keyGen.init(new KeyGenerationParameters(random, 192));
-
-            return keyGen;
-        }
-        else if (NTTObjectIdentifiers.id_camellia128_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (NTTObjectIdentifiers.id_camellia192_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 192);
-        }
-        else if (NTTObjectIdentifiers.id_camellia256_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 256);
-        }
-        else if (KISAObjectIdentifiers.id_seedCBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (CMSAlgorithm.CAST5_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
-        {
-            DESKeyGenerator keyGen = new DESKeyGenerator();
-
-            keyGen.init(new KeyGenerationParameters(random, 64));
-
-            return keyGen;
-        }
-        else if (PKCSObjectIdentifiers.rc4.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else
-        {
-            throw new CMSException("cannot recognise cipher: " + algorithm);
-        }
-
-    }
-
-    private CipherKeyGenerator createCipherKeyGenerator(SecureRandom random, int keySize)
-    {
-        CipherKeyGenerator keyGen = new CipherKeyGenerator();
-
-        keyGen.init(new KeyGenerationParameters(random, keySize));
-
-        return keyGen;
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
index 20f9ebb..df62fd6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
@@ -12,7 +12,9 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x509.Certificate;
 import org.bouncycastle.asn1.x509.Extension;
@@ -24,6 +26,7 @@
 {
     private static final Set mqvAlgs = new HashSet();
     private static final Set ecAlgs = new HashSet();
+    private static final Set gostAlgs = new HashSet();
 
     static
     {
@@ -43,6 +46,13 @@
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme);
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme);
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme);
+
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512);
     }
 
     static boolean isMQV(ASN1ObjectIdentifier algorithm)
@@ -55,6 +65,11 @@
         return ecAlgs.contains(algorithm);
     }
 
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
     static boolean isRFC2631(ASN1ObjectIdentifier algorithm)
     {
         return algorithm.equals(PKCSObjectIdentifiers.id_alg_ESDH) || algorithm.equals(PKCSObjectIdentifiers.id_alg_SSDH);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
index 8cef68d..b2ddf01 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
@@ -35,6 +35,7 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
@@ -74,6 +75,7 @@
         BASE_CIPHER_NAMES.put(CMSAlgorithm.CAMELLIA256_CBC, "Camellia");
         BASE_CIPHER_NAMES.put(CMSAlgorithm.SEED_CBC, "SEED");
         BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.rc4, "RC4");
+        BASE_CIPHER_NAMES.put(CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
 
         CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_CBC,  "DES/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.RC2_CBC,  "RC2/CBC/PKCS5Padding");
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
index 0de417a..7cb6254 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
@@ -6,6 +6,7 @@
 import java.security.cert.X509Certificate;
 
 import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cms.CMSAttributeTableGenerator;
 import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
@@ -111,6 +112,14 @@
         return this;
     }
 
+    public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, X509CertificateHolder certificate)
+        throws OperatorCreationException
+    {
+        ContentSigner contentSigner = helper.createContentSigner(algorithmName, privateKey);
+
+        return configureAndBuild().build(contentSigner, certificate);
+    }
+
     public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, X509Certificate certificate)
         throws OperatorCreationException, CertificateEncodingException
     {
@@ -120,7 +129,7 @@
     }
 
     public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, byte[] keyIdentifier)
-        throws OperatorCreationException, CertificateEncodingException
+        throws OperatorCreationException
     {
         ContentSigner contentSigner = helper.createContentSigner(algorithmName, privateKey);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
index 01c5791..147c6a1 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
@@ -76,7 +76,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Recipient.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
index 14217d2..f46f05d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
@@ -25,16 +25,21 @@
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.cms.ecc.ECCCMSSharedInfo;
 import org.bouncycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.Gost2814789KeyWrapParameters;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.KeyAgreeRecipient;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
 import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.SecretKeySizeProvider;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 public abstract class JceKeyAgreeRecipient
@@ -172,6 +177,13 @@
                     userKeyingMaterialSpec = new UserKeyingMaterialSpec(userKeyingMaterial.getOctets());
                 }
             }
+            else if (CMSUtils.isGOST(keyEncAlg.getAlgorithm()))
+            {
+                if (userKeyingMaterial != null)
+                {
+                    userKeyingMaterialSpec = new UserKeyingMaterialSpec(userKeyingMaterial.getOctets());
+                }
+            }
             else
             {
                 throw new CMSException("Unknown key agreement algorithm: " + keyEncAlg.getAlgorithm());
@@ -210,6 +222,19 @@
                 SecretKey agreedWrapKey = calculateAgreedWrapKey(keyEncryptionAlgorithm, wrapAlg,
                     senderPublicKey, userKeyingMaterial, recipientKey, ecc_cms_Generator);
 
+                if (wrapAlg.getAlgorithm().equals(CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap)
+                    || wrapAlg.getAlgorithm().equals(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap))
+                {
+                    Gost2814789EncryptedKey encKey = Gost2814789EncryptedKey.getInstance(encryptedContentEncryptionKey);
+                    Gost2814789KeyWrapParameters wrapParams = Gost2814789KeyWrapParameters.getInstance(wrapAlg.getParameters());
+             
+                    Cipher keyCipher = helper.createCipher(wrapAlg.getAlgorithm());
+
+                    keyCipher.init(Cipher.UNWRAP_MODE, agreedWrapKey, new GOST28147WrapParameterSpec(wrapParams.getEncryptionParamSet(), userKeyingMaterial.getOctets()));
+
+                    return keyCipher.unwrap(Arrays.concatenate(encKey.getEncryptedKey(), encKey.getMacKey()), helper.getBaseCipherName(contentEncryptionAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
+                }
+
                 return unwrapSessionKey(wrapAlg.getAlgorithm(), agreedWrapKey, contentEncryptionAlgorithm.getAlgorithm(), encryptedContentEncryptionKey);
             }
             catch (InvalidKeyException e)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
index ec9bd10..5e14f1b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
@@ -20,6 +20,7 @@
 import javax.crypto.SecretKey;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
@@ -30,11 +31,14 @@
 import org.bouncycastle.asn1.cms.RecipientEncryptedKey;
 import org.bouncycastle.asn1.cms.RecipientKeyIdentifier;
 import org.bouncycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.KeyAgreeRecipientInfoGenerator;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
 import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
@@ -149,6 +153,7 @@
             try
             {
                 AlgorithmParameterSpec agreementParamSpec;
+                ASN1ObjectIdentifier keyEncAlg = keyEncryptionAlgorithm.getAlgorithm();
 
                 if (CMSUtils.isMQV(keyAgreementOID))
                 {
@@ -156,7 +161,7 @@
                 }
                 else if (CMSUtils.isEC(keyAgreementOID))
                 {
-                    byte[] ukmKeyingMaterial = ecc_cms_Generator.generateKDFMaterial(keyEncryptionAlgorithm, keySizeProvider.getKeySize(keyEncryptionAlgorithm.getAlgorithm()), userKeyingMaterial);
+                    byte[] ukmKeyingMaterial = ecc_cms_Generator.generateKDFMaterial(keyEncryptionAlgorithm, keySizeProvider.getKeySize(keyEncAlg), userKeyingMaterial);
 
                     agreementParamSpec = new UserKeyingMaterialSpec(ukmKeyingMaterial);
                 }
@@ -175,6 +180,17 @@
                         agreementParamSpec = null;
                     }
                 }
+                else if (CMSUtils.isGOST(keyAgreementOID))
+                {
+                    if (userKeyingMaterial != null)
+                    {
+                        agreementParamSpec = new UserKeyingMaterialSpec(userKeyingMaterial);
+                    }
+                    else
+                    {
+                        throw new CMSException("User keying material must be set for static keys.");
+                    }
+                }
                 else
                 {
                     throw new CMSException("Unknown key agreement algorithm: " + keyAgreementOID);
@@ -185,22 +201,43 @@
                 keyAgreement.init(senderPrivateKey, agreementParamSpec, random);
                 keyAgreement.doPhase(recipientPublicKey, true);
 
-                SecretKey keyEncryptionKey = keyAgreement.generateSecret(keyEncryptionAlgorithm.getAlgorithm().getId());
+                SecretKey keyEncryptionKey = keyAgreement.generateSecret(keyEncAlg.getId());
 
                 // Wrap the content encryption key with the agreement key
-                Cipher keyEncryptionCipher = helper.createCipher(keyEncryptionAlgorithm.getAlgorithm());
+                Cipher keyEncryptionCipher = helper.createCipher(keyEncAlg);
+                ASN1OctetString encryptedKey;
 
-                keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);
+                if (keyEncAlg.equals(CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap)
+                    || keyEncAlg.equals(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap))
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, new GOST28147WrapParameterSpec(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, userKeyingMaterial));
 
-                byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
+                    byte[] encKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
 
-                ASN1OctetString encryptedKey = new DEROctetString(encryptedKeyBytes);
+                    Gost2814789EncryptedKey encKey = new Gost2814789EncryptedKey(
+                        Arrays.copyOfRange(encKeyBytes, 0, encKeyBytes.length - 4),
+                        Arrays.copyOfRange(encKeyBytes, encKeyBytes.length - 4, encKeyBytes.length));
+
+                    encryptedKey = new DEROctetString(encKey.getEncoded(ASN1Encoding.DER));
+                }
+                else
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);
+
+                    byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
+
+                    encryptedKey = new DEROctetString(encryptedKeyBytes);
+                }
 
                 recipientEncryptedKeys.add(new RecipientEncryptedKey(karId, encryptedKey));
             }
             catch (GeneralSecurityException e)
             {
-                throw new CMSException("Cannot perform agreement step: " + e.getMessage(), e);
+                throw new CMSException("cannot perform agreement step: " + e.getMessage(), e);
+            }
+            catch (IOException e)
+            {
+                throw new CMSException("unable to encode wrapped key: " + e.getMessage(), e);
             }
         }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
index 61f32bb..a152b95 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
@@ -1,18 +1,32 @@
 package org.bouncycastle.cms.jcajce;
 
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.GostR3410KeyTransport;
+import org.bouncycastle.asn1.cryptopro.GostR3410TransportParameters;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.KeyTransRecipient;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.OperatorException;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper;
+import org.bouncycastle.util.Arrays;
 
 public abstract class JceKeyTransRecipient
     implements KeyTransRecipient
@@ -63,12 +77,12 @@
      * the standard lookup table won't work. Use this method to establish a specific mapping from an
      * algorithm identifier to a specific algorithm.
      * <p>
-     *     For example:
+     * For example:
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
-     * @param algorithm  OID of algorithm in recipient.
+     *
+     * @param algorithm     OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Recipient.
      */
@@ -110,7 +124,7 @@
 
     /**
      * Set the provider to use for content processing.  If providerName is null a "no provider" search will be
-     *  used to satisfy getInstance calls.
+     * used to satisfy getInstance calls.
      *
      * @param providerName the name of the provider to use.
      * @return this recipient.
@@ -128,6 +142,7 @@
      * This setting will not have any affect if the encryption algorithm in the recipient does not specify a particular key size, or
      * if the unwrapper is a HSM and the byte encoding of the unwrapped secret key is not available.
      * </p>
+     *
      * @param doValidate true if unwrapped key's should be validated against the content encryption algorithm, false otherwise.
      * @return this recipient.
      */
@@ -141,32 +156,68 @@
     protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedEncryptionKey)
         throws CMSException
     {
-        JceAsymmetricKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey).setMustProduceEncodableUnwrappedKey(unwrappedKeyMustBeEncodable);
-
-        if (!extraMappings.isEmpty())
+        if (CMSUtils.isGOST(keyEncryptionAlgorithm.getAlgorithm()))
         {
-            for (Iterator it = extraMappings.keySet().iterator(); it.hasNext();)
+            try
             {
-                ASN1ObjectIdentifier algorithm = (ASN1ObjectIdentifier)it.next();
+                GostR3410KeyTransport transport = GostR3410KeyTransport.getInstance(encryptedEncryptionKey);
 
-                unwrapper.setAlgorithmMapping(algorithm, (String)extraMappings.get(algorithm));
+                GostR3410TransportParameters transParams = transport.getTransportParameters();
+
+                KeyFactory keyFactory = helper.createKeyFactory(keyEncryptionAlgorithm.getAlgorithm());
+
+                PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(transParams.getEphemeralPublicKey().getEncoded()));
+
+                KeyAgreement agreement = helper.createKeyAgreement(keyEncryptionAlgorithm.getAlgorithm());
+
+                agreement.init(recipientKey, new UserKeyingMaterialSpec(transParams.getUkm()));
+
+                agreement.doPhase(pubKey, true);
+
+                SecretKey key = agreement.generateSecret(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.getId());
+
+                Cipher keyCipher = helper.createCipher(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap);
+
+                keyCipher.init(Cipher.UNWRAP_MODE, key, new GOST28147WrapParameterSpec(transParams.getEncryptionParamSet(), transParams.getUkm()));
+
+                Gost2814789EncryptedKey encKey = transport.getSessionEncryptedKey();
+
+                return keyCipher.unwrap(Arrays.concatenate(encKey.getEncryptedKey(), encKey.getMacKey()), helper.getBaseCipherName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
+            }
+            catch (Exception e)
+            {
+                throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
             }
         }
-
-        try
+        else
         {
-            Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+            JceAsymmetricKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey).setMustProduceEncodableUnwrappedKey(unwrappedKeyMustBeEncodable);
 
-            if (validateKeySize)
+            if (!extraMappings.isEmpty())
             {
-                helper.keySizeCheck(encryptedKeyAlgorithm, key);
+                for (Iterator it = extraMappings.keySet().iterator(); it.hasNext(); )
+                {
+                    ASN1ObjectIdentifier algorithm = (ASN1ObjectIdentifier)it.next();
+
+                    unwrapper.setAlgorithmMapping(algorithm, (String)extraMappings.get(algorithm));
+                }
             }
 
-            return key;
-        }
-        catch (OperatorException e)
-        {
-            throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
+            try
+            {
+                Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+
+                if (validateKeySize)
+                {
+                    helper.keySizeCheck(encryptedKeyAlgorithm, key);
+                }
+
+                return key;
+            }
+            catch (OperatorException e)
+            {
+                throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
+            }
         }
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
index 60a2ff2..f624c8c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
@@ -73,7 +73,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current RecipientInfoGenerator.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
index b271457..eb76983 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
@@ -1,7 +1,9 @@
 package org.bouncycastle.cms.test;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.math.BigInteger;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.Key;
@@ -10,8 +12,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
+import java.security.SecureRandom;
 import java.security.Security;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.MGF1ParameterSpec;
@@ -45,12 +49,14 @@
 import org.bouncycastle.asn1.cms.EncryptedContentInfo;
 import org.bouncycastle.asn1.cms.EnvelopedData;
 import org.bouncycastle.asn1.cms.GCMParameters;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Extension;
@@ -62,6 +68,7 @@
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSTypedData;
 import org.bouncycastle.cms.KeyAgreeRecipientInformation;
 import org.bouncycastle.cms.KeyTransRecipientInformation;
 import org.bouncycastle.cms.OriginatorInfoGenerator;
@@ -87,6 +94,7 @@
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
 import org.bouncycastle.cms.jcajce.JcePasswordEnvelopedRecipient;
 import org.bouncycastle.cms.jcajce.JcePasswordRecipientInfoGenerator;
+import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMKeyPair;
 import org.bouncycastle.openssl.PEMParser;
@@ -243,6 +251,286 @@
             "no34BspoV/i4f0uLhJap84bTHcF/ZRSXCmQOCRGdSvQkXHeNPI5Lus6lOHuU" +
             "vUDbQC8=");
 
+    // from RFC 4490
+
+    private byte[] gost3410_RecipCert = Base64.decode(
+        "MIIB0DCCAX8CECv1xh7CEb0Xx9zUYma0LiEwCAYGKoUDAgIDMG0xHzAdBgNVBAMM" +
+            "Fkdvc3RSMzQxMC0yMDAxIGV4YW1wbGUxEjAQBgNVBAoMCUNyeXB0b1BybzELMAkG" +
+            "A1UEBhMCUlUxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDAxQGV4YW1wbGUu" +
+            "Y29tMB4XDTA1MDgxNjE0MTgyMFoXDTE1MDgxNjE0MTgyMFowbTEfMB0GA1UEAwwW" +
+            "R29zdFIzNDEwLTIwMDEgZXhhbXBsZTESMBAGA1UECgwJQ3J5cHRvUHJvMQswCQYD" +
+            "VQQGEwJSVTEpMCcGCSqGSIb3DQEJARYaR29zdFIzNDEwLTIwMDFAZXhhbXBsZS5j" +
+            "b20wYzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYHKoUDAgIeAQNDAARAhJVodWACGkB1" +
+            "CM0TjDGJLP3lBQN6Q1z0bSsP508yfleP68wWuZWIA9CafIWuD+SN6qa7flbHy7Df" +
+            "D2a8yuoaYDAIBgYqhQMCAgMDQQA8L8kJRLcnqeyn1en7U23Sw6pkfEQu3u0xFkVP" +
+            "vFQ/3cHeF26NG+xxtZPz3TaTVXdoiYkXYiD02rEx1bUcM97i");
+
+    private byte[] gost3410_2001_KeyTrans = Base64.decode(
+        "MIIBpwYJKoZIhvcNAQcDoIIBmDCCAZQCAQAxggFTMIIBTwIBADCBgTBtMR8wHQYD" +
+            "VQQDDBZHb3N0UjM0MTAtMjAwMSBleGFtcGxlMRIwEAYDVQQKDAlDcnlwdG9Qcm8x" +
+            "CzAJBgNVBAYTAlJVMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAwMUBleGFt" +
+            "cGxlLmNvbQIQK/XGHsIRvRfH3NRiZrQuITAcBgYqhQMCAhMwEgYHKoUDAgIkAAYH" +
+            "KoUDAgIeAQSBpzCBpDAoBCBqL6ghBpVon5/kR6qey2EVK35BYLxdjfv1PSgbGJr5" +
+            "dQQENm2Yt6B4BgcqhQMCAh8BoGMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwIC" +
+            "HgEDQwAEQE0rLzOQ5tyj3VUqzd/g7/sx93N+Tv+/eImKK8PNMZQESw5gSJYf28dd" +
+            "Em/askCKd7W96vLsNMsjn5uL3Z4SwPYECJeV4ywrrSsMMDgGCSqGSIb3DQEHATAd" +
+            "BgYqhQMCAhUwEwQIvBCLHwv/NCkGByqFAwICHwGADKqOch3uT7Mu4w+hNw==");
+
+    private byte[] gost3410_2001_KeyAgree = Base64.decode(
+        "MIIBpAYJKoZIhvcNAQcDoIIBlTCCAZECAQIxggFQoYIBTAIBA6BloWMwHAYGKoUD" +
+            "AgITMBIGByqFAwICJAAGByqFAwICHgEDQwAEQLNVOfRngZcrpcTZhB8n+4HtCDLm" +
+            "mtTyAHi4/4Nk6tIdsHg8ff4DwfQG5DvMFrnF9vYZNxwXuKCqx9GhlLOlNiChCgQI" +
+            "L/D20YZLMoowHgYGKoUDAgJgMBQGByqFAwICDQAwCQYHKoUDAgIfATCBszCBsDCB" +
+            "gTBtMR8wHQYDVQQDDBZHb3N0UjM0MTAtMjAwMSBleGFtcGxlMRIwEAYDVQQKDAlD" +
+            "cnlwdG9Qcm8xCzAJBgNVBAYTAlJVMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAt" +
+            "MjAwMUBleGFtcGxlLmNvbQIQK/XGHsIRvRfH3NRiZrQuIQQqMCgEIBajHOfOTukN" +
+            "8ex0aQRoHsefOu24Ox8dSn75pdnLGdXoBAST/YZ+MDgGCSqGSIb3DQEHATAdBgYq" +
+            "hQMCAhUwEwQItzXhegc1oh0GByqFAwICHwGADDmxivS/qeJlJbZVyQ==");
+
+    public byte[] gost2001_Rand_Cert = Base64.decode(
+        "MIIELDCCA9ugAwIBAgIENqPHFzAIBgYqhQMCAgMwgckxCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQ" +
+            "vjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQ" +
+            "odC10YDQs9C10LXQstC40YcwHhcNMTcwNzE1MTQwMDAwWhcNMzcwNzE1MTQwMDAwWjCByTELMAkGA1UEBhMCUlUxIDAeBgNV" +
+            "BAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQ" +
+            "oNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g" +
+            "0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhzBjMBwGBiqFAwICEzASBgcqhQMCAiQABgcqhQMCAh4BA0MA" +
+            "BEC0WD4VzaInvp+WfjF+XIdZeWMrNSJVxUM6d/acwVMPwetEBtr1U82Cgf2U5eoz6eHxaLsAVG+qbiiMwV/4GKsao4IBpTCC" +
+            "AaEwDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr" +
+            "BgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E" +
+            "FgQUqcUQmyYjxhQ9t5JX327oLxMjtkcwgfkGA1UdIwSB8TCB7oAUqcUQmyYjxhQ9t5JX327oLxMjtkehgc+kgcwwgckxCzAJ" +
+            "BgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC9" +
+            "0LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UE" +
+            "Awwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQodC10YDQs9C10LXQstC40YeCBDajxxcwCAYGKoUDAgIDA0EA" +
+            "2rrXsssEqxuRPtVRa+vlrgoXUa9WV+24uZ1LzsiMehSOv/pUo7kJZwoA5VCedJw0C8dce6Uc6lDJkNzpHN40hA=="
+    );
+
+    public byte[] gost2001_Rand_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQgDWFcH/5KjwIwXrMdyO5CBnJdoOVtKp7WMb4EIljc+K4="
+    );
+
+    public byte[] gost2001_Rand_Msg = Base64.decode(
+        "MIIB+AYJKoZIhvcNAQcDoIIB6TCCAeUCAQAxggGkMIIBoAIBADCB0jCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf" +
+            "0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy" +
+            "0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrR" +
+            "gdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIENqPHFzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYHKoUDAgIeAQSBpzCBpDAo" +
+            "BCCbkNQAmR9ny2u5W8MvFHs8iO91uA2iCy+2nccpwOQ0agQE9BJtXaB4BgcqhQMCAh8BoGMwHAYGKoUDAgITMBIGByqFAwIC" +
+            "JAAGByqFAwICHgEDQwAEQOeSFV7jo7EvygKSgHH79eel7sgWu0yW4swAK81Pw8jHMazuL6SpTUqUWNPW1jf4aFFHQAQmrxWV" +
+            "maCQn7gSJl8ECFgM3TO2P26NMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIC4ytWGecO5AGByqFAwICHwGADIzrpurLkuk0" +
+            "xGGidg=="
+    );
+
+    public byte[] gost2001_Rand_Sender_Cert = Base64.decode(
+        "MIIERTCCA/SgAwIBAgIEUu7tIDAIBgYqhQMCAgMwgdExCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEoMCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQ" +
+            "tSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/Rg9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy" +
+            "0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijAeFw0xNzA3MTYxNDAwMDBaFw0zNzA3MTYxNDAwMDBaMIHRMQswCQYDVQQGEwJS" +
+            "VTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAm" +
+            "BgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTArBgNVBAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQ" +
+            "sdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7QvdGj0LPQuNC90YowYzAcBgYqhQMCAhMwEgYHKoUD" +
+            "AgIkAAYHKoUDAgIeAQNDAARAM++vMY04j9Bvcn71wM9atNkRo4lCixrOR82HncQbwnyBS6R0BqRmL+Q32TzEYpslzRkQnj/z" +
+            "yORa31QVSRghQaOCAa4wggGqMA4GA1UdDwEB/wQEAwIB/jBjBgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUF" +
+            "BwMDBggrBgEFBQcDBAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUFBwMJMA8GA1UdEwEB" +
+            "/wQFMAMBAf8wHQYDVR0OBBYEFCLkv9o8dmaV1StuS8QFO64FJXXXMIIBAQYDVR0jBIH5MIH2gBQi5L/aPHZmldUrbkvEBTuu" +
+            "BSV116GB16SB1DCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQ" +
+            "odC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQuNC1INC70LjRhtCwMS0wKwYDVQQM" +
+            "DCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV0LLQs9C10L3RltC5INCe0L3Ro9Cz" +
+            "0LjQvdGKggRS7u0gMAgGBiqFAwICAwNBAIMLOOeDFPnrGkC/QG/pvLRZhEeiVkGVgy/h5WJancJDouHzedhI+mJqBFEYRoIy" +
+            "4KP5Q93Bf1NClXwIfnTOxWo="
+    );
+
+    public byte[] gost2001_Rand_Sender_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQgGmpna37puqaRGBZjUAX5UfWaL67C9rvxCpOIexI0KUM="
+    );
+
+    public byte[] gost2001_Rand_Reci_Cert = Base64.decode(
+        "MIIELDCCA9ugAwIBAgIERMAcpzAIBgYqhQMCAgMwgckxCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQ" +
+            "vjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQ" +
+            "odC10YDQs9C10LXQstC40YcwHhcNMTcwNzE2MTQwMDAwWhcNMzcwNzE2MTQwMDAwWjCByTELMAkGA1UEBhMCUlUxIDAeBgNV" +
+            "BAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQ" +
+            "oNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g" +
+            "0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhzBjMBwGBiqFAwICEzASBgcqhQMCAiQABgcqhQMCAh4BA0MA" +
+            "BEA6Dzd7VQJA7712CfHiH4L0TVcaH+iLJ6vHkfdgAvS+8mGt/L2H9qQP7O41SgDKQqtfrr+tHDig7/ft5Bl1TFNoo4IBpTCC" +
+            "AaEwDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr" +
+            "BgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E" +
+            "FgQU8ZLn4r4PajaqWCwLYW6XauO3XsgwgfkGA1UdIwSB8TCB7oAU8ZLn4r4PajaqWCwLYW6XauO3Xsihgc+kgcwwgckxCzAJ" +
+            "BgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC9" +
+            "0LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UE" +
+            "Awwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQodC10YDQs9C10LXQstC40YeCBETAHKcwCAYGKoUDAgIDA0EA" +
+            "Ul4Y7XAhFUEoTUwdue+wbyxk86SpIFwC6NuVjTSIF3F9ACxfz2N6iwHaRv6GTVRIAEjj5G/rhdxRivvC8hU4QQ=="
+    );
+
+    public byte[] gost2001_Rand_Reci_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQg5oDAn/BdWX4RSfHeqZyHAo/CNAy+2a0Jq3Z922cYeSQ="
+    );
+
+    public byte[] gost2001_Rand_Gen_Msg = Base64.decode(
+        "MIICAQYJKoZIhvcNAQcDoIIB8jCCAe4CAQAxggGtoYIBqQIBA6BloWMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgED" +
+            "QwAEQDPvrzGNOI/Qb3J+9cDPWrTZEaOJQosazkfNh53EG8J8gUukdAakZi/kN9k8xGKbJc0ZEJ4/88jkWt9UFUkYIUGhCgQI" +
+            "SQHkq1IzGZ8wKAYGKoUDAgJgMB4GByqFAwICDQEwEwYHKoUDAgIfAQQISQHkq1IzGZ8wggEFMIIBATCB0jCByTELMAkGA1UE" +
+            "BhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6" +
+            "MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQ" +
+            "n9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIERMAcpwQqMCgEIA4jC8qro8xNnn+R" +
+            "JTNYpV8dSdw82e/pnqnyo21o+qZkBAT9DaUDMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIziBZysW+ewMGByqFAwICHwGA" +
+            "DFKaSCs2xd4ef/khFQ=="
+    );
+
+    public byte[] gost2012_Sender_Cert = Base64.decode(
+        "MIIETDCCA/mgAwIBAgIEB/tRdzAKBggqhQMHAQEDAjCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQ" +
+            "uNC1INC70LjRhtCwMS0wKwYDVQQMDCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV" +
+            "0LLQs9C10L3RltC5INCe0L3Ro9Cz0LjQvdGKMB4XDTE3MDcxNTE0MDAwMFoXDTM3MDcxNTE0MDAwMFowgdExCzAJBgNVBAYT" +
+            "AlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEo" +
+            "MCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQtSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/R" +
+            "g9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijBmMB8GCCqFAwcBAQEBMBMG" +
+            "ByqFAwICJAAGCCqFAwcBAQICA0MABEAl9XE868NRYm3CQXCPO+BJlVi7kxORfoyRaHyWyKBFf4TYV4eEUF/WjAf3fAqsndp6" +
+            "v1DNqa3KS1R1yqn1Ug4do4IBrjCCAaowDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYI" +
+            "KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYD" +
+            "VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUzhoR/a0hWGOpy6GPEm7LBCJ3dLYwggEBBgNVHSMEgfkwgfaAFM4aEf2tIVhjqcuh" +
+            "jxJuywQid3S2oYHXpIHUMIHRMQswCQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNV" +
+            "BAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAmBgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTAr" +
+            "BgNVBAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQsdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7Q" +
+            "vdGj0LPQuNC90YqCBAf7UXcwCgYIKoUDBwEBAwIDQQDcFDvbdfUu1087tslF70OeZgLW5QHRtPLUaldE9x1Geu2veJos9fZ7" +
+            "nqISVcd1wrf6FfADt3Tw2pQuG8mVCNUi"
+    );
+
+    public byte[] gost2012_Sender_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgYARzlWBWAJLs64jQbYW4UEXqFN/ChtWCSHqRgivT" +
+            "8Ds="
+    );
+
+    public byte[] gost2012_Reci_Cert = Base64.decode(
+        "MIIEMzCCA+CgAwIBAgIEe7X7RjAKBggqhQMHAQEDAjCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTUxNDAwMDBaFw0zNzA3MTUxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQGQ4aJ3On0XqEt62PUfquYCAx0690AzlyE9IO8r5zkNKldvK4THC1IgBHkRzKiewquMm0YuYh76NI01uNjThOjyj" +
+            "ggGlMIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUH" +
+            "AwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0G" +
+            "A1UdDgQWBBROPw+FggywJjV9aLLSKz2Cr0BD9zCB+QYDVR0jBIHxMIHugBROPw+FggywJjV9aLLSKz2Cr0BD96GBz6SBzDCB" +
+            "yTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQ" +
+            "tdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTsw" +
+            "OQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IEe7X7RjAKBggqhQMH" +
+            "AQEDAgNBAJR6UhzmUlRzlbiCU8IjhrR15c2uFtcHqHaUfiO8XJ2bnOiwxADZbnqlN3Foul6QrTXa5Vu1UbA2hFobJeuDniQ="
+    );
+
+    public byte[] gost2012_Reci_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgbtgmrFxhZLQm9H1Gx0+BAVTP6ZVLu20KcmKNzdIh" +
+            "rKc="
+    );
+
+    public byte[] gost2012_Reci_Msg = Base64.decode(
+        "MIICBgYJKoZIhvcNAQcDoIIB9zCCAfMCAQAxggGyoYIBrgIBA6BooWYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQCX1cTzrw1FibcJBcI874EmVWLuTE5F+jJFofJbIoEV/hNhXh4RQX9aMB/d8Cqyd2nq/UM2prcpLVHXKqfVSDh2h" +
+            "CgQIDIhh5975RYMwKgYIKoUDBwEBBgEwHgYHKoUDAgINATATBgcqhQMCAh8BBAgMiGHn3vlFgzCCAQUwggEBMIHSMIHJMQsw" +
+            "CQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3Q" +
+            "vdC40LoxHzAdBgNVBAsMFtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNV" +
+            "BAMMMtCf0YPRiNC60LjQvSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHAgR7tftGBCowKAQgLMyx3zUe" +
+            "56F7eAKUAezilo3fxp6M/E+YkVVUDgFadfcEBHMmXJMwOAYJKoZIhvcNAQcBMB0GBiqFAwICFTATBAhJHfyezbxrUQYHKoUD" +
+            "AgIfAYAMLLM89stnSyrWGWSW"
+    );
+
+    public byte[] gost2012_512_Sender_Cert = Base64.decode(
+        "MIIE0jCCBD6gAwIBAgIEMBwU/jAKBggqhQMHAQEDAzCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQ" +
+            "uNC1INC70LjRhtCwMS0wKwYDVQQMDCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV" +
+            "0LLQs9C10L3RltC5INCe0L3Ro9Cz0LjQvdGKMB4XDTE3MDcxNTE0MDAwMFoXDTM3MDcxNTE0MDAwMFowgdExCzAJBgNVBAYT" +
+            "AlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEo" +
+            "MCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQtSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/R" +
+            "g9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijCBqjAhBggqhQMHAQEBAjAV" +
+            "BgkqhQMHAQIBAgEGCCqFAwcBAQIDA4GEAASBgLnNMC1uA9NjhZMyIotCn+4H+iqcTv5paCYmRIuIvWZO7OvUv3u9aWK5Lb0w" +
+            "CH2Imbg/ffZV84xSwbNST83w4IFh8u1mAnf302+uuqt62pBU3VtPOPt3RYRwEABSDuTlBP2VocXa2iP53HM09fxhS/AJ14eR" +
+            "K2oJ4cNpASXDH1mSo4IBrjCCAaowDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB" +
+            "BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0T" +
+            "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUEImfPZM/dIJULOrK4d/vMchap9kwggEBBgNVHSMEgfkwgfaAFBCJnz2TP3SCVCzqyuHf" +
+            "7zHIWqfZoYHXpIHUMIHRMQswCQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoM" +
+            "FtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAmBgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTArBgNV" +
+            "BAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQsdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7QvdGj" +
+            "0LPQuNC90YqCBDAcFP4wCgYIKoUDBwEBAwMDgYEAKZRx05mBwO7VIzj1FFJcHlfbHuLF+XZbFZaVfWc32R+KLxBJ0t1RuQ34" +
+            "KtjQhu8/oU2rR/pKcmyHRw3nxJy+DExdj7sWJ01uWH6vBa+nsXS8OzSIg+wb9hlrFy0wZSkQjyNMtSiNg+On1yzFeI2fxuAY" +
+            "OtIKHdqht+V+6M0g8BA="
+    );
+
+    public byte[] gost2012_512_Sender_Key = Base64.decode(
+        "MGoCAQAwIQYIKoUDBwEBBgIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRCBEDYpenYz4GDc/sIGl34Cv1T4xtWDlt7FB28ghXT" +
+            "n4MXm43IvLwW3YclZbRz7V9W5lR0XoftGJ9q3ICv/IN2F+Dr"
+    );
+
+    public byte[] gost2012_512_Reci_Cert = Base64.decode(
+        "MIIEuTCCBCWgAwIBAgIECpLweDAKBggqhQMHAQEDAzCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTUxNDAwMDBaFw0zNzA3MTUxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMIGqMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUD" +
+            "BwEBAgMDgYQABIGAnZAIQhH/2nmSIZWfn+K3ftHGWbx1vrh/IeA43Q/z7h9jVPcVV3Csju92lgL5cnXyBAV90CVGw0/bCu1N" +
+            "CYUpC0EVx5OmTd54fqicmFgZLqEnX6sbCXvpgCdvXhyYl+h7PTGHcuwGsMXZlIKVQLq6quVKh/UI/IfGK5CcPkX0PVCjggGl" +
+            "MIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQG" +
+            "CCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud" +
+            "DgQWBBRvBhSgd/YSnT1ldXAE2V92ksV6WzCB+QYDVR0jBIHxMIHugBRvBhSgd/YSnT1ldXAE2V92ksV6W6GBz6SBzDCByTEL" +
+            "MAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC9" +
+            "0L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYD" +
+            "VQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IECpLweDAKBggqhQMHAQED" +
+            "AwOBgQDilJAjXm+OK+mkfOk2ij3qKj00+gyFzJbxtk8wKEG7QmvlOPQvywke1pmCh8b1Z48OFOdmfKnTLE/D4AI/MQECUb1h" +
+            "ChUfgfrSw0LY205tqxp6aqDtc2iPI7XHQAKE+jD819zubjCBzVDOiyRXatiRsEtfXPTBvqQdisM4rSw+OQ=="
+
+    );
+
+    public byte[] gost2012_512_Reci_Key = Base64.decode(
+        "MGoCAQAwIQYIKoUDBwEBBgIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRCBEDbd6/MUJS1QjpkwGUCg8OtxzuxiU2qm2VDBDDN" +
+            "ZQ8/GtO12OiysmJHAXS9fpO1TRuyySw0r5r4x2g0NCWtVdQf"
+    );
+
+    public byte[] gost2012_512_Reci_Msg = Base64.decode(
+        "MIICTAYJKoZIhvcNAQcDoIICPTCCAjkCAQAxggH4oYIB9AIBA6CBraGBqjAhBggqhQMHAQEBAjAVBgkqhQMHAQIBAgEGCCqF" +
+            "AwcBAQIDA4GEAASBgLnNMC1uA9NjhZMyIotCn+4H+iqcTv5paCYmRIuIvWZO7OvUv3u9aWK5Lb0wCH2Imbg/ffZV84xSwbNS" +
+            "T83w4IFh8u1mAnf302+uuqt62pBU3VtPOPt3RYRwEABSDuTlBP2VocXa2iP53HM09fxhS/AJ14eRK2oJ4cNpASXDH1mSoQoE" +
+            "CGGh2agBkurNMCoGCCqFAwcBAQYCMB4GByqFAwICDQEwEwYHKoUDAgIfAQQIYaHZqAGS6s0wggEFMIIBATCB0jCByTELMAkG" +
+            "A1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3Q" +
+            "uNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQD" +
+            "DDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIECpLweAQqMCgEIBEN53tKgcd9" +
+            "VW9uczUiwSM0pS/a7/vKIvTIqnIR0E5pBAQ+WRdXMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIbDvPAW4Wm0UGByqFAwIC" +
+            "HwGADFMeOJyH3t7YSNgxsA=="
+    );
+
+    public byte[] gost2012_KeyTrans_Reci_Cert = Base64.decode(
+        "MIIEMzCCA+CgAwIBAgIEBSqgszAKBggqhQMHAQEDAjCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTYxNDAwMDBaFw0zNzA3MTYxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQEG5/wUY0LkiqETYAZY6o5mrjwWQNBYbSIKghYgKzLgSv1RCuTEFXRIJQcMG0V80auKVZNty9kcvn9P0IcJpGfGj" +
+            "ggGlMIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUH" +
+            "AwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0G" +
+            "A1UdDgQWBBQJwiUIQOJNbB0Fzh6ucd3uRE9QzDCB+QYDVR0jBIHxMIHugBQJwiUIQOJNbB0Fzh6ucd3uRE9QzKGBz6SBzDCB" +
+            "yTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQ" +
+            "tdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTsw" +
+            "OQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IEBSqgszAKBggqhQMH" +
+            "AQEDAgNBAKLmdCiVR9MWeoC+MNudXGny3l2uDBBttvhTli0gDEaQLnBFyvD+cfSLgsheoz8vwhyqD/6W3ATBMRiGjqNJjQE=");
+
+    public byte[] gost2012_KeyTrans_Reci_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgy+dPu0sLqJ/Fokomiu69lRA48HaPNkP7kmzDHOxP" +
+            "QFc="
+    );
+
+    public byte[] gost2012_KeyTrans_Msg = Base64.decode(
+        "MIIB/gYJKoZIhvcNAQcDoIIB7zCCAesCAQAxggGqMIIBpgIBADCB0jCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf" +
+            "0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy" +
+            "0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrR" +
+            "gdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIEBSqgszAfBggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgSBqjCB" +
+            "pzAoBCBnHA+9wEUh7KIkYlboGbtxRfrTL1oPGU3Tzaw8/khaWgQE+N56jaB7BgcqhQMCAh8BoGYwHwYIKoUDBwEBAQEwEwYH" +
+            "KoUDAgIkAAYIKoUDBwEBAgIDQwAEQMbb4wVWm1EWIIXKDseCNE6JHmS+4fNh2uB+10Isg7g8/1Wvdh66IFir6fyp8NRwwMkU" +
+            "QM0dmAfcpN6M2RSj83wECMCTi+FRlTafMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIzZlyAleTrCEGByqFAwICHwGADIO7" +
+            "l43OVnBpGM+FjQ=="
+    );
+
     public NewEnvelopedDataTest()
     {
     }
@@ -1320,7 +1608,8 @@
         CMSEnvelopedData ed;
         RecipientInformationStore recipients;
         Collection c;
-        Iterator it;ContentInfo eContentInfo = ContentInfo.getInstance(edData);
+        Iterator it;
+        ContentInfo eContentInfo = ContentInfo.getInstance(edData);
 
         EnvelopedData envD = EnvelopedData.getInstance(eContentInfo.getContent());
 
@@ -1821,6 +2110,538 @@
         processInput(ecKey, expected, "ecdh/encSessH.asc", new AlgorithmIdentifier(CMSAlgorithm.AES256_WRAP));
     }
 
+    public void testGost3410_2012_KeyTransGen()
+        throws Exception
+    {
+        byte[] data = Strings.toByteArray("hello world!");
+
+
+        CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator();
+
+
+        X509Certificate cert = (X509Certificate)CertificateFactory
+                                                    .getInstance("X.509", "BC")
+                                                    .generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert));
+        JceKeyTransRecipientInfoGenerator jceKey = new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC");
+        cmsEnvelopedDataGenerator.addRecipientInfoGenerator(jceKey);
+        CMSTypedData msg = new CMSProcessableByteArray(data);
+        OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider("BC").build();
+        CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator.generate(msg, encryptor);
+
+        byte[] encryptedData = cmsEnvelopedData.getEncoded();
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(encryptedData);
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        Iterator it = c.iterator();
+
+         while (it.hasNext())
+         {
+             RecipientInformation recipient = (RecipientInformation)it.next();
+
+             assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512.getId());
+
+             byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+             assertTrue(Arrays.equals(data, recData));
+         }
+
+    }
+
+    public void testGost3410_2001_KeyTrans()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new org.bouncycastle.jce.spec.ECPrivateKeySpec(
+            new BigInteger("0B293BE050D0082BDAE785631A6BAB68F35B42786D6DDA56AFAF169891040F77", 16),
+            ECGOST3410NamedCurveTable.getParameterSpec("GostR3410-2001-CryptoPro-XchA")));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost3410_2001_KeyTrans);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("sample text\n", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost3410_RecipCert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_KeyTrans()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_KeyTrans_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_KeyTrans_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_KeyTrans_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new org.bouncycastle.jce.spec.ECPrivateKeySpec(
+            new BigInteger("0B293BE050D0082BDAE785631A6BAB68F35B42786D6DDA56AFAF169891040F77", 16),
+            ECGOST3410NamedCurveTable.getParameterSpec("GostR3410-2001-CryptoPro-XchA")));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost3410_2001_KeyAgree);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("sample text\n", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost3410_RecipCert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyTransRand()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2001_Rand_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello world!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgreeRand()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2001_Rand_Gen_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_Reci_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_512_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_512_Reci_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World! Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_256,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World! Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_256_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_256,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_512_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_512,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
     private void processInput(ECPrivateKey ecKey, byte[] expected, String input, AlgorithmIdentifier wrapAlg)
         throws CMSException, IOException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
index 7277098..b354c52 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
@@ -24,13 +24,13 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.bouncycastle.asn1.ASN1ApplicationSpecific;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERApplicationSpecific;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSet;
@@ -41,10 +41,12 @@
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.SignedData;
 import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ocsp.OCSPResponse;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCRLStore;
@@ -566,6 +568,30 @@
             + "/JNmKBiVxuxZYYHI20CZHrgjb+ARczWuOJuBVEGEgbW/t7hMVX8X+MPAzZek"
             + "Ndi9ZfkurEeYdDpGluYBH910/P95ibG8nrJyTaoxhmOJhJ/o/SO54m8oDlI0");
 
+    private static final Set noParams = new HashSet();
+
+    static
+    {
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA224);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA384);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA512);
+        noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+    }
+    
     public NewSignedDataTest(String name)
     {
         super(name);
@@ -1320,6 +1346,30 @@
         }
     }
 
+    public void testRawSHA256MissingNull()
+        throws Exception
+    {
+        final byte[] document = getInput("rawsha256nonull.p7m");
+
+        final CMSSignedData s = new CMSSignedData(document);
+
+        final Store certStore = s.getCertificates();
+        final SignerInformation signerInformation = (SignerInformation)s.getSignerInfos().getSigners().iterator().next();
+
+        Collection          certCollection = certStore.getMatches(signerInformation.getSID());
+
+        Iterator        certIt = certCollection.iterator();
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)certIt.next());
+
+        final SignerInformationVerifier signerInformationVerifier =
+            new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey());
+
+        if (!signerInformation.verify(signerInformationVerifier))
+        {
+            fail("raw sig failed");
+        }
+    }
+
     public void testLwSHA1WithRSAAndAttributeTable()
         throws Exception
     {
@@ -1371,6 +1421,57 @@
         verifyRSASignatures(s, md.digest("Hello world!".getBytes()));
     }
 
+    public void testLwSHA3_256WithRSAAndAttributeTable()
+        throws Exception
+    {
+        MessageDigest       md = MessageDigest.getInstance("SHA3-256", BC);
+        List                certList = new ArrayList();
+        CMSTypedData        msg = new CMSProcessableByteArray("Hello world!".getBytes());
+
+        certList.add(_origCert);
+        certList.add(_signCert);
+
+        Store           certs = new JcaCertStore(certList);
+
+        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+
+        Attribute attr = new Attribute(CMSAttributes.messageDigest,
+                                       new DERSet(
+                                            new DEROctetString(
+                                                md.digest("Hello world!".getBytes()))));
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(attr);
+
+        AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(_origKP.getPrivate().getEncoded());
+
+        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA3-256withRSA");
+        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+
+        BcContentSignerBuilder contentSignerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
+
+        gen.addSignerInfoGenerator(
+            new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
+                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)))
+                .build(contentSignerBuilder.build(privKey), new JcaX509CertificateHolder(_origCert)));
+
+        gen.addCertificates(certs);
+
+        CMSSignedData s = gen.generate(new CMSAbsentContent(), false);
+
+        //
+        // the signature is detached, so need to add msg before passing on
+        //
+        s = new CMSSignedData(msg, s.getEncoded());
+        //
+        // compute expected content digest
+        //
+
+        verifySignatures(s, md.digest("Hello world!".getBytes()));
+        verifyRSASignatures(s, md.digest("Hello world!".getBytes()));
+    }
+
     public void testSHA1WithRSAEncapsulated()
         throws Exception
     {
@@ -1407,6 +1508,48 @@
         rsaPSSTest("SHA384withRSAandMGF1");
     }
 
+    public void testSHA3_224WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-224withRSAandMGF1");
+    }
+
+    public void testSHA3_256WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-256withRSAandMGF1");
+    }
+
+    public void testSHA3_384WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-384withRSAandMGF1");
+    }
+
+    public void testSHA3_224WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-224withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_224);
+    }
+
+    public void testSHA3_256WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-256withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_256);
+    }
+
+    public void testSHA3_384WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-384withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_384);
+    }
+
+    public void testSHA3_512WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-512withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_512);
+    }
+
     // RFC 5754 update
     public void testSHA224WithRSAEncapsulated()
         throws Exception
@@ -1421,6 +1564,30 @@
         encapsulatedTest(_signKP, _signCert, "SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption);
     }
 
+    public void testSHA3_224WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-224withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+    }
+
+    public void testSHA3_256WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-256withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+    }
+
+    public void testSHA3_384WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-384withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+    }
+
+    public void testSHA3_512WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-512withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
+    }
+
     public void testRIPEMD128WithRSAEncapsulated()
         throws Exception
     {
@@ -1475,6 +1642,30 @@
         encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA512withECDSA");
     }
 
+    public void testECDSASHA3_224Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-224withECDSA");
+    }
+
+    public void testECDSASHA3_256Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-256withECDSA");
+    }
+
+    public void testECDSASHA3_384Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-384withECDSA");
+    }
+
+    public void testECDSASHA3_512Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-512withECDSA");
+    }
+
     public void testECDSASHA512EncapsulatedWithKeyFactoryAsEC()
         throws Exception
     {
@@ -1866,7 +2057,14 @@
             if (sigAlgOid != null)
             {
                 assertEquals(sigAlgOid.getId(), signer.getEncryptionAlgOID());
-                assertEquals(DERNull.INSTANCE, ASN1Primitive.fromByteArray(signer.getEncryptionAlgParams()));
+                if (noParams.contains(sigAlgOid))
+                {
+                    assertNull(signer.getEncryptionAlgParams());
+                }
+                else
+                {
+                    assertEquals(DERNull.INSTANCE, ASN1Primitive.fromByteArray(signer.getEncryptionAlgParams()));
+                }
             }
 
             digestAlgorithms.remove(signer.getDigestAlgorithmID());
@@ -2369,7 +2567,7 @@
     public void testMixed()
         throws Exception
     {
-        DERApplicationSpecific derApplicationSpecific = (DERApplicationSpecific)ASN1Primitive.fromByteArray(mixedSignedData);
+        ASN1ApplicationSpecific derApplicationSpecific = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(mixedSignedData);
 
         CMSSignedData s = new CMSSignedData(new ByteArrayInputStream(derApplicationSpecific.getContents()));
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java b/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
index 90a46b4..856d39d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
@@ -6,7 +6,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import junit.framework.TestCase;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -34,9 +33,12 @@
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.dvcs.DVCSException;
+import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 
+import junit.framework.TestCase;
+
 public class DVCSParseTest
     extends TestCase
 {
@@ -268,7 +270,7 @@
      */
     private void validate(String name, DVCSRequestInformation info, DVCSRequestInformation expected)
     {
-        validate(name + ".version", new Integer(info.getVersion()), new Integer(expected.getVersion()));
+        validate(name + ".version", Integers.valueOf(info.getVersion()), Integers.valueOf(expected.getVersion()));
         validate(name + ".service", info.getService().getValue(), expected.getService().getValue());
         validate(name + ".nonce", info.getNonce(), expected.getNonce());
         validate(name + ".requestTime", info.getRequestTime(), expected.getRequestTime());
@@ -344,7 +346,7 @@
         {
             return;
         }
-        validate(name + ".version", new Integer(result.getVersion()), new Integer(expected.getVersion()));
+        validate(name + ".version", Integers.valueOf(result.getVersion()), Integers.valueOf(expected.getVersion()));
         validate(name + ".dvReqInfo", result.getDvReqInfo(), expected.getDvReqInfo());
         validate(name + ".messageImprint", result.getMessageImprint(), expected.getMessageImprint());
         validate(name + ".serialNumber", result.getSerialNumber(), expected.getSerialNumber());
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java b/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
index 0250983..dfa60d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
@@ -23,7 +23,7 @@
      * Create a CSRAttributesResponse from the passed in bytes.
      *
      * @param responseEncoding BER/DER encoding of the certificate.
-     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     * @throws ESTException in the event of corrupted data, or an incorrect structure.
      */
     public CSRAttributesResponse(byte[] responseEncoding)
         throws ESTException
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java b/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
index f9452bf..c315d23 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
@@ -6,7 +6,7 @@
  * ESTClient implement connection to the server.
  * <p>
  * Implementations should be aware that they are responsible for
- * satisfying <a hrref="https://tools.ietf.org/html/rfc7030#section-3.3">RFC7030 3.3 - TLS Layer</a>
+ * satisfying <a href="https://tools.ietf.org/html/rfc7030#section-3.3">RFC7030 3.3 - TLS Layer</a>
  * including SRP modes.
  */
 public interface ESTClient
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java b/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
index b4b2ade..854c448 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
@@ -21,6 +21,7 @@
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
@@ -163,10 +164,17 @@
                         {
                             throw new IllegalArgumentException("User must not contain a ':'");
                         }
-                        String userPass = username + ":" + new String(password);
-                        answer.setHeader("Authorization", "Basic " + Base64.toBase64String(userPass.getBytes()));
+                        //userPass = username + ":" + password;
+                        char[]  userPass = new char[username.length() + 1 + password.length];
+                        System.arraycopy(username.toCharArray(), 0, userPass, 0, username.length());
+                        userPass[username.length()] = ':';
+                        System.arraycopy(password, 0, userPass, username.length() + 1, password.length);
+
+                        answer.setHeader("Authorization", "Basic " + Base64.toBase64String(Strings.toByteArray(userPass)));
 
                         res = req.getClient().doRequest(answer.build());
+
+                        Arrays.fill(userPass, (char)0);
                     }
                     else
                     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
index fe8e374..a3e8362 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
@@ -7,8 +7,8 @@
  * Channel Binding Provider provides a method of extracting the
  * ChannelBinding that can be customised specifically for the provider.
  * Presently JSSE does not support RFC 5920.
- *
- * @See https://bugs.openjdk.java.net/browse/JDK-6491070
+ * <p>
+ * See https://bugs.openjdk.java.net/browse/JDK-6491070
  */
 public interface ChannelBindingProvider
 {
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
index 2dec2c6..05b6a36 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
@@ -9,6 +9,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.net.ssl.SSLSession;
 
@@ -18,6 +20,7 @@
 import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.est.ESTException;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
 
 
 /**
@@ -26,6 +29,7 @@
 public class JsseDefaultHostnameAuthorizer
     implements JsseHostnameAuthorizer
 {
+    private static Logger LOG = Logger.getLogger(JsseDefaultHostnameAuthorizer.class.getName());
 
     private final Set<String> knownSuffixes;
 
@@ -84,7 +88,8 @@
                 for (Iterator it = n.iterator(); it.hasNext();)
                 {
                     List l = (List)it.next();
-                    switch (((Number)l.get(0)).intValue())
+                    int type = ((Number)l.get(0)).intValue();
+                    switch (type)
                     {
                     case 2:
                         if (isValidNameMatch(name, l.get(1).toString(), knownSuffixes))
@@ -99,7 +104,21 @@
                         }
                         break;
                     default:
-                        throw new RuntimeException("Unable to handle ");
+                        // ignore, maybe log
+                        if (LOG.isLoggable(Level.INFO))
+                        {
+                            String value;
+                            if (l.get(1) instanceof byte[])
+                            {
+                                value = Hex.toHexString((byte[])l.get(1));
+                            }
+                            else
+                            {
+                                value = l.get(1).toString();
+                            }
+
+                            LOG.log(Level.INFO, "ignoring type " + type + " value = " + value);
+                        }
                     }
                 }
 
@@ -123,7 +142,7 @@
 
         // Common Name match only.
         RDN[] rdNs = X500Name.getInstance(cert.getSubjectX500Principal().getEncoded()).getRDNs();
-        for (int i = 0; i != rdNs.length; i++)
+        for (int i = rdNs.length - 1; i >= 0; --i)
         {
             RDN rdn = rdNs[i];
             AttributeTypeAndValue[] typesAndValues = rdn.getTypesAndValues();
@@ -132,7 +151,7 @@
                 AttributeTypeAndValue atv = typesAndValues[j];
                 if (atv.getType().equals(BCStyle.CN))
                 {
-                    return isValidNameMatch(name, rdn.getFirst().getValue().toString(), knownSuffixes);
+                    return isValidNameMatch(name, atv.getValue().toString(), knownSuffixes);
                 }
             }
         }
@@ -180,7 +199,7 @@
 
                 if (wildIndex > 0)
                 {
-                    if (loweredName.startsWith(dnsName.substring(0, wildIndex - 1)) && loweredName.endsWith(end))
+                    if (loweredName.startsWith(dnsName.substring(0, wildIndex)) && loweredName.endsWith(end))
                     {
                         return loweredName.substring(wildIndex, loweredName.length() - end.length()).indexOf('.') < 0;
                     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
index a1d50eb..34080fe 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
@@ -35,6 +35,7 @@
 
         suite.addTestSuite(ESTParsingTest.class);
         suite.addTestSuite(HostNameAuthorizerMatchTest.class);
+        suite.addTestSuite(TestHostNameAuthorizer.class);
 
         return new ESTTestSetup(suite);
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java b/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
index 32b0618..5614072 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
@@ -32,6 +32,9 @@
             {"Invalid 13", "foo.example.com","*.example.com",true},
             {"Invalid 14", "bar.foo.example.com", "*.example.com", false},
             {"Invalid 15", "example.com", "*.example.com", false},
+            {"Invalid 16", "foobaz.example.com","b*z.example.com",false},
+            {"Invalid 17", "foobaz.example.com","ob*z.example.com",false},
+            { "Valid", "foobaz.example.com","foob*z.example.com",true}
         };
 
         for (Object[] j : v)
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java b/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java
new file mode 100644
index 0000000..3a2792a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.est.test;
+
+
+import java.io.InputStreamReader;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.est.jcajce.JsseDefaultHostnameAuthorizer;
+import org.bouncycastle.util.io.pem.PemReader;
+
+/**
+ * TestHostNameAuthorizer tests the hostname authorizer only. EST related functions
+ * are not tested here.
+ */
+public class TestHostNameAuthorizer
+    extends TestCase
+{
+    private static X509Certificate readPemCertificate(String path)
+        throws Exception
+    {
+        InputStreamReader fr = new InputStreamReader(TestHostNameAuthorizer.class.getResourceAsStream(path));
+        PemReader reader = new PemReader(fr);
+        X509CertificateHolder fromFile = new X509CertificateHolder(reader.readPemObject().getContent());
+        reader.close();
+        fr.close();
+        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(fromFile);
+    }
+
+    /*
+        The following tests do not attempt to validate the certificates.
+        They only test hostname verification behavior.
+     */
+    public void testCNMatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_match_wc.pem");
+
+        assertTrue("Common Name match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("cisco.com", cert));
+    }
+
+    public void testCNMismatch_1()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_wc.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark", cert));
+    }
+
+
+    // 192.168.1.50
+    public void testCNIPMismatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_ip.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("127.0.0.1", cert));
+    }
+
+    public void testWCMismatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_ip.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+    }
+
+    public void testSANMatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_match.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.cisco.com", cert));
+    }
+
+    public void testSANMatchIP()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_match_ip.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("192.168.51.140", cert));
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("127.0.0.1", cert));
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("10.0.0.1", cert));
+    }
+
+    public void testSANMatchWC()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_wc.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("roundhouse.yahoo.com", cert));
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+    }
+
+    public void testSANMismatchIP()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_ip.pem");
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.me", cert));
+    }
+
+    public void testSANMismatchWC()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_wc.pem");
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.me", cert));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem
new file mode 100644
index 0000000..0860f79
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 865 (0x361)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:41:31 2014 GMT
+            Not After : Dec 16 12:41:31 2022 GMT
+        Subject: CN=*.cisco.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:b7:08:e6:18:f2:32:d7:07:44:4b:f3:b1:83:01:
+                    59:f8:bc:ec:26:71:92:9a:53:70:f2:c0:be:2a:d6:
+                    26:6f:45:11:86:d7:ee:37:9d:d3:2f:22:b2:8b:9b:
+                    c5:96:00:36:73:97:c3:4c:f2:7a:0b:2c:e0:cc:d9:
+                    f0:ec:ba:1b:75:8c:66:b1:86:10:fd:be:df:6b:67:
+                    9c:0e:6b:2a:0e:d0:80:a8:dc:7a:d4:df:6e:79:28:
+                    a7:60:1a:11:b7:ae:40:94:bb:b4:11:ed:1b:6f:a7:
+                    91:ae:33:ec:bf:9c:30:f3:dc:91:2c:b4:3e:8c:c9:
+                    bd:f1:d1:aa:f6:c2:1d:6a:cd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:76:4f:3a:6c:b4:99:cb:1e:37:f4:0d:6e:e1:74:
+         4b:99:bb:f5:c4:b6:3d:c1:61:df:8c:d7:1f:9f:e7:d3:64:d6:
+         02:20:64:38:8f:6f:32:37:2b:7d:cf:28:93:e5:e6:e7:70:c5:
+         a9:12:04:b0:4b:a5:29:7b:23:df:85:f2:18:44:8b:d2
+-----BEGIN CERTIFICATE-----
+MIIBezCCASOgAwIBAgICA2EwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTI0MTMxWhcNMjIxMjE2MTI0MTMxWjAWMRQwEgYDVQQD
+DAsqLmNpc2NvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtwjmGPIy
+1wdES/OxgwFZ+LzsJnGSmlNw8sC+KtYmb0URhtfuN53TLyKyi5vFlgA2c5fDTPJ6
+CyzgzNnw7LobdYxmsYYQ/b7fa2ecDmsqDtCAqNx61N9ueSinYBoRt65AlLu0Ee0b
+b6eRrjPsv5ww89yRLLQ+jMm98dGq9sIdas0CAwEAAaMaMBgwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwCQYHKoZIzj0EAQNHADBEAiB2TzpstJnLHjf0DW7hdEuZu/XE
+tj3BYd+M1x+f59Nk1gIgZDiPbzI3K33PKJPl5udwxakSBLBLpSl7I9+F8hhEi9I=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem
new file mode 100644
index 0000000..5a0b3d3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 863 (0x35f)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:36:22 2014 GMT
+            Not After : Dec 16 12:36:22 2022 GMT
+        Subject: CN=hostname
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c2:4e:9f:27:15:91:6b:2b:e7:85:a8:50:d9:5b:
+                    1a:a9:23:0a:84:1c:fd:e7:24:dc:29:18:f2:52:55:
+                    43:25:e4:3e:ce:02:51:9c:93:19:67:89:c9:93:6d:
+                    dc:5d:56:ad:cb:b0:7e:2c:7a:ad:98:17:7f:bb:19:
+                    62:7d:2e:f0:0b:cf:c1:18:6f:6f:3a:fc:3d:3c:03:
+                    9b:18:66:5f:dc:2a:fa:72:54:bf:5f:b0:75:dd:bf:
+                    84:40:b1:3a:c5:65:2d:84:ee:48:76:1d:45:fa:1d:
+                    e2:b2:25:5e:aa:06:8c:11:66:ef:40:f0:68:14:08:
+                    a8:7e:62:4a:d2:e9:88:bd:3d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:45:02:21:00:a8:bd:82:16:2c:9c:bf:77:1a:4d:fc:0f:a5:
+         a6:da:6e:e7:2f:45:fc:58:be:e3:0c:d2:a7:36:41:1f:45:c0:
+         80:02:20:6f:82:eb:4b:05:63:c9:e3:c7:f8:42:c0:ff:f1:0f:
+         5f:95:db:95:6e:71:fb:05:f0:52:e0:a6:82:53:45:f6:e3
+-----BEGIN CERTIFICATE-----
+MIIBeTCCASCgAwIBAgICA18wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTIzNjIyWhcNMjIxMjE2MTIzNjIyWjATMREwDwYDVQQD
+DAhob3N0bmFtZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwk6fJxWRayvn
+hahQ2VsaqSMKhBz95yTcKRjyUlVDJeQ+zgJRnJMZZ4nJk23cXVaty7B+LHqtmBd/
+uxlifS7wC8/BGG9vOvw9PAObGGZf3Cr6clS/X7B13b+EQLE6xWUthO5Idh1F+h3i
+siVeqgaMEWbvQPBoFAiofmJK0umIvT0CAwEAAaMaMBgwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwCQYHKoZIzj0EAQNIADBFAiEAqL2CFiycv3caTfwPpababucvRfxY
+vuMM0qc2QR9FwIACIG+C60sFY8njx/hCwP/xD1+V25VucfsF8FLgpoJTRfbj
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem
new file mode 100644
index 0000000..65b7a24
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem
@@ -0,0 +1,45 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 864 (0x360)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:39:24 2014 GMT
+            Not After : Dec 16 12:39:24 2022 GMT
+        Subject: CN=192.168.1.50
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:d8:fe:96:35:15:34:48:a5:6c:21:65:8e:0b:9f:
+                    85:59:2e:24:f6:9b:23:2a:d9:d3:71:92:b3:24:2d:
+                    1f:ae:f5:bb:1b:84:e9:ed:42:8a:b9:47:bc:92:70:
+                    69:93:a7:c8:50:4b:05:89:36:67:34:b4:2a:97:fb:
+                    64:9e:49:19:68:0d:21:36:36:63:6f:df:d9:39:f7:
+                    e9:da:ff:fe:9a:a8:e6:d5:75:bb:3f:e5:38:f5:c2:
+                    26:f4:f1:f4:b6:5c:9b:a7:4b:2c:7d:34:ff:c0:87:
+                    ad:dc:2c:6a:bd:22:cc:13:78:ff:f5:93:c7:63:10:
+                    44:e0:3f:2c:04:91:26:9b:eb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:45:02:21:00:b9:1f:ee:63:e9:0a:79:a6:76:72:e8:d8:93:
+         b8:26:aa:ff:15:04:2b:f0:37:bb:45:96:5b:0b:ce:15:67:b7:
+         75:02:20:21:62:07:24:76:f4:98:90:f4:6d:7e:d7:57:62:a6:
+         6a:b1:40:b7:d2:73:1c:58:24:eb:a9:3a:19:90:34:0e:ba
+-----BEGIN CERTIFICATE-----
+MIIBfTCCASSgAwIBAgICA2AwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTIzOTI0WhcNMjIxMjE2MTIzOTI0WjAXMRUwEwYDVQQD
+DAwxOTIuMTY4LjEuNTAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANj+ljUV
+NEilbCFljgufhVkuJPabIyrZ03GSsyQtH671uxuE6e1CirlHvJJwaZOnyFBLBYk2
+ZzS0Kpf7ZJ5JGWgNITY2Y2/f2Tn36dr//pqo5tV1uz/lOPXCJvTx9LZcm6dLLH00
+/8CHrdwsar0izBN4//WTx2MQROA/LASRJpvrAgMBAAGjGjAYMAkGA1UdEwQCMAAw
+CwYDVR0PBAQDAgXgMAkGByqGSM49BAEDSAAwRQIhALkf7mPpCnmmdnLo2JO4Jqr/
+FQQr8De7RZZbC84VZ7d1AiAhYgckdvSYkPRtftdXYqZqsUC30nMcWCTrqToZkDQO
+ug==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem
new file mode 100644
index 0000000..827c71c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 867 (0x363)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:43:07 2014 GMT
+            Not After : Dec 16 12:43:07 2022 GMT
+        Subject: CN=*.google.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c0:4a:fd:4b:ac:bc:cd:ca:d2:7d:ba:03:d4:49:
+                    5e:68:fb:47:dd:f5:01:9c:ec:65:82:7f:50:9f:24:
+                    3f:9f:44:96:14:d5:9c:64:a9:19:51:83:2f:5e:62:
+                    11:a0:46:14:1f:e8:d9:c4:61:23:6b:fe:96:59:a3:
+                    cd:e5:c3:82:08:c4:3f:55:a4:5c:7a:63:9d:bb:58:
+                    ec:79:62:31:c2:4d:c4:1d:43:05:bc:09:78:a1:c1:
+                    27:21:41:b7:03:82:11:96:5d:b5:97:92:a1:93:f8:
+                    1c:e2:5f:33:e6:03:0e:03:9e:84:6a:72:d6:00:9f:
+                    77:75:2d:be:e6:84:fb:22:b3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:09:d4:15:d9:f2:48:dd:be:68:6f:1a:dd:48:fb:
+         85:e3:f3:e4:f8:67:a6:36:fc:0f:b2:bb:23:f7:ba:92:77:bc:
+         02:20:4f:aa:2f:29:1f:df:4f:0e:fa:fe:57:6e:85:5e:30:bd:
+         21:56:c0:ef:30:be:7b:48:6a:f1:71:46:f2:17:fe:b6
+-----BEGIN CERTIFICATE-----
+MIIBfDCCASSgAwIBAgICA2MwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTI0MzA3WhcNMjIxMjE2MTI0MzA3WjAXMRUwEwYDVQQD
+DAwqLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBK/Uus
+vM3K0n26A9RJXmj7R931AZzsZYJ/UJ8kP59ElhTVnGSpGVGDL15iEaBGFB/o2cRh
+I2v+llmjzeXDggjEP1WkXHpjnbtY7HliMcJNxB1DBbwJeKHBJyFBtwOCEZZdtZeS
+oZP4HOJfM+YDDgOehGpy1gCfd3UtvuaE+yKzAgMBAAGjGjAYMAkGA1UdEwQCMAAw
+CwYDVR0PBAQDAgXgMAkGByqGSM49BAEDRwAwRAIgCdQV2fJI3b5obxrdSPuF4/Pk
++GemNvwPsrsj97qSd7wCIE+qLykf308O+v5XboVeML0hVsDvML57SGrxcUbyF/62
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem
new file mode 100644
index 0000000..8c3ce2f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 687 (0x2af)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 13:55:10 2013 GMT
+            Not After : Nov  5 13:55:10 2017 GMT
+        Subject: CN=tester
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ee:44:f3:93:2c:a6:3f:9c:ba:2a:83:b0:bd:a5:
+                    74:a5:c8:d3:a8:a0:44:4d:eb:ab:f5:4d:72:5a:d8:
+                    6f:ce:74:bb:4a:d1:8a:a8:9a:e5:e2:a7:0c:95:c7:
+                    a0:7d:c8:84:6f:63:b2:c3:09:f4:ea:0c:06:f7:99:
+                    e2:0e:c3:f0:cf:44:30:33:08:f8:69:79:7a:63:34:
+                    6d:ed:a9:cf:f7:9b:ca:dd:24:25:cd:bc:0e:cc:17:
+                    cc:1f:8c:1c:15:7b:5f:ca:7e:26:15:dc:a5:54:7e:
+                    9c:47:46:59:1e:80:f0:37:e4:c3:d7:96:df:48:d2:
+                    a7:14:d8:8f:7a:78:ee:f3:5d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:localhost.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:46:02:21:00:97:1b:17:9e:9e:df:8f:fb:d2:18:5f:89:d8:
+         30:31:87:9c:f8:56:9b:6d:e1:b6:87:bf:c7:d6:c3:a9:ab:f3:
+         6c:02:21:00:fb:40:3e:6a:9a:72:d8:ac:95:b5:62:39:22:69:
+         34:61:ad:55:0f:e8:5f:51:08:48:24:53:b7:5c:72:cc:5d:ae
+-----BEGIN CERTIFICATE-----
+MIIBmDCCAT6gAwIBAgICAq8wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTM1NTEwWhcNMTcxMTA1MTM1NTEwWjARMQ8wDQYDVQQD
+DAZ0ZXN0ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO5E85Mspj+cuiqD
+sL2ldKXI06igRE3rq/VNclrYb850u0rRiqia5eKnDJXHoH3IhG9jssMJ9OoMBveZ
+4g7D8M9EMDMI+Gl5emM0be2pz/ebyt0kJc28DswXzB+MHBV7X8p+JhXcpVR+nEdG
+WR6A8Dfkw9eW30jSpxTYj3p47vNdAgMBAAGjOjA4MAkGA1UdEwQCMAAwCwYDVR0P
+BAQDAgXgMB4GA1UdEQQXMBWCE2xvY2FsaG9zdC5jaXNjby5jb20wCQYHKoZIzj0E
+AQNJADBGAiEAlxsXnp7fj/vSGF+J2DAxh5z4Vptt4baHv8fWw6mr82wCIQD7QD5q
+mnLYrJW1YjkiaTRhrVUP6F9RCEgkU7dccsxdrg==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem
new file mode 100644
index 0000000..dd676b7
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 717 (0x2cd)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:00:41 2013 GMT
+            Not After : Nov  5 15:00:41 2017 GMT
+        Subject: CN=tester13
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c2:04:ef:f9:51:a9:a6:72:ad:23:e7:af:7d:51:
+                    8b:dc:7e:d0:71:33:5d:93:65:52:6e:79:df:30:58:
+                    cf:08:4e:47:8d:4a:1f:6a:ea:48:67:68:27:10:0d:
+                    a8:c2:80:e8:0d:10:12:34:32:e9:d8:77:e5:15:ec:
+                    9c:95:ba:f4:16:57:e5:aa:6f:18:9e:ee:4e:6c:ef:
+                    4c:f3:bf:91:f0:4b:b0:8d:c0:ff:8f:6e:f1:f3:b9:
+                    31:8e:73:d9:5c:c5:5f:fd:30:ae:36:ba:80:aa:75:
+                    ae:4d:50:3b:ba:b3:9a:a6:d2:3a:45:20:c3:7e:86:
+                    79:af:74:d7:c2:d4:c1:d4:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                IP Address:192.168.51.140, IP Address:127.0.0.1
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:6e:64:22:3a:61:01:a6:bb:5a:3d:52:12:4e:b0:
+         14:50:34:6d:0d:44:ba:28:24:88:50:a8:3a:45:01:76:1e:3a:
+         02:20:5e:8f:cf:9b:74:e8:a3:29:7c:bb:15:2b:34:14:7a:ad:
+         1e:98:07:2a:cf:75:88:88:45:51:d7:2c:98:a0:4c:01
+-----BEGIN CERTIFICATE-----
+MIIBjzCCATegAwIBAgICAs0wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUwMDQxWhcNMTcxMTA1MTUwMDQxWjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwgTv+VGppnKt
+I+evfVGL3H7QcTNdk2VSbnnfMFjPCE5HjUofaupIZ2gnEA2owoDoDRASNDLp2Hfl
+Feyclbr0Flflqm8Ynu5ObO9M87+R8EuwjcD/j27x87kxjnPZXMVf/TCuNrqAqnWu
+TVA7urOaptI6RSDDfoZ5r3TXwtTB1PsCAwEAAaMxMC8wCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFQYDVR0RBA4wDIcEwKgzjIcEfwAAATAJBgcqhkjOPQQBA0cAMEQC
+IG5kIjphAaa7Wj1SEk6wFFA0bQ1EuigkiFCoOkUBdh46AiBej8+bdOijKXy7FSs0
+FHqtHpgHKs91iIhFUdcsmKBMAQ==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem
new file mode 100644
index 0000000..1a267de
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 719 (0x2cf)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:10:16 2013 GMT
+            Not After : Nov  5 15:10:16 2017 GMT
+        Subject: CN=tester14
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ae:3a:26:93:ee:cb:de:f6:5b:b5:fb:a3:2e:b6:
+                    2b:cc:eb:49:1a:76:38:60:30:eb:7f:9a:99:f5:d2:
+                    7f:fd:c8:da:bd:f3:38:4b:7d:27:fb:15:81:23:59:
+                    22:92:1f:ad:77:f7:5f:50:e7:69:e2:64:2a:68:33:
+                    63:62:02:10:fa:2c:e1:06:ae:cc:04:79:e5:8c:1f:
+                    ea:9a:26:9e:03:b0:4f:b4:f8:1c:2b:21:6b:b0:6d:
+                    68:24:ae:0c:2e:a7:36:7b:27:d4:f1:13:ef:75:5e:
+                    e2:11:49:fc:d4:f8:4d:2d:63:3e:c8:08:44:a3:c8:
+                    79:42:4e:85:a5:5e:ab:91:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:*.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:47:98:0f:e8:c3:0f:b1:8e:80:bc:ec:66:30:da:
+         68:0d:2e:c3:de:aa:20:66:e8:8a:dd:d0:12:97:dd:67:af:0d:
+         02:20:6f:8a:1a:37:f4:19:ce:5d:8c:5e:af:b2:e7:7d:72:5b:
+         7e:dc:a9:7e:49:ec:bf:c4:d5:5b:38:5b:9e:b2:1e:27
+-----BEGIN CERTIFICATE-----
+MIIBkDCCATigAwIBAgICAs8wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUxMDE2WhcNMTcxMTA1MTUxMDE2WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxNDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArjomk+7L3vZb
+tfujLrYrzOtJGnY4YDDrf5qZ9dJ//cjavfM4S30n+xWBI1kikh+td/dfUOdp4mQq
+aDNjYgIQ+izhBq7MBHnljB/qmiaeA7BPtPgcKyFrsG1oJK4MLqc2eyfU8RPvdV7i
+EUn81PhNLWM+yAhEo8h5Qk6FpV6rkVMCAwEAAaMyMDAwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFgYDVR0RBA8wDYILKi5jaXNjby5jb20wCQYHKoZIzj0EAQNHADBE
+AiBHmA/oww+xjoC87GYw2mgNLsPeqiBm6Ird0BKX3WevDQIgb4oaN/QZzl2MXq+y
+531yW37cqX5J7L/E1Vs4W56yHic=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem
new file mode 100644
index 0000000..2c9f5d2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 715 (0x2cb)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 14:35:46 2013 GMT
+            Not After : Nov  5 14:35:46 2017 GMT
+        Subject: CN=tester11
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:bd:98:4d:6e:91:3f:05:65:cf:ee:aa:24:16:b3:
+                    44:ba:d8:89:b1:c9:bd:10:0a:70:92:99:de:ea:63:
+                    04:ec:d5:e3:d5:3d:41:20:f7:ab:00:cc:21:a3:b9:
+                    bf:88:83:3d:14:f4:a9:84:95:23:6c:98:d5:d3:90:
+                    1f:cb:c0:6c:90:71:57:fd:39:e6:e4:99:29:83:d6:
+                    28:58:d3:ca:3f:70:06:a4:f0:25:82:b1:73:4e:03:
+                    a6:d9:51:cd:6b:70:c6:a9:4c:75:36:4e:eb:37:46:
+                    7c:1c:2e:cf:9b:be:e5:a2:0e:1b:21:74:e8:72:a2:
+                    ad:ed:b1:3c:a0:4a:d4:c3:17
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:roundhouse.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:28:51:20:46:6e:43:bc:37:f4:83:17:30:bd:04:
+         e1:6f:3c:e8:91:63:6e:d9:d2:24:79:c5:2d:3c:0c:c0:92:ed:
+         02:20:5e:e3:2c:6c:16:66:d1:dd:1d:f5:14:a1:bb:e7:54:55:
+         fa:e9:fd:76:a5:6a:f0:56:2d:13:27:c0:c6:4e:3b:0f
+-----BEGIN CERTIFICATE-----
+MIIBmTCCAUGgAwIBAgICAsswCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTQzNTQ2WhcNMTcxMTA1MTQzNTQ2WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvZhNbpE/BWXP
+7qokFrNEutiJscm9EApwkpne6mME7NXj1T1BIPerAMwho7m/iIM9FPSphJUjbJjV
+05Afy8BskHFX/Tnm5Jkpg9YoWNPKP3AGpPAlgrFzTgOm2VHNa3DGqUx1Nk7rN0Z8
+HC7Pm77log4bIXTocqKt7bE8oErUwxcCAwEAAaM7MDkwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwHwYDVR0RBBgwFoIUcm91bmRob3VzZS5jaXNjby5jb20wCQYHKoZI
+zj0EAQNHADBEAiAoUSBGbkO8N/SDFzC9BOFvPOiRY27Z0iR5xS08DMCS7QIgXuMs
+bBZm0d0d9RShu+dUVfrp/XalavBWLRMnwMZOOw8=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem
new file mode 100644
index 0000000..a804944
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 716 (0x2cc)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 14:56:29 2013 GMT
+            Not After : Nov  5 14:56:29 2017 GMT
+        Subject: CN=tester12
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c6:65:bf:f2:9f:fd:67:96:f9:f8:69:6b:83:c8:
+                    c6:d9:a8:53:03:0e:b5:7c:79:f9:83:05:6f:60:d8:
+                    0c:ec:33:b7:2a:95:48:d2:eb:d0:ba:cd:de:0c:71:
+                    ec:c6:ef:ab:ea:4e:d0:4d:46:e1:d0:4d:9d:4c:31:
+                    40:69:09:02:d1:66:0c:c2:be:6b:e5:ea:f5:15:38:
+                    16:b2:34:20:8d:19:ee:61:b0:4c:d3:59:ec:c3:64:
+                    fd:36:54:e3:49:2f:ee:8b:e3:06:42:ee:d7:af:5d:
+                    31:6c:43:c6:b4:41:40:dc:e2:b3:ed:f6:95:f1:9c:
+                    ec:72:d9:9c:07:af:32:cc:41
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                IP Address:192.168.50.65
+    Signature Algorithm: ecdsa-with-SHA1
+         30:46:02:21:00:ef:13:e8:c2:e0:30:5a:5f:93:41:7d:14:f1:
+         d0:c1:a9:44:d3:11:72:52:c1:6c:b1:22:12:09:d8:96:0b:fa:
+         1f:02:21:00:d3:74:15:65:fc:dc:d9:0f:e8:1c:4d:1d:b3:2e:
+         37:78:47:d0:69:95:9b:bc:a2:b5:c7:4f:32:0d:50:84:45:34
+-----BEGIN CERTIFICATE-----
+MIIBizCCATGgAwIBAgICAswwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTQ1NjI5WhcNMTcxMTA1MTQ1NjI5WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxmW/8p/9Z5b5
++Glrg8jG2ahTAw61fHn5gwVvYNgM7DO3KpVI0uvQus3eDHHsxu+r6k7QTUbh0E2d
+TDFAaQkC0WYMwr5r5er1FTgWsjQgjRnuYbBM01nsw2T9NlTjSS/ui+MGQu7Xr10x
+bEPGtEFA3OKz7faV8ZzsctmcB68yzEECAwEAAaMrMCkwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwDwYDVR0RBAgwBocEwKgyQTAJBgcqhkjOPQQBA0kAMEYCIQDvE+jC
+4DBaX5NBfRTx0MGpRNMRclLBbLEiEgnYlgv6HwIhANN0FWX83NkP6BxNHbMuN3hH
+0GmVm7yitcdPMg1QhEU0
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem
new file mode 100644
index 0000000..774c5d5
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 721 (0x2d1)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:15:18 2013 GMT
+            Not After : Nov  5 15:15:18 2017 GMT
+        Subject: CN=tester15
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a0:42:6b:73:58:0c:f2:85:6e:af:50:b3:19:44:
+                    a1:44:03:15:2b:e2:f5:0b:34:20:a3:c8:f2:d5:03:
+                    9c:5f:80:f7:ed:29:f5:9e:d4:0e:5f:64:99:a2:f1:
+                    dd:8e:d1:5b:99:85:48:1a:6f:7d:1d:50:d3:13:7b:
+                    48:df:2c:60:62:4b:5a:8b:19:2f:00:c3:b3:09:1c:
+                    09:51:27:eb:ab:ed:fb:06:62:31:69:fb:1e:f9:11:
+                    12:75:dd:a4:f9:8a:99:e9:f4:48:96:db:89:b8:64:
+                    fc:55:7f:5f:4f:1b:89:07:0c:05:b8:aa:4d:c6:e6:
+                    41:ee:7a:c3:f4:25:93:65:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:*.yahoo.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:20:9c:0e:e3:29:89:f4:2d:07:c5:66:52:9a:9a:
+         56:10:30:03:b1:9c:a3:aa:fd:2a:d2:d9:c7:67:1e:4b:f8:6a:
+         02:20:12:01:05:2b:73:8d:2b:aa:d3:5d:9e:f5:e5:4e:c8:a8:
+         eb:86:6e:02:95:f4:7d:57:d7:69:39:d8:67:7b:8b:1f
+-----BEGIN CERTIFICATE-----
+MIIBkDCCATigAwIBAgICAtEwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUxNTE4WhcNMTcxMTA1MTUxNTE4WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxNTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAoEJrc1gM8oVu
+r1CzGUShRAMVK+L1CzQgo8jy1QOcX4D37Sn1ntQOX2SZovHdjtFbmYVIGm99HVDT
+E3tI3yxgYktaixkvAMOzCRwJUSfrq+37BmIxafse+RESdd2k+YqZ6fRIltuJuGT8
+VX9fTxuJBwwFuKpNxuZB7nrD9CWTZVMCAwEAAaMyMDAwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFgYDVR0RBA8wDYILKi55YWhvby5jb20wCQYHKoZIzj0EAQNHADBE
+AiAgnA7jKYn0LQfFZlKamlYQMAOxnKOq/SrS2cdnHkv4agIgEgEFK3ONK6rTXZ71
+5U7IqOuGbgKV9H1X12k52Gd7ix8=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem
new file mode 100644
index 0000000..52cdb17
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC3COYY8jLXB0RL87GDAVn4vOwmcZKaU3DywL4q1iZvRRGG1+43
+ndMvIrKLm8WWADZzl8NM8noLLODM2fDsuht1jGaxhhD9vt9rZ5wOayoO0ICo3HrU
+3255KKdgGhG3rkCUu7QR7Rtvp5GuM+y/nDDz3JEstD6Myb3x0ar2wh1qzQIDAQAB
+AoGBAKMOA2Wvxci3SYlb9AQf3RXwv3NT8+UVdmZbfE3O7vhrED0O+cGEENFJ2ML0
+JyYE1H1PcoWG8WApP65ebPmGpdQkSK+/AOp+FV2mM4/PYPoyLDTtt2qQuQj78jdv
+jE1ePtB5hQPgL8ab8sfLRJDubfruPXaqEvbJd/6aMfBE5oThAkEA6ckLfTsnOn3G
+JEAv8Ym4DG6q+VJFNLEFKDZuaxCnQP+hYdfhRNTRnIK/2GtWlP4/NSE6d/S9Vyzl
+E5B8nHEICQJBAMhtUZn3VgB8cmWBzn4c6f60+ukN9ic1toYw5t8jPo2IuivX7j+B
+jOvu22xqj1xCisliCtYi6b3TFKtnc3BmlaUCQQCFUk+pBRjj9GIQvkIZHo7FGD+M
+m2w4FlN4kUH68K5RFPb1k2U2GZ/H/5BkXSItKajmJaLwUbPAiSvCMn29wX/xAkBu
+5C0V5sbqlfAlQWCiXhUJG9EHSPY8U3edX0kdhD6DyHZY86uZ72+sygcVQQ/4l8h6
+C4i7Wa3BnRv+icpREjERAkA0nZPIl+AvS+dHMYSa2Khysy/ikYpQZPqx85f/phD9
+cJVSBV6U8kR9FzEqHI4GmUMSwFex1JVxe8u29+piocA/
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem
new file mode 100644
index 0000000..4a76c85
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXwIBAAKBgQDCTp8nFZFrK+eFqFDZWxqpIwqEHP3nJNwpGPJSVUMl5D7OAlGc
+kxlnicmTbdxdVq3LsH4seq2YF3+7GWJ9LvALz8EYb286/D08A5sYZl/cKvpyVL9f
+sHXdv4RAsTrFZS2E7kh2HUX6HeKyJV6qBowRZu9A8GgUCKh+YkrS6Yi9PQIDAQAB
+AoGBAJ8chpMYxEXAZAw88keqpMBP43Kf0wKOWoKE3RmynBPRPeSwXWgbxLfWSuyh
+D8yXCCY91nRR2QksZPovJUlhQErdac3DtD991ucZUoVuY9hJXnKJ2PKbyc5sWtwj
+8u9Uj4XHukk+NPgAAyGfg3qREVFU+Cs8TBTqbQOofAjFvcM5AkEA8R31DCCsuan6
+66/tJbbgwGmnhh7NmUSfGM/ayb0fPAhhvy+MdGXlV4BjBOswc++3FsUsdPdOhaDH
+Hvk9Vv/HqwJBAM5M/dRQXNl0yXlDFyzPTZK8sq/3b0YZ2bZHNt2j0ID69RGd6+//
+qMC4msUudWahMBrJpz3ImHMd724jTB8JBrcCQQDA5DNesWEc4wQuur4gNs5Uf/Ga
+FVhrbf9NLTOdwqOTNi1kcpiNodq4hqgwjipkBXjWSYUISbRSqM3DVKciy1sjAkEA
+kAU/ztCrgUP+wyELNMUkKqSBOkdK721sh8v/iYurK+AG5l2RMOMSNDisE6vzWcp9
+grX0gzVe+D6VwBX0NlZe0QJBAJbLKGVhThv0XxkocM8g2wnbE+z3VZrcY93WuLuD
+WxOe2/JDbI9KeXbUMdeTAq89nVicv3JiQaYD/ZzhPnQJvb4=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem
new file mode 100644
index 0000000..0433f9e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDY/pY1FTRIpWwhZY4Ln4VZLiT2myMq2dNxkrMkLR+u9bsbhOnt
+Qoq5R7yScGmTp8hQSwWJNmc0tCqX+2SeSRloDSE2NmNv39k59+na//6aqObVdbs/
+5Tj1wib08fS2XJunSyx9NP/Ah63cLGq9IswTeP/1k8djEETgPywEkSab6wIDAQAB
+AoGAdbXNB6ij/xB1UryDTHzRWmo0tO29Kv6Uu9RHh0VPHZrBUBFO0Fy7YfyvJ4UB
+UI7AlAXOT/uKCsX9IQrHLzIaLB5SWb0tpCsCjB8++SQeIfKdpcNAyAhR3w9qQu2w
+c+d3AepsFeHwcSrqoeLAdg3/qfY5MMD6AFyI3+Xfjr27tUECQQD7GiRwUCJMd5dW
+P/kiU95MNuKg5N756QUSX3xRAjInQSJrZdILmQWITeUtdDROdtnmtOAHWK0YSVb9
+b0+N4DXhAkEA3Tohd8qbcvF63pcFMWW1D+gid1njEWQ1QktGQFvN1qP3OMAp+hxJ
+W3KCNWgdtIdJNnHGX4suStAlwZnDhxszSwJAZjkmTHLTA75L5dj1W3w5K13MtSN3
+gtXSMsCco335XPGvSXmSIRaSogLUIcUE5kyMONe5vEPlc9WnjFUcVe3JAQJAX7Vj
+f1DfObYxIxWRAJLw52XVa28u5npE9F5ekT9maQLc1OeGAZe1QOPkYzidCVoyGWTV
+nsY3C9TLUNu2FMB2ywJBAIEbxQzQ0C/FnZ6mt1IZOBvatdxbqhQQx8H2Lued1Jh5
+zkxLQBunmCxZkZgA4oGZnX3s2CjKJWHhib7zXnd0Ygc=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem
new file mode 100644
index 0000000..c98eed2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDASv1LrLzNytJ9ugPUSV5o+0fd9QGc7GWCf1CfJD+fRJYU1Zxk
+qRlRgy9eYhGgRhQf6NnEYSNr/pZZo83lw4IIxD9VpFx6Y527WOx5YjHCTcQdQwW8
+CXihwSchQbcDghGWXbWXkqGT+BziXzPmAw4DnoRqctYAn3d1Lb7mhPsiswIDAQAB
+AoGBAIdbQflm2nAx+QEvU0q3apGTb/85W61OahX18JZVOcOuz8ZSpwfkUEIkic9y
+UN309m2PxtqJVhnK16K5v1Gg0YKJ4fbffodwQstLhinQwjNEb+rfXusmzKyjV6nu
+zZ1C6+b5wijQenybIxRIm4sWFGxm5k5IFQiPQXN2bzkAHVfBAkEA4Bm1ohumDcRd
+2G/KuUdaiNdM6/GkviyXPDKa4Jvkh3pLcVzd2xa9Eb5Hc7tiwWdL8LPEbqssxHpu
+Eeics5NMCwJBANuqNF3aaufOKHtwsDg4c1G/mh3MweoNXAFNVCfW+a9E/0nKVqiV
+194XfRaq24w8iN5vG4l8wPbKUHiVitZSBPkCQQDZ4IUT/7eZKPJorH+3VSUzZ8e0
+mXInJyk+cKDdgRNVmFPi+nwENv5JUusbHPMtv+U4Nz7ire/PcoyzQqmfebTzAkAi
+XMHMXLqUgH98r+ghI1OG2j41oy5CesyFt58OjGaKsHRvCqP7w1T9fRcoUJn/mgif
+Iypfm5BkCsoD0wDg/fuhAkEA3APsWcyaOdTIRdmjnJVZ5wlWKRgMnPT6kTwkRlY8
+Fl1c5uNZnxtpgmOd+10O5a/16aNoumsFfDP4z/anAeo0NA==
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem
new file mode 100644
index 0000000..894542a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDuRPOTLKY/nLoqg7C9pXSlyNOooERN66v1TXJa2G/OdLtK0Yqo
+muXipwyVx6B9yIRvY7LDCfTqDAb3meIOw/DPRDAzCPhpeXpjNG3tqc/3m8rdJCXN
+vA7MF8wfjBwVe1/KfiYV3KVUfpxHRlkegPA35MPXlt9I0qcU2I96eO7zXQIDAQAB
+AoGAOl3dpPHND8wetodn5iz/tBTL9IS99rCCgSalmqdmTBbAQoWuB7taEPmG1bH/
+GJDkED1F7w0jV6n2kvS5MZDCDirPulZgQEhDSIIt8H7epLgZ9duv2axi3C4F3SOZ
+BJihh7EQVwXA1BMoyV6a3mhkOpJWoAt9O3F+pU7tgUymSakCQQD3o9wLjeBPmZST
+4kszwcASBlvXqLUGvdD6eg9n/gfVx0wkQPeKMVzMB78W2hDysGvK7e5IYiLXOUvl
+d50NRzL/AkEA9lAb0/Ja2QGMXQWtCVkjf3Sdh3ZYdROcaXqUaETU0fzitPqk+FwE
+5rXiCTi+/BOMFXqpQ9ECqmu7JaEwl+WFowJBANXHny5aTEprSthfgowrtqPY2XBT
+M/Od6cpRlPsxUZI7en/RleC2vGVmSpBvsDHSGzwUKqBSF8G0tNBjcjEERgECQQDH
+LdiS97RHL11WC7T1jkOKWb7ZP/YgFo+xLBK+joqalCivuM+WlrLP7dyvIFgwd44c
+AGjIEkhZj2xy7XniGxSJAkBfZ6/FU6TzTtXEppqN5Y19FswaLpG4JExZddxfmN57
+dhN1j5jjXlldpZXpkKXM/DwxPfNI6vVq+p771sTdFY9l
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem
new file mode 100644
index 0000000..1aab227
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDCBO/5Uammcq0j5699UYvcftBxM12TZVJued8wWM8ITkeNSh9q
+6khnaCcQDajCgOgNEBI0MunYd+UV7JyVuvQWV+Wqbxie7k5s70zzv5HwS7CNwP+P
+bvHzuTGOc9lcxV/9MK42uoCqda5NUDu6s5qm0jpFIMN+hnmvdNfC1MHU+wIDAQAB
+AoGBAKhLwB10SMSpFKbggckiwz8wgX6wH6uLaEWMUZ2Oa4e43zgf48DKGKQaJEg4
+kPqm9qLeHexJfa7X0U+DPxBiavazw/oEoHKf3pLOm/HQfr0gKik4hYxeBk1tqLhR
+7xUbzcxAWw9cBKr3qzrvKp86S1LzUD0nkLvH4pouCSgTcf6BAkEA5wVKh6Iaf8zs
++hyIvPVldOQPMyfW6XRUFkJj4wueGIlyAgzqKYGjVmgQ4ZI7JnooY9zT9U03vecL
+LocRQeAc9wJBANb/cF2COqYyTYjBvps29cTTOZajdhLYJB8yJcMici3epjW682gN
+epDM0SwiHsN6f7vzTsQTxEJGi7O/MY2Tmx0CQBkNbtakkqEXVP+OhpmPNxBlBuzy
+PhEVB2Vej7x4bw2UIiPsyJv1rgPZxzLfC8ERcD3cHbQ1Sn0yh/jkts9hAWMCQQCz
+LxkbFdqDgREVlUOiYN/cNp6caYeSlQkl5smKJD88839a8IDEi9dGryz1t0okaQfk
+QRe4WzEKe3kbSZGnCQoRAkEAmQBdntXZgFCrdMQbYvGGmjnPHGYK/OeKZN2ZVIKI
+VKMTz74yAiA6l/W0D7KY+aR+bm3lxUPCqIdryA7K1JhGJA==
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem
new file mode 100644
index 0000000..e296fac
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCuOiaT7sve9lu1+6MutivM60kadjhgMOt/mpn10n/9yNq98zhL
+fSf7FYEjWSKSH613919Q52niZCpoM2NiAhD6LOEGrswEeeWMH+qaJp4DsE+0+Bwr
+IWuwbWgkrgwupzZ7J9TxE+91XuIRSfzU+E0tYz7ICESjyHlCToWlXquRUwIDAQAB
+AoGAJWbcY0OPRpUSiRW1KVD4RCx6bBDW92dpFuKkjI4c5elZdA+jlSkQDSnHqEP5
+VO8x+SAEuUSEiZJBPE9T1XVmGUQczf87ZQHZRSwrIohih/MrvyrNakszD1VPlGIs
+fLwKAMFfM2/aXignZx9UJaMcTvzDNZ/FhlxgzYnrukfzPUECQQDVe/BijfLQs711
+KZyYWMchW84LcXYa3mtt2EVwfmZlvAlKIe0WyJ9KskYvssWKLIHcZvRIHiQgs+T/
+pPBUmgkhAkEA0OzF3uU2oX4aVhmu8H3R9AmH7mKFjySdTAFPyZMdh/AonsKO+B1t
+wGI+9jOgDC3qOp3GVygUTO7xpa+qSpUH8wJARrxZSx6DjRlxNigChgwsduYnYG1I
+1+BIsk3NvFd1cFIctd6F4124QhTN4rIWeBEFOlU2rcqm59sTjo1FQJMFYQJBAJYb
+uGNYjrLjNqrsExwpWmMbQfYOBWX+aaHFQ31R3SLhc1317eDozAUw0Yn1N6Xoi2UI
+HQxb+JH6D9b6asDhaO8CQCqNag8fEhmXzWiZDM+ngreFJsFFYiwwbcSOA0p3JBG/
+o39PeTTW4VC8+Y7KIO5rM8ZzuT3cArfsZLC8erAc2oI=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem
new file mode 100644
index 0000000..c057fa6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC9mE1ukT8FZc/uqiQWs0S62Imxyb0QCnCSmd7qYwTs1ePVPUEg
+96sAzCGjub+Igz0U9KmElSNsmNXTkB/LwGyQcVf9OebkmSmD1ihY08o/cAak8CWC
+sXNOA6bZUc1rcMapTHU2Tus3RnwcLs+bvuWiDhshdOhyoq3tsTygStTDFwIDAQAB
+AoGAVhUcsyMHdi/v5PEjSZtDjYJbonlyeiIDUszCRbGHvvCubVdOhuVsowQMEgZ4
+TEcqKKWdgjEk0F3kWBaMGTrZD0OlvjkhRDvR53P6mvDuN1+rDo4NxYe3GPX+Rylw
+YmkqJNkbspwPeu2tjD7gCRio/PiaQVAHmnZEi7JY0pjPH8ECQQDrh+3paJyZ3USt
+d+znBZwvXOH5tdEd7TMeSRlaETJIR+H+9043v4jbk2myH6fq/Ga2qKNEHJwj3A/q
+yYWHbdvPAkEAzhJkdmL61V03/Bs3EfV3q1K+7T7C+gBEJeXGgjfNbnPaKubej2R4
+yrYX3WLVKNwAyn9QBdc1C/3Li7On1+6OOQJAJnf9E7JSYY8SOILt3o28BMBW6EIZ
+9ZlYUAoAagWFAOk8sDgUPma6I6LgEIe7FVZEMHE6uVUy62h18RMZDBWVYQJBAMOD
+WcOkxz1WEz7jB03HqgFnIXRwPob5Z67ZIYz8qrDnxA8+OdysQ5p4R44pLfp1OV7m
+iaLw+whkhBEIo+9QC6kCQQDqJiutzJMhsp4hIDhB/hhmWlkbe5LZOFqLLejvSvhF
++vQb9bJ7OjcOD+xl6Zq0QZWg6PVtw+mV8rBqnvhn6Rgi
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem
new file mode 100644
index 0000000..1e1cf9f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDGZb/yn/1nlvn4aWuDyMbZqFMDDrV8efmDBW9g2AzsM7cqlUjS
+69C6zd4McezG76vqTtBNRuHQTZ1MMUBpCQLRZgzCvmvl6vUVOBayNCCNGe5hsEzT
+WezDZP02VONJL+6L4wZC7tevXTFsQ8a0QUDc4rPt9pXxnOxy2ZwHrzLMQQIDAQAB
+AoGAYlsLH9NlIzfb0OUiYgDlY/5ouPlPy9gEdIn7P32cWnUA92Fu6vHxVsq8rFrG
+UZF8HvrXlZfwVv5DXwSDtA69kAXKWgcStRK6idBLZZjpQqg/a05ajTuSaxeBh/PV
+63Ddff5W+GDnaFIsDxjSEscafYNFoYD6uaq9EuzbOjpSsOECQQD1kmH9s4Io9QFB
+wQ9WEpzc1GdoOq6RVTyWkGoA1Vvzb+3DphjlLK1OpFLzIEXFdOuqWdCul+MTsqEu
+U/k+V651AkEAztKIlF6YcY4jajrtEppaD+uyqPjiwO+YwY+dBpXuELmwtBEtDHV/
+Y5kMu5BLNNND5GFzqmNNIkCxRQ25lXTFHQJAKDPLuQXvNBdQGlypzxSC+6AGQckB
+lSdYIOoJgJNtV0Ams5X4k2QqByrdrZHF+lp2zk+f20VgVnQy8OusbSlblQJAeJFZ
+F41RtxkSYjI51BdxG0je3QxCPBEw7t3Gv3kV0GA/t6kvpsy0V554QrtqbEAgmvmI
+kTLUW3x1GvlWT99DrQJBAJH+dTB8xvxHxTC/KxErx48imZGZ/lqlpAeADY7ZMv2i
+Aidlu36/giQLmVPPpGdHbXmrXsR7PRn/woak5PZG8TI=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem
new file mode 100644
index 0000000..6a969e6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCgQmtzWAzyhW6vULMZRKFEAxUr4vULNCCjyPLVA5xfgPftKfWe
+1A5fZJmi8d2O0VuZhUgab30dUNMTe0jfLGBiS1qLGS8Aw7MJHAlRJ+ur7fsGYjFp
++x75ERJ13aT5ipnp9EiW24m4ZPxVf19PG4kHDAW4qk3G5kHuesP0JZNlUwIDAQAB
+AoGACOzqExh6UB+CVJ1H/EZIPQN28GTJhQA4OSb970MuOFt0c2780QW6bzGpJNwX
+nNaoJVq480/2ReW0e8dH/bX5ACnf/sZnLdhhOQ450ltB7KD+/B8U0nHwL5t0m8yj
+cq3CbYDa+xHc9DJOBc0bGzCPwHcMVPrutwk/uSbxwdKSk4ECQQDUTRIrWX+SlRyL
+84ImjwH8eS3Ak/WxyTy0kA88tzDrS6zjUKtYnVbdRhKP793p1qCAbRqG3mO8npGw
+2kVSfs+zAkEAwT8VcmVoiNp97OKRWW8wHvuqef3bDdCB5Gtyvrg/NZoJxrrECQYM
+A9bMLzJDETpBCTnKYFXcFNMHCNUCQ7FD4QJBAMsghr7lXfNxKiQZtDCSafMYOpdk
+uhMGGUgmAf40xOCMwnmoaJlbyY3jisUfWzNugJkASv458DG7gmg1H1jELpMCQEW9
+4puahZN+Zxvq2NISEqfb47aLK1dc/MPXE15JWGab5RNXrBAloohwKNiPpyy8fWQ6
++xKxafAKCOvjFCIiA4ECQQC0zNtjb+4T/zG25PzfRB8qTppRWi0gH+8TXExXuW92
+8icrxcynK0GXTZehoC5LnTeijixA/3Z2FPhYQmnq/GJi
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java b/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java
new file mode 100644
index 0000000..992acb2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.mime.encoding.Base64InputStream;
+import org.bouncycastle.mime.encoding.QuotedPrintableInputStream;
+
+public class BasicMimeParser
+    implements MimeParser
+{
+    private final InputStream src;
+    private final MimeParserContext parserContext;
+    private final String defaultContentTransferEncoding;
+    private Headers headers;
+
+    private boolean isMultipart = false;
+    private final String boundary;
+
+    public BasicMimeParser(
+        InputStream src)
+        throws IOException
+    {
+        this(null, new Headers(src, "7bit"), src);
+    }
+
+    public BasicMimeParser(
+        MimeParserContext parserContext, InputStream src)
+        throws IOException
+    {
+        this(parserContext, new Headers(src, parserContext.getDefaultContentTransferEncoding()), src);
+    }
+
+    public BasicMimeParser(
+        Headers headers, InputStream content)
+    {
+        this(null, headers, content);
+    }
+
+    public BasicMimeParser(
+        MimeParserContext parserContext, Headers headers, InputStream src)
+    {
+        if (headers.isMultipart())
+        {
+            isMultipart = true;
+            boundary = headers.getBoundary();
+        }
+        else
+        {
+            boundary = null;
+        }
+
+        this.headers = headers;
+        this.parserContext = parserContext;
+        this.src = src;
+        this.defaultContentTransferEncoding = (parserContext != null) ? parserContext.getDefaultContentTransferEncoding() : "7bit";
+    }
+
+
+
+    public void parse(MimeParserListener listener)
+        throws IOException
+    {
+        MimeContext context = listener.createContext(parserContext, headers);
+
+        String s;
+        if (isMultipart)    // Signed
+        {
+            MimeMultipartContext mContext = (MimeMultipartContext)context;
+            String startBoundary = "--" + boundary;
+            boolean startFound = false;
+            int partNo = 0;
+            LineReader rd = new LineReader(src);
+            while ((s = rd.readLine()) != null && !"--".equals(s))
+            {
+                if (startFound)
+                {
+                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
+                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);
+
+                    MimeContext partContext = mContext.createContext(partNo++);
+                    inputStream = partContext.applyContext(headers, inputStream);
+
+                    listener.object(parserContext, headers, processStream(headers, inputStream));
+
+                    if (inputStream.read() >= 0)
+                    {
+                        throw new IOException("MIME object not fully processed");
+                    }
+                }
+                else if (startBoundary.equals(s))
+                {
+                    startFound = true;
+                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
+                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);
+
+                    MimeContext partContext = mContext.createContext(partNo++);
+                    inputStream = partContext.applyContext(headers, inputStream);
+
+                    listener.object(parserContext, headers, processStream(headers, inputStream));
+
+                    if (inputStream.read() >= 0)
+                    {
+                        throw new IOException("MIME object not fully processed");
+                    }
+                }
+            }
+        }
+        else
+        {
+            InputStream inputStream = context.applyContext(headers, src);
+
+            listener.object(parserContext, headers, processStream(headers, inputStream));
+        }
+    }
+
+    public boolean isMultipart()
+    {
+        return isMultipart;
+    }
+
+    private InputStream processStream(Headers headers, InputStream inputStream)
+    {
+        if (headers.getContentTransferEncoding().equals("base64"))
+        {
+            return new Base64InputStream(inputStream);
+        }
+        else if (headers.getContentTransferEncoding().equals("quoted-printable"))
+        {
+            return new QuotedPrintableInputStream(inputStream);
+        }
+        else
+        {
+            return inputStream;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java
new file mode 100644
index 0000000..585e696
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Strings;
+
+public class BoundaryLimitedInputStream
+    extends InputStream
+{
+    final private InputStream src;
+    final private byte[] boundary;
+    final private byte[] buf;
+
+    private int bufOff = 0;
+    private int index = 0;
+    private boolean ended = false;
+
+    private int lastI;
+
+    public BoundaryLimitedInputStream(InputStream src, String startBoundary)
+    {
+        this.src = src;
+        boundary = Strings.toByteArray(startBoundary);
+        this.buf = new byte[startBoundary.length() + 3];
+        this.bufOff = 0;
+    }
+
+
+    public int read()
+        throws IOException
+    {
+        if (ended)
+        {
+            return -1;
+        }
+
+        int i;
+        if (index < bufOff)
+        {
+            i = buf[index++] & 0xff;
+            // if this happens we know we've already failed on a "\r\n"
+            if (index < bufOff)
+            {
+                return i;
+            }
+            index = bufOff = 0;
+        }
+        else
+        {
+            i = src.read();
+        }
+
+        lastI = i;
+
+        if (i < 0)
+        {
+            return -1;
+        }
+
+        if (i == '\r' || i == '\n') // check for start of boundary
+        {
+            int ch;
+            index = 0;
+            if (i == '\r')
+            {
+                ch = src.read();
+                if (ch == '\n')
+                {
+                    buf[bufOff++] = '\n';
+                    ch = src.read();
+                }
+            }
+            else
+            {
+                ch = src.read();
+            }
+
+            if (ch == '-')
+            {
+                buf[bufOff++] = '-';
+                ch = src.read();
+            }
+
+            if (ch == '-')
+            {
+                buf[bufOff++] = '-';
+
+                int base = bufOff;
+                int c;
+
+                while ((bufOff - base) != boundary.length && (c = src.read()) >= 0)
+                {
+                    buf[bufOff] = (byte)c;
+                    if (buf[bufOff] != boundary[bufOff - base])
+                    {
+                        bufOff++;
+                        break;
+                    }
+                    bufOff++;
+                }
+                
+                // we have a match
+                if (bufOff - base == boundary.length)
+                {
+                    ended = true;
+                    return -1;
+                }
+            }
+            else
+            {
+                if (ch >= 0)
+                {
+                    buf[bufOff++] = (byte)ch;
+                }
+            }
+        }
+
+        return i;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java
new file mode 100644
index 0000000..41d7d54
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.mime;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.mime.smime.SMimeParserContext;
+
+public class CanonicalOutputStream
+    extends FilterOutputStream
+{
+    protected int lastb;
+    protected static byte newline[];
+    private final boolean is7Bit;
+
+    public CanonicalOutputStream(SMimeParserContext parserContext, Headers headers, OutputStream outputstream)
+    {
+        super(outputstream);
+        lastb = -1;
+        // TODO: eventually may need to handle multiparts with binary...
+        if (headers.getContentType() != null)
+        {
+            is7Bit = headers.getContentType() != null && !headers.getContentType().equals("binary");
+        }
+        else
+        {
+            is7Bit = parserContext.getDefaultContentTransferEncoding().equals("7bit");
+        }
+    }
+
+    public void write(int i)
+        throws IOException
+    {
+        if (is7Bit)
+        {
+            if (i == '\r')
+            {
+                out.write(newline);
+            }
+            else if (i == '\n')
+            {
+                if (lastb != '\r')
+                {
+                    out.write(newline);
+                }
+            }
+            else
+            {
+                out.write(i);
+            }
+        }
+        else
+        {
+            out.write(i);
+        }
+        
+        lastb = i;
+    }
+
+    public void write(byte[] buf)
+        throws IOException
+    {
+        this.write(buf, 0, buf.length);
+    }
+
+    public void write(byte buf[], int off, int len)
+        throws IOException
+    {
+        for (int i = off; i != off + len; i++)
+        {
+            this.write(buf[i]);
+        }
+    }
+
+    public void writeln()
+        throws IOException
+    {
+        super.out.write(newline);
+    }
+
+    static 
+    {
+        newline = new byte[2];
+        newline[0] = '\r';
+        newline[1] = '\n';
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java
new file mode 100644
index 0000000..5afd06c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ConstantMimeContext
+    implements MimeContext, MimeMultipartContext
+{
+    public InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException
+    {
+        return contentStream;
+    }
+
+    public MimeContext createContext(int partNo)
+        throws IOException
+    {
+        return this;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java b/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java
new file mode 100644
index 0000000..3b67089
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java
@@ -0,0 +1,254 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bouncycastle.util.Iterable;
+import org.bouncycastle.util.Strings;
+
+public class Headers
+    implements Iterable<String>
+{
+    private final Map<String, List> headers = new TreeMap<String, List>(String.CASE_INSENSITIVE_ORDER);
+    private final List<String> headersAsPresented;
+    private final String contentTransferEncoding;
+
+    private String boundary;
+    private boolean multipart;
+    private String contentType;
+    private Map<String, String> contentTypeParameters;
+
+    private static List<String> parseHeaders(InputStream src)
+        throws IOException
+    {
+        String s;
+        List<String> headerLines = new ArrayList<String>();
+        LineReader   rd = new LineReader(src);
+
+        while ((s = rd.readLine()) != null)
+        {
+            if (s.length() == 0)
+            {
+                break;
+            }
+            headerLines.add(s);
+        }
+
+        return headerLines;
+    }
+
+    public Headers(InputStream source, String defaultContentTransferEncoding)
+        throws IOException
+    {
+        this(parseHeaders(source), defaultContentTransferEncoding);
+    }
+
+    public Headers(List<String> headerLines, String defaultContentTransferEncoding)
+    {
+        this.headersAsPresented = headerLines;
+
+        String header = "";
+        for (Iterator it = headerLines.iterator(); it.hasNext();)
+        {
+            String line = (String)it.next();
+            if (line.startsWith(" ") || line.startsWith("\t"))
+            {
+                header = header + line.trim();
+            }
+            else
+            {
+                if (header.length() != 0)
+                {
+                    this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
+                }
+                header = line;
+            }
+        }
+
+        // pick up last header line
+        if (header.trim().length() != 0)
+        {
+            this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
+        }
+
+        String contentTypeHeader = (this.getValues("Content-Type") == null) ? "text/plain" : this.getValues("Content-Type")[0];
+
+        int parameterIndex = contentTypeHeader.indexOf(';');
+        if (parameterIndex < 0)
+        {
+            contentType = contentTypeHeader;
+            contentTypeParameters = Collections.EMPTY_MAP;
+        }
+        else
+        {
+            contentType = contentTypeHeader.substring(0, parameterIndex);
+            contentTypeParameters = createContentTypeParameters(contentTypeHeader.substring(parameterIndex + 1).trim());
+        }
+
+        contentTransferEncoding = this.getValues("Content-Transfer-Encoding") == null ? defaultContentTransferEncoding : this.getValues("Content-Transfer-Encoding")[0];
+
+        if (contentType.indexOf("multipart") >= 0)
+        {
+            multipart = true;
+            String bound = (String)contentTypeParameters.get("boundary");
+            boundary = bound.substring(1, bound.length() - 1); // quoted-string
+        }
+        else
+        {
+            boundary = null;
+            multipart = false;
+        }
+    }
+
+    /**
+     * Return the a Map of the ContentType attributes and their values.
+     *
+     * @return a Map of ContentType parameters - empty if none present.
+     */
+    public Map<String, String> getContentTypeAttributes()
+    {
+        return contentTypeParameters;
+    }
+
+    /**
+     * Return the a list of the ContentType parameters.
+     *
+     * @return a list of ContentType parameters - empty if none present.
+     */
+    private Map<String, String> createContentTypeParameters(String contentTypeParameters)
+    {
+        String[] parameterSplit = contentTypeParameters.split(";");
+        Map<String, String> rv = new LinkedHashMap<String, String>();
+
+        for (int i = 0; i != parameterSplit.length; i++)
+        {
+            String parameter = parameterSplit[i];
+
+            int eqIndex = parameter.indexOf('=');
+            if (eqIndex < 0)
+            {
+                throw new IllegalArgumentException("malformed Content-Type header");
+            }
+
+            rv.put(parameter.substring(0, eqIndex).trim(), parameter.substring(eqIndex + 1).trim());
+        }
+
+        return Collections.unmodifiableMap(rv);
+    }
+
+    public boolean isMultipart()
+    {
+        return multipart;
+    }
+
+    public String getBoundary()
+    {
+        return boundary;
+    }
+
+    public String getContentType()
+    {
+        return contentType;
+    }
+
+    public String getContentTransferEncoding()
+    {
+        return contentTransferEncoding;
+    }
+
+    private void put(String field, String value)
+    {
+        synchronized (this)
+        {
+            KV kv = new KV(field, value);
+            List<KV> list = (List<KV>)headers.get(field);
+            if (list == null)
+            {
+                list = new ArrayList<KV>();
+                headers.put(field, list);
+            }
+            list.add(kv);
+        }
+    }
+
+    public Iterator<String> getNames()
+    {
+        return headers.keySet().iterator();
+    }
+
+    public String[] getValues(String header)
+    {
+
+        synchronized (this)
+        {
+            List<KV> kvList = (List<KV>)headers.get(header);
+            if (kvList == null)
+            {
+                return null;
+            }
+            String[] out = new String[kvList.size()];
+
+            for (int t = 0; t < kvList.size(); t++)
+            {
+                out[t] = ((KV)kvList.get(t)).value;
+            }
+
+            return out;
+        }
+    }
+
+    public boolean isEmpty()
+    {
+        synchronized (this)
+        {
+            return headers.isEmpty();
+        }
+    }
+
+    public boolean containsKey(String s)
+    {
+        return headers.containsKey(s);
+    }
+
+    public Iterator<String> iterator()
+    {
+        return headers.keySet().iterator();
+    }
+
+    public void dumpHeaders(OutputStream outputStream)
+        throws IOException
+    {
+        for (Iterator it = headersAsPresented.iterator(); it.hasNext();)
+        {
+            outputStream.write(Strings.toUTF8ByteArray(it.next().toString()));
+            outputStream.write('\r');
+            outputStream.write('\n');
+        }
+    }
+
+    private class KV
+    {
+        public final String key;
+        public final String value;
+
+        public KV(String key, String value)
+        {
+            this.key = key;
+            this.value = value;
+        }
+
+        public KV(KV kv)
+        {
+            this.key = kv.key;
+            this.value = kv.value;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java b/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java
new file mode 100644
index 0000000..f0e4aea
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.mime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * Read regular text lines, allowing for a single character look ahead.
+ */
+class LineReader
+{
+    private final InputStream src;
+
+    private int lastC = -1;
+
+    LineReader(InputStream src)
+    {
+        this.src = src;
+    }
+
+    String readLine()
+        throws IOException
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        int ch;
+
+        if (lastC != -1)
+        {
+            if (lastC == '\r')   // to get this we must have '\r\r' so blank line
+            {
+                return "";
+            }
+            ch = lastC;
+            lastC = -1;
+        }
+        else
+        {
+            ch = src.read();
+        }
+
+        while (ch >= 0 && ch != '\r' && ch != '\n')
+        {
+            bOut.write(ch);
+            ch = src.read();
+        }
+
+        if (ch == '\r')
+        {
+            int c = src.read();
+            if (c != '\n' && c >= 0)
+            {
+                lastC = c;
+            }
+        }
+
+        if (ch < 0)
+        {
+            return null;
+        }
+
+        return Strings.fromUTF8ByteArray(bOut.toByteArray());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java
new file mode 100644
index 0000000..5b7b31e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface MimeContext
+{
+    InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java
new file mode 100644
index 0000000..67e6c3e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the mime package and its sub-packages.
+ */
+public class MimeIOException
+    extends IOException
+{
+    private Throwable cause;
+
+    public MimeIOException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public MimeIOException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java
new file mode 100644
index 0000000..223ab06
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+public interface MimeMultipartContext
+    extends MimeContext
+{
+    public MimeContext createContext(int partNo)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java
new file mode 100644
index 0000000..edd9cc3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+/**
+ * Base interface for a MIME parser.
+ */
+public interface MimeParser
+{
+    /**
+     * Trigger the start of parsing.
+     *
+     * @param listener callback to be signalled as each MIME object is identified.
+     * @throws IOException on a parsing/IO exception.
+     */
+    void parse(MimeParserListener listener)
+        throws IOException;
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java
new file mode 100644
index 0000000..f00476f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.mime;
+
+/**
+ * Base interface for a MIME parser context.
+ */
+public interface MimeParserContext
+{
+    /**
+     * Return the default value for Content-Transfer-Encoding for data we are parsing.
+     *
+     * @return the default Content-Transfer-Encoding.
+     */
+    String getDefaultContentTransferEncoding();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java
new file mode 100644
index 0000000..d22e870
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Base interface for a MIME parser listener.
+ */
+public interface MimeParserListener
+{
+    /**
+     * Create an appropriate context object for the MIME object represented by headers.
+     *
+     * @param parserContext context object for the current parser.
+     * @param headers MIME headers for the object that has been discovered.
+     * @return a MimeContext
+     */
+    MimeContext createContext(MimeParserContext parserContext, Headers headers);
+
+    /**
+     * Signal that a MIME object has been discovered.
+     *
+     * @param parserContext context object for the current parser.
+     * @param headers headers for the MIME object.
+     * @param inputStream input stream representing its content.
+     * @throws IOException in case of a parsing/processing error.
+     */
+    void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java
new file mode 100644
index 0000000..d81e1f7
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface MimeParserProvider
+{
+    MimeParser createParser(InputStream source)
+        throws IOException;
+
+    MimeParser createParser(Headers headers, InputStream source)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java
new file mode 100644
index 0000000..cf08d63
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public abstract class MimeWriter
+{
+    protected final Headers headers;
+
+    protected MimeWriter(Headers headers)
+    {
+        this.headers = headers;
+    }
+
+    public Headers getHeaders()
+    {
+        return headers;
+    }
+
+    public abstract OutputStream getContentStream()
+        throws IOException;
+
+
+    protected static List<String> mapToLines(Map<String, String> headers)
+    {
+        List hdrs = new ArrayList(headers.size());
+
+        for (Iterator<String> it = headers.keySet().iterator(); it.hasNext();)
+        {
+            String key = (String)it.next();
+
+            hdrs.add(key + ": " + headers.get(key));
+        }
+
+        return hdrs;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java
new file mode 100644
index 0000000..75334ac
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java
@@ -0,0 +1,182 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reader for Base64 armored objects which converts them into binary data.
+ */
+public class Base64InputStream
+    extends InputStream
+{
+    /*
+     * set up the decoding table.
+     */
+    private static final byte[] decodingTable;
+
+    static
+    {
+        decodingTable = new byte[128];
+
+        for (int i = 'A'; i <= 'Z'; i++)
+        {
+            decodingTable[i] = (byte)(i - 'A');
+        }
+
+        for (int i = 'a'; i <= 'z'; i++)
+        {
+            decodingTable[i] = (byte)(i - 'a' + 26);
+        }
+
+        for (int i = '0'; i <= '9'; i++)
+        {
+            decodingTable[i] = (byte)(i - '0' + 52);
+        }
+
+        decodingTable['+'] = 62;
+        decodingTable['/'] = 63;
+    }
+
+    /**
+     * decode the base 64 encoded input data.
+     *
+     * @return the offset the data starts in out.
+     */
+    private int decode(
+        int      in0,
+        int      in1,
+        int      in2,
+        int      in3,
+        int[]    out)
+        throws EOFException
+    {
+        int    b1, b2, b3, b4;
+
+        if (in3 < 0)
+        {
+            throw new EOFException("unexpected end of file in armored stream.");
+        }
+
+        if (in2 == '=')
+        {
+            b1 = decodingTable[in0] &0xff;
+            b2 = decodingTable[in1] & 0xff;
+
+            out[2] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+
+            return 2;
+        }
+        else if (in3 == '=')
+        {
+            b1 = decodingTable[in0];
+            b2 = decodingTable[in1];
+            b3 = decodingTable[in2];
+
+            out[1] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+            out[2] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+
+            return 1;
+        }
+        else
+        {
+            b1 = decodingTable[in0];
+            b2 = decodingTable[in1];
+            b3 = decodingTable[in2];
+            b4 = decodingTable[in3];
+
+            out[0] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+            out[1] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+            out[2] = ((b3 << 6) | b4) & 0xff;
+
+            return 0;
+        }
+    }
+
+    InputStream    in;
+    int[]          outBuf = new int[3];
+    int            bufPtr = 3;
+    boolean        isEndOfStream;
+
+    /**
+     * Create a stream for reading a PGP armoured message, parsing up to a header
+     * and then reading the data that follows.
+     *
+     * @param in
+     */
+    public Base64InputStream(
+        InputStream    in)
+    {
+        this.in = in;
+    }
+    
+    public int available()
+        throws IOException
+    {
+        return in.available();
+    }
+    
+    private int readIgnoreSpace() 
+        throws IOException
+    {
+        int    c = in.read();
+        
+        while (c == ' ' || c == '\t')
+        {
+            c = in.read();
+        }
+        
+        return c;
+    }
+    
+    public int read()
+        throws IOException
+    {
+        int    c;
+
+        if (bufPtr > 2)
+        {
+            c = readIgnoreSpace();
+            
+            if (c == '\r' || c == '\n')
+            {
+                c = readIgnoreSpace();
+                
+                while (c == '\n' || c == '\r')
+                {
+                    c = readIgnoreSpace();
+                }
+
+                if (c < 0)                // EOF
+                {
+                    isEndOfStream = true;
+                    return -1;
+                }
+
+                bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf);
+            }
+            else
+            {
+                if (c >= 0)
+                {
+                    bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf);
+                }
+                else
+                {
+                    isEndOfStream = true;
+                    return -1;
+                }
+            }
+        }
+
+        c = outBuf[bufPtr++];
+
+        return c;
+    }
+    
+    public void close()
+        throws IOException
+    {
+        in.close();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java
new file mode 100644
index 0000000..51e7b1f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.encoders.Base64;
+
+public class Base64OutputStream
+    extends FilterOutputStream
+{
+    byte[] buffer = new byte[54];
+    int    bufOff;
+
+    public Base64OutputStream(OutputStream stream)
+    {
+        super(stream);
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        doWrite((byte)b);
+    }
+
+    public void write(byte[] buf, int bufOff, int len)
+        throws IOException
+    {
+        for (int i = 0; i != len; i++)
+        {
+            doWrite(buf[bufOff + i]);
+        }
+    }
+
+    public void write(byte[] buf)
+        throws IOException
+    {
+        write(buf, 0, buf.length);
+    }
+
+    public void close()
+        throws IOException
+    {
+        if (bufOff > 0)
+        {
+            Base64.encode(buffer, 0, bufOff, out);
+        }
+        out.close();
+    }
+    
+    private void doWrite(byte b)
+        throws IOException
+    {
+        buffer[bufOff++] = b;
+        if (bufOff == buffer.length)
+        {
+            Base64.encode(buffer, 0, buffer.length, out);
+            out.write('\r');
+            out.write('\n');
+            bufOff = 0;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java
new file mode 100644
index 0000000..4f82309
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Input stream that processes quoted-printable data, converting it into what was originally intended.
+ */
+public class QuotedPrintableInputStream
+    extends FilterInputStream
+{
+
+    public QuotedPrintableInputStream(InputStream input)
+    {
+        super(input);
+    }
+
+    public int read(byte[] buf, int bufOff, int len) throws IOException
+    {
+        int i = 0;
+        while (i != len)
+        {
+            int ch = this.read();
+            if (ch < 0)
+            {
+                break;
+            }
+            buf[i + bufOff] = (byte)ch;
+            i++;
+        }
+
+        if (i == 0)
+        {
+            return -1;
+        }
+        
+        return i;
+    }
+
+    public int read()
+        throws IOException
+    {
+        int v = in.read();
+        if (v == -1)
+        {
+            return -1;
+        }
+ 
+        // V was the quote '=' character/
+        while (v == '=')
+        {
+            //
+            // Get the next character.
+            //
+            int j = in.read();
+            if (j == -1)
+            {
+                throw new IllegalStateException("Quoted '=' at end of stream");
+            }
+
+            // For systems generating CRLF line endings.
+            if (j == '\r')
+            {
+                j = in.read();
+                if (j == '\n')
+                {
+                    //
+                    // This was a line break that was not actually a line break in the original information.
+                    // So return the next data.
+                    //
+                    j = in.read();
+                }
+                v = j;
+                continue;
+            }
+            else if (j == '\n')
+            {
+                // As above but without preceding CR.
+                v = in.read();
+                continue;
+            }
+            else
+            {
+
+                int chr = 0;
+
+                if (j >= '0' && j <= '9')
+                {
+                    chr = j - '0';
+                }
+                else if (j >= 'A' && j <= 'F')
+                {
+                    chr = 10 + (j - 'A');
+                }
+                else
+                {
+                    throw new IllegalStateException("Expecting '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF");
+                }
+
+                chr <<= 4;
+
+                j = in.read();
+
+                if (j >= '0' && j <= '9')
+                {
+                    chr |= j - '0';
+                }
+                else if (j >= 'A' && j <= 'F')
+                {
+                    chr |= 10 + (j - 'A');
+                }
+                else
+                {
+                    throw new IllegalStateException("Expecting second '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF");
+                }
+
+                return chr;
+            }
+        }
+
+        return v;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java
new file mode 100644
index 0000000..998c3bc
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java
@@ -0,0 +1,185 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInfoGenerator;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeIOException;
+import org.bouncycastle.mime.MimeWriter;
+import org.bouncycastle.mime.encoding.Base64OutputStream;
+import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Writer for SMIME Enveloped objects.
+ */
+public class SMIMEEnvelopedWriter
+    extends MimeWriter
+{
+    public static class Builder
+    {
+        private static final String[] stdHeaders;
+        private static final String[] stdValues;
+
+        static
+        {
+            stdHeaders = new String[]
+                {
+                    "Content-Type",
+                    "Content-Disposition",
+                    "Content-Transfer-Encoding",
+                    "Content-Description"
+                };
+
+            stdValues = new String[]
+                {
+                    "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data",
+                    "attachment; filename=\"smime.p7m\"",
+                    "base64",
+                    "S/MIME Encrypted Message"
+                };
+        }
+
+        private final CMSEnvelopedDataStreamGenerator envGen = new CMSEnvelopedDataStreamGenerator();
+        private final Map<String, String> headers = new LinkedHashMap<String, String>();
+
+        String contentTransferEncoding = "base64";
+
+        public Builder()
+        {
+            for (int i = 0; i != stdHeaders.length; i++)
+            {
+                headers.put(stdHeaders[i], stdValues[i]);
+            }
+        }
+
+        public Builder setUnprotectedAttributeGenerator(CMSAttributeTableGenerator unprotectedAttributeGenerator)
+        {
+            this.envGen.setUnprotectedAttributeGenerator(unprotectedAttributeGenerator);
+
+            return this;
+        }
+
+        public Builder setOriginatorInfo(OriginatorInformation originatorInfo)
+        {
+            this.envGen.setOriginatorInfo(originatorInfo);
+
+            return this;
+        }
+
+        /**
+         * Specify a MIME header (name, value) pair for this builder. If the headerName already exists it will
+         * be overridden.
+         *
+         * @param headerName name of the MIME header.
+         * @param headerValue value of the MIME header.
+         *
+         * @return the current Builder instance.
+         */
+        public Builder withHeader(String headerName, String headerValue)
+        {
+            this.headers.put(headerName, headerValue);
+
+            return this;
+        }
+        
+        /**
+         * Add a generator to produce the recipient info required.
+         *
+         * @param recipientGenerator a generator of a recipient info object.
+         *
+         * @return the current Builder instance.
+         */
+        public Builder addRecipientInfoGenerator(RecipientInfoGenerator recipientGenerator)
+        {
+            this.envGen.addRecipientInfoGenerator(recipientGenerator);
+
+            return this;
+        }
+
+        public SMIMEEnvelopedWriter build(OutputStream mimeOut, OutputEncryptor outEnc)
+        {
+            return new SMIMEEnvelopedWriter(this, outEnc, mimeOut);
+        }
+    }
+
+    private final CMSEnvelopedDataStreamGenerator envGen;
+
+    private final OutputEncryptor outEnc;
+    private final OutputStream mimeOut;
+    private final String contentTransferEncoding;
+
+    private SMIMEEnvelopedWriter(Builder builder, OutputEncryptor outEnc, OutputStream mimeOut)
+    {
+        super(new Headers(mapToLines(builder.headers), builder.contentTransferEncoding));
+
+        this.envGen = builder.envGen;
+        this.contentTransferEncoding = builder.contentTransferEncoding;
+        this.outEnc = outEnc;
+        this.mimeOut = mimeOut;
+    }
+    
+    public OutputStream getContentStream()
+        throws IOException
+    {
+        headers.dumpHeaders(mimeOut);
+
+        mimeOut.write(Strings.toByteArray("\r\n"));
+
+        try
+        {
+            OutputStream outStream;
+
+            if ("base64".equals(contentTransferEncoding))
+            {
+                outStream = new Base64OutputStream(mimeOut);
+                
+                return new ContentOutputStream(envGen.open(SMimeUtils.createUnclosable(outStream), outEnc), outStream);
+            }
+            else
+            {
+                return new ContentOutputStream(envGen.open(SMimeUtils.createUnclosable(mimeOut), outEnc), null);
+            }
+        }
+        catch (CMSException e)
+        {
+            throw new MimeIOException(e.getMessage(), e);
+        }
+    }
+
+    private class ContentOutputStream
+        extends OutputStream
+    {
+        private final OutputStream main;
+        private final OutputStream backing;
+
+        ContentOutputStream(OutputStream main, OutputStream backing)
+        {
+            this.main = main;
+            this.backing = backing;
+        }
+
+        public void write(int i)
+            throws IOException
+        {
+            main.write(i);
+        }
+
+        public void close()
+            throws IOException
+        {
+            main.close();
+            if (backing != null)
+            {
+                backing.close();
+            }
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java
new file mode 100644
index 0000000..86e8361
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java
@@ -0,0 +1,396 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeWriter;
+import org.bouncycastle.mime.encoding.Base64OutputStream;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Writer for SMIME Signed objects.
+ */
+public class SMIMESignedWriter
+    extends MimeWriter
+{
+    public static final Map RFC3851_MICALGS;
+    public static final Map RFC5751_MICALGS;
+    public static final Map STANDARD_MICALGS;
+
+    static
+    {
+        Map stdMicAlgs = new HashMap();
+
+        stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
+        stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
+        stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
+        stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
+        stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC5751_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
+
+        Map oldMicAlgs = new HashMap();
+
+        oldMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        oldMicAlgs.put(CMSAlgorithm.SHA1, "sha1");
+        oldMicAlgs.put(CMSAlgorithm.SHA224, "sha224");
+        oldMicAlgs.put(CMSAlgorithm.SHA256, "sha256");
+        oldMicAlgs.put(CMSAlgorithm.SHA384, "sha384");
+        oldMicAlgs.put(CMSAlgorithm.SHA512, "sha512");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC3851_MICALGS = Collections.unmodifiableMap(oldMicAlgs);
+
+        STANDARD_MICALGS = RFC5751_MICALGS;
+    }
+
+    public static class Builder
+    {
+        private static final String[] detHeaders;
+        private static final String[] detValues;
+        private static final String[] encHeaders;
+        private static final String[] encValues;
+
+        static
+        {
+            detHeaders = new String[]
+                {
+                    "Content-Type"
+                };
+
+            detValues = new String[]
+                {
+                    "multipart/signed; protocol=\"application/pkcs7-signature\"",
+                };
+
+            encHeaders = new String[]
+                {
+                    "Content-Type",
+                    "Content-Disposition",
+                    "Content-Transfer-Encoding",
+                    "Content-Description"
+                };
+
+            encValues = new String[]
+                {
+                    "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data",
+                    "attachment; filename=\"smime.p7m\"",
+                    "base64",
+                    "S/MIME Signed Message"
+                };
+        }
+
+        private final CMSSignedDataStreamGenerator sigGen = new CMSSignedDataStreamGenerator();
+        private final Map<String, String> extraHeaders = new LinkedHashMap<String, String>();
+        private final boolean encapsulated;
+        private final Map micAlgs = STANDARD_MICALGS;
+        
+        String contentTransferEncoding = "base64";
+
+        public Builder()
+        {
+            this(false);
+        }
+
+        public Builder(
+            boolean encapsulated)
+        {
+            this.encapsulated = encapsulated;
+        }
+
+        /**
+         * Specify a MIME header (name, value) pair for this builder. If the headerName already exists it will
+         * be overridden.
+         *
+         * @param headerName  name of the MIME header.
+         * @param headerValue value of the MIME header.
+         * @return the current Builder instance.
+         */
+        public Builder withHeader(String headerName, String headerValue)
+        {
+            this.extraHeaders.put(headerName, headerValue);
+
+            return this;
+        }
+
+        public Builder addCertificate(X509CertificateHolder certificate)
+            throws CMSException
+        {
+            this.sigGen.addCertificate(certificate);
+
+            return this;
+        }
+
+        public Builder addCertificates(Store certificates)
+            throws CMSException
+        {
+            this.sigGen.addCertificates(certificates);
+
+            return this;
+        }
+
+        /**
+         * Add a generator to produce the signer info required.
+         *
+         * @param signerGenerator a generator for a signer info object.
+         * @return the current Builder instance.
+         */
+        public Builder addSignerInfoGenerator(SignerInfoGenerator signerGenerator)
+        {
+            this.sigGen.addSignerInfoGenerator(signerGenerator);
+
+            return this;
+        }
+
+        public SMIMESignedWriter build(OutputStream mimeOut)
+        {
+            Map<String, String> headers = new LinkedHashMap<String, String>();
+
+            String boundary;
+            if (encapsulated)
+            {
+                boundary = null;
+                for (int i = 0; i != encHeaders.length; i++)
+                {
+                    headers.put(encHeaders[i], encValues[i]);
+                }
+            }
+            else
+            {
+                boundary = generateBoundary();
+
+                // handle Content-Type specially
+                StringBuffer contValue = new StringBuffer(detValues[0]);
+
+                addHashHeader(contValue, sigGen.getDigestAlgorithms());
+
+                addBoundary(contValue, boundary);
+                headers.put(detHeaders[0], contValue.toString());
+
+                for (int i = 1; i < detHeaders.length; i++)
+                {
+                    headers.put(detHeaders[i], detValues[i]);
+                }
+            }
+
+            for (Iterator it = extraHeaders.entrySet().iterator(); it.hasNext();)
+            {
+                Map.Entry ent = (Map.Entry)it.next();
+                headers.put((String)ent.getKey(), (String)ent.getValue());
+            }
+
+            return new SMIMESignedWriter(this, headers, boundary, mimeOut);
+        }
+
+        private void addHashHeader(
+            StringBuffer header,
+            List signers)
+        {
+            int count = 0;
+
+            //
+            // build the hash header
+            //
+            Iterator it = signers.iterator();
+            Set micAlgSet = new TreeSet();
+
+            while (it.hasNext())
+            {
+                AlgorithmIdentifier digest = (AlgorithmIdentifier)it.next();
+
+                String micAlg = (String)micAlgs.get(digest.getAlgorithm());
+
+                if (micAlg == null)
+                {
+                    micAlgSet.add("unknown");
+                }
+                else
+                {
+                    micAlgSet.add(micAlg);
+                }
+            }
+
+            it = micAlgSet.iterator();
+
+            while (it.hasNext())
+            {
+                String alg = (String)it.next();
+
+                if (count == 0)
+                {
+                    if (micAlgSet.size() != 1)
+                    {
+                        header.append("; micalg=\"");
+                    }
+                    else
+                    {
+                        header.append("; micalg=");
+                    }
+                }
+                else
+                {
+                    header.append(',');
+                }
+
+                header.append(alg);
+
+                count++;
+            }
+
+            if (count != 0)
+            {
+                if (micAlgSet.size() != 1)
+                {
+                    header.append('\"');
+                }
+            }
+        }
+
+        private void addBoundary(
+             StringBuffer header,
+             String boundary)
+        {
+             header.append(";\r\n\tboundary=\"");
+             header.append(boundary);
+             header.append("\"");
+        }
+
+        private String generateBoundary()
+        {
+            SecureRandom random = new SecureRandom();
+
+            return "==" + new BigInteger(180, random).setBit(179).toString(16) + "=";
+        }
+    }
+
+    private final CMSSignedDataStreamGenerator sigGen;
+
+    private final String boundary;
+    private final OutputStream mimeOut;
+    private final String contentTransferEncoding;
+
+    private SMIMESignedWriter(Builder builder, Map<String, String> headers, String boundary, OutputStream mimeOut)
+    {
+        super(new Headers(mapToLines(headers), builder.contentTransferEncoding));
+
+        this.sigGen = builder.sigGen;
+        this.contentTransferEncoding = builder.contentTransferEncoding;
+        this.boundary = boundary;
+        this.mimeOut = mimeOut;
+    }
+
+    /**
+     * Return a content stream for the signer - note data written to this stream needs to properly
+     * canonicalised if necessary.
+     *
+     * @return an output stream for data to be signed to be written to.
+     * @throws IOException on a stream error.
+     */
+    public OutputStream getContentStream()
+        throws IOException
+    {
+        headers.dumpHeaders(mimeOut);
+
+        mimeOut.write(Strings.toByteArray("\r\n"));
+
+        if (boundary == null)
+        {
+            return null; // TODO: new ContentOutputStream(sigGen.open(mimeOut, true), mimeOut);
+        }
+        else
+        {
+            mimeOut.write(Strings.toByteArray("This is an S/MIME signed message\r\n"));
+            mimeOut.write(Strings.toByteArray("\r\n--"));
+            mimeOut.write(Strings.toByteArray(boundary));
+            mimeOut.write(Strings.toByteArray("\r\n"));
+
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+            Base64OutputStream stream = new Base64OutputStream(bOut);
+
+            return new ContentOutputStream(sigGen.open(stream,false, SMimeUtils.createUnclosable(mimeOut)), mimeOut, bOut, stream);
+        }
+    }
+
+    private class ContentOutputStream
+        extends OutputStream
+    {
+        private final OutputStream main;
+        private final OutputStream backing;
+        private final ByteArrayOutputStream sigStream;
+        private final OutputStream sigBase;
+
+        ContentOutputStream(OutputStream main, OutputStream backing, ByteArrayOutputStream sigStream, OutputStream sigBase)
+        {
+            this.main = main;
+            this.backing = backing;
+            this.sigStream = sigStream;
+            this.sigBase = sigBase;
+        }
+
+        public void write(int i)
+            throws IOException
+        {
+            main.write(i);
+        }
+
+        public void close()
+            throws IOException
+        {
+            if (boundary != null)
+            {
+                main.close();
+
+                backing.write(Strings.toByteArray("\r\n--"));
+                backing.write(Strings.toByteArray(boundary));
+                backing.write(Strings.toByteArray("\r\n"));
+
+                backing.write(Strings.toByteArray("Content-Type: application/pkcs7-signature; name=\"smime.p7s\"\r\n"));
+                backing.write(Strings.toByteArray("Content-Transfer-Encoding: base64\r\n"));
+                backing.write(Strings.toByteArray("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"));
+                backing.write(Strings.toByteArray("\r\n"));
+
+                if (sigBase != null)
+                {
+                    sigBase.close();
+                }
+                
+                backing.write(sigStream.toByteArray());
+
+                backing.write(Strings.toByteArray("\r\n--"));
+                backing.write(Strings.toByteArray(boundary));
+                backing.write(Strings.toByteArray("--\r\n"));
+            }
+
+            if (backing != null)
+            {
+                backing.close();
+            }
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java
new file mode 100644
index 0000000..3395ab1
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.mime.CanonicalOutputStream;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeMultipartContext;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.io.TeeInputStream;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+public class SMimeMultipartContext
+    implements MimeMultipartContext
+{
+    private final SMimeParserContext parserContext;
+
+    private DigestCalculator[] calculators;
+
+
+    public SMimeMultipartContext(MimeParserContext parserContext, Headers headers)
+    {
+        this.parserContext = (SMimeParserContext)parserContext;
+        this.calculators = createDigestCalculators(headers);
+    }
+
+    DigestCalculator[] getDigestCalculators()
+    {
+        return calculators;
+    }
+
+    OutputStream getDigestOutputStream()
+    {
+        if (calculators.length == 1)
+        {
+            return calculators[0].getOutputStream();
+        }
+        else
+        {
+            OutputStream compoundStream = calculators[0].getOutputStream();
+
+            for (int i = 1; i < calculators.length; i++)
+            {
+                compoundStream = new TeeOutputStream(calculators[i].getOutputStream(), compoundStream);
+            }
+
+            return compoundStream;
+        }
+    }
+
+    private DigestCalculator[] createDigestCalculators(Headers headers)
+    {
+        try
+        {
+            Map<String, String> contentTypeFields = headers.getContentTypeAttributes();
+
+            String micalgs = (String)contentTypeFields.get("micalg");
+            if (micalgs == null)
+            {
+                throw new IllegalStateException("No micalg field on content-type header");
+            }
+
+            String[] algs = micalgs.substring(micalgs.indexOf('=') + 1).split(",");
+            DigestCalculator[] dcOut = new DigestCalculator[algs.length];
+
+            for (int t = 0; t < algs.length; t++)
+            {
+                // Deal with possibility of quoted parts, eg  "SHA1","SHA256" etc
+                String alg = SMimeUtils.lessQuotes(algs[t]).trim();
+                dcOut[t] = parserContext.getDigestCalculatorProvider().get(
+                    new AlgorithmIdentifier(SMimeUtils.getDigestOID(alg)));
+            }
+
+            return dcOut;
+        }
+        catch (OperatorCreationException e)
+        {
+            return null;
+        }
+    }
+
+    public MimeContext createContext(final int partNo)
+        throws IOException
+    {
+        return new MimeContext()
+        {
+            public InputStream applyContext(Headers headers, InputStream contentStream)
+                throws IOException
+            {
+                if (partNo == 0)
+                {
+                    OutputStream digestOut = getDigestOutputStream();
+
+                    headers.dumpHeaders(digestOut);
+
+                    digestOut.write('\r');
+                    digestOut.write('\n');
+
+                    return new TeeInputStream(contentStream, new CanonicalOutputStream(parserContext, headers, digestOut));
+                }
+
+                return contentStream;
+            }
+        };
+    }
+
+    public InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException
+    {
+        return contentStream;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java
new file mode 100644
index 0000000..f494414
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.mime.smime;
+
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+
+public class SMimeParserContext
+    implements MimeParserContext
+{
+    private final String defaultContentTransferEncoding;
+    private final DigestCalculatorProvider digestCalculatorProvider;
+
+    public SMimeParserContext(String defaultContentTransferEncoding, DigestCalculatorProvider digestCalculatorProvider)
+    {
+        this.defaultContentTransferEncoding = defaultContentTransferEncoding;
+        this.digestCalculatorProvider = digestCalculatorProvider;
+    }
+
+    public String getDefaultContentTransferEncoding()
+    {
+        return defaultContentTransferEncoding;
+    }
+
+    public DigestCalculatorProvider getDigestCalculatorProvider()
+    {
+        return digestCalculatorProvider;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java
new file mode 100644
index 0000000..d6e1ee8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSEnvelopedDataParser;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.mime.ConstantMimeContext;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeIOException;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserListener;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.io.Streams;
+
+public abstract class SMimeParserListener
+    implements MimeParserListener
+{
+    private DigestCalculator[] digestCalculators;
+    private SMimeMultipartContext parent;
+
+    public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+    {
+        if (headers.isMultipart())
+        {
+            parent = new SMimeMultipartContext(parserContext, headers);
+            this.digestCalculators = parent.getDigestCalculators();
+            return parent;
+        }
+        else
+        {
+            return new ConstantMimeContext();
+        }
+    }
+
+    public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException
+    {
+        try
+        {
+            if (headers.getContentType().equals("application/pkcs7-signature")
+                || headers.getContentType().equals("application/x-pkcs7-signature"))
+            {
+                Map<ASN1ObjectIdentifier, byte[]> hashes = new HashMap<ASN1ObjectIdentifier, byte[]>();
+
+                for (int i = 0; i != digestCalculators.length; i++)
+                {
+                    digestCalculators[i].getOutputStream().close();
+
+                    hashes.put(digestCalculators[i].getAlgorithmIdentifier().getAlgorithm(), digestCalculators[i].getDigest());
+                }
+
+                byte[] sigBlock = Streams.readAll(inputStream);
+
+                CMSSignedData signedData = new CMSSignedData(hashes, sigBlock);
+
+                signedData(parserContext, headers, signedData.getCertificates(), signedData.getCRLs(), signedData.getAttributeCertificates(), signedData.getSignerInfos());
+            }
+            else if (headers.getContentType().equals("application/pkcs7-mime")
+                  || headers.getContentType().equals("application/x-pkcs7-mime"))
+            {
+                CMSEnvelopedDataParser envelopedDataParser = new CMSEnvelopedDataParser(inputStream);
+
+                envelopedData(parserContext, headers, envelopedDataParser.getOriginatorInfo(), envelopedDataParser.getRecipientInfos());
+
+                envelopedDataParser.close();
+            }
+            else
+            {
+                content(parserContext, headers, inputStream);
+            }
+        }
+        catch (CMSException e)
+        {
+            throw new MimeIOException("CMS failure: " + e.getMessage(), e);
+        }
+    }
+
+    public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException
+    {
+        throw new IllegalStateException("content handling not implemented");
+    }
+
+    public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+        throws IOException, CMSException
+    {
+        throw new IllegalStateException("signedData handling not implemented");
+    }
+
+    public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originatorInformation, RecipientInformationStore recipients)
+        throws IOException, CMSException
+    {
+        throw new IllegalStateException("envelopedData handling not implemented");
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java
new file mode 100644
index 0000000..5c5bba6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.mime.BasicMimeParser;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+
+public class SMimeParserProvider
+    implements MimeParserProvider
+{
+    private final String defaultContentTransferEncoding;
+    private final DigestCalculatorProvider digestCalculatorProvider;
+
+    public SMimeParserProvider(String defaultContentTransferEncoding, DigestCalculatorProvider digestCalculatorProvider)
+    {
+        this.defaultContentTransferEncoding = defaultContentTransferEncoding;
+        this.digestCalculatorProvider = digestCalculatorProvider;
+    }
+
+    public MimeParser createParser(InputStream source)
+        throws IOException
+    {
+        return new BasicMimeParser(new SMimeParserContext(defaultContentTransferEncoding, digestCalculatorProvider), source);
+    }
+
+    public MimeParser createParser(Headers headers, InputStream source)
+        throws IOException
+    {
+        return new BasicMimeParser(new SMimeParserContext(defaultContentTransferEncoding, digestCalculatorProvider), headers, source);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java
new file mode 100644
index 0000000..6f9daeb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.util.Strings;
+
+class SMimeUtils
+{
+    private static final Map RFC5751_MICALGS;
+    private static final Map RFC3851_MICALGS;
+    private static final Map STANDARD_MICALGS;
+    private static final Map forMic;
+
+    private static final byte[] nl = new byte[2];
+
+
+    static
+    {
+        nl[0] = '\r';
+        nl[1] = '\n';
+
+
+        Map stdMicAlgs = new HashMap();
+
+        stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
+        stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
+        stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
+        stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
+        stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC5751_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
+
+        Map oldMicAlgs = new HashMap();
+
+        oldMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        oldMicAlgs.put(CMSAlgorithm.SHA1, "sha1");
+        oldMicAlgs.put(CMSAlgorithm.SHA224, "sha224");
+        oldMicAlgs.put(CMSAlgorithm.SHA256, "sha256");
+        oldMicAlgs.put(CMSAlgorithm.SHA384, "sha384");
+        oldMicAlgs.put(CMSAlgorithm.SHA512, "sha512");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+
+        RFC3851_MICALGS = Collections.unmodifiableMap(oldMicAlgs);
+
+        STANDARD_MICALGS = RFC5751_MICALGS;
+
+
+        Map<String, ASN1ObjectIdentifier> mic = new TreeMap<String, ASN1ObjectIdentifier>(String.CASE_INSENSITIVE_ORDER);
+
+        for (Iterator it = STANDARD_MICALGS.keySet().iterator(); it.hasNext();)
+        {
+            Object key = it.next();
+            mic.put(STANDARD_MICALGS.get(key).toString(), (ASN1ObjectIdentifier)key);
+        }
+
+        for (Iterator it = RFC3851_MICALGS.keySet().iterator(); it.hasNext();)
+        {
+            Object key = it.next();
+            mic.put(RFC3851_MICALGS.get(key).toString(), (ASN1ObjectIdentifier)key);
+        }
+
+        forMic = Collections.unmodifiableMap(mic);
+
+    }
+
+    static String lessQuotes(String in)
+    {
+        if (in == null || in.length() <= 1)  // make sure we have at least 2 characters
+        {
+            return in;
+        }
+
+        if (in.charAt(0) == '"' && in.charAt(in.length() - 1) == '"')
+        {
+            return in.substring(1, in.length() - 1);
+        }
+
+        return in;
+    }
+
+    static String getParameter(String startsWith, List<String> parameters)
+    {
+        for (Iterator<String> paramIt = parameters.iterator(); paramIt.hasNext(); )
+        {
+            String param = (String)paramIt.next();
+            if (param.startsWith(startsWith))
+            {
+                return param;
+            }
+        }
+
+        return null;
+    }
+
+    static ASN1ObjectIdentifier getDigestOID(String alg)
+    {
+        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)forMic.get(Strings.toLowerCase(alg));
+
+        if (oid == null)
+        {
+            throw new IllegalArgumentException("unknown micalg passed: " + alg);
+        }
+
+        return oid;
+    }
+
+    static OutputStream createUnclosable(OutputStream destination)
+    {
+        return new FilterOutputStream(destination)
+        {
+            public void close()
+                throws IOException
+            {
+
+            }
+        };
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java
new file mode 100644
index 0000000..5c17aa6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.mime.test;
+
+import java.security.Security;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public class AllTests
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    public void setUp()
+    {
+        if (Security.getProvider(BC) != null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static Test suite()
+        throws Exception
+    {
+        TestSuite suite = new TestSuite("MIME tests");
+
+        suite.addTestSuite(Base64TransferEncodingTest.class);
+        suite.addTestSuite(MimeParserTest.class);
+        suite.addTestSuite(MultipartParserTest.class);
+        suite.addTestSuite(QuotedPrintableTest.class);
+        suite.addTestSuite(TestBoundaryLimitedInputStream.class);
+        suite.addTestSuite(TestSMIMEEnveloped.class);
+        suite.addTestSuite(TestSMIMESigned.class);
+        suite.addTestSuite(TestSMIMESignEncrypt.class);
+
+        return new MIMETestSetup(suite);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java
new file mode 100644
index 0000000..cf765e2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java
@@ -0,0 +1,266 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.util.encoders.Base64;
+
+public class Base64TransferEncodingTest
+    extends TestCase
+{
+    private SecureRandom random = new SecureRandom();
+
+    /**
+     * Test the decoding of some base64 arranged in lines of
+     * 64 byte base 64 encoded rows terminated CRLF.
+     *
+     * @throws Exception
+     */
+    public void testDecodeWellFormed()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+            
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode without CR only LF.
+     *
+     * @throws Exception
+     */
+    public void testDecodeWithoutCR()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode with long lines past the length in the spec.
+     *
+     * @throws Exception
+     */
+    public void testDecodeLongLines()
+        throws Exception
+    {
+        byte[][] original = new byte[4][765];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 1023 bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode with long lines past the length in the spec.
+     *
+     * @throws Exception
+     */
+    public void testExcessiveLongLine()
+        throws Exception
+    {
+        byte[][] original = new byte[4][766];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 1023 bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        try
+        {
+            verifyDecode(original, bos);
+        }
+        catch (Exception ex)
+        {
+            TestCase.assertEquals("End of line of base64 not reached before line buffer overflow.", ex.getMessage());
+        }
+    }
+
+
+    /**
+     * Test decode of empty data.
+     *
+     * @throws Exception
+     */
+    public void testEmpty()
+        throws Exception
+    {
+        // Assertions in verifyDecode()
+        verifyDecode(new byte[0][0], new ByteArrayOutputStream());
+    }
+
+
+    private void verifyDecode(byte[][] original, ByteArrayOutputStream bos)
+        throws IOException
+    {
+//        MimeParserInputStream source = new MimeParserInputStream(new ByteArrayInputStream(bos.toByteArray()), 1024);
+//        Base64TransferDecoder bte = new Base64TransferDecoder(source, 1024);
+//
+//        for (byte[] row : original)
+//        {
+//            for (byte expected : row)
+//            {
+//                TestCase.assertEquals(expected & 0xFF, bte.read());
+//            }
+//        }
+//
+//        TestCase.assertEquals(-1, bte.read());
+
+    }
+
+
+    /**
+     * This test causes the final line of base64 to not be a multiple of 64.
+     *
+     * @throws Exception
+     */
+    public void testDecodeLengths()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+        original[original.length - 1] = new byte[22];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * This test causes the final line of base64 to not be a multiple of 64.
+     *
+     * @throws Exception
+     */
+    public void testPartialLineEnding()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+        original[original.length - 1] = new byte[22];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    public void testMultilined()
+        throws Exception
+    {
+        String b64 = "MIAGCSqGSIb3DQEHA6CAMIACAQAxggFOMIIBSgIBADCBsjCBrDELMAkGA1UEBhMCQVQxEDAOBgNV\n" +
+            "BAgTB0F1c3RyaWExDzANBgNVBAcTBlZpZW5uYTEaMBgGA1UEChMRVGlhbmkgU3Bpcml0IEdtYkgx\n" +
+            "GTAXBgNVBAsTEERlbW8gRW52aXJvbm1lbnQxEDAOBgNVBAMTB1Rlc3QgQ0ExMTAvBgkqhkiG9w0B\n" +
+            "CQEWIm1hc3NpbWlsaWFuby5tYXNpQHRpYW5pLXNwaXJpdC5jb20CAQkwDQYJKoZIhvcNAQEBBQAE\n" +
+            "gYALxKaiVW43jHjDiJ4kC6N90lpyG0jxeJ7nynWaR4YkDiUQ/jE8cJwRX0jBQeWKRvf3Y+XhRuB3\n" +
+            "B76cKxBGTgMh6pCuLoIvgBJq54kqql/xz3hO7QRvvuHnEljlw2uhd0PQqQYe8oLdu1Yqyo9+9Jsx\n" +
+            "I7QX43E2H5b3nNGND24djDCABgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBD+UNge0S52HEPuFBEq\n" +
+            "IEvYoIAEggHAcOET1XS7H/OZALZ0cyns3p6kxgAlblE4BvMQnAen8VlhDehp130WdDF4jC+zRjza\n" +
+            "ZftPatKq/Hlhu0wuj+FZESjy2d2hR7FT8qCqGda70IyyOhloG7Ym+17E0MyYQsH38i+uC8NjcSeo\n" +
+            "egggsQoidePpg/9BNFMA4j6vORFcNBvnwj71mV2icx7mUud97cXobJnrfm3hmEmYkm7wL413cibH\n" +
+            "b8K3yNu/hMqJViT0GvlhQdR9hDgu5i2WhiE2UTaFu3xL2xNhzXBvhOwj/gikzFIWva4S/2JfK3M8\n" +
+            "A0lYu6f1vYUF2jazi81wQFEF7qKyp7zx7X2iZjn8DDSCY73izHafF1JJijDFaHrD5245kaSJ7MKP\n" +
+            "jJ/HWk9lbed0ay8f96QuvWEEKSy4xejy6w7DKxKr4icN7KDE5Nyc2ZAJxmCm50B7yHpNZfKQ38E+\n" +
+            "e/bCgvAESFcnw9pRJz9mXmwazxEvCpoO/ezgmgro+59CCRKqdUeOyyLQg6d7xqUcgeY1SoDxzEre\n" +
+            "i4IBlig6+HWLs+9OPMa2fuYYIVZvg7mpeM4lEfdhRssWBWwTTmrtwRbAaT7BTCtlvfqzpHrycp5O\n" +
+            "zgAAAAAAAAAAAAA=";
+
+
+        byte[] data = Base64.decode(b64);
+
+//
+//        MimeParserInputStream mpin = new MimeParserInputStream(new ByteArrayInputStream(b64.getBytes()), 1024);
+//        Base64TransferDecoder btd = new Base64TransferDecoder(mpin, 1024);
+//
+//
+//        for (int t = 0; t < data.length; t++)
+//        {
+//            TestCase.assertEquals("Position: " + t, data[t] & 0xFF, btd.read());
+//        }
+//
+//        TestCase.assertEquals(-1, btd.read());
+
+    }
+
+}
+
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java
new file mode 100644
index 0000000..17e8484
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java
@@ -0,0 +1,28 @@
+
+package org.bouncycastle.mime.test;
+
+import java.security.Security;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+class MIMETestSetup
+    extends TestSetup
+{
+    public MIMETestSetup(Test test)
+    {
+        super(test);
+    }
+
+    protected void setUp()
+    {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    protected void tearDown()
+    {
+        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+    }
+
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java
new file mode 100644
index 0000000..9519569
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.util.Strings;
+
+public class MimeParserTest
+    extends TestCase
+{
+    public void testMixtureOfHeaders()
+        throws Exception
+    {
+
+        String parts[] = new String[]{
+            "Received", "from mr11p26im-asmtp003.me.com (mr11p26im-asmtp003.me.com [17.110.86.110]) " +
+            "by tauceti.org.au (Our Mail Server) with ESMTP (TLS) id 23294071-1879654 " +
+            "for <megan@cryptoworkshop.com>; Fri, 29 Jun 2018 14:52:26 +1000\n",
+            "Return-Path", " <pogobot@icloud.com>\n",
+            "X-Verify-SMTP", " Host 17.110.86.110 sending to us was not listening\r\n"
+        };
+
+
+        String values = parts[0] + ":" + parts[1] + parts[2] + ":" + parts[3] + parts[4] + ":" + parts[5] + "\r\n";
+
+        Headers headers = new Headers(new ByteArrayInputStream(Strings.toByteArray(values)), "7bit");
+
+        for (int t = 0; t < parts.length; t += 2)
+        {
+            TestCase.assertEquals("Part " + t, parts[t + 1].trim(), headers.getValues(parts[t])[0]);
+        }
+
+    }
+
+    public void testEndOfHeaders()
+        throws Exception
+    {
+        String values = "Foo: bar\r\n\r\n";
+
+        Headers headers = new Headers(new ByteArrayInputStream(Strings.toByteArray(values)), "7bit");
+
+        assertEquals("bar", headers.getValues("Foo")[0]);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java
new file mode 100644
index 0000000..ec8ea48
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java
@@ -0,0 +1,447 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.BasicMimeParser;
+import org.bouncycastle.mime.ConstantMimeContext;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeMultipartContext;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserListener;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+public class MultipartParserTest
+    extends TestCase
+{
+
+    protected void setUp()
+        throws Exception
+    {
+        if (Security.getProvider("BC") == null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_wellformed()
+        throws Exception
+    {
+        String value = "multipart/alternative;\n" +
+            " boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(1, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"}", fieldValues.toString());
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_wellformed_multi()
+        throws Exception
+    {
+        String value = "multipart/signed;\n" +
+            " boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"; micalg=\"SHA1\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/signed", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\", micalg=\"SHA1\"}", fieldValues.toString());
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_broken()
+        throws Exception
+    {
+
+        // Verify limit checking
+
+        String value = "multipart/alternative;\n" +
+            " boundary=\"cats\"; micalg=";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"cats\", micalg=}", fieldValues.toString());
+    }
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_empty_micalg()
+        throws Exception
+    {
+
+        // Verify limit checking
+
+        String value = "multipart/alternative;\n" +
+            " boundary=\"cats\"; micalg=\"\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"cats\", micalg=\"\"}", headers.getContentTypeAttributes().toString());
+    }
+
+    public void testSignedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("quotable.message"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+
+                dataParsed.markDone();
+            }
+
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    public void testInvalidSha256SignedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("3nnn_smime.eml"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        // in this case the signature is invalid
+                        assertEquals(false, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    public void testEmbeddedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("embeddedmulti.message"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    public void testMultipartAlternative()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("multi-alternative.eml"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+
+                MimeParser basicMimeParser = new BasicMimeParser(parserContext, headers, inputStream);
+
+                basicMimeParser.parse(new MimeParserListener()
+                {
+                    public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+                    {
+                        return new ConstantMimeContext();
+                    }
+
+                    public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                        throws IOException
+                    {
+                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                        Streams.pipeAll((InputStream)inputStream, bos);
+                        results.add(bos.toString());
+                        System.out.println("#######################################################################");
+                        System.out.println(bos.toString());
+                        System.out.println("#######################################################################");
+                    }
+                });
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    /**
+     * Happy path mime multipart test.
+     *
+     * @throws IOException
+     */
+    public void testMimeMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        BasicMimeParser p = new BasicMimeParser(this.getClass().getResourceAsStream("simplemultipart.eml"));
+
+        p.parse(new MimeParserListener()
+        {
+            public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+            {
+                return new MimeMultipartContext()
+                {
+                    public InputStream applyContext(Headers headers, InputStream contentStream)
+                        throws IOException
+                    {
+                        return contentStream;
+                    }
+
+                    public MimeContext createContext(int partNo)
+                        throws IOException
+                    {
+                        return new MimeContext()
+                        {
+                            public InputStream applyContext(Headers headers, InputStream contentStream)
+                                throws IOException
+                            {
+                                return contentStream;
+                            }
+                        };
+                    }
+                };
+            }
+
+            public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                results.add(Strings.fromByteArray(Streams.readAll(inputStream)));
+            }
+        });
+
+
+        String[] expected = new String[]{
+            "The cat sat on the mat\n" +
+                "\n" +
+                "Boo!\n" +
+                "\n",
+            "<html><head><meta http-equiv=\"Content-Type\" object=\"text/html; charset=us-ascii\"></head><object style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><meta http-equiv=\"Content-Type\" object=\"text/html; charset=us-ascii\" class=\"\"><div style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\">The cat sat on the mat<div class=\"\"><br class=\"\"></div><div class=\"\"><font size=\"7\" class=\"\">Boo!</font></div><div class=\"\"><font size=\"7\" class=\"\"><br class=\"\"></font></div><div class=\"\"><img src=\"http://img2.thejournal.ie/inline/1162441/original/?width=630&amp;version=1162441\" alt=\"Image result for cows\" class=\"\"></div></div></object></html>"
+        };
+
+        TestCase.assertEquals("Size same:", expected.length, results.size());
+
+        for (int t = 0; t < results.size(); t++)
+        {
+            TestCase.assertEquals("Part: " + t, expected[t], results.get(t));
+        }
+
+    }
+
+
+
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java
new file mode 100644
index 0000000..22fae1a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.encoding.QuotedPrintableInputStream;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+public class QuotedPrintableTest
+    extends TestCase
+{
+    public void testQuotedPrintable()
+        throws IOException
+    {
+        String qp = "J'interdis aux marchands de vanter trop leur marchandises. Car ils se font =\n" +
+            "vite p=C3=A9dagogues et t'enseignent comme but ce qui n'est par essence qu'=\n" +
+            "un moyen, et te trompant ainsi sur la route =C3=A0 suivre les voil=C3=A0 bi=\n" +
+            "ent=C3=B4t qui te d=C3=A9gradent, car si leur musique est vulgaire ils te f=\n" +
+            "abriquent pour te la vendre une =C3=A2me vulgaire."; // From wikipedia.
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+        TestCase.assertEquals("J'interdis aux marchands de vanter trop leur marchandises. Car ils se font vite pédagogues et t'enseignent comme but ce qui n'est par essence qu'un moyen, et te trompant ainsi sur la route à suivre les voilà bientôt qui te dégradent, car si leur musique est vulgaire ils te fabriquent pour te la vendre une âme vulgaire.", bos.toString());
+    }
+
+    public void testCRLFHandling()
+        throws Exception
+    {
+        // Some client use CR others use CRLF.
+
+        String qp = "The cat sat =\r\non the mat";
+        String expected = "The cat sat on the mat";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+
+        TestCase.assertEquals(expected, bos.toString());
+
+    }
+
+    public void testLFHandling()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =\non the mat";
+        String expected = "The cat sat on the mat";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+        TestCase.assertEquals(expected, bos.toString());
+    }
+
+    /**
+     * No character after '='.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_1()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =";
+
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Quoted '=' at end of stream", ioex.getMessage());
+        }
+    }
+
+    /**
+     * Not hex digit on first character.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_2()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =Z";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Expecting '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF", ioex.getMessage());
+        }
+    }
+
+    /**
+     * Not hex digit on second character.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_3()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =AZ";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Expecting second '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF", ioex.getMessage());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java
new file mode 100644
index 0000000..d5f59b9
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * File to guarantee no back tracking...
+ */
+public class ReadOnceInputStream
+    extends ByteArrayInputStream
+{
+    public ReadOnceInputStream(byte[] buf)
+    {
+        super(buf);
+    }
+
+    public boolean markSupported()
+    {
+        return false;
+    }
+
+    int currPos = -22;
+
+    public int read()
+    {
+        if (0 > currPos)
+        {
+            currPos = 0;
+        }
+        currPos++;
+
+        return super.read();
+    }
+
+    public int read(byte b[], int off, int len)
+    {
+        if (off < currPos)
+        {
+            throw new RuntimeException("off " + off + " > currPos " + currPos);
+        }
+        currPos = off;
+        int res = super.read(b, off, len);
+        if (res < 0)
+        {
+            throw new RuntimeException("off " + off + " > currPos " + currPos + " res " + res);
+        }
+        currPos += res;
+        return res;
+    }
+
+    public int read(byte b[])
+        throws IOException
+    {
+        int res = super.read(b);
+        currPos += res;
+        return res;
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java
new file mode 100644
index 0000000..fd0b637
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.BoundaryLimitedInputStream;
+import org.bouncycastle.util.io.Streams;
+
+public class TestBoundaryLimitedInputStream
+    extends TestCase
+{
+    public void testBoundaryAfterCRLF()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\r\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterCRLFTrailingLineInContent()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep\r\n";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\r\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterLF()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterLFTrailingLine()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep\n";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin,"banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java
new file mode 100644
index 0000000..504ee9e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.mime.test;
+
+class TestDoneFlag
+{
+    private boolean done = false;
+
+    void markDone()
+    {
+        done = true;
+    }
+
+    boolean isDone()
+    {
+        return done;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java
new file mode 100644
index 0000000..0001cd8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMEEnvelopedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMEEnveloped
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String          _signDN;
+    private static KeyPair          _signKP;
+
+    private static String          _reciDN;
+    private static KeyPair          _reciKP;
+
+    private static X509Certificate _reciCert;
+
+    private static boolean         _initialised = false;
+
+    private static final byte[] testMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+        "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+        "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+        "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+        "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+        "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+        "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+        "wMTMyLS0NCg==");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            _signDN   = "O=Bouncy Castle, C=AU";
+            _signKP   = CMSTestUtil.makeKeyPair();
+
+            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP   = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+    
+    public void testSMIMEEnveloped()
+        throws Exception
+    {
+        InputStream inputStream = this.getClass().getResourceAsStream("test256.message");
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(Streams.readAll(inputStream)));
+
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(loadCert("cert.pem")));
+
+                assertNotNull(recipInfo);
+
+                byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(loadKey("key.pem")));
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    public void testKeyTransAES128()
+        throws Exception
+    {
+        //
+        // output
+        //
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        SMIMEEnvelopedWriter.Builder envBldr = new SMIMEEnvelopedWriter.Builder();
+
+        envBldr.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+        SMIMEEnvelopedWriter envWrt = envBldr.build(bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        OutputStream out = envWrt.getContentStream();
+
+        out.write(testMessage);
+
+        out.close();
+        
+        //
+        // parse
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(_reciCert));
+
+                assertNotNull(recipInfo);
+
+                byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()));
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    private X509Certificate loadCert(String name)
+        throws IOException
+    {
+        try
+        {
+            return (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(getClass().getResourceAsStream(name));
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    private PrivateKey loadKey(String name)
+        throws IOException
+    {
+        return new JcaPEMKeyConverter().setProvider("BC").getKeyPair((PEMKeyPair)(new PEMParser(new InputStreamReader(getClass().getResourceAsStream(name)))).readObject()).getPrivate();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java
new file mode 100644
index 0000000..8018cf8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java
@@ -0,0 +1,227 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSTypedStream;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerId;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMEEnvelopedWriter;
+import org.bouncycastle.mime.smime.SMIMESignedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMESignEncrypt
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String _signDN;
+    private static KeyPair _signKP;
+
+    private static String _reciDN;
+    private static KeyPair _reciKP;
+
+    private static X509Certificate _signCert;
+    private static X509Certificate _reciCert;
+
+    private static boolean _initialised = false;
+
+    private static final byte[] simpleMessage = Strings.toByteArray(
+        "Content-Type: text/plain; name=null; charset=us-ascii\r\n" +
+            "Content-Transfer-Encoding: 7bit\r\n" +
+            "Content-Disposition: inline; filename=null\r\n" +
+            "\r\n" +
+            "Hello, world!\r\n");
+
+    private static final byte[] simpleMessageContent = Strings.toByteArray(
+        "Hello, world!\r\n");
+
+    private static final byte[] testMultipartMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+            "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+            "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+            "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+            "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+            "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+            "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+            "wMTMyLS0NCg==");
+
+    private static final byte[] testMultipartMessageContent = Base64.decode(
+        "LS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzINCkNvbnRlbnQtVHlwZTogdGV4dC9w" +
+            "bGFpbjsgbmFtZT1udWxsOyBjaGFyc2V0PXVzLWFzY2lpDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5n" +
+            "OiA3Yml0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBpbmxpbmU7IGZpbGVuYW1lPW51bGwNCg0KQ2lhbyBm" +
+            "cm9tIHZpZW5uYQ0KLS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzItLQ0K");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            //create certificate of the sender(signature certificate)
+            _signDN = "O=Bouncy Castle, C=AU";
+            _signKP = CMSTestUtil.makeKeyPair();
+            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
+
+            //create certificate of the receiver (encryption certificate)
+            _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+
+    public void testSignThenEncrypt()
+        throws Exception
+    { 
+  	
+    	//output that will contain signed and encrypted content
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        SMIMEEnvelopedWriter.Builder envBldr = new SMIMEEnvelopedWriter.Builder();
+
+        //specify encryption certificate
+        envBldr.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+        SMIMEEnvelopedWriter envWrt = envBldr.build(bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        OutputStream envOut = envWrt.getContentStream();
+
+        SMIMESignedWriter.Builder sigBldr = new SMIMESignedWriter.Builder();
+
+        //specify signature certificate
+        sigBldr.addCertificate(new JcaX509CertificateHolder(_signCert));
+
+        sigBldr.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(BC).build("SHA256withRSA", _signKP.getPrivate(), _signCert));
+
+        //add the encryption stream to the signature stream
+        SMIMESignedWriter sigWrt = sigBldr.build(envOut);
+
+        OutputStream sigOut = sigWrt.getContentStream();
+
+        sigOut.write(simpleMessage);
+        
+        //sign file using sender private key
+        sigOut.close();
+        
+        //write full message to the byte array output stream before actually closing the SMIME Enveloped Writer (before this, bOut contains only the headers?)
+        envOut.close();
+
+        bOut.close();
+        
+        //
+        // parse / decrypt and compare to original file 
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(_reciCert));
+
+                assertNotNull(recipInfo);
+                
+                //decrypt the file using the receiver's private key before verifying signature
+                CMSTypedStream content = recipInfo.getContentStream(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()));
+
+                MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+                
+                MimeParser p = provider.createParser(content.getContentStream());
+
+                p.parse(new SMimeParserListener()
+                {
+                    public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                        throws IOException
+                    {
+                        byte[] content = Streams.readAll(inputStream);
+
+                        assertTrue(org.bouncycastle.util.Arrays.areEqual(simpleMessageContent, content));
+                    }
+
+                    public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                        throws IOException, CMSException
+                    {
+                        SignerInformation signerInfo = signers.get(new JcaSignerId(_signCert));
+
+                        assertNotNull(signerInfo);
+
+                        Collection certCollection = certificates.getMatches(signerInfo.getSID());
+
+                        Iterator certIt = certCollection.iterator();
+                        X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                        try
+                        {
+                            assertEquals(true, signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                        }
+                        catch (OperatorCreationException e)
+                        {
+                            throw new CMSException(e.getMessage(), e);
+                        }
+                        catch (CertificateException e)
+                        {
+                            throw new CMSException(e.getMessage(), e);
+                        }
+
+                        dataParsed.markDone();
+                    }
+                });
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java
new file mode 100644
index 0000000..4851945
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerId;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMESignedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMESigned
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String _signDN;
+    private static KeyPair _signKP;
+
+    private static String _reciDN;
+    private static KeyPair _reciKP;
+
+    private static X509Certificate _signCert;
+    private static X509Certificate _reciCert;
+
+    private static boolean _initialised = false;
+
+    private static final byte[] simpleMessage = Strings.toByteArray(
+        "Content-Type: text/plain; name=null; charset=us-ascii\r\n" +
+            "Content-Transfer-Encoding: 7bit\r\n" +
+            "Content-Disposition: inline; filename=null\r\n" +
+            "\r\n" +
+            "Hello, world!\r\n");
+
+    private static final byte[] simpleMessageContent = Strings.toByteArray(
+            "Hello, world!\r\n");
+
+    private static final byte[] testMultipartMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+            "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+            "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+            "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+            "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+            "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+            "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+            "wMTMyLS0NCg==");
+
+    private static final byte[] testMultipartMessageContent = Base64.decode(
+        "LS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzINCkNvbnRlbnQtVHlwZTogdGV4dC9w" +
+            "bGFpbjsgbmFtZT1udWxsOyBjaGFyc2V0PXVzLWFzY2lpDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5n" +
+            "OiA3Yml0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBpbmxpbmU7IGZpbGVuYW1lPW51bGwNCg0KQ2lhbyBm" +
+            "cm9tIHZpZW5uYQ0KLS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzItLQ0K");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            _signDN = "O=Bouncy Castle, C=AU";
+            _signKP = CMSTestUtil.makeKeyPair();
+            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
+
+            _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+
+    public void testSimpleGeneration()
+        throws Exception
+    {
+        generationTest(simpleMessage, simpleMessageContent);
+    }
+
+    public void testEmbeddedMultipartGeneration()
+        throws Exception
+    {
+        generationTest(testMultipartMessage, testMultipartMessageContent);
+    }
+
+    private void generationTest(byte[] message, final byte[] messageContent)
+        throws Exception
+    {
+        //
+        // output
+        //
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        SMIMESignedWriter.Builder sigBldr = new SMIMESignedWriter.Builder();
+
+        sigBldr.addCertificate(new JcaX509CertificateHolder(_signCert));
+        
+        sigBldr.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(BC).build("SHA256withRSA", _signKP.getPrivate(), _signCert));
+
+        SMIMESignedWriter sigWrt = sigBldr.build(bOut);
+
+        OutputStream out = sigWrt.getContentStream();
+
+        out.write(message);
+
+        out.close();
+        
+        //
+        // parse
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                byte[] content = Streams.readAll(inputStream);
+
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(messageContent, content));
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws IOException, CMSException
+            {
+                SignerInformation signerInfo = signers.get(new JcaSignerId(_signCert));
+
+                assertNotNull(signerInfo);
+
+                Collection certCollection = certificates.getMatches(signerInfo.getSID());
+
+                Iterator certIt = certCollection.iterator();
+                X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                try
+                {
+                    assertEquals(true, signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                }
+                catch (OperatorCreationException e)
+                {
+                    throw new CMSException(e.getMessage(), e);
+                }
+                catch (CertificateException e)
+                {
+                    throw new CMSException(e.getMessage(), e);
+                }
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
index e1f9c35..c0c867d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
@@ -79,6 +79,13 @@
         parsers.put("PRIVATE KEY", new PrivateKeyParser());
     }
 
+    /**
+     * Read the next PEM object attempting to interpret the header and
+     * create a higher level object from the content.
+     *
+     * @return the next object in the stream, null if no objects left.
+     * @throws IOException in case of a parse error.
+     */
     public Object readObject()
         throws IOException
     {
@@ -229,9 +236,17 @@
                 org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(seq);
                 AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
                 PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);
-                SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
 
-                return new PEMKeyPair(pubInfo, privInfo);
+                if (pKey.getPublicKey() != null)
+                {
+                    SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
+
+                    return new PEMKeyPair(pubInfo, privInfo);
+                }
+                else
+                {
+                    return new PEMKeyPair(null, privInfo);
+                }
             }
             catch (IOException e)
             {
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
index f822cba..cbabdf6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
@@ -5,10 +5,13 @@
 import java.io.OutputStream;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.operator.OutputEncryptor;
 import org.bouncycastle.util.io.pem.PemGenerationException;
 import org.bouncycastle.util.io.pem.PemObject;
@@ -30,6 +33,18 @@
     public static final ASN1ObjectIdentifier PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC;
     public static final ASN1ObjectIdentifier PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC;
 
+    public static final AlgorithmIdentifier PRF_HMACSHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA224 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA224, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA256 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA384 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA384, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA512 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACGOST3411 = new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3411Hmac, DERNull.INSTANCE);
+
+    public static final AlgorithmIdentifier PRF_HMACSHA3_224 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_224, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_384 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_384, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_512 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+
     private PrivateKeyInfo key;
     private OutputEncryptor outputEncryptor;
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
index 709af31..386e74c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
@@ -3,6 +3,7 @@
 import java.io.IOException;
 
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.util.Arrays;
 
@@ -26,7 +27,17 @@
         ASN1InputStream aIn = new ASN1InputStream(encoding);
 
         this.certificateHolder = new X509CertificateHolder(aIn.readObject().getEncoded());
-        this.trustBlock = new CertificateTrustBlock(aIn.readObject().getEncoded());
+
+        ASN1Object tBlock = aIn.readObject();
+
+        if (tBlock != null)
+        {
+            this.trustBlock = new CertificateTrustBlock(tBlock.getEncoded());
+        }
+        else
+        {
+            this.trustBlock = null;
+        }
     }
 
     public byte[] getEncoded()
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
index c2877d2..762719d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
@@ -30,9 +30,12 @@
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Strings;
 
+/**
+ * DecryptorProviderBuilder for producing DecryptorProvider for use with PKCS8EncryptedPrivateKeyInfo.
+ */
 public class JceOpenSSLPKCS8DecryptorProviderBuilder
 {
-    private JcaJceHelper helper = new DefaultJcaJceHelper();
+    private JcaJceHelper helper;
 
     public JceOpenSSLPKCS8DecryptorProviderBuilder()
     {
@@ -77,8 +80,17 @@
 
                         String oid = scheme.getAlgorithm().getId();
 
-                        SecretKey key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount);
+                        SecretKey key;
 
+                        if (PEMUtilities.isHmacSHA1(defParams.getPrf()))
+                        {
+                            key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount);
+                        }
+                        else
+                        {
+                            key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount, defParams.getPrf());
+                        }
+                        
                         cipher = helper.createCipher(oid);
                         AlgorithmParameters algParams = helper.createAlgorithmParameters(oid);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
index 95d0883..612b531 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
@@ -16,9 +16,11 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.EncryptionScheme;
 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
@@ -62,6 +64,7 @@
     private char[] password;
 
     private SecretKey key;
+    private AlgorithmIdentifier prf = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
 
     public JceOpenSSLPKCS8EncryptorBuilder(ASN1ObjectIdentifier algorithm)
     {
@@ -84,6 +87,20 @@
         return this;
     }
 
+    /**
+     * Set the PRF to use for key generation. By default this is HmacSHA1.
+     *
+     * @param prf algorithm id for PRF.
+     *
+     * @return the current builder.
+     */
+    public JceOpenSSLPKCS8EncryptorBuilder setPRF(AlgorithmIdentifier prf)
+    {
+        this.prf = prf;
+
+        return this;
+    }
+
     public JceOpenSSLPKCS8EncryptorBuilder setIterationCount(int iterationCount)
     {
         this.iterationCount = iterationCount;
@@ -110,15 +127,11 @@
     {
         final AlgorithmIdentifier algID;
 
-        salt = new byte[20];
-
         if (random == null)
         {
             random = new SecureRandom();
         }
 
-        random.nextBytes(salt);
-
         try
         {
             this.cipher = helper.createCipher(algOID.getId());
@@ -135,12 +148,16 @@
 
         if (PEMUtilities.isPKCS5Scheme2(algOID))
         {
+            salt = new byte[PEMUtilities.getSaltSize(prf.getAlgorithm())];
+
+            random.nextBytes(salt);
+
             params = paramGen.generateParameters();
 
             try
             {
-                KeyDerivationFunc scheme = new KeyDerivationFunc(algOID, ASN1Primitive.fromByteArray(params.getEncoded()));
-                KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount));
+                EncryptionScheme scheme = new EncryptionScheme(algOID, ASN1Primitive.fromByteArray(params.getEncoded()));
+                KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount, prf));
 
                 ASN1EncodableVector v = new ASN1EncodableVector();
 
@@ -156,7 +173,14 @@
 
             try
             {
-                key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount);
+                if (PEMUtilities.isHmacSHA1(prf))
+                {
+                    key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount);
+                }
+                else
+                {
+                    key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount, prf);
+                }
 
                 cipher.init(Cipher.ENCRYPT_MODE, key, params);
             }
@@ -169,6 +193,10 @@
         {
             ASN1EncodableVector v = new ASN1EncodableVector();
 
+            salt = new byte[20];
+
+            random.nextBytes(salt);
+
             v.add(new DEROctetString(salt));
             v.add(new ASN1Integer(iterationCount));
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
index 83d2098..7a97804 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
@@ -20,8 +20,10 @@
 import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.openssl.EncryptionException;
 import org.bouncycastle.openssl.PEMException;
@@ -32,6 +34,8 @@
     private static final Map KEYSIZES = new HashMap();
     private static final Set PKCS5_SCHEME_1 = new HashSet();
     private static final Set PKCS5_SCHEME_2 = new HashSet();
+    private static final Map PRFS = new HashMap();
+    private static final Map PRFS_SALT = new HashMap();
 
     static
     {
@@ -58,6 +62,28 @@
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192));
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128));
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40));
+
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "PBKDF2withHMACSHA1");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "PBKDF2withHMACSHA256");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "PBKDF2withHMACSHA512");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "PBKDF2withHMACSHA224");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "PBKDF2withHMACSHA384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, "PBKDF2withHMACSHA3-224");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, "PBKDF2withHMACSHA3-256");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, "PBKDF2withHMACSHA3-384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, "PBKDF2withHMACSHA3-512");
+        PRFS.put(CryptoProObjectIdentifiers.gostR3411Hmac, "PBKDF2withHMACGOST3411");
+
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
+        PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
     }
 
     static int getKeySize(String algorithm)
@@ -70,6 +96,21 @@
         return ((Integer)KEYSIZES.get(algorithm)).intValue();
     }
 
+    static int getSaltSize(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS_SALT.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no salt size for algorithm: " + algorithm);
+        }
+
+        return ((Integer)PRFS_SALT.get(algorithm)).intValue();
+    }
+
+    static boolean isHmacSHA1(AlgorithmIdentifier prf)
+    {
+        return prf == null || prf.getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA1);
+    }
+
     static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
     {
         return PKCS5_SCHEME_1.contains(algOid);
@@ -89,6 +130,22 @@
         throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
     {
         SecretKeyFactory keyGen = helper.createSecretKeyFactory("PBKDF2with8BIT");
+                            
+        SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
+
+        return new SecretKeySpec(sKey.getEncoded(), algorithm);
+    }
+
+    public static SecretKey generateSecretKeyForPKCS5Scheme2(JcaJceHelper helper, String algorithm, char[] password, byte[] salt, int iterationCount, AlgorithmIdentifier prf)
+        throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
+    {
+        String prfName = (String)PRFS.get(prf.getAlgorithm());
+        if (prfName == null)
+        {
+            throw new NoSuchAlgorithmException("unknown PRF in PKCS#2: " + prf.getAlgorithm());
+        }
+
+        SecretKeyFactory keyGen = helper.createSecretKeyFactory(prfName);
 
         SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
index b7b25c3..1534827 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
@@ -17,6 +17,7 @@
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.PKCS8Generator;
@@ -85,6 +86,17 @@
         encryptedTestNew(key, PKCS8Generator.AES_256_CBC);
         encryptedTestNew(key, PKCS8Generator.DES3_CBC);
         encryptedTestNew(key, PKCS8Generator.PBE_SHA1_3DES);
+
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA1);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA224);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA256);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA384);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA512);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_224);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_256);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_384);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_512);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACGOST3411);
     }
 
     private void encryptedTestNew(PrivateKey key, ASN1ObjectIdentifier algorithm)
@@ -114,6 +126,34 @@
         assertEquals(key, rdKey);
     }
 
+    private void encryptedTestNew(PrivateKey key, ASN1ObjectIdentifier algorithm, AlgorithmIdentifier prf)
+        throws NoSuchProviderException, NoSuchAlgorithmException, IOException, OperatorCreationException, PKCSException
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        JcaPEMWriter pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
+
+        JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(algorithm);
+
+        encryptorBuilder.setProvider("BC");
+        encryptorBuilder.setPasssword("hello".toCharArray());
+        encryptorBuilder.setPRF(prf);
+
+        PKCS8Generator pkcs8 = new JcaPKCS8Generator(key, encryptorBuilder.build());
+
+        pWrt.writeObject(pkcs8);
+
+        pWrt.close();
+
+        PEMParser pRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
+
+        PKCS8EncryptedPrivateKeyInfo pInfo = (PKCS8EncryptedPrivateKeyInfo)pRd.readObject();
+
+        PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(pInfo.decryptPrivateKeyInfo(new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray())));
+
+
+        assertEquals(key, rdKey);
+    }
+
     public void testVectors()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
index 338fe7d..0b816ae 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
@@ -58,6 +58,7 @@
         return "PEMParserTest";
     }
 
+
     private PEMParser openPEMResource(
         String          fileName)
     {
@@ -267,6 +268,7 @@
         doDudPasswordTest("aaf9c4d",17, "corrupted stream - out of bounds length found");
 
         doNoPasswordTest();
+        doNoECPublicKeyTest();
 
         // encrypted private key test
         InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build("password".toCharArray());
@@ -323,6 +325,8 @@
         trusted = (X509TrustedCertificateBlock)pemRd.readObject();
 
         checkTrustedCert(trusted);
+
+
     }
 
     private void checkTrustedCert(X509TrustedCertificateBlock trusted)
@@ -355,15 +359,15 @@
 
     private void keyPairTest(
         String   name,
-        KeyPair pair) 
+        KeyPair pair)
         throws IOException
     {
         PEMParser pemRd;
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
         JcaPEMWriter             pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
-        
+
         pWrt.writeObject(pair.getPublic());
-        
+
         pWrt.close();
 
         pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
@@ -377,22 +381,22 @@
         {
             fail("Failed public key read: " + name);
         }
-        
+
         bOut = new ByteArrayOutputStream();
         pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
-        
+
         pWrt.writeObject(pair.getPrivate());
-        
+
         pWrt.close();
-        
+
         pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
-        
+
         KeyPair kPair = converter.getKeyPair((PEMKeyPair)pemRd.readObject());
         if (!kPair.getPrivate().equals(pair.getPrivate()))
         {
             fail("Failed private key read: " + name);
         }
-        
+
         if (!kPair.getPublic().equals(pair.getPublic()))
         {
             fail("Failed private key public read: " + name);
@@ -532,6 +536,25 @@
         }
     }
 
+    private void doNoECPublicKeyTest()
+        throws Exception
+    {
+        // EC private key without the public key defined. Note: this encoding is actually invalid.
+        String ecSample =
+                    "-----BEGIN EC PRIVATE KEY-----\n" +
+                    "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgvYiiubZYNO1WXXi3\n" +
+                    "jmGT9DLeFemvlmR1zTA0FdcSAG2gCgYIKoZIzj0DAQehRANCAATNXYa06ykwhxuy\n" +
+                    "Dg+q6zsVqOLk9LtXz/1fzf9AkAVm9lBMTZAh+FRfregBgl08LATztGlTh/z0dPnp\n" +
+                    "dW2jFrDn\n" +
+                    "-----END EC PRIVATE KEY-----";
+
+        PEMParser pemRd = new PEMParser(new StringReader(ecSample));
+
+        PEMKeyPair kp = (PEMKeyPair)pemRd.readObject();
+
+        isTrue(kp.getPublicKeyInfo() == null);
+    }
+
     public static void main(
         String[]    args)
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
index fadef60..2274a19 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
@@ -4,8 +4,18 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for an operator that is able to create a signature from
+ * a stream of output.
+ */
 public interface ContentSigner
 {
+    /**
+     * Return the algorithm identifier describing the signature
+     * algorithm and parameters this signer generates.
+     *
+     * @return algorithm oid and parameters.
+     */
     AlgorithmIdentifier getAlgorithmIdentifier();
 
     /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
index 54d9ef1..1729a60 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
@@ -4,11 +4,15 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for an operator that is able to verify a signature based
+ * on data in a stream of output.
+ */
 public interface ContentVerifier
 {
     /**
      * Return the algorithm identifier describing the signature
-     * algorithm and parameters this expander supports.
+     * algorithm and parameters this verifier supports.
      *
      * @return algorithm oid and parameters.
      */
@@ -24,6 +28,9 @@
     OutputStream getOutputStream();
 
     /**
+     * Return true if the expected value of the signature matches the data passed
+     * into the stream.
+     *
      * @param expected expected value of the signature on the data.
      * @return true if the signature verifies, false otherwise
      */
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
index 1f48c53..ad9f5b6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
@@ -14,6 +14,7 @@
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -37,19 +38,28 @@
         algorithms.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
         algorithms.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410-94");
         algorithms.put(CryptoProObjectIdentifiers.gostR3411, "GOST3411");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411WITHGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411WITHGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411WITHECGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411WITHECGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha384, "SHA384WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha512, "SHA512WITHDSA");
+
         algorithms.put(NISTObjectIdentifiers.id_sha224, "SHA224");
         algorithms.put(NISTObjectIdentifiers.id_sha256, "SHA256");
         algorithms.put(NISTObjectIdentifiers.id_sha384, "SHA384");
         algorithms.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
         algorithms.put(OIWObjectIdentifiers.elGamalAlgorithm, "ELGAMAL");
         algorithms.put(OIWObjectIdentifiers.idSHA1, "SHA1");
         algorithms.put(OIWObjectIdentifiers.md5WithRSA, "MD5WITHRSA");
@@ -65,6 +75,10 @@
         algorithms.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
         algorithms.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
         algorithms.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512WITHRSA");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
@@ -77,7 +91,19 @@
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, "SHA3-224WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, "SHA3-256WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "SHA3-384WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, "SHA3-512WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha384, "SHA384WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha512, "SHA512WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_224, "SHA3-224WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_256, "SHA3-256WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_384, "SHA3-384WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_512, "SHA3-512WITHDSA");
         algorithms.put(GNUObjectIdentifiers.Tiger_192, "Tiger");
 
         algorithms.put(PKCSObjectIdentifiers.RC2_CBC, "RC2/CBC");
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
index fbe8797..98fdbad 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -8,10 +8,13 @@
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -52,6 +55,13 @@
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, NISTObjectIdentifiers.id_sha256);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, TeleTrusTObjectIdentifiers.ripemd160);
+
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, OIWObjectIdentifiers.idSHA1);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, NISTObjectIdentifiers.id_sha512);
 
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
@@ -77,10 +87,14 @@
 
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
 
         digestOids.put(BCObjectIdentifiers.sphincs256_with_SHA3_512, NISTObjectIdentifiers.id_sha3_512);
         digestOids.put(BCObjectIdentifiers.sphincs256_with_SHA512, NISTObjectIdentifiers.id_sha512);
 
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+        
         digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
         digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
         digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
@@ -106,6 +120,8 @@
         digestNameToOids.put("SHAKE-256", NISTObjectIdentifiers.id_shake256);
 
         digestNameToOids.put("GOST3411", CryptoProObjectIdentifiers.gostR3411);
+        digestNameToOids.put("GOST3411-2012-256", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestNameToOids.put("GOST3411-2012-512", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
 
         digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2);
         digestNameToOids.put("MD4", PKCSObjectIdentifiers.md4);
@@ -114,6 +130,8 @@
         digestNameToOids.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
         digestNameToOids.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
         digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
+
+        digestNameToOids.put("SM3", GMObjectIdentifiers.sm3);
     }
 
     public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
index 49e5027..9366086 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
@@ -31,6 +31,9 @@
         keySizes.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, Integers.valueOf(192));
         keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, Integers.valueOf(192));
 
+        keySizes.put(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, Integers.valueOf(64));
+        keySizes.put(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, Integers.valueOf(64));
+
         keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128));
         keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192));
         keySizes.put(NISTObjectIdentifiers.id_aes256_CBC, Integers.valueOf(256));
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
index b428e0d..d81ecca 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -18,6 +18,7 @@
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -38,6 +39,8 @@
     private static final ASN1ObjectIdentifier ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS;
     private static final ASN1ObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94;
     private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001;
+    private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+    private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512;
 
     static
     {
@@ -60,6 +63,10 @@
         algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
@@ -72,6 +79,22 @@
         algorithms.put("SHA256WITHDSA", NISTObjectIdentifiers.dsa_with_sha256);
         algorithms.put("SHA384WITHDSA", NISTObjectIdentifiers.dsa_with_sha384);
         algorithms.put("SHA512WITHDSA", NISTObjectIdentifiers.dsa_with_sha512);
+        algorithms.put("SHA3-224WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        algorithms.put("SHA3-256WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        algorithms.put("SHA3-384WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        algorithms.put("SHA3-512WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        algorithms.put("SHA3-224WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        algorithms.put("SHA3-256WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        algorithms.put("SHA3-384WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        algorithms.put("SHA3-512WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+        algorithms.put("SHA3-224WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        algorithms.put("SHA3-256WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        algorithms.put("SHA3-384WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        algorithms.put("SHA3-512WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
+        algorithms.put("SHA3-224WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        algorithms.put("SHA3-256WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        algorithms.put("SHA3-384WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        algorithms.put("SHA3-512WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
         algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1);
         algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1);
         algorithms.put("SHA224WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA224);
@@ -83,6 +106,14 @@
         algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
         algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
         algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        algorithms.put("GOST3411WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411-2012-256WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411-2012-256WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411-2012-512WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
         algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
         algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
         algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
@@ -97,6 +128,43 @@
         algorithms.put("SHA3-512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA3_512);
         algorithms.put("SHA512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA512);
         algorithms.put("SM3WITHSM2", GMObjectIdentifiers.sm2sign_with_sm3);
+
+        algorithms.put("SHA256WITHXMSS", BCObjectIdentifiers.xmss_SHA256ph);
+        algorithms.put("SHA512WITHXMSS", BCObjectIdentifiers.xmss_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA256ph);
+        algorithms.put("SHA512WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSS-SHA256", BCObjectIdentifiers.xmss_SHA256ph);
+        algorithms.put("SHA512WITHXMSS-SHA512", BCObjectIdentifiers.xmss_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSS-SHAKE128", BCObjectIdentifiers.xmss_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSS-SHAKE256", BCObjectIdentifiers.xmss_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSSMT-SHA256", BCObjectIdentifiers.xmss_mt_SHA256ph);
+        algorithms.put("SHA512WITHXMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        algorithms.put("XMSS-SHA256", BCObjectIdentifiers.xmss_SHA256);
+        algorithms.put("XMSS-SHA512", BCObjectIdentifiers.xmss_SHA512);
+        algorithms.put("XMSS-SHAKE128", BCObjectIdentifiers.xmss_SHAKE128);
+        algorithms.put("XMSS-SHAKE256", BCObjectIdentifiers.xmss_SHAKE256);
+
+        algorithms.put("XMSSMT-SHA256", BCObjectIdentifiers.xmss_mt_SHA256);
+        algorithms.put("XMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512);
+        algorithms.put("XMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128);
+        algorithms.put("XMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256);
+
+        algorithms.put("QTESLA-I", BCObjectIdentifiers.qTESLA_I);
+        algorithms.put("QTESLA-III-SIZE", BCObjectIdentifiers.qTESLA_III_size);
+        algorithms.put("QTESLA-III-SPEED", BCObjectIdentifiers.qTESLA_III_speed);
+        algorithms.put("QTESLA-P-I", BCObjectIdentifiers.qTESLA_p_I);
+        algorithms.put("QTESLA-P-III", BCObjectIdentifiers.qTESLA_p_III);
+
         //
         // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
         // The parameters field SHALL be NULL for RSA based signature algorithms.
@@ -111,12 +179,22 @@
         noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
 
         //
         // RFC 4491
         //
         noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        noParams.add(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        noParams.add(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
 
         //
         // SPHINCS-256
@@ -125,6 +203,36 @@
         noParams.add(BCObjectIdentifiers.sphincs256_with_SHA3_512);
 
         //
+        // XMSS
+        //
+        noParams.add(BCObjectIdentifiers.xmss_SHA256ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHA512ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA512ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        noParams.add(BCObjectIdentifiers.xmss_SHA256);
+        noParams.add(BCObjectIdentifiers.xmss_SHA512);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE128);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE256);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA256);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA512);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256);
+
+        //
+        // qTESLA
+        //
+        noParams.add(BCObjectIdentifiers.qTESLA_I);
+        noParams.add(BCObjectIdentifiers.qTESLA_III_size);
+        noParams.add(BCObjectIdentifiers.qTESLA_III_speed);
+        noParams.add(BCObjectIdentifiers.qTESLA_p_I);
+        noParams.add(BCObjectIdentifiers.qTESLA_p_III);
+
+        //
         // SM2
         //
         noParams.add(GMObjectIdentifiers.sm2sign_with_sm3);
@@ -140,6 +248,10 @@
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
 
         //
         // explicit params
@@ -159,6 +271,18 @@
         AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
         params.put("SHA512WITHRSAANDMGF1", createPSSParams(sha512AlgId, 64));
 
+        AlgorithmIdentifier sha3_224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_224, DERNull.INSTANCE);
+        params.put("SHA3-224WITHRSAANDMGF1", createPSSParams(sha3_224AlgId, 28));
+
+        AlgorithmIdentifier sha3_256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256, DERNull.INSTANCE);
+        params.put("SHA3-256WITHRSAANDMGF1", createPSSParams(sha3_256AlgId, 32));
+
+        AlgorithmIdentifier sha3_384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_384, DERNull.INSTANCE);
+        params.put("SHA3-384WITHRSAANDMGF1", createPSSParams(sha3_384AlgId, 48));
+
+        AlgorithmIdentifier sha3_512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_512, DERNull.INSTANCE);
+        params.put("SHA3-512WITHRSAANDMGF1", createPSSParams(sha3_512AlgId, 64));
+
         //
         // digests
         //
@@ -166,6 +290,23 @@
         digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
         digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+
         digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
         digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
@@ -175,13 +316,14 @@
         digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
     }
 
     private static AlgorithmIdentifier generate(String signatureAlgorithm)
     {
         AlgorithmIdentifier sigAlgId;
-        AlgorithmIdentifier encAlgId;
-        AlgorithmIdentifier digAlgId;
 
         String algorithmName = Strings.toUpperCase(signatureAlgorithm);
         ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
@@ -203,24 +345,6 @@
             sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
         }
 
-        if (pkcs15RsaEncryption.contains(sigOID))
-        {
-            encAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
-        }
-        else
-        {
-            encAlgId = sigAlgId;
-        }
-
-        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
-        {
-            digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm();
-        }
-        else
-        {
-            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigOID), DERNull.INSTANCE);
-        }
-
         return sigAlgId;
     }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
index 2365270..d912f00 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
@@ -2,6 +2,9 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * The base interface for a provider of DigestCalculator implementations.
+ */
 public interface DigestCalculatorProvider
 {
     DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java b/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
index 0572afc..45144cc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
@@ -4,6 +4,10 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for a key initialized operator that is able to calculate a MAC from
+ * a stream of output.
+ */
 public interface MacCalculator
 {
     AlgorithmIdentifier getAlgorithmIdentifier();
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
index ea563d5..3634f99 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
@@ -8,22 +8,11 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.ExtendedDigest;
-import org.bouncycastle.crypto.digests.GOST3411Digest;
-import org.bouncycastle.crypto.digests.MD2Digest;
-import org.bouncycastle.crypto.digests.MD4Digest;
-import org.bouncycastle.crypto.digests.MD5Digest;
-import org.bouncycastle.crypto.digests.RIPEMD128Digest;
-import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-import org.bouncycastle.crypto.digests.RIPEMD256Digest;
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.digests.SHA224Digest;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.digests.SHA384Digest;
-import org.bouncycastle.crypto.digests.SHA3Digest;
-import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.*;
 import org.bouncycastle.operator.OperatorCreationException;
 
 public class BcDefaultDigestProvider
@@ -126,6 +115,20 @@
                 return new GOST3411Digest();
             }
         });
+        table.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new GOST3411_2012_256Digest();
+            }
+        });
+        table.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new GOST3411_2012_512Digest();
+            }
+        });
         table.put(TeleTrusTObjectIdentifiers.ripemd128, new BcDigestProvider()
         {
             public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
index 5583194..b834458 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.operator.jcajce;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.PrivateKey;
@@ -8,15 +7,23 @@
 import java.security.SecureRandom;
 import java.security.Signature;
 import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
 
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.io.OutputStreamFactory;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.OperatorStreamException;
 import org.bouncycastle.operator.RuntimeOperatorException;
 
 public class JcaContentSignerBuilder
@@ -25,11 +32,32 @@
     private SecureRandom random;
     private String signatureAlgorithm;
     private AlgorithmIdentifier sigAlgId;
+    private AlgorithmParameterSpec sigAlgSpec;
 
     public JcaContentSignerBuilder(String signatureAlgorithm)
     {
         this.signatureAlgorithm = signatureAlgorithm;
         this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
+        this.sigAlgSpec = null;
+    }
+
+    public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+
+        if (sigParamSpec instanceof PSSParameterSpec)
+        {
+            PSSParameterSpec pssSpec = (PSSParameterSpec)sigParamSpec;
+
+            this.sigAlgSpec = pssSpec;
+            this.sigAlgId = new AlgorithmIdentifier(
+                                    PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams(pssSpec));
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sigParamSpec: "
+                            + ((sigParamSpec == null) ? "null" : sigParamSpec.getClass().getName()));
+        }
     }
 
     public JcaContentSignerBuilder setProvider(Provider provider)
@@ -72,7 +100,7 @@
 
             return new ContentSigner()
             {
-                private SignatureOutputStream stream = new SignatureOutputStream(sig);
+                private OutputStream stream = OutputStreamFactory.createStream(sig);
 
                 public AlgorithmIdentifier getAlgorithmIdentifier()
                 {
@@ -88,7 +116,7 @@
                 {
                     try
                     {
-                        return stream.getSignature();
+                        return sig.sign();
                     }
                     catch (SignatureException e)
                     {
@@ -103,59 +131,16 @@
         }
     }
 
-    private class SignatureOutputStream
-        extends OutputStream
+    private static RSASSAPSSparams createPSSParams(PSSParameterSpec pssSpec)
     {
-        private Signature sig;
+        DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
+           AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
+           AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
 
-        SignatureOutputStream(Signature sig)
-        {
-            this.sig = sig;
-        }
-
-        public void write(byte[] bytes, int off, int len)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes, off, len);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(byte[] bytes)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(int b)
-            throws IOException
-        {
-            try
-            {
-                sig.update((byte)b);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        byte[] getSignature()
-            throws SignatureException
-        {
-            return sig.sign();
-        }
+        return new RSASSAPSSparams(
+            digId,
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, mgfDig),
+            new ASN1Integer(pssSpec.getSaltLength()),
+            new ASN1Integer(pssSpec.getTrailerField()));
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
index 5f82d40..67150bc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.operator.jcajce;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.Provider;
@@ -15,13 +14,13 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.jcajce.io.OutputStreamFactory;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.ContentVerifier;
 import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.OperatorStreamException;
 import org.bouncycastle.operator.RawContentVerifier;
 import org.bouncycastle.operator.RuntimeOperatorException;
 
@@ -69,8 +68,6 @@
 
         return new ContentVerifierProvider()
         {
-            private SignatureOutputStream stream;
-
             public boolean hasAssociatedCertificate()
             {
                 return true;
@@ -84,13 +81,12 @@
             public ContentVerifier get(AlgorithmIdentifier algorithm)
                 throws OperatorCreationException
             {
+                Signature sig;
                 try
                 {
-                    Signature sig = helper.createSignature(algorithm);
+                    sig = helper.createSignature(algorithm);
 
                     sig.initVerify(certificate.getPublicKey());
-
-                    stream = new SignatureOutputStream(sig);
                 }
                 catch (GeneralSecurityException e)
                 {
@@ -101,11 +97,11 @@
 
                 if (rawSig != null)
                 {
-                    return new RawSigVerifier(algorithm, stream, rawSig);
+                    return new RawSigVerifier(algorithm, sig, rawSig);
                 }
                 else
                 {
-                    return new SigVerifier(algorithm, stream);
+                    return new SigVerifier(algorithm, sig);
                 }
             }
         };
@@ -129,17 +125,17 @@
             public ContentVerifier get(AlgorithmIdentifier algorithm)
                 throws OperatorCreationException
             {
-                SignatureOutputStream stream = createSignatureStream(algorithm, publicKey);
+                Signature sig = createSignature(algorithm, publicKey);
 
                 Signature rawSig = createRawSig(algorithm, publicKey);
 
                 if (rawSig != null)
                 {
-                    return new RawSigVerifier(algorithm, stream, rawSig);
+                    return new RawSigVerifier(algorithm, sig, rawSig);
                 }
                 else
                 {
-                    return new SigVerifier(algorithm, stream);
+                    return new SigVerifier(algorithm, sig);
                 }
             }
         };
@@ -151,7 +147,7 @@
         return this.build(helper.convertPublicKey(publicKey));
     }
 
-    private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
+    private Signature createSignature(AlgorithmIdentifier algorithm, PublicKey publicKey)
         throws OperatorCreationException
     {
         try
@@ -160,7 +156,7 @@
 
             sig.initVerify(publicKey);
 
-            return new SignatureOutputStream(sig);
+            return sig;
         }
         catch (GeneralSecurityException e)
         {
@@ -190,14 +186,16 @@
     private class SigVerifier
         implements ContentVerifier
     {
-        private AlgorithmIdentifier algorithm;
+        private final AlgorithmIdentifier algorithm;
+        private final Signature signature;
 
-        protected SignatureOutputStream stream;
+        protected final OutputStream stream;
 
-        SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
+        SigVerifier(AlgorithmIdentifier algorithm, Signature signature)
         {
             this.algorithm = algorithm;
-            this.stream = stream;
+            this.signature = signature;
+            this.stream = OutputStreamFactory.createStream(signature);
         }
 
         public AlgorithmIdentifier getAlgorithmIdentifier()
@@ -219,7 +217,7 @@
         {
             try
             {
-                return stream.verify(expected);
+                return signature.verify(expected);
             }
             catch (SignatureException e)
             {
@@ -234,9 +232,9 @@
     {
         private Signature rawSignature;
 
-        RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature)
+        RawSigVerifier(AlgorithmIdentifier algorithm, Signature standardSig, Signature rawSignature)
         {
-            super(algorithm, stream);
+            super(algorithm, standardSig);
             this.rawSignature = rawSignature;
         }
 
@@ -279,7 +277,7 @@
                 // standard signature will not be freed if verify is not called on it.
                 try
                 {
-                    stream.verify(expected);
+                    rawSignature.verify(expected);
                 }
                 catch (Exception e)
                 {
@@ -288,60 +286,4 @@
             }
         }
     }
-
-    private class SignatureOutputStream
-        extends OutputStream
-    {
-        private Signature sig;
-
-        SignatureOutputStream(Signature sig)
-        {
-            this.sig = sig;
-        }
-
-        public void write(byte[] bytes, int off, int len)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes, off, len);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(byte[] bytes)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(int b)
-            throws IOException
-        {
-            try
-            {
-                sig.update((byte)b);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        boolean verify(byte[] expected)
-            throws SignatureException
-        {
-            return sig.verify(expected);
-        }
-    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
index 2bbdd5f..bbde317 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
@@ -77,7 +77,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return  the current Unwrapper.
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
index b8063b4..2f14c1f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
@@ -3,39 +3,72 @@
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.Provider;
 import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.MGF1ParameterSpec;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
 import javax.crypto.spec.OAEPParameterSpec;
 import javax.crypto.spec.PSource;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.GostR3410KeyTransport;
+import org.bouncycastle.asn1.cryptopro.GostR3410TransportParameters;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.AsymmetricKeyWrapper;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OperatorException;
+import org.bouncycastle.util.Arrays;
 
 public class JceAsymmetricKeyWrapper
     extends AsymmetricKeyWrapper
 {
+    private static final Set gostAlgs = new HashSet();
+
+    static
+    {
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512);
+    }
+
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
     private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
     private Map extraMappings = new HashMap();
     private PublicKey publicKey;
@@ -110,7 +143,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Wrapper.
@@ -125,54 +157,117 @@
     public byte[] generateWrappedKey(GenericKey encryptionKey)
         throws OperatorException
     {
-        Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings);
-        AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
-
         byte[] encryptedKeyBytes = null;
 
-        try
-        {
-            if (algParams != null)
-            {
-                keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, algParams, random);
-            }
-            else
-            {
-                keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random);
-            }
-            encryptedKeyBytes = keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey));
-        }
-        catch (InvalidKeyException e)
-        {
-        }
-        catch (GeneralSecurityException e)
-        {
-        }
-        catch (IllegalStateException e)
-        {
-        }
-        catch (UnsupportedOperationException e)
-        {
-        }
-        catch (ProviderException e)
-        {
-        }
-
-        // some providers do not support WRAP (this appears to be only for asymmetric algorithms)
-        if (encryptedKeyBytes == null)
+        if (isGOST(getAlgorithmIdentifier().getAlgorithm()))
         {
             try
             {
-                keyEncryptionCipher.init(Cipher.ENCRYPT_MODE, publicKey, random);
-                encryptedKeyBytes = keyEncryptionCipher.doFinal(OperatorUtils.getJceKey(encryptionKey).getEncoded());
+                if (random == null)
+                {
+                    random = new SecureRandom();
+                }
+                KeyPairGenerator kpGen = helper.createKeyPairGenerator(getAlgorithmIdentifier().getAlgorithm());
+
+                kpGen.initialize(((ECPublicKey)publicKey).getParams(), random);
+
+                KeyPair ephKp = kpGen.generateKeyPair();
+
+                byte[] ukm = new byte[8];
+
+                random.nextBytes(ukm);
+
+                SubjectPublicKeyInfo ephKeyInfo = SubjectPublicKeyInfo.getInstance(ephKp.getPublic().getEncoded());
+
+                GostR3410TransportParameters transParams;
+
+                if (ephKeyInfo.getAlgorithm().getAlgorithm().on(RosstandartObjectIdentifiers.id_tc26))
+                {
+                    transParams = new GostR3410TransportParameters(
+                        RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z, ephKeyInfo, ukm);
+                }
+                else
+                {
+                    transParams = new GostR3410TransportParameters(
+                                CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, ephKeyInfo, ukm);
+                }
+
+                KeyAgreement agreement = helper.createKeyAgreement(getAlgorithmIdentifier().getAlgorithm());
+
+                agreement.init(ephKp.getPrivate(), new UserKeyingMaterialSpec(transParams.getUkm()));
+
+                agreement.doPhase(publicKey, true);
+
+                SecretKey key = agreement.generateSecret(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.getId());
+      
+                byte[] encKey = OperatorUtils.getJceKey(encryptionKey).getEncoded();
+
+                Cipher keyCipher = helper.createCipher(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap);
+
+                keyCipher.init(Cipher.WRAP_MODE, key, new GOST28147WrapParameterSpec(transParams.getEncryptionParamSet(), transParams.getUkm()));
+
+                byte[] keyData = keyCipher.wrap(new SecretKeySpec(encKey, "GOST"));
+
+                GostR3410KeyTransport transport = new GostR3410KeyTransport(
+                                new Gost2814789EncryptedKey(
+                                    Arrays.copyOfRange(keyData, 0, 32), Arrays.copyOfRange(keyData, 32, 36)), transParams);
+
+                return transport.getEncoded();
+            }
+            catch (Exception e)
+            {
+                throw new OperatorException("exception wrapping key: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings);
+            AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
+
+            try
+            {
+                if (algParams != null)
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, algParams, random);
+                }
+                else
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random);
+                }
+                encryptedKeyBytes = keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey));
             }
             catch (InvalidKeyException e)
             {
-                throw new OperatorException("unable to encrypt contents key", e);
             }
             catch (GeneralSecurityException e)
             {
-                throw new OperatorException("unable to encrypt contents key", e);
+            }
+            catch (IllegalStateException e)
+            {
+            }
+            catch (UnsupportedOperationException e)
+            {
+            }
+            catch (ProviderException e)
+            {
+            }
+
+            // some providers do not support WRAP (this appears to be only for asymmetric algorithms)
+            if (encryptedKeyBytes == null)
+            {
+                try
+                {
+                    keyEncryptionCipher.init(Cipher.ENCRYPT_MODE, publicKey, random);
+                    encryptedKeyBytes = keyEncryptionCipher.doFinal(OperatorUtils.getJceKey(encryptionKey).getEncoded());
+                }
+                catch (InvalidKeyException e)
+                {
+                    throw new OperatorException("unable to encrypt contents key", e);
+                }
+                catch (GeneralSecurityException e)
+                {
+                    throw new OperatorException("unable to encrypt contents key", e);
+                }
             }
         }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceInputDecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceInputDecryptorProviderBuilder.java
new file mode 100644
index 0000000..e615b88
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceInputDecryptorProviderBuilder.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.InputStream;
+import java.security.Provider;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.operator.InputDecryptor;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A generic decryptor provider for IETF style algorithms.
+ */
+public class JceInputDecryptorProviderBuilder
+{
+    private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+    public JceInputDecryptorProviderBuilder()
+    {
+    }
+
+    public JceInputDecryptorProviderBuilder setProvider(Provider provider)
+    {
+        this.helper = new ProviderJcaJceHelper(provider);
+
+        return this;
+    }
+
+    public JceInputDecryptorProviderBuilder setProvider(String providerName)
+    {
+        this.helper = new NamedJcaJceHelper(providerName);
+
+        return this;
+    }
+
+    /**
+     * Build a decryptor provider which will use the passed in bytes for the symmetric key.
+     *
+     * @param keyBytes bytes representing the key to use.
+     * @return an decryptor provider.
+     */
+    public InputDecryptorProvider build(byte[] keyBytes)
+    {
+        final byte[] encKeyBytes = Arrays.clone(keyBytes);
+
+        return new InputDecryptorProvider()
+        {
+            private Cipher cipher;
+            private AlgorithmIdentifier encryptionAlg;
+
+            public InputDecryptor get(final AlgorithmIdentifier algorithmIdentifier)
+                throws OperatorCreationException
+            {
+                encryptionAlg = algorithmIdentifier;
+
+                ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm();
+
+                try
+                {
+                    cipher = helper.createCipher(algorithm.getId());
+                    SecretKey key = new SecretKeySpec(encKeyBytes, algorithm.getId());
+                    
+                    ASN1Encodable encParams = algorithmIdentifier.getParameters();
+
+                    if (encParams instanceof ASN1OctetString)
+                    {
+                        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets()));
+                    }
+                    else
+                    {
+                        // TODO: at the moment it's just GOST, but...
+                        GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams);
+
+                        cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
+                    }
+                }
+                catch (Exception e)
+                {
+                    throw new OperatorCreationException("unable to create InputDecryptor: " + e.getMessage(), e);
+                }
+
+                return new InputDecryptor()
+                {
+                    public AlgorithmIdentifier getAlgorithmIdentifier()
+                    {
+                        return encryptionAlg;
+                    }
+
+                    public InputStream getInputStream(InputStream input)
+                    {
+                        return new CipherInputStream(input, cipher);
+                    }
+                };
+            }
+        };
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
index 9431ad3..23e4d3b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -5,6 +5,7 @@
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
@@ -20,6 +21,7 @@
 import java.util.Map;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -34,11 +36,13 @@
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.jcajce.util.AlgorithmParametersUtils;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.MessageDigestUtils;
@@ -65,6 +69,8 @@
         oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
         oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
         oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
@@ -90,17 +96,19 @@
         oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
         oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
 
-        oids.put(OIWObjectIdentifiers.idSHA1, "SHA-1");
-        oids.put(NISTObjectIdentifiers.id_sha224, "SHA-224");
-        oids.put(NISTObjectIdentifiers.id_sha256, "SHA-256");
-        oids.put(NISTObjectIdentifiers.id_sha384, "SHA-384");
-        oids.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
+        oids.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+        oids.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+        oids.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+        oids.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+        oids.put(NISTObjectIdentifiers.id_sha512, "SHA512");
         oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
 
         asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
 
+        asymmetricWrapperAlgNames.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+
         symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap");
         symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap");
         symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap");
@@ -147,6 +155,73 @@
         return ((Integer)symmetricWrapperKeySizes.get(algOid)).intValue();
     }
 
+    KeyPairGenerator createKeyPairGenerator(ASN1ObjectIdentifier algorithm)
+        throws CMSException
+    {
+        try
+        {
+            String agreementName = null; //(String)BASE_CIPHER_NAMES.get(algorithm);
+
+            if (agreementName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createKeyPairGenerator(agreementName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // Ignore
+                }
+            }
+            return helper.createKeyPairGenerator(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new CMSException("cannot create key agreement: " + e.getMessage(), e);
+        }
+    }
+
+    Cipher createCipher(ASN1ObjectIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        try
+        {
+            return helper.createCipher(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
+        }
+    }
+
+    KeyAgreement createKeyAgreement(ASN1ObjectIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        try
+        {
+            String agreementName = null; //(String)BASE_CIPHER_NAMES.get(algorithm);
+
+            if (agreementName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createKeyAgreement(agreementName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // Ignore
+                }
+            }
+            return helper.createKeyAgreement(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create key agreement: " + e.getMessage(), e);
+        }
+    }
+
     Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
         throws OperatorCreationException
     {
@@ -275,7 +350,7 @@
             //
             if (oids.get(digAlgId.getAlgorithm()) != null)
             {
-                String  digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
+                String digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
 
                 dig = helper.createDigest(digestAlgorithm);
             }
@@ -291,7 +366,7 @@
     Signature createSignature(AlgorithmIdentifier sigAlgId)
         throws GeneralSecurityException
     {
-        Signature   sig;
+        Signature sig;
 
         try
         {
@@ -304,7 +379,7 @@
             //
             if (oids.get(sigAlgId.getAlgorithm()) != null)
             {
-                String  signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
+                String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
 
                 sig = helper.createSignature(signatureAlgorithm);
             }
@@ -317,7 +392,7 @@
         if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
         {
             ASN1Sequence seq = ASN1Sequence.getInstance(sigAlgId.getParameters());
-          
+
             if (notDefaultPSSParams(seq))
             {
                 try
@@ -340,7 +415,7 @@
 
     public Signature createRawSignature(AlgorithmIdentifier algorithm)
     {
-        Signature   sig;
+        Signature sig;
 
         try
         {
@@ -404,7 +479,7 @@
             return name.substring(0, dIndex) + name.substring(dIndex + 1);
         }
 
-        return MessageDigestUtils.getDigestName(oid);
+        return name;
     }
 
     public X509Certificate convertCertificate(X509CertificateHolder certHolder)
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
index c4785f6..dd48195 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
@@ -38,8 +38,8 @@
  *  Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
  *
  *  Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
- *    type    ATTRIBUTE.&id({IOSet}),
- *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ *    type    ATTRIBUTE.&amp;id({IOSet}),
+ *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type})
  *  }
  * </pre>
  */
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
index a1c9164..324e528 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
@@ -6,6 +6,7 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.operator.InputDecryptor;
 import org.bouncycastle.operator.InputDecryptorProvider;
 import org.bouncycastle.util.io.Streams;
@@ -45,6 +46,16 @@
         this(parseBytes(encryptedPrivateKeyInfo));
     }
 
+    public AlgorithmIdentifier getEncryptionAlgorithm()
+    {
+        return encryptedPrivateKeyInfo.getEncryptionAlgorithm();
+    }
+
+    public byte[] getEncryptedData()
+    {
+        return encryptedPrivateKeyInfo.getEncryptedData();
+    }
+
     public EncryptedPrivateKeyInfo toASN1Structure()
     {
          return encryptedPrivateKeyInfo;
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
index 653aa57..9bafaf6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
@@ -27,6 +27,11 @@
 {
     private PrivateKeyInfo privateKeyInfo;
 
+    public PKCS8EncryptedPrivateKeyInfoBuilder(byte[] privateKeyInfo)
+    {
+        this(PrivateKeyInfo.getInstance(privateKeyInfo));
+    }
+
     public PKCS8EncryptedPrivateKeyInfoBuilder(PrivateKeyInfo privateKeyInfo)
     {
         this.privateKeyInfo = privateKeyInfo;
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java
new file mode 100644
index 0000000..61e6f94
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.pkcs;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.util.Integers;
+
+class PKCSUtils
+{
+    private static final Map PRFS_SALT = new HashMap();
+
+    static
+    {
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
+        PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
+    }
+
+    static int getSaltSize(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS_SALT.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no salt size for algorithm: " + algorithm);
+        }
+
+        return ((Integer)PRFS_SALT.get(algorithm)).intValue();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
index d7627ea..330332d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
@@ -15,14 +15,20 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
+import org.bouncycastle.asn1.pkcs.PBEParameter;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.PasswordConverter;
+import org.bouncycastle.jcajce.PBKDF1Key;
 import org.bouncycastle.jcajce.PKCS12KeyWithParameters;
 import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
+import org.bouncycastle.jcajce.spec.ScryptKeySpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -107,18 +113,32 @@
                     else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
                     {
                         PBES2Parameters alg = PBES2Parameters.getInstance(algorithmIdentifier.getParameters());
-                        PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
-                        AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
 
-                        SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
-
-                        if (func.isDefaultPrf())
+                        if (MiscObjectIdentifiers.id_scrypt.equals(alg.getKeyDerivationFunc().getAlgorithm()))
                         {
-                            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+                            ScryptParams params = ScryptParams.getInstance(alg.getKeyDerivationFunc().getParameters());
+                            AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+                            SecretKeyFactory keyFact = helper.createSecretKeyFactory("SCRYPT");
+
+                            key = keyFact.generateSecret(new ScryptKeySpec(password,
+                                       params.getSalt(), params.getCostParameter().intValue(), params.getBlockSize().intValue(),
+                                       params.getParallelizationParameter().intValue(), keySizeProvider.getKeySize(encScheme)));
                         }
                         else
                         {
-                            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+                            SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
+                            PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+                            AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+                            if (func.isDefaultPrf())
+                            {
+                                key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+                            }
+                            else
+                            {
+                                key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+                            }
                         }
 
                         cipher = helper.createCipher(alg.getEncryptionScheme().getAlgorithm().getId());
@@ -138,6 +158,20 @@
                             cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
                         }
                     }
+                    else if (algorithm.equals(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC)
+                        || algorithm.equals(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC))
+                    {
+                        PBEParameter pbeParams = PBEParameter.getInstance(algorithmIdentifier.getParameters());
+
+                        cipher = helper.createCipher(algorithm.getId());
+
+                        cipher.init(Cipher.DECRYPT_MODE, new PBKDF1Key(password, PasswordConverter.ASCII),
+                                new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount().intValue()));
+                    }
+                    else
+                    {
+                        throw new OperatorCreationException("unable to create InputDecryptor: algorithm " + algorithm + " unknown.");
+                    }
                 }
                 catch (Exception e)
                 {
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
index 06d60d2..8763b3f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
@@ -13,6 +13,8 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
 import org.bouncycastle.asn1.pkcs.EncryptionScheme;
 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
@@ -20,7 +22,11 @@
 import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.PBKDF2Config;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+import org.bouncycastle.crypto.util.ScryptConfig;
 import org.bouncycastle.jcajce.PKCS12KeyWithParameters;
+import org.bouncycastle.jcajce.spec.ScryptKeySpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -33,27 +39,44 @@
 
 public class JcePKCSPBEOutputEncryptorBuilder
 {
+    private final PBKDFConfig pbkdf;
+
     private JcaJceHelper helper = new DefaultJcaJceHelper();
     private ASN1ObjectIdentifier algorithm;
     private ASN1ObjectIdentifier keyEncAlgorithm;
     private SecureRandom random;
     private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
     private int iterationCount = 1024;
+    private PBKDF2Config.Builder pbkdfBuilder = new PBKDF2Config.Builder();
 
-    public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm)
+    public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier keyEncryptionAlg)
     {
-        if (isPKCS12(algorithm))
+        this.pbkdf = null;
+        if (isPKCS12(keyEncryptionAlg))
         {
-            this.algorithm = algorithm;
-            this.keyEncAlgorithm = algorithm;
+            this.algorithm = keyEncryptionAlg;
+            this.keyEncAlgorithm = keyEncryptionAlg;
         }
         else
         {
             this.algorithm = PKCSObjectIdentifiers.id_PBES2;
-            this.keyEncAlgorithm = algorithm;
+            this.keyEncAlgorithm = keyEncryptionAlg;
         }
     }
 
+    /**
+     * Constructor allowing different derivation functions such as PBKDF2 and scrypt.
+     *
+     * @param pbkdfAlgorithm key derivation algorithm definition to use.
+     * @param keyEncryptionAlg encryption algorithm to apply the derived key with.
+     */
+    public JcePKCSPBEOutputEncryptorBuilder(PBKDFConfig pbkdfAlgorithm, ASN1ObjectIdentifier keyEncryptionAlg)
+    {
+        this.algorithm = PKCSObjectIdentifiers.id_PBES2;
+        this.pbkdf = pbkdfAlgorithm;
+        this.keyEncAlgorithm = keyEncryptionAlg;
+    }
+
     public JcePKCSPBEOutputEncryptorBuilder setProvider(Provider provider)
     {
         this.helper = new ProviderJcaJceHelper(provider);
@@ -68,12 +91,18 @@
         return this;
     }
 
+    public JcePKCSPBEOutputEncryptorBuilder setRandom(SecureRandom random)
+    {
+        this.random = random;
+
+        return this;
+    }
+
     /**
      * Set the lookup provider of AlgorithmIdentifier returning key_size_in_bits used to
      * handle PKCS5 decryption.
      *
-     * @param keySizeProvider  a provider of integer secret key sizes.
-     *
+     * @param keySizeProvider a provider of integer secret key sizes.
      * @return the current builder.
      */
     public JcePKCSPBEOutputEncryptorBuilder setKeySizeProvider(SecretKeySizeProvider keySizeProvider)
@@ -84,14 +113,38 @@
     }
 
     /**
+     * Set the PRF to use for key generation. By default this is HmacSHA1.
+     *
+     * @param prf algorithm id for PRF.
+     * @return the current builder.
+     * @throws IllegalStateException if this builder was intialised with a PBKDFDef
+     */
+    public JcePKCSPBEOutputEncryptorBuilder setPRF(AlgorithmIdentifier prf)
+    {
+        if (pbkdf != null)
+        {
+            throw new IllegalStateException("set PRF count using PBKDFDef");
+        }
+        this.pbkdfBuilder.withPRF(prf);
+
+        return this;
+    }
+
+    /**
      * Set the iteration count for the PBE calculation.
      *
      * @param iterationCount the iteration count to apply to the key creation.
      * @return the current builder.
+     * @throws IllegalStateException if this builder was intialised with a PBKDFDef
      */
     public JcePKCSPBEOutputEncryptorBuilder setIterationCount(int iterationCount)
     {
+        if (pbkdf != null)
+        {
+            throw new IllegalStateException("set iteration count using PBKDFDef");
+        }
         this.iterationCount = iterationCount;
+        this.pbkdfBuilder.withIterationCount(iterationCount);
 
         return this;
     }
@@ -108,14 +161,15 @@
         }
 
         final AlgorithmIdentifier encryptionAlg;
-        final byte[] salt = new byte[20];
-
-        random.nextBytes(salt);
 
         try
         {
             if (isPKCS12(algorithm))
             {
+                byte[] salt = new byte[20];
+
+                random.nextBytes(salt);
+
                 cipher = helper.createCipher(algorithm.getId());
 
                 cipher.init(Cipher.ENCRYPT_MODE, new PKCS12KeyWithParameters(password, salt, iterationCount));
@@ -124,19 +178,61 @@
             }
             else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
             {
-                SecretKeyFactory keyFact = helper.createSecretKeyFactory(PKCSObjectIdentifiers.id_PBKDF2.getId());
+                PBKDFConfig pbkDef = (pbkdf == null) ? pbkdfBuilder.build() : pbkdf;
 
-                key = keyFact.generateSecret(new PBEKeySpec(password, salt, iterationCount, keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm))));
+                if (MiscObjectIdentifiers.id_scrypt.equals(pbkDef.getAlgorithm()))
+                {
+                    ScryptConfig skdf = (ScryptConfig)pbkDef;
 
-                cipher = helper.createCipher(keyEncAlgorithm.getId());
+                    byte[] salt = new byte[skdf.getSaltLength()];
 
-                cipher.init(Cipher.ENCRYPT_MODE, key, random);
+                    random.nextBytes(salt);
 
-                PBES2Parameters algParams = new PBES2Parameters(
-                                   new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount)),
-                                   new EncryptionScheme(keyEncAlgorithm, ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded())));
+                    ScryptParams params = new ScryptParams(
+                                                salt,
+                                                skdf.getCostParameter(),
+                                                skdf.getBlockSize(),
+                                                skdf.getParallelizationParameter());
+                    
+                    SecretKeyFactory keyFact = helper.createSecretKeyFactory("SCRYPT");
 
-                encryptionAlg = new AlgorithmIdentifier(algorithm, algParams);
+                    key = keyFact.generateSecret(new ScryptKeySpec(password,
+                        salt, skdf.getCostParameter(), skdf.getBlockSize(), skdf.getParallelizationParameter(),
+                                                 keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm))));
+
+                    cipher = helper.createCipher(keyEncAlgorithm.getId());
+
+                    cipher.init(Cipher.ENCRYPT_MODE, key, random);
+
+                    PBES2Parameters algParams = new PBES2Parameters(
+                        new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params),
+                        new EncryptionScheme(keyEncAlgorithm, ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded())));
+
+                    encryptionAlg = new AlgorithmIdentifier(algorithm, algParams);
+                }
+                else
+                {
+                    PBKDF2Config pkdf = (PBKDF2Config)pbkDef;
+
+                    byte[] salt = new byte[pkdf.getSaltLength()];
+
+                    random.nextBytes(salt);
+
+                    SecretKeyFactory keyFact = helper.createSecretKeyFactory(JceUtils.getAlgorithm(pkdf.getPRF().getAlgorithm()));
+
+                    key = keyFact.generateSecret(new PBEKeySpec(password, salt, pkdf.getIterationCount(),
+                                            keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm))));
+
+                    cipher = helper.createCipher(keyEncAlgorithm.getId());
+
+                    cipher.init(Cipher.ENCRYPT_MODE, key, random);
+
+                    PBES2Parameters algParams = new PBES2Parameters(
+                        new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, pkdf.getIterationCount(), pkdf.getPRF())),
+                        new EncryptionScheme(keyEncAlgorithm, ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded())));
+
+                    encryptionAlg = new AlgorithmIdentifier(algorithm, algParams);
+                }
             }
             else
             {
@@ -189,11 +285,11 @@
      * @return a byte array representing the password.
      */
     private static byte[] PKCS5PasswordToBytes(
-        char[]  password)
+        char[] password)
     {
         if (password != null)
         {
-            byte[]  bytes = new byte[password.length];
+            byte[] bytes = new byte[password.length];
 
             for (int i = 0; i != bytes.length; i++)
             {
@@ -216,14 +312,14 @@
      * @return a byte array representing the password.
      */
     private static byte[] PKCS12PasswordToBytes(
-        char[]  password)
+        char[] password)
     {
         if (password != null && password.length > 0)
         {
-                                       // +1 for extra 2 pad bytes.
-            byte[]  bytes = new byte[(password.length + 1) * 2];
+            // +1 for extra 2 pad bytes.
+            byte[] bytes = new byte[(password.length + 1) * 2];
 
-            for (int i = 0; i != password.length; i ++)
+            for (int i = 0; i != password.length; i++)
             {
                 bytes[i * 2] = (byte)(password[i] >>> 8);
                 bytes[i * 2 + 1] = (byte)password[i];
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JceUtils.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JceUtils.java
new file mode 100644
index 0000000..b7533f6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JceUtils.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.pkcs.jcajce;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+class JceUtils
+{
+    private static final Map PRFS = new HashMap();
+
+    static
+    {
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "PBKDF2withHMACSHA1");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "PBKDF2withHMACSHA256");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "PBKDF2withHMACSHA512");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "PBKDF2withHMACSHA224");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "PBKDF2withHMACSHA384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, "PBKDF2withHMACSHA3-224");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, "PBKDF2withHMACSHA3-256");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, "PBKDF2withHMACSHA3-384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, "PBKDF2withHMACSHA3-512");
+        PRFS.put(CryptoProObjectIdentifiers.gostR3411Hmac, "PBKDF2withHMACGOST3411");
+    }
+
+    static String getAlgorithm(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no prf for algorithm: " + algorithm);
+        }
+
+        return ((String)PRFS.get(algorithm));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/AllTests.java
index 809c5c5..bbf75bf 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/AllTests.java
@@ -18,6 +18,7 @@
         
         suite.addTestSuite(PfxPduTest.class);
         suite.addTestSuite(PKCS10Test.class);
+        suite.addTestSuite(PKCS8Test.class);
 
         return new BCTestSetup(suite);
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS10Test.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS10Test.java
index a0c13d2..302a16b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS10Test.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS10Test.java
@@ -4,6 +4,7 @@
 import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.Security;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 
@@ -12,14 +13,24 @@
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.pkcs.CertificationRequest;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
 import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
 import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+import org.bouncycastle.util.encoders.Base64;
 
 public class PKCS10Test
     extends TestCase
 {
+    private static final byte[] anssiPkcs10 = Base64.decode(
+        "MIHLMHMCAQAwDzENMAsGA1UEAwwEYmx1YjBbMBUGByqGSM49AgEGCiqBegGB"
+          + "X2WCAAEDQgAEB9POXLIasfF55GSxY9vshIIEnvv47B9jGZgZFN6VFHPvqe8G"
+          + "j+6UpLjP0vvoInC8uu/X3JJJTsrgGrxbfOOG1KAAMAoGCCqGSM49BAMCA0gA"
+          + "MEUCIQCgTdLV3IS5NuL9CHMDPOj6BumAQPdjzbgkGZghoY/wJAIgcEgF/2f4"
+          + "5wYlIELOq18Uxksc0sOkbZm/nRXs1VX4rsM=");
+
      //
     // personal keys
     //
@@ -37,6 +48,11 @@
         new BigInteger("d3d83daf2a0cecd3367ae6f8ae1aeb82e9ac2f816c6fc483533d8297dd7884cd", 16),
         new BigInteger("b8f52fc6f38593dabb661d3f50f8897f8106eee68b1bce78a95b132b4e5b5d19", 16));
 
+    public void setUp()
+    {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
     public void testLeaveOffEmpty()
         throws Exception
     {
@@ -66,6 +82,14 @@
         assertNotNull(CertificationRequest.getInstance(request.getEncoded()).getCertificationRequestInfo().getAttributes());
     }
 
+    public void testRequest()
+        throws Exception
+    {
+        JcaPKCS10CertificationRequest req = new JcaPKCS10CertificationRequest(anssiPkcs10);
+
+        assertTrue(req.getPublicKey().toString().startsWith("EC Public Key [9a:f5:f3:36:55:81:27:66:dd:d8:76:5a:96:6b:26:7b:0c:61:a2:94]"));
+    }
+
     public static void main(String args[])
     {
         junit.textui.TestRunner.run(suite());
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS8Test.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS8Test.java
new file mode 100644
index 0000000..af1fc11
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PKCS8Test.java
@@ -0,0 +1,246 @@
+package org.bouncycastle.pkcs.test;
+
+import java.math.BigInteger;
+import java.security.Security;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.PBES2Parameters;
+import org.bouncycastle.asn1.pkcs.PBKDF2Params;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+import org.bouncycastle.crypto.util.ScryptConfig;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder;
+import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
+import org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+
+public class PKCS8Test
+    extends TestCase
+{
+    private static BigInteger modulus = new BigInteger(
+        "b6ce33ccbf839457b0d32487b6c807bca584f85c627466b787fc09d0b1f73d97c9a381eca20e0ba851d317a8964327fa0010de76c" +
+            "6c0facb83f13612752d166b49d9ba272c38c9a4ed71a94ea69f7bbdc63d7a5c5d3f3c039223e4ac1bb5d433c6bf01e68364a7ef4f" +
+            "061f7cdfba82fa471bb1444b2034e53cc9c3e402a8fa89", 16);
+
+    private static byte[] pkInfo = Base64.decode(
+        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALbOM8y/g5RXsNMkh7bIB7ylhPhcYnRmt4f8CdCx9z2XyaOB7KIOC6hR" +
+            "0xeolkMn+gAQ3nbGwPrLg/E2EnUtFmtJ2bonLDjJpO1xqU6mn3u9xj16XF0/PAOSI+SsG7XUM8a/AeaDZKfvTwYffN+6gvpHG7FESyA0" +
+            "5TzJw+QCqPqJAgMBAAECgYAMQxCeb0o4LRmjUBP6YriCIugkcK35+NneuT0/TnCzJPdVjGV/CUom5DYwpBJQNuJCFt+VQAe5yuTyzRm3" +
+            "2mpicusxKsMHqJRJFWIQ5ztuRehGF1KB+NPze7GxWVB2vRWJQQhlgq/nRsAjWoUfxbFkKBlNPhUnLm1klwBptpqpcQJBAOBiAnrrraBu" +
+            "3Lc9B8QtCdEAIr5LYyWYd3jSvyTt04OI8Q3l7zG9omKpdIskGNu7n5RRYixsNXAVCaiHsyHHCO8CQQDQkGdtlH5fQZ2PJVSNZ6RlDhUq" +
+            "6RGqajnkXw/sK1GR388FGqc9xTB9Eu1vg7ywlsuWSWpiCe/q+1nGVJufLAQHAkEAyTba5oQGNYJ1J1Txa/i/fs7SWTedd49cQ9spUeJ7" +
+            "9M6O7FmvwDlAL52qR0Rdjl6YYhcBJLj8yr/y41CdUML9vQJAYGDqurOtNj2vHrAkg3fKezxnwb2UgUi3WfYn+H4IIr3m/7fSYvQVtSai" +
+            "/C5Hat80U0230HhBGzhtwv3kMEj5zwJAViD4ceQRYyC+G2z5fyFz8Ca6sjDB9LwY0YEOFxR+3nqtteJI2vgITl4HrrnTRGuiVSY6pqkX" +
+            "hX2DZcWDZMieLA==");
+
+    private static byte[] pkcs8Sha256 = Base64.decode(
+    "MIIC9TBvBgkqhkiG9w0BBQ0wYjBBBgkqhkiG9w0BBQwwNAQgsa99yy9MqsJQ+4l5" +
+        "SehvabVidNKBoJeqPJDZAPmbKCgCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUD" +
+        "BAEqBBBAncGTD5Yp0oqVklTgmzt4BIICgDJLy5EF12+l9cjYIRVLcHFc7QE7prBy" +
+        "yj+nENvxqPJaVAVo+VVguOPUSGKQAeZnUhpU1kwKa4EyUhA5CvVtTcQ3hd7v769E" +
+        "n59EJ2NKFNOmplxbE3QU/Z7g63ECDvu4jsUVjZmWGrzDXwDEkraG5VdtVhIfdFOj" +
+        "yR1CEnxLqq5l3qlkCjKFap/UBh6wbsItJJYw4HJ/7fCJtY8xKG9b1hxHiyH2Yhrh" +
+        "Pak2P4ukFClg8Kzv92ZbUKSv92C/zrlkUWx7+u7b97YZVd/nL4VLVnQW79YI7ApL" +
+        "QHFNZ9Jp3cm3XtddzlJJVWwghF+slvULsGzQ05yMICyCRHEwPAPSXvf6VpNezQ+v" +
+        "8mD+lS7IavJRx7S4y2NFzZCLDaZfX32/S4vRv7Q4Ax87YKHqHwLnZRLWn3QimtX5" +
+        "oJPsf8Sj0/w31W49c7I3a4rWLAWO3fTVSvH+vQdPUCq9geqatOjiwSnUy6oSP4f3" +
+        "vvdMlvNFafWyLwqONM8nKijNSSk8fjJtncvIDBCRYmwuQFmkFBRtCpHNeY43vrCV" +
+        "O04x5N6PPTnnp/Ru7xYbrEyO2SQX/JJQV2l/pZgyF4w/2Y3i0hW+dItkoFVPGuY0" +
+        "XfBgPlVx5w/72Et8GKh8a5E03IPJOa3J+/vhx9hYc7Hc4AJHsQQwiXco5ybBNZBV" +
+        "wteKQwP6XRL5GMWJr4v4fJk0ksZ7sDAIlLWOeZu0jxWSx3VLC6QR9Ij+uMGkkY3t" +
+        "nVxJii5qFSnWSH5e2Qk9HJ64a0ossKBFaln8wT/2tryBxa1+YZDYwGrasG4EvKHC" +
+        "N0PVvCZ6nreGoWpaBlTolOl7HpbjcsryQ2SMkWNIurrivWKAoqRQ53Q=");
+
+
+    private static byte[] pkcs8Gost3411 = Base64.decode(
+        "MIIC8zBtBgkqhkiG9w0BBQ0wYDA/BgkqhkiG9w0BBQwwMgQgo40hf88LUYklfxUE" +
+            "HO0KjJFo52p9lEqfYDDmJJosDpYCAggAMAoGBiqFAwICCgUAMB0GCWCGSAFlAwQB" +
+            "KgQQJXNzoxem4QvtoToJbJVt1ASCAoBtcnpMvp/Skip+m8e0A+Hh8BnzoRDkKoeD" +
+            "QFuyR1HRfXa6iZ+CT5Bt38kM7shDA1se1uEo5WnDCydmzQ5WdHinMaokryd+3l65" +
+            "AszZLrbK56E78820RMTwFevDAXcwhneomCkEg059r+GfO0OLe6YJ1JR88uiPWxJy" +
+            "gthltJoefOnK87cG53oOPAmgKkMS0lbd13FeYduo9r2473O80CtTpA0p5GHFHdI8" +
+            "9ebu8PWoGez+HR3FU5+m4Qj63spW2F1qblgocywABFqVCWVp/8h4dptQ7754jNmK" +
+            "HN4MWgXYb5SdScz8IkE9Cv+Xn0tAW5eqhgYDot4GfbYRqCjup0jnCmgNxFo/TMOS" +
+            "H1EMXeFnEEEft/fx8K1jZ1jtfJQRBY1N0jOBBzsMKVgj8GYkFAlOYXziCK+YzYjY" +
+            "gmD2/IQ1+VfPnCkT14BqM9KzJidOjMDE9jlMiBhaBzee0qpdCLZ9bPQ0L6s6Urwm" +
+            "mR7l1nCvLY5GYRBUC/ZOZf+MiPEpD/Lu+DUv5RgPEDStSXoKqtxvgpsT4upDVpEw" +
+            "i4z7TWGpkcOGZJEe8JHEw5rDC15FHm44WoeFhlgLaFhjUD9Ou4CYM3LYT6VwUbmF" +
+            "XBBVuKssFbbvOcU1ez4vfx7i8r09R/olVmopsiUBapyLwfck3hlQEYrAJKHQ9HFV" +
+            "qYM9tU0OoaZB1qYYmLQPIe99cr66xTmfUkRQaJ2RAhbZQDPTX3Bm4SseEfmrkfuY" +
+            "/RzOT5l2cgEOuTmkzhfgxVqyhOBeWfGPWYWtDD2QmQBcAHZbf9XVaoRe7YDRXTG/" +
+            "WhEN3fKJaM/Qfif5wwWvHjQb5TWrTyeNNuh4YtXsyQ3PkwOxHrmm"
+    );
+
+    // from RFC 7914
+    private static byte[] pkcs8Scrypt = Base64.decode(
+        "MIHiME0GCSqGSIb3DQEFDTBAMB8GCSsGAQQB2kcECzASBAVNb3VzZQIDEAAAAgEI" +
+            "AgEBMB0GCWCGSAFlAwQBKgQQyYmguHMsOwzGMPoyObk/JgSBkJb47EWd5iAqJlyy" +
+            "+ni5ftd6gZgOPaLQClL7mEZc2KQay0VhjZm/7MbBUNbqOAXNM6OGebXxVp6sHUAL" +
+            "iBGY/Dls7B1TsWeGObE0sS1MXEpuREuloZjcsNVcNXWPlLdZtkSH6uwWzR0PyG/Z" +
+            "+ZXfNodZtd/voKlvLOw5B3opGIFaLkbtLZQwMiGtl42AS89lZg=="
+    );
+
+    private static byte[] scryptKey = Base64.decode(
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg4RaNK5CuHY3CXr9f" +
+            "/CdVgOhEurMohrQmWbbLZK4ZInyhRANCAARs2WMV6UMlLjLaoc0Dsdnj4Vlffc9T" +
+            "t48lJU0RiCzXc280Vg/H5fm1xAP1B7UnIVcBqgDHDcfqWm1h/xSeCHXS"
+    );
+
+    public void setUp()
+    {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    public void testSHA256()
+        throws Exception
+    {
+        PKCS8EncryptedPrivateKeyInfo info = new PKCS8EncryptedPrivateKeyInfo(pkcs8Sha256);
+        
+        PrivateKeyInfo pkInfo = info.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray()));
+
+        RSAPrivateKey k = RSAPrivateKey.getInstance(pkInfo.parsePrivateKey());
+
+        assertEquals(modulus, k.getModulus());
+    }
+
+    public void testGOST3411()
+        throws Exception
+    {
+        PKCS8EncryptedPrivateKeyInfo info = new PKCS8EncryptedPrivateKeyInfo(pkcs8Gost3411);
+
+        PrivateKeyInfo pkInfo = info.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray()));
+
+        RSAPrivateKey k = RSAPrivateKey.getInstance(pkInfo.parsePrivateKey());
+
+        assertEquals(modulus, k.getModulus());
+    }
+
+    public void testScrypt()
+        throws Exception
+    {
+        if (getJvmVersion() < 7)  // runs out of memory
+        {
+            return;
+        }
+        
+        PKCS8EncryptedPrivateKeyInfo info = new PKCS8EncryptedPrivateKeyInfo(pkcs8Scrypt);
+
+        PrivateKeyInfo pkInfo = info.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("Rabbit".toCharArray()));
+        
+        assertTrue(Arrays.areEqual(scryptKey, pkInfo.getEncoded()));
+    }
+
+    public void testSHA256Encryption()
+        throws Exception
+    {
+        PKCS8EncryptedPrivateKeyInfoBuilder bldr = new PKCS8EncryptedPrivateKeyInfoBuilder(pkInfo);
+
+        PKCS8EncryptedPrivateKeyInfo encInfo = bldr.build(
+            new JcePKCSPBEOutputEncryptorBuilder(NISTObjectIdentifiers.id_aes256_CBC)
+                .setPRF(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE))
+                .setProvider("BC")
+                .build("hello".toCharArray()));
+
+        EncryptedPrivateKeyInfo encPkInfo = EncryptedPrivateKeyInfo.getInstance(encInfo.getEncoded());
+
+        assertEquals(
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE),
+            PBKDF2Params.getInstance(
+                PBES2Parameters.getInstance(encPkInfo.getEncryptionAlgorithm().getParameters())
+                    .getKeyDerivationFunc().getParameters())
+                .getPrf());
+
+        PrivateKeyInfo pkInfo = encInfo.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray()));
+
+        RSAPrivateKey k = RSAPrivateKey.getInstance(pkInfo.parsePrivateKey());
+
+        assertEquals(modulus, k.getModulus());
+    }
+
+    public void testSHA3_256Encryption()
+         throws Exception
+     {
+         PKCS8EncryptedPrivateKeyInfoBuilder bldr = new PKCS8EncryptedPrivateKeyInfoBuilder(pkInfo);
+
+         PKCS8EncryptedPrivateKeyInfo encInfo = bldr.build(
+             new JcePKCSPBEOutputEncryptorBuilder(NISTObjectIdentifiers.id_aes256_CBC)
+                 .setPRF(new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256, DERNull.INSTANCE))
+                 .setProvider("BC")
+                 .build("hello".toCharArray()));
+
+         EncryptedPrivateKeyInfo encPkInfo = EncryptedPrivateKeyInfo.getInstance(encInfo.getEncoded());
+
+         assertEquals(
+             new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256, DERNull.INSTANCE),
+             PBKDF2Params.getInstance(
+                 PBES2Parameters.getInstance(encPkInfo.getEncryptionAlgorithm().getParameters())
+                     .getKeyDerivationFunc().getParameters())
+                 .getPrf());
+
+         PrivateKeyInfo pkInfo = encInfo.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray()));
+
+         RSAPrivateKey k = RSAPrivateKey.getInstance(pkInfo.parsePrivateKey());
+
+         assertEquals(modulus, k.getModulus());
+     }
+
+    public void testScryptEncryption()
+        throws Exception
+    {
+        if (getJvmVersion() < 7)      // runs out of memory
+        {
+            return;
+        }
+
+        PKCS8EncryptedPrivateKeyInfoBuilder bldr = new PKCS8EncryptedPrivateKeyInfoBuilder(scryptKey);
+
+        PBKDFConfig scrypt = new ScryptConfig.Builder(1048576, 8, 1)
+                                        .withSaltLength(20).build();
+
+        PKCS8EncryptedPrivateKeyInfo encInfo = bldr.build(
+            new JcePKCSPBEOutputEncryptorBuilder(scrypt, NISTObjectIdentifiers.id_aes256_CBC)
+                .setProvider("BC")
+                .build("Rabbit".toCharArray()));
+
+        EncryptedPrivateKeyInfo encPkInfo = EncryptedPrivateKeyInfo.getInstance(encInfo.getEncoded());
+
+        PKCS8EncryptedPrivateKeyInfo info = new PKCS8EncryptedPrivateKeyInfo(encPkInfo);
+
+        PrivateKeyInfo pkInfo = info.decryptPrivateKeyInfo(new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC").build("Rabbit".toCharArray()));
+
+        assertTrue(Arrays.areEqual(scryptKey, pkInfo.getEncoded()));
+    }
+
+    private static int getJvmVersion()
+    {
+        String version = System.getProperty("java.version");
+
+        if (version.startsWith("1.7"))
+        {
+            return 7;
+        }
+        if (version.startsWith("1.8"))
+        {
+            return 8;
+        }
+        if (version.startsWith("1.9"))
+        {
+            return 9;
+        }
+        if (version.startsWith("1.1"))
+        {
+            return 10;
+        }
+
+        return -1;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
index d1665e7..be1f769 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
@@ -3,6 +3,8 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.Key;
 import java.security.KeyFactory;
 import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
@@ -11,10 +13,17 @@
 import java.security.Security;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.KeySpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 import java.util.Date;
 
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
 import junit.framework.TestCase;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
@@ -529,7 +538,31 @@
       + "LjAKBgYqhQMCAgkFAAQgIwD0CRCwva2Bjdlv5g970H2bCB1aafBNr/hxJLZE"
       + "Ey4ECAW3VYXJzJpYAgIIAA==");
 
-    /**
+    private byte[] desWithSha1 = Base64.decode(
+        "MIIBgTAbBgkqhkiG9w0BBQowDgQId6NZWs1Be5wCAgQABIIBYLineU3" +
+            "SS0NCA6Olpt9VciMD4gUHsaqqKZ7tZK83ig66ic4U/CwFEcc6sozkkk" +
+            "3tGp1PJ9XOofcRZhrAegUshROPtexMYlsarIlYvL+1dUzY2BZXVV34Z" +
+            "SBdko8+QI0G84neTh7lL0x/MoE+MV2LHNxjMSj1oDIp5DJ43LQ6oTxa" +
+            "IjMEH8UZSK9Lr/oWtBO4Gfm2OBIDfVLfdVGTX5D7a/dXgzunraVkHMm" +
+            "zHUqPoqw0HZewSYTCdU0qf0H695K81S1OcMEpV53oyCxw/chzIinzDC" +
+            "L+OjxUmFEKh7exfUKPeV4J6R5Wa1Ec0Xff+TWQ9yiwGnByGkd8eWCyf" +
+            "WsduibO7akY1/XiPziEUPTvs8guTdBm3l625AJOaHMn5PtDMuMSj2dM" +
+            "KpDnyOgNj5xADOJyetmZMcoC6dzNWs1zBZAQAmJ2soC114k03xhLaID" +
+            "NfNqx9WueoGaZ3qXbSUawlR8=");
+
+    private byte[] desWithMD5 = Base64.decode(
+        "MIIBgTAbBgkqhkiG9w0BBQMwDgQIdKRvcJb9DdgCAgQABIIBYEZ0Bpqy" +
+            "l/LNlzo/EhcPnGcgwvLdkh3mTwFxb5wOhDS+/cz82XrtFNosyvGUPo7V" +
+            "CyJjg0C05prNOOug4n5EEIcr0B/6p7ZKw9JLEq/gkfTUhVXS7tFsIzjD" +
+            "giVGc9T59fcqr4NWFtFAHxKb24ZESYL4BponDxWql465+s4oFLjEWob1" +
+            "AOA268q5PpWP1Og2BS0mBPuh6x/QOXzyfxaNRcJewT0uh0fCgCS05A+2" +
+            "wI7mJgQk1kEWdHPBMv/LAHiXgULa1gS+aLto8fISoHObY0H/KTTJ7rhY" +
+            "Epkjjw1khc0wrMBlpbcVJvqvxeMeelp26vPjqRL+08gUhHdzsJ3SokCD" +
+            "j5Z0Mmh1haduOXAALcdO5st6ZBqkA8o886bTqBYYRIFGzZIhJzOhe8iD" +
+            "GhHLM2yiA0RxlCtlnNMXruHKEvFYgzI3lVQov4jU5MIL1XjH0zPGyu9t" +
+            "/q8tpS4nbkRgGj8=");
+
+    /*
      * we generate the CA's certificate
      */
     public static X509Certificate createMasterCert(
@@ -563,7 +596,7 @@
         return new JcaX509CertificateConverter().setProvider(BC).getCertificate(cert);
     }
 
-    /**
+    /*
      * we generate an intermediate certificate signed by our CA
      */
     public static X509Certificate createIntermediateCert(
@@ -619,8 +652,8 @@
         return new JcaX509CertificateConverter().setProvider(BC).getCertificate(cert);
     }
 
-    /**
-     * we generate a certificate signed by our CA's intermediate certficate
+    /*
+     * we generate a certificate signed by our CA's intermediate certificate
      */
     public static X509Certificate createCert(
         PublicKey pubKey,
@@ -652,9 +685,6 @@
         //
         // create the certificate - version 3
         //
-        //
-        // create the certificate - version 3
-        //
         X509v3CertificateBuilder v3CertBuilder = new JcaX509v3CertificateBuilder(
             issuerBuilder.build(),
             BigInteger.valueOf(3),
@@ -792,6 +822,44 @@
         assertTrue(Arrays.areEqual(info.getEncoded(), privKey.getEncoded()));
     }
 
+    public void testEncryptedPrivateKeyInfoDESWithSHA1()
+        throws Exception
+    {
+        checkEncryptedPrivateKeyInfo("PKCS#5 Scheme 1".toCharArray(), desWithSha1);
+    }
+
+    public void testEncryptedPrivateKeyInfoDESWithMD5()
+        throws Exception
+    {
+        checkEncryptedPrivateKeyInfo("PKCS#5 Scheme 1".toCharArray(), desWithMD5);
+    }
+
+    private void checkEncryptedPrivateKeyInfo(char[] password, byte[] encodedEncPKInfo)
+        throws Exception
+    {
+        KeyFactory fact = KeyFactory.getInstance("RSA", BC);
+        PrivateKey privKey = fact.generatePrivate(privKeySpec);
+        EncryptedPrivateKeyInfo encPKInfo = new EncryptedPrivateKeyInfo(encodedEncPKInfo);
+
+        Cipher cipher = Cipher.getInstance(encPKInfo.getAlgName(), "BC");
+
+        PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
+
+        SecretKeyFactory skFac = SecretKeyFactory.getInstance(encPKInfo.getAlgName(), "BC");
+
+        Key pbeKey = skFac.generateSecret(pbeKeySpec);
+
+        AlgorithmParameters algParams = encPKInfo.getAlgParameters();
+
+        cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
+
+        KeySpec pkcs8KeySpec = encPKInfo.getKeySpec(cipher);
+
+        RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey)fact.generatePrivate(pkcs8KeySpec);
+
+        assertEquals(privKey, rsaPriv);
+    }
+
     public void testKeyBag()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java
new file mode 100644
index 0000000..c7211dc
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.pkix.jcajce;
+
+class AnnotatedException
+    extends Exception
+{
+    private Throwable _underlyingException;
+
+    public AnnotatedException(String string, Throwable e)
+    {
+        super(string);
+
+        _underlyingException = e;
+    }
+
+    public AnnotatedException(String string)
+    {
+        this(string, null);
+    }
+
+    public Throwable getCause()
+    {
+        return _underlyingException;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java
new file mode 100644
index 0000000..06f58bf
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.cert.CertPathValidatorException;
+
+class CRLNotFoundException
+    extends CertPathValidatorException
+{
+    CRLNotFoundException(String message)
+    {
+        super(message);
+    }
+
+    public CRLNotFoundException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java
new file mode 100644
index 0000000..8c334d4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.util.Date;
+
+class CertStatus
+{
+    public static final int UNREVOKED = 11;
+
+    public static final int UNDETERMINED = 12;
+
+    int certStatus = UNREVOKED;
+
+    Date revocationDate = null;
+
+    /**
+     * @return Returns the revocationDate.
+     */
+    public Date getRevocationDate()
+    {
+        return revocationDate;
+    }
+
+    /**
+     * @param revocationDate The revocationDate to set.
+     */
+    public void setRevocationDate(Date revocationDate)
+    {
+        this.revocationDate = revocationDate;
+    }
+
+    /**
+     * @return Returns the certStatus.
+     */
+    public int getCertStatus()
+    {
+        return certStatus;
+    }
+
+    /**
+     * @param certStatus The certStatus to set.
+     */
+    public void setCertStatus(int certStatus)
+    {
+        this.certStatus = certStatus;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java
new file mode 100644
index 0000000..19e02ce
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.StoreException;
+
+class PKIXCRLUtil
+{
+    public Set findCRLs(PKIXCRLStoreSelector crlselect, Date validityDate, List certStores, List pkixCrlStores)
+        throws AnnotatedException
+    {
+        Set initialSet = new HashSet();
+
+        // get complete CRL(s)
+        try
+        {
+            initialSet.addAll(findCRLs(crlselect, pkixCrlStores));
+            initialSet.addAll(findCRLs(crlselect, certStores));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Exception obtaining complete CRLs.", e);
+        }
+
+        Set finalSet = new HashSet();
+
+        // based on RFC 5280 6.3.3
+        for (Iterator it = initialSet.iterator(); it.hasNext();)
+        {
+            X509CRL crl = (X509CRL)it.next();
+
+            if (crl.getNextUpdate().after(validityDate))
+            {
+                X509Certificate cert = crlselect.getCertificateChecking();
+
+                if (cert != null)
+                {
+                    if (crl.getThisUpdate().before(cert.getNotAfter()))
+                    {
+                        finalSet.add(crl);
+                    }
+                }
+                else
+                {
+                    finalSet.add(crl);
+                }
+            }
+        }
+
+        return finalSet;
+    }
+
+    /**
+     * Return a Collection of all CRLs found in the X509Store's that are
+     * matching the crlSelect criteriums.
+     *
+     * @param crlSelect a {@link PKIXCRLStoreSelector} object that will be used
+     *            to select the CRLs
+     * @param crlStores a List containing only
+     *            {@link Store} objects.
+     *            These are used to search for CRLs
+     *
+     * @return a Collection of all found {@link X509CRL X509CRL} objects. May be
+     *         empty but never <code>null</code>.
+     */
+    private final Collection findCRLs(PKIXCRLStoreSelector crlSelect,
+        List crlStores) throws AnnotatedException
+    {
+        Set crls = new HashSet();
+        Iterator iter = crlStores.iterator();
+
+        AnnotatedException lastException = null;
+        boolean foundValidStore = false;
+
+        while (iter.hasNext())
+        {
+            Object obj = iter.next();
+
+            if (obj instanceof Store)
+            {
+                Store store = (Store)obj;
+
+                try
+                {
+                    crls.addAll(store.getMatches(crlSelect));
+                    foundValidStore = true;
+                }
+                catch (StoreException e)
+                {
+                    lastException = new AnnotatedException(
+                        "Exception searching in X.509 CRL store.", e);
+                }
+            }
+            else
+            {
+                CertStore store = (CertStore)obj;
+
+                try
+                {
+                    crls.addAll(PKIXCRLStoreSelector.getCRLs(crlSelect, store));
+                    foundValidStore = true;
+                }
+                catch (CertStoreException e)
+                {
+                    lastException = new AnnotatedException(
+                        "Exception searching in X.509 CRL store.", e);
+                }
+            }
+        }
+        if (!foundValidStore && lastException != null)
+        {
+            throw lastException;
+        }
+        return crls;
+    }
+
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXDefaultJcaJceHelper.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXDefaultJcaJceHelper.java
new file mode 100644
index 0000000..bd9f3da
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXDefaultJcaJceHelper.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertPathBuilder;
+
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+
+class PKIXDefaultJcaJceHelper
+    extends DefaultJcaJceHelper
+    implements PKIXJcaJceHelper
+{
+    public PKIXDefaultJcaJceHelper()
+    {
+        super();
+    }
+
+    public CertPathBuilder createCertPathBuilder(String type)
+        throws NoSuchAlgorithmException
+    {
+        return CertPathBuilder.getInstance(type);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java
new file mode 100644
index 0000000..b3cd88e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertPathBuilder;
+
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+
+interface PKIXJcaJceHelper
+    extends JcaJceHelper
+{
+    CertPathBuilder createCertPathBuilder(String type)
+        throws NoSuchAlgorithmException, NoSuchProviderException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXNamedJcaJceHelper.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXNamedJcaJceHelper.java
new file mode 100644
index 0000000..20875f4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXNamedJcaJceHelper.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertPathBuilder;
+
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+
+class PKIXNamedJcaJceHelper
+    extends NamedJcaJceHelper
+    implements PKIXJcaJceHelper
+{
+    public PKIXNamedJcaJceHelper(String providerName)
+    {
+        super(providerName);
+    }
+
+    public CertPathBuilder createCertPathBuilder(String type)
+        throws NoSuchAlgorithmException, NoSuchProviderException
+    {
+        return CertPathBuilder.getInstance(type, providerName);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXProviderJcaJceHelper.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXProviderJcaJceHelper.java
new file mode 100644
index 0000000..988a68b
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXProviderJcaJceHelper.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.cert.CertPathBuilder;
+
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+
+class PKIXProviderJcaJceHelper
+    extends ProviderJcaJceHelper
+    implements PKIXJcaJceHelper
+{
+    public PKIXProviderJcaJceHelper(Provider provider)
+    {
+        super(provider);
+    }
+
+    public CertPathBuilder createCertPathBuilder(String type)
+        throws NoSuchAlgorithmException
+    {
+        return CertPathBuilder.getInstance(type, provider);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java
new file mode 100644
index 0000000..728528a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java
@@ -0,0 +1,1021 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLSelector;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509Extension;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
+import org.bouncycastle.jcajce.PKIXCertStoreSelector;
+import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
+import org.bouncycastle.jcajce.PKIXExtendedParameters;
+import org.bouncycastle.util.Arrays;
+
+class RFC3280CertPathUtilities
+{
+    private static final PKIXCRLUtil CRL_UTIL = new PKIXCRLUtil();
+
+    /**
+     * If the complete CRL includes an issuing distribution point (IDP) CRL
+     * extension check the following:
+     * <p>
+     * (i) If the distribution point name is present in the IDP CRL extension
+     * and the distribution field is present in the DP, then verify that one of
+     * the names in the IDP matches one of the names in the DP. If the
+     * distribution point name is present in the IDP CRL extension and the
+     * distribution field is omitted from the DP, then verify that one of the
+     * names in the IDP matches one of the names in the cRLIssuer field of the
+     * DP.
+     * </p>
+     * <p>
+     * (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL
+     * extension, verify that the certificate does not include the basic
+     * constraints extension with the cA boolean asserted.
+     * </p>
+     * <p>
+     * (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL
+     * extension, verify that the certificate includes the basic constraints
+     * extension with the cA boolean asserted.
+     * </p>
+     * <p>
+     * (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted.
+     * </p>
+     *
+     * @param dp   The distribution point.
+     * @param cert The certificate.
+     * @param crl  The CRL.
+     * @throws AnnotatedException if one of the conditions is not met or an error occurs.
+     */
+    protected static void processCRLB2(
+        DistributionPoint dp,
+        Object cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        IssuingDistributionPoint idp = null;
+        try
+        {
+            idp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
+                Extension.issuingDistributionPoint));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+        }
+        // (b) (2) (i)
+        // distribution point name is present
+        if (idp != null)
+        {
+            if (idp.getDistributionPoint() != null)
+            {
+                // make list of names
+                DistributionPointName dpName = IssuingDistributionPoint.getInstance(idp).getDistributionPoint();
+                List names = new ArrayList();
+
+                if (dpName.getType() == DistributionPointName.FULL_NAME)
+                {
+                    GeneralName[] genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+                    for (int j = 0; j < genNames.length; j++)
+                    {
+                        names.add(genNames[j]);
+                    }
+                }
+                if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+                {
+                    ASN1EncodableVector vec = new ASN1EncodableVector();
+                    try
+                    {
+                        Enumeration e = ASN1Sequence.getInstance(crl.getIssuerX500Principal().getEncoded()).getObjects();
+                        while (e.hasMoreElements())
+                        {
+                            vec.add((ASN1Encodable)e.nextElement());
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        throw new AnnotatedException("Could not read CRL issuer.", e);
+                    }
+                    vec.add(dpName.getName());
+                    names.add(new GeneralName(X500Name.getInstance(new DERSequence(vec))));
+                }
+                boolean matches = false;
+                // verify that one of the names in the IDP matches one
+                // of the names in the DP.
+                if (dp.getDistributionPoint() != null)
+                {
+                    dpName = dp.getDistributionPoint();
+                    GeneralName[] genNames = null;
+                    if (dpName.getType() == DistributionPointName.FULL_NAME)
+                    {
+                        genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+                    }
+                    if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+                    {
+                        if (dp.getCRLIssuer() != null)
+                        {
+                            genNames = dp.getCRLIssuer().getNames();
+                        }
+                        else
+                        {
+                            genNames = new GeneralName[1];
+                            try
+                            {
+                                genNames[0] = new GeneralName(X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()));
+                            }
+                            catch (Exception e)
+                            {
+                                throw new AnnotatedException("Could not read certificate issuer.", e);
+                            }
+                        }
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            Enumeration e = ASN1Sequence.getInstance(genNames[j].getName().toASN1Primitive()).getObjects();
+                            ASN1EncodableVector vec = new ASN1EncodableVector();
+                            while (e.hasMoreElements())
+                            {
+                                vec.add((ASN1Encodable)e.nextElement());
+                            }
+                            vec.add(dpName.getName());
+                            genNames[j] = new GeneralName(X500Name.getInstance(new DERSequence(vec)));
+                        }
+                    }
+                    if (genNames != null)
+                    {
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            if (names.contains(genNames[j]))
+                            {
+                                matches = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!matches)
+                    {
+                        throw new AnnotatedException(
+                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+                    }
+                }
+                // verify that one of the names in
+                // the IDP matches one of the names in the cRLIssuer field of
+                // the DP
+                else
+                {
+                    if (dp.getCRLIssuer() == null)
+                    {
+                        throw new AnnotatedException("Either the cRLIssuer or the distributionPoint field must "
+                            + "be contained in DistributionPoint.");
+                    }
+                    GeneralName[] genNames = dp.getCRLIssuer().getNames();
+                    for (int j = 0; j < genNames.length; j++)
+                    {
+                        if (names.contains(genNames[j]))
+                        {
+                            matches = true;
+                            break;
+                        }
+                    }
+                    if (!matches)
+                    {
+                        throw new AnnotatedException(
+                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+                    }
+                }
+            }
+            BasicConstraints bc = null;
+            try
+            {
+                bc = BasicConstraints.getInstance(RevocationUtilities.getExtensionValue((X509Extension)cert,
+                    Extension.basicConstraints));
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException("Basic constraints extension could not be decoded.", e);
+            }
+
+            if (cert instanceof X509Certificate)
+            {
+                // (b) (2) (ii)
+                if (idp.onlyContainsUserCerts() && (bc != null && bc.isCA()))
+                {
+                    throw new AnnotatedException("CA Cert CRL only contains user certificates.");
+                }
+
+                // (b) (2) (iii)
+                if (idp.onlyContainsCACerts() && (bc == null || !bc.isCA()))
+                {
+                    throw new AnnotatedException("End CRL only contains CA certificates.");
+                }
+            }
+
+            // (b) (2) (iv)
+            if (idp.onlyContainsAttributeCerts())
+            {
+                throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted.");
+            }
+        }
+    }
+
+    /**
+     * If the DP includes cRLIssuer, then verify that the issuer field in the
+     * complete CRL matches cRLIssuer in the DP and that the complete CRL
+     * contains an issuing distribution point extension with the indirectCRL
+     * boolean asserted. Otherwise, verify that the CRL issuer matches the
+     * certificate issuer.
+     *
+     * @param dp   The distribution point.
+     * @param cert The certificate ot attribute certificate.
+     * @param crl  The CRL for <code>cert</code>.
+     * @throws AnnotatedException if one of the above conditions does not apply or an error
+     *                            occurs.
+     */
+    protected static void processCRLB1(
+        DistributionPoint dp,
+        Object cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        ASN1Primitive idp = RevocationUtilities.getExtensionValue(crl, Extension.issuingDistributionPoint);
+        boolean isIndirect = false;
+        if (idp != null)
+        {
+            if (IssuingDistributionPoint.getInstance(idp).isIndirectCRL())
+            {
+                isIndirect = true;
+            }
+        }
+        byte[] issuerBytes;
+
+            issuerBytes = crl.getIssuerX500Principal().getEncoded();
+
+
+        boolean matchIssuer = false;
+        if (dp.getCRLIssuer() != null)
+        {
+            GeneralName genNames[] = dp.getCRLIssuer().getNames();
+            for (int j = 0; j < genNames.length; j++)
+            {
+                if (genNames[j].getTagNo() == GeneralName.directoryName)
+                {
+                    try
+                    {
+                        if (Arrays.areEqual(genNames[j].getName().toASN1Primitive().getEncoded(), issuerBytes))
+                        {
+                            matchIssuer = true;
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "CRL issuer information from distribution point cannot be decoded.", e);
+                    }
+                }
+            }
+            if (matchIssuer && !isIndirect)
+            {
+                throw new AnnotatedException("Distribution point contains cRLIssuer field but CRL is not indirect.");
+            }
+            if (!matchIssuer)
+            {
+                throw new AnnotatedException("CRL issuer of CRL does not match CRL issuer of distribution point.");
+            }
+        }
+        else
+        {
+            if (crl.getIssuerX500Principal().equals(((X509Certificate)cert).getIssuerX500Principal()))
+            {
+                matchIssuer = true;
+            }
+        }
+        if (!matchIssuer)
+        {
+            throw new AnnotatedException("Cannot find matching CRL issuer for certificate.");
+        }
+    }
+
+    protected static ReasonsMask processCRLD(
+        X509CRL crl,
+        DistributionPoint dp)
+        throws AnnotatedException
+    {
+        IssuingDistributionPoint idp = null;
+        try
+        {
+            idp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
+                Extension.issuingDistributionPoint));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+        }
+        // (d) (1)
+        if (idp != null && idp.getOnlySomeReasons() != null && dp.getReasons() != null)
+        {
+            return new ReasonsMask(dp.getReasons()).intersect(new ReasonsMask(idp.getOnlySomeReasons()));
+        }
+        // (d) (4)
+        if ((idp == null || idp.getOnlySomeReasons() == null) && dp.getReasons() == null)
+        {
+            return ReasonsMask.allReasons;
+        }
+        // (d) (2) and (d)(3)
+        return (dp.getReasons() == null
+            ? ReasonsMask.allReasons
+            : new ReasonsMask(dp.getReasons())).intersect(idp == null
+            ? ReasonsMask.allReasons
+            : new ReasonsMask(idp.getOnlySomeReasons()));
+
+    }
+
+
+    public static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
+
+    public static final String FRESHEST_CRL = Extension.freshestCRL.getId();
+
+    public static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId();
+
+    public static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId();
+
+    public static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId();
+
+    /*
+     * key usage bits
+     */
+    protected static final int KEY_CERT_SIGN = 5;
+
+    protected static final int CRL_SIGN = 6;
+
+    /**
+     * Obtain and validate the certification path for the complete CRL issuer.
+     * If a key usage extension is present in the CRL issuer's certificate,
+     * verify that the cRLSign bit is set.
+     *
+     * @param crl                CRL which contains revocation information for the certificate
+     *                           <code>cert</code>.
+     * @param cert               The attribute certificate or certificate to check if it is
+     *                           revoked.
+     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+     * @param defaultCRLSignKey  The public key of the issuer certificate
+     *                           <code>defaultCRLSignCert</code>.
+     * @param paramsPKIX         paramsPKIX PKIX parameters.
+     * @param certPathCerts      The certificates on the certification path.
+     * @return A <code>Set</code> with all keys of possible CRL issuer
+     *         certificates.
+     * @throws AnnotatedException if the CRL is not valid or the status cannot be checked or
+     *                            some error occurs.
+     */
+    protected static Set processCRLF(
+        X509CRL crl,
+        Object cert,
+        X509Certificate defaultCRLSignCert,
+        PublicKey defaultCRLSignKey,
+        PKIXExtendedParameters paramsPKIX,
+        List certPathCerts,
+        PKIXJcaJceHelper helper)
+        throws AnnotatedException
+    {
+        // (f)
+
+        // get issuer from CRL
+        X509CertSelector certSelector = new X509CertSelector();
+        try
+        {
+            byte[] issuerPrincipal = crl.getIssuerX500Principal().getEncoded();
+            certSelector.setSubject(issuerPrincipal);
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException(
+                "subject criteria for certificate selector to find issuer certificate for CRL could not be set", e);
+        }
+
+        PKIXCertStoreSelector selector = new PKIXCertStoreSelector.Builder(certSelector).build();
+
+        // get CRL signing certs
+        Collection coll;
+        try
+        {
+            coll = RevocationUtilities.findCertificates(selector, paramsPKIX.getCertificateStores());
+            coll.addAll(RevocationUtilities.findCertificates(selector, paramsPKIX.getCertStores()));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Issuer certificate for CRL cannot be searched.", e);
+        }
+
+        coll.add(defaultCRLSignCert);
+
+        Iterator cert_it = coll.iterator();
+
+        List validCerts = new ArrayList();
+        List validKeys = new ArrayList();
+
+        while (cert_it.hasNext())
+        {
+            X509Certificate signingCert = (X509Certificate)cert_it.next();
+
+            /*
+             * CA of the certificate, for which this CRL is checked, has also
+             * signed CRL, so skip the path validation, because is already done
+             */
+            if (signingCert.equals(defaultCRLSignCert))
+            {
+                validCerts.add(signingCert);
+                validKeys.add(defaultCRLSignKey);
+                continue;
+            }
+            try
+            {
+                CertPathBuilder builder = helper.createCertPathBuilder("PKIX");
+                X509CertSelector tmpCertSelector = new X509CertSelector();
+                tmpCertSelector.setCertificate(signingCert);
+
+                PKIXExtendedParameters.Builder paramsBuilder = new PKIXExtendedParameters.Builder(paramsPKIX)
+                    .setTargetConstraints(new PKIXCertStoreSelector.Builder(tmpCertSelector).build());
+
+                /*
+                 * if signingCert is placed not higher on the cert path a
+                 * dependency loop results. CRL for cert is checked, but
+                 * signingCert is needed for checking the CRL which is dependent
+                 * on checking cert because it is higher in the cert path and so
+                 * signing signingCert transitively. so, revocation is disabled,
+                 * forgery attacks of the CRL are detected in this outer loop
+                 * for all other it must be enabled to prevent forgery attacks
+                 */
+                if (certPathCerts.contains(signingCert))
+                {
+                    paramsBuilder.setRevocationEnabled(false);
+                }
+                else
+                {
+                    paramsBuilder.setRevocationEnabled(true);
+                }
+
+                PKIXExtendedBuilderParameters extParams = new PKIXExtendedBuilderParameters.Builder(paramsBuilder.build()).build();
+
+                List certs = builder.build(extParams).getCertPath().getCertificates();
+                validCerts.add(signingCert);
+                validKeys.add(RevocationUtilities.getNextWorkingKey(certs, 0, helper));
+            }
+            catch (CertPathBuilderException e)
+            {
+                throw new AnnotatedException("CertPath for CRL signer failed to validate.", e);
+            }
+            catch (CertPathValidatorException e)
+            {
+                throw new AnnotatedException("Public key of issuer certificate of CRL could not be retrieved.", e);
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(e.getMessage());
+            }
+        }
+
+        Set checkKeys = new HashSet();
+
+        AnnotatedException lastException = null;
+        for (int i = 0; i < validCerts.size(); i++)
+        {
+            X509Certificate signCert = (X509Certificate)validCerts.get(i);
+            boolean[] keyusage = signCert.getKeyUsage();
+
+            if (keyusage != null && (keyusage.length < 7 || !keyusage[CRL_SIGN]))
+            {
+                lastException = new AnnotatedException(
+                    "Issuer certificate key usage extension does not permit CRL signing.");
+            }
+            else
+            {
+                checkKeys.add(validKeys.get(i));
+            }
+        }
+
+        if (checkKeys.isEmpty() && lastException == null)
+        {
+            throw new AnnotatedException("Cannot find a valid issuer certificate.");
+        }
+        if (checkKeys.isEmpty() && lastException != null)
+        {
+            throw lastException;
+        }
+
+        return checkKeys;
+    }
+
+    protected static PublicKey processCRLG(
+        X509CRL crl,
+        Set keys)
+        throws AnnotatedException
+    {
+        Exception lastException = null;
+        for (Iterator it = keys.iterator(); it.hasNext();)
+        {
+            PublicKey key = (PublicKey)it.next();
+            try
+            {
+                crl.verify(key);
+                return key;
+            }
+            catch (Exception e)
+            {
+                lastException = e;
+            }
+        }
+        throw new AnnotatedException("Cannot verify CRL.", lastException);
+    }
+
+    protected static X509CRL processCRLH(
+        Set deltacrls,
+        PublicKey key)
+        throws AnnotatedException
+    {
+        Exception lastException = null;
+
+        for (Iterator it = deltacrls.iterator(); it.hasNext();)
+        {
+            X509CRL crl = (X509CRL)it.next();
+            try
+            {
+                crl.verify(key);
+                return crl;
+            }
+            catch (Exception e)
+            {
+                lastException = e;
+            }
+        }
+
+        if (lastException != null)
+        {
+            throw new AnnotatedException("Cannot verify delta CRL.", lastException);
+        }
+        return null;
+    }
+
+    protected static Set processCRLA1i(
+        Date currentDate,
+        PKIXExtendedParameters paramsPKIX,
+        X509Certificate cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        Set set = new HashSet();
+        if (paramsPKIX.isUseDeltasEnabled())
+        {
+            CRLDistPoint freshestCRL = null;
+            try
+            {
+                freshestCRL = CRLDistPoint
+                    .getInstance(RevocationUtilities.getExtensionValue(cert, Extension.freshestCRL));
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException("Freshest CRL extension could not be decoded from certificate.", e);
+            }
+            if (freshestCRL == null)
+            {
+                try
+                {
+                    freshestCRL = CRLDistPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
+                        Extension.freshestCRL));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException("Freshest CRL extension could not be decoded from CRL.", e);
+                }
+            }
+            if (freshestCRL != null)
+            {
+                List crlStores = new ArrayList();
+
+                crlStores.addAll(paramsPKIX.getCRLStores());
+
+                try
+                {
+                    crlStores.addAll(RevocationUtilities.getAdditionalStoresFromCRLDistributionPoint(freshestCRL, paramsPKIX.getNamedCRLStoreMap()));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException(
+                        "No new delta CRL locations could be added from Freshest CRL extension.", e);
+                }
+
+                // get delta CRL(s)
+                try
+                {
+                    set.addAll(RevocationUtilities.getDeltaCRLs(currentDate, crl, paramsPKIX.getCertStores(), crlStores));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+                }
+            }
+        }
+        return set;
+    }
+
+    protected static Set[] processCRLA1ii(
+        Date currentDate,
+        PKIXExtendedParameters paramsPKIX,
+        X509Certificate cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        Set deltaSet = new HashSet();
+        X509CRLSelector crlselect = new X509CRLSelector();
+        crlselect.setCertificateChecking(cert);
+
+        try
+        {
+            crlselect.addIssuerName(crl.getIssuerX500Principal().getEncoded());
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException("Cannot extract issuer from CRL." + e, e);
+        }
+
+        PKIXCRLStoreSelector extSelect = new PKIXCRLStoreSelector.Builder(crlselect).setCompleteCRLEnabled(true).build();
+
+        Date validityDate = currentDate;
+
+        if (paramsPKIX.getDate() != null)
+        {
+            validityDate = paramsPKIX.getDate();
+        }
+
+        Set completeSet = CRL_UTIL.findCRLs(extSelect, validityDate, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores());
+
+        if (paramsPKIX.isUseDeltasEnabled())
+        {
+            // get delta CRL(s)
+            try
+            {
+                deltaSet.addAll(RevocationUtilities.getDeltaCRLs(validityDate, crl, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores()));
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+            }
+        }
+        return new Set[]
+            {
+                completeSet,
+                deltaSet};
+    }
+
+
+
+    /**
+     * If use-deltas is set, verify the issuer and scope of the delta CRL.
+     *
+     * @param deltaCRL    The delta CRL.
+     * @param completeCRL The complete CRL.
+     * @param pkixParams  The PKIX paramaters.
+     * @throws AnnotatedException if an exception occurs.
+     */
+    protected static void processCRLC(
+        X509CRL deltaCRL,
+        X509CRL completeCRL,
+        PKIXExtendedParameters pkixParams)
+        throws AnnotatedException
+    {
+        if (deltaCRL == null)
+        {
+            return;
+        }
+        IssuingDistributionPoint completeidp = null;
+        try
+        {
+            completeidp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(
+                completeCRL, Extension.issuingDistributionPoint));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("issuing distribution point extension could not be decoded.", e);
+        }
+
+        if (pkixParams.isUseDeltasEnabled())
+        {
+            // (c) (1)
+            if (!deltaCRL.getIssuerX500Principal().equals(completeCRL.getIssuerX500Principal()))
+            {
+                throw new AnnotatedException("complete CRL issuer does not match delta CRL issuer");
+            }
+
+            // (c) (2)
+            IssuingDistributionPoint deltaidp = null;
+            try
+            {
+                deltaidp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(
+                    deltaCRL, Extension.issuingDistributionPoint));
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "Issuing distribution point extension from delta CRL could not be decoded.", e);
+            }
+
+            boolean match = false;
+            if (completeidp == null)
+            {
+                if (deltaidp == null)
+                {
+                    match = true;
+                }
+            }
+            else
+            {
+                if (completeidp.equals(deltaidp))
+                {
+                    match = true;
+                }
+            }
+            if (!match)
+            {
+                throw new AnnotatedException(
+                    "Issuing distribution point extension from delta CRL and complete CRL does not match.");
+            }
+
+            // (c) (3)
+            ASN1Primitive completeKeyIdentifier = null;
+            try
+            {
+                completeKeyIdentifier = RevocationUtilities.getExtensionValue(
+                    completeCRL, Extension.authorityKeyIdentifier);
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException(
+                    "Authority key identifier extension could not be extracted from complete CRL.", e);
+            }
+
+            ASN1Primitive deltaKeyIdentifier = null;
+            try
+            {
+                deltaKeyIdentifier = RevocationUtilities.getExtensionValue(
+                    deltaCRL, Extension.authorityKeyIdentifier);
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException(
+                    "Authority key identifier extension could not be extracted from delta CRL.", e);
+            }
+
+            if (completeKeyIdentifier == null)
+            {
+                throw new AnnotatedException("CRL authority key identifier is null.");
+            }
+
+            if (deltaKeyIdentifier == null)
+            {
+                throw new AnnotatedException("Delta CRL authority key identifier is null.");
+            }
+
+            if (!completeKeyIdentifier.equals(deltaKeyIdentifier))
+            {
+                throw new AnnotatedException(
+                    "Delta CRL authority key identifier does not match complete CRL authority key identifier.");
+            }
+        }
+    }
+
+    protected static void processCRLI(
+        Date validDate,
+        X509CRL deltacrl,
+        Object cert,
+        CertStatus certStatus,
+        PKIXExtendedParameters pkixParams)
+        throws AnnotatedException
+    {
+        if (pkixParams.isUseDeltasEnabled() && deltacrl != null)
+        {
+            RevocationUtilities.getCertStatus(validDate, deltacrl, cert, certStatus);
+        }
+    }
+
+    protected static void processCRLJ(
+        Date validDate,
+        X509CRL completecrl,
+        Object cert,
+        CertStatus certStatus)
+        throws AnnotatedException
+    {
+        if (certStatus.getCertStatus() == CertStatus.UNREVOKED)
+        {
+            RevocationUtilities.getCertStatus(validDate, completecrl, cert, certStatus);
+        }
+    }
+
+    /**
+     * Checks a distribution point for revocation information for the
+     * certificate <code>cert</code>.
+     *
+     * @param dp                 The distribution point to consider.
+     * @param paramsPKIX         PKIX parameters.
+     * @param cert               Certificate to check if it is revoked.
+     * @param validDate          The date when the certificate revocation status should be
+     *                           checked.
+     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+     * @param defaultCRLSignKey  The public key of the issuer certificate
+     *                           <code>defaultCRLSignCert</code>.
+     * @param certStatus         The current certificate revocation status.
+     * @param reasonMask         The reasons mask which is already checked.
+     * @param certPathCerts      The certificates of the certification path.
+     * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+     *                            or some error occurs.
+     */
+    static void checkCRL(
+        DistributionPoint dp,
+        PKIXExtendedParameters paramsPKIX,
+        X509Certificate cert,
+        Date validDate,
+        X509Certificate defaultCRLSignCert,
+        PublicKey defaultCRLSignKey,
+        CertStatus certStatus,
+        ReasonsMask reasonMask,
+        List certPathCerts,
+        PKIXJcaJceHelper helper)
+        throws AnnotatedException, CRLNotFoundException
+    {
+        Date currentDate = new Date(System.currentTimeMillis());
+        if (validDate.getTime() > currentDate.getTime())
+        {
+            throw new AnnotatedException("Validation time is in future.");
+        }
+
+        // (a)
+        /*
+         * We always get timely valid CRLs, so there is no step (a) (1).
+         * "locally cached" CRLs are assumed to be in getStore(), additional
+         * CRLs must be enabled in the ExtendedPKIXParameters and are in
+         * getAdditionalStore()
+         */
+
+        Date validityDate = currentDate;
+
+        if (paramsPKIX.getDate() != null)
+        {
+            validityDate = paramsPKIX.getDate();
+        }
+
+        Set crls = RevocationUtilities.getCompleteCRLs(dp, cert, validityDate, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores());
+        boolean validCrlFound = false;
+        AnnotatedException lastException = null;
+        Iterator crl_iter = crls.iterator();
+
+        while (crl_iter.hasNext() && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonMask.isAllReasons())
+        {
+            try
+            {
+                X509CRL crl = (X509CRL)crl_iter.next();
+
+                // (d)
+                ReasonsMask interimReasonsMask = RFC3280CertPathUtilities.processCRLD(crl, dp);
+
+                // (e)
+                /*
+                 * The reasons mask is updated at the end, so only valid CRLs
+                 * can update it. If this CRL does not contain new reasons it
+                 * must be ignored.
+                 */
+                if (!interimReasonsMask.hasNewReasons(reasonMask))
+                {
+                    continue;
+                }
+
+                // (f)
+                Set keys = RFC3280CertPathUtilities.processCRLF(crl, cert, defaultCRLSignCert, defaultCRLSignKey,
+                    paramsPKIX, certPathCerts, helper);
+                // (g)
+                PublicKey key = RFC3280CertPathUtilities.processCRLG(crl, keys);
+
+                X509CRL deltaCRL = null;
+
+                if (paramsPKIX.isUseDeltasEnabled())
+                {
+                    // get delta CRLs
+                    Set deltaCRLs = RevocationUtilities.getDeltaCRLs(validityDate, crl, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores());
+                    // we only want one valid delta CRL
+                    // (h)
+                    deltaCRL = RFC3280CertPathUtilities.processCRLH(deltaCRLs, key);
+                }
+
+                /*
+                 * CRL must be be valid at the current time, not the validation
+                 * time. If a certificate is revoked with reason keyCompromise,
+                 * cACompromise, it can be used for forgery, also for the past.
+                 * This reason may not be contained in older CRLs.
+                 */
+
+                /*
+                 * in the chain model signatures stay valid also after the
+                 * certificate has been expired, so they do not have to be in
+                 * the CRL validity time
+                 */
+
+                if (paramsPKIX.getValidityModel() != PKIXExtendedParameters.CHAIN_VALIDITY_MODEL)
+                {
+                    /*
+                     * if a certificate has expired, but was revoked, it is not
+                     * more in the CRL, so it would be regarded as valid if the
+                     * first check is not done
+                     */
+                    if (cert.getNotAfter().getTime() < crl.getThisUpdate().getTime())
+                    {
+                        throw new AnnotatedException("No valid CRL for current time found.");
+                    }
+                }
+
+                RFC3280CertPathUtilities.processCRLB1(dp, cert, crl);
+
+                // (b) (2)
+                RFC3280CertPathUtilities.processCRLB2(dp, cert, crl);
+
+                // (c)
+                RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX);
+
+                // (i)
+                RFC3280CertPathUtilities.processCRLI(validDate, deltaCRL, cert, certStatus, paramsPKIX);
+
+                // (j)
+                RFC3280CertPathUtilities.processCRLJ(validDate, crl, cert, certStatus);
+
+                // (k)
+                if (certStatus.getCertStatus() == CRLReason.removeFromCRL)
+                {
+                    certStatus.setCertStatus(CertStatus.UNREVOKED);
+                }
+
+                // update reasons mask
+                reasonMask.addReasons(interimReasonsMask);
+
+                Set criticalExtensions = crl.getCriticalExtensionOIDs();
+                if (criticalExtensions != null)
+                {
+                    criticalExtensions = new HashSet(criticalExtensions);
+                    criticalExtensions.remove(Extension.issuingDistributionPoint.getId());
+                    criticalExtensions.remove(Extension.deltaCRLIndicator.getId());
+
+                    if (!criticalExtensions.isEmpty())
+                    {
+                        throw new AnnotatedException("CRL contains unsupported critical extensions.");
+                    }
+                }
+
+                if (deltaCRL != null)
+                {
+                    criticalExtensions = deltaCRL.getCriticalExtensionOIDs();
+                    if (criticalExtensions != null)
+                    {
+                        criticalExtensions = new HashSet(criticalExtensions);
+                        criticalExtensions.remove(Extension.issuingDistributionPoint.getId());
+                        criticalExtensions.remove(Extension.deltaCRLIndicator.getId());
+                        if (!criticalExtensions.isEmpty())
+                        {
+                            throw new AnnotatedException("Delta CRL contains unsupported critical extension.");
+                        }
+                    }
+                }
+
+                validCrlFound = true;
+            }
+            catch (AnnotatedException e)
+            {
+                lastException = e;
+            }
+        }
+        if (!validCrlFound)
+        {
+            throw lastException;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java
new file mode 100644
index 0000000..d5b1fb0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.pkix.jcajce;
+
+import org.bouncycastle.asn1.x509.ReasonFlags;
+
+/**
+ * This class helps to handle CRL revocation reasons mask. Each CRL handles a
+ * certain set of revocation reasons.
+ */
+class ReasonsMask
+{
+    private int _reasons;
+
+    /**
+     * Constructs are reason mask with the reasons.
+     * 
+     * @param reasons The reasons.
+     */
+    ReasonsMask(ReasonFlags reasons)
+    {
+        _reasons = reasons.intValue();
+    }
+
+    private ReasonsMask(int reasons)
+    {
+        _reasons = reasons;
+    }
+
+    /**
+     * A reason mask with no reason.
+     * 
+     */
+    ReasonsMask()
+    {
+        this(0);
+    }
+
+    /**
+     * A mask with all revocation reasons.
+     */
+    static final ReasonsMask allReasons = new ReasonsMask(ReasonFlags.aACompromise
+            | ReasonFlags.affiliationChanged | ReasonFlags.cACompromise
+            | ReasonFlags.certificateHold | ReasonFlags.cessationOfOperation
+            | ReasonFlags.keyCompromise | ReasonFlags.privilegeWithdrawn
+            | ReasonFlags.unused | ReasonFlags.superseded);
+
+    /**
+     * Adds all reasons from the reasons mask to this mask.
+     * 
+     * @param mask The reasons mask to add.
+     */
+    void addReasons(ReasonsMask mask)
+    {
+        _reasons = _reasons | mask.getReasons();
+    }
+
+    /**
+     * Returns <code>true</code> if this reasons mask contains all possible
+     * reasons.
+     * 
+     * @return <code>true</code> if this reasons mask contains all possible
+     *         reasons.
+     */
+    boolean isAllReasons()
+    {
+        return _reasons == allReasons._reasons ? true : false;
+    }
+
+    /**
+     * Intersects this mask with the given reasons mask.
+     * 
+     * @param mask The mask to intersect with.
+     * @return The intersection of this and teh given mask.
+     */
+    ReasonsMask intersect(ReasonsMask mask)
+    {
+        ReasonsMask _mask = new ReasonsMask();
+        _mask.addReasons(new ReasonsMask(_reasons & mask.getReasons()));
+        return _mask;
+    }
+
+    /**
+     * Returns <code>true</code> if the passed reasons mask has new reasons.
+     * 
+     * @param mask The reasons mask which should be tested for new reasons.
+     * @return <code>true</code> if the passed reasons mask has new reasons.
+     */
+    boolean hasNewReasons(ReasonsMask mask)
+    {
+        return ((_reasons | mask.getReasons() ^ _reasons) != 0);
+    }
+
+    /**
+     * Returns the reasons in this mask.
+     * 
+     * @return Returns the reasons.
+     */
+    int getReasons()
+    {
+        return _reasons;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java
new file mode 100644
index 0000000..6af44e3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java
@@ -0,0 +1,1081 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.cert.CRLException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509CRLSelector;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.RFC4519Style;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.PKIXCRLStore;
+import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
+import org.bouncycastle.jcajce.PKIXCertStore;
+import org.bouncycastle.jcajce.PKIXCertStoreSelector;
+import org.bouncycastle.jcajce.PKIXExtendedParameters;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.StoreException;
+
+class RevocationUtilities
+{
+    protected static final PKIXCRLUtil CRL_UTIL = new PKIXCRLUtil();
+
+    protected static final String CERTIFICATE_POLICIES = Extension.certificatePolicies.getId();
+    protected static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId();
+    protected static final String POLICY_MAPPINGS = Extension.policyMappings.getId();
+    protected static final String SUBJECT_ALTERNATIVE_NAME = Extension.subjectAlternativeName.getId();
+    protected static final String NAME_CONSTRAINTS = Extension.nameConstraints.getId();
+    protected static final String KEY_USAGE = Extension.keyUsage.getId();
+    protected static final String INHIBIT_ANY_POLICY = Extension.inhibitAnyPolicy.getId();
+    protected static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
+    protected static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId();
+    protected static final String POLICY_CONSTRAINTS = Extension.policyConstraints.getId();
+    protected static final String FRESHEST_CRL = Extension.freshestCRL.getId();
+    protected static final String CRL_DISTRIBUTION_POINTS = Extension.cRLDistributionPoints.getId();
+    protected static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId();
+
+    protected static final String ANY_POLICY = "2.5.29.32.0";
+
+    protected static final String CRL_NUMBER = Extension.cRLNumber.getId();
+
+    /*
+    * key usage bits
+    */
+    protected static final int KEY_CERT_SIGN = 5;
+    protected static final int CRL_SIGN = 6;
+
+    protected static final String[] crlReasons = new String[]{
+        "unspecified",
+        "keyCompromise",
+        "cACompromise",
+        "affiliationChanged",
+        "superseded",
+        "cessationOfOperation",
+        "certificateHold",
+        "unknown",
+        "removeFromCRL",
+        "privilegeWithdrawn",
+        "aACompromise"};
+
+    /**
+     * Search the given Set of TrustAnchor's for one that is the
+     * issuer of the given X509 certificate. Uses the default provider
+     * for signature verification.
+     *
+     * @param cert         the X509 certificate
+     * @param trustAnchors a Set of TrustAnchor's
+     * @return the <code>TrustAnchor</code> object if found or
+     *         <code>null</code> if not.
+     * @throws AnnotatedException if a TrustAnchor was found but the signature verification
+     * on the given certificate has thrown an exception.
+     */
+    protected static TrustAnchor findTrustAnchor(
+        X509Certificate cert,
+        Set trustAnchors)
+        throws AnnotatedException
+    {
+        return findTrustAnchor(cert, trustAnchors, null);
+    }
+
+    /**
+     * Search the given Set of TrustAnchor's for one that is the
+     * issuer of the given X509 certificate. Uses the specified
+     * provider for signature verification, or the default provider
+     * if null.
+     *
+     * @param cert         the X509 certificate
+     * @param trustAnchors a Set of TrustAnchor's
+     * @param sigProvider  the provider to use for signature verification
+     * @return the <code>TrustAnchor</code> object if found or
+     *         <code>null</code> if not.
+     * @throws AnnotatedException if a TrustAnchor was found but the signature verification
+     * on the given certificate has thrown an exception.
+     */
+    protected static TrustAnchor findTrustAnchor(
+        X509Certificate cert,
+        Set trustAnchors,
+        String sigProvider)
+        throws AnnotatedException
+    {
+        TrustAnchor trust = null;
+        PublicKey trustPublicKey = null;
+        Exception invalidKeyEx = null;
+
+        X509CertSelector certSelectX509 = new X509CertSelector();
+        X500Name certIssuer = X500Name.getInstance(cert.getIssuerX500Principal());
+
+        try
+        {
+            certSelectX509.setSubject(certIssuer.getEncoded());
+        }
+        catch (IOException ex)
+        {
+            throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex);
+        }
+
+        Iterator iter = trustAnchors.iterator();
+        while (iter.hasNext() && trust == null)
+        {
+            trust = (TrustAnchor)iter.next();
+            if (trust.getTrustedCert() != null)
+            {
+                if (certSelectX509.match(trust.getTrustedCert()))
+                {
+                    trustPublicKey = trust.getTrustedCert().getPublicKey();
+                }
+                else
+                {
+                    trust = null;
+                }
+            }
+            else if (trust.getCAName() != null
+                && trust.getCAPublicKey() != null)
+            {
+                try
+                {
+                    X500Name caName = X500Name.getInstance(trust.getCA().getEncoded());
+                    if (certIssuer.equals(caName))
+                    {
+                        trustPublicKey = trust.getCAPublicKey();
+                    }
+                    else
+                    {
+                        trust = null;
+                    }
+                }
+                catch (IllegalArgumentException ex)
+                {
+                    trust = null;
+                }
+            }
+            else
+            {
+                trust = null;
+            }
+
+            if (trustPublicKey != null)
+            {
+                try
+                {
+                    verifyX509Certificate(cert, trustPublicKey, sigProvider);
+                }
+                catch (Exception ex)
+                {
+                    invalidKeyEx = ex;
+                    trust = null;
+                    trustPublicKey = null;
+                }
+            }
+        }
+
+        if (trust == null && invalidKeyEx != null)
+        {
+            throw new AnnotatedException("TrustAnchor found but certificate validation failed.", invalidKeyEx);
+        }
+
+        return trust;
+    }
+
+    static boolean isIssuerTrustAnchor(
+        X509Certificate cert,
+        Set trustAnchors,
+        String sigProvider)
+        throws AnnotatedException
+    {
+        try
+        {
+            return findTrustAnchor(cert, trustAnchors, sigProvider) != null;
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+
+    static List<PKIXCertStore> getAdditionalStoresFromAltNames(
+        byte[] issuerAlternativeName,
+        Map<GeneralName, PKIXCertStore> altNameCertStoreMap)
+        throws CertificateParsingException
+    {
+        // if in the IssuerAltName extension an URI
+        // is given, add an additional X.509 store
+        if (issuerAlternativeName != null)
+        {
+            GeneralNames issuerAltName = GeneralNames.getInstance(ASN1OctetString.getInstance(issuerAlternativeName).getOctets());
+
+            GeneralName[] names = issuerAltName.getNames();
+            List<PKIXCertStore>  stores = new ArrayList<PKIXCertStore>();
+
+            for (int i = 0; i != names.length; i++)
+            {
+                GeneralName altName = names[i];
+
+                PKIXCertStore altStore = altNameCertStoreMap.get(altName);
+
+                if (altStore != null)
+                {
+                    stores.add(altStore);
+                }
+            }
+
+            return stores;
+        }
+        else
+        {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    protected static Date getValidDate(PKIXExtendedParameters paramsPKIX)
+    {
+        Date validDate = paramsPKIX.getDate();
+
+        if (validDate == null)
+        {
+            validDate = new Date();
+        }
+
+        return validDate;
+    }
+
+    protected static boolean isSelfIssued(X509Certificate cert)
+    {
+        return cert.getSubjectDN().equals(cert.getIssuerDN());
+    }
+
+
+    /**
+     * Extract the value of the given extension, if it exists.
+     *
+     * @param ext The extension object.
+     * @param oid The object identifier to obtain.
+     * @throws AnnotatedException if the extension cannot be read.
+     */
+    protected static ASN1Primitive getExtensionValue(
+        java.security.cert.X509Extension ext,
+        ASN1ObjectIdentifier oid)
+        throws AnnotatedException
+    {
+        byte[] bytes = ext.getExtensionValue(oid.getId());
+        if (bytes == null)
+        {
+            return null;
+        }
+
+        return getObject(oid, bytes);
+    }
+
+    private static ASN1Primitive getObject(
+        ASN1ObjectIdentifier oid,
+        byte[] ext)
+        throws AnnotatedException
+    {
+        try
+        {
+            return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(ext).getOctets());
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("exception processing extension " + oid, e);
+        }
+    }
+
+    protected static AlgorithmIdentifier getAlgorithmIdentifier(
+        PublicKey key)
+        throws CertPathValidatorException
+    {
+        try
+        {
+            ASN1InputStream aIn = new ASN1InputStream(key.getEncoded());
+
+            SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(aIn.readObject());
+
+            return info.getAlgorithm();
+        }
+        catch (Exception e)
+        {
+            throw new CertPathValidatorException("subject public key cannot be decoded", e);
+        }
+    }
+
+    // crl checking
+
+    /**
+     * Return a Collection of all certificates or attribute certificates found
+     * in the X509Store's that are matching the certSelect criteriums.
+     *
+     * @param certSelect a {@link Selector} object that will be used to select
+     *                   the certificates
+     * @param certStores a List containing only {@link Store} objects. These
+     *                   are used to search for certificates.
+     * @return a Collection of all found {@link X509Certificate}
+     *         May be empty but never <code>null</code>.
+     */
+    protected static Collection findCertificates(PKIXCertStoreSelector certSelect,
+                                                 List certStores)
+        throws AnnotatedException
+    {
+        Set certs = new LinkedHashSet();
+        Iterator iter = certStores.iterator();
+
+        while (iter.hasNext())
+        {
+            Object obj = iter.next();
+
+            if (obj instanceof Store)
+            {
+                Store certStore = (Store)obj;
+                try
+                {
+                    certs.addAll(certStore.getMatches(certSelect));
+                }
+                catch (StoreException e)
+                {
+                    throw new AnnotatedException(
+                            "Problem while picking certificates from X.509 store.", e);
+                }
+            }
+            else
+            {
+                CertStore certStore = (CertStore)obj;
+
+                try
+                {
+                    certs.addAll(PKIXCertStoreSelector.getCertificates(certSelect, certStore));
+                }
+                catch (CertStoreException e)
+                {
+                    throw new AnnotatedException(
+                        "Problem while picking certificates from certificate store.",
+                        e);
+                }
+            }
+        }
+        return certs;
+    }
+
+    static List<PKIXCRLStore> getAdditionalStoresFromCRLDistributionPoint(CRLDistPoint crldp, Map<GeneralName, PKIXCRLStore> namedCRLStoreMap)
+        throws AnnotatedException
+    {
+        if (crldp != null)
+        {
+            DistributionPoint dps[] = null;
+            try
+            {
+                dps = crldp.getDistributionPoints();
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "Distribution points could not be read.", e);
+            }
+            List<PKIXCRLStore> stores = new ArrayList<PKIXCRLStore>();
+
+            for (int i = 0; i < dps.length; i++)
+            {
+                DistributionPointName dpn = dps[i].getDistributionPoint();
+                // look for URIs in fullName
+                if (dpn != null)
+                {
+                    if (dpn.getType() == DistributionPointName.FULL_NAME)
+                    {
+                        GeneralName[] genNames = GeneralNames.getInstance(
+                            dpn.getName()).getNames();
+
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            PKIXCRLStore store = namedCRLStoreMap.get(genNames[j]);
+                            if (store != null)
+                            {
+                                stores.add(store);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return stores;
+        }
+        else
+        {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    /**
+     * Add the CRL issuers from the cRLIssuer field of the distribution point or
+     * from the certificate if not given to the issuer criterion of the
+     * <code>selector</code>.
+     * <p>
+     * The <code>issuerPrincipals</code> are a collection with a single
+     * <code>X500Name</code> for <code>X509Certificate</code>s.
+     * </p>
+     * @param dp               The distribution point.
+     * @param issuerPrincipals The issuers of the certificate or attribute
+     *                         certificate which contains the distribution point.
+     * @param selector         The CRL selector.
+     * @throws AnnotatedException if an exception occurs while processing.
+     * @throws ClassCastException if <code>issuerPrincipals</code> does not
+     * contain only <code>X500Name</code>s.
+     */
+    protected static void getCRLIssuersFromDistributionPoint(
+        DistributionPoint dp,
+        Collection issuerPrincipals,
+        X509CRLSelector selector)
+        throws AnnotatedException
+    {
+        List issuers = new ArrayList();
+        // indirect CRL
+        if (dp.getCRLIssuer() != null)
+        {
+            GeneralName genNames[] = dp.getCRLIssuer().getNames();
+            // look for a DN
+            for (int j = 0; j < genNames.length; j++)
+            {
+                if (genNames[j].getTagNo() == GeneralName.directoryName)
+                {
+                    try
+                    {
+                        issuers.add(X500Name.getInstance(genNames[j].getName()
+                            .toASN1Primitive().getEncoded()));
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "CRL issuer information from distribution point cannot be decoded.",
+                            e);
+                    }
+                }
+            }
+        }
+        else
+        {
+            /*
+             * certificate issuer is CRL issuer, distributionPoint field MUST be
+             * present.
+             */
+            if (dp.getDistributionPoint() == null)
+            {
+                throw new AnnotatedException(
+                    "CRL issuer is omitted from distribution point but no distributionPoint field present.");
+            }
+            // add and check issuer principals
+            for (Iterator it = issuerPrincipals.iterator(); it.hasNext(); )
+            {
+                issuers.add(it.next());
+            }
+        }
+        // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
+        // distributionPoint
+//        if (dp.getDistributionPoint() != null)
+//        {
+//            // look for nameRelativeToCRLIssuer
+//            if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+//            {
+//                // append fragment to issuer, only one
+//                // issuer can be there, if this is given
+//                if (issuers.size() != 1)
+//                {
+//                    throw new AnnotatedException(
+//                        "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
+//                }
+//                ASN1Encodable relName = dp.getDistributionPoint().getName();
+//                Iterator it = issuers.iterator();
+//                List issuersTemp = new ArrayList(issuers.size());
+//                while (it.hasNext())
+//                {
+//                    Enumeration e = null;
+//                    try
+//                    {
+//                        e = ASN1Sequence.getInstance(
+//                            new ASN1InputStream(((X500Principal) it.next())
+//                                .getEncoded()).readObject()).getObjects();
+//                    }
+//                    catch (IOException ex)
+//                    {
+//                        throw new AnnotatedException(
+//                            "Cannot decode CRL issuer information.", ex);
+//                    }
+//                    ASN1EncodableVector v = new ASN1EncodableVector();
+//                    while (e.hasMoreElements())
+//                    {
+//                        v.add((ASN1Encodable) e.nextElement());
+//                    }
+//                    v.add(relName);
+//                    issuersTemp.add(new X500Principal(new DERSequence(v)
+//                        .getDEREncoded()));
+//                }
+//                issuers.clear();
+//                issuers.addAll(issuersTemp);
+//            }
+//        }
+        Iterator it = issuers.iterator();
+        while (it.hasNext())
+        {
+            try
+            {
+                selector.addIssuerName(((X500Name)it.next()).getEncoded());
+            }
+            catch (IOException ex)
+            {
+                throw new AnnotatedException(
+                    "Cannot decode CRL issuer information.", ex);
+            }
+        }
+    }
+
+    private static BigInteger getSerialNumber(
+        Object cert)
+    {
+        return ((X509Certificate)cert).getSerialNumber();
+    }
+
+    protected static void getCertStatus(
+        Date validDate,
+        X509CRL crl,
+        Object cert,
+        CertStatus certStatus)
+        throws AnnotatedException
+    {
+        X509CRLEntry crl_entry = null;
+
+        boolean isIndirect;
+        try
+        {
+            isIndirect = isIndirectCRL(crl);
+        }
+        catch (CRLException exception)
+        {
+            throw new AnnotatedException("Failed check for indirect CRL.", exception);
+        }
+
+        if (isIndirect)
+        {
+            crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));
+
+            if (crl_entry == null)
+            {
+                return;
+            }
+
+            X500Principal certificateIssuer = crl_entry.getCertificateIssuer();
+
+            X500Name certIssuer;
+            if (certificateIssuer == null)
+            {
+                certIssuer = X500Name.getInstance(crl.getIssuerX500Principal());
+            }
+            else
+            {
+                certIssuer = X500Name.getInstance(certificateIssuer.getEncoded());
+            }
+
+            if (!X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()).equals(certIssuer))
+            {
+                return;
+            }
+        }
+        else if (!X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()).equals(X500Name.getInstance(crl.getIssuerX500Principal().getEncoded())))
+        {
+            return;  // not for our issuer, ignore
+        }
+        else
+        {
+            crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));
+
+            if (crl_entry == null)
+            {
+                return;
+            }
+        }
+
+        ASN1Enumerated reasonCode = null;
+        if (crl_entry.hasExtensions())
+        {
+            try
+            {
+                reasonCode = ASN1Enumerated
+                    .getInstance(RevocationUtilities
+                        .getExtensionValue(crl_entry,
+                            Extension.reasonCode));
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "Reason code CRL entry extension could not be decoded.",
+                    e);
+            }
+        }
+
+        // for reason keyCompromise, caCompromise, aACompromise or
+        // unspecified
+        if (!(validDate.getTime() < crl_entry.getRevocationDate().getTime())
+            || reasonCode == null
+            || reasonCode.getValue().intValue() == 0
+            || reasonCode.getValue().intValue() == 1
+            || reasonCode.getValue().intValue() == 2
+            || reasonCode.getValue().intValue() == 8)
+        {
+
+            // (i) or (j) (1)
+            if (reasonCode != null)
+            {
+                certStatus.setCertStatus(reasonCode.getValue().intValue());
+            }
+            // (i) or (j) (2)
+            else
+            {
+                certStatus.setCertStatus(CRLReason.unspecified);
+            }
+            certStatus.setRevocationDate(crl_entry.getRevocationDate());
+        }
+    }
+
+    /**
+     * Fetches delta CRLs according to RFC 3280 section 5.2.4.
+     *
+     * @param validityDate The date for which the delta CRLs must be valid.
+     * @param completeCRL The complete CRL the delta CRL is for.
+     * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
+     * @throws AnnotatedException if an exception occurs while picking the delta
+     * CRLs.
+     */
+    protected static Set getDeltaCRLs(Date validityDate,
+                                      X509CRL completeCRL, List<CertStore> certStores, List<PKIXCRLStore> pkixCrlStores)
+        throws AnnotatedException
+    {
+        X509CRLSelector baseDeltaSelect = new X509CRLSelector();
+        // 5.2.4 (a)
+        try
+        {
+            baseDeltaSelect.addIssuerName(X500Name.getInstance(completeCRL.getIssuerX500Principal().getEncoded()).getEncoded());
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException("cannot extract issuer from CRL.", e);
+        }
+
+
+
+        BigInteger completeCRLNumber = null;
+        try
+        {
+            ASN1Primitive derObject = RevocationUtilities.getExtensionValue(completeCRL,
+                Extension.cRLNumber);
+            if (derObject != null)
+            {
+                completeCRLNumber = ASN1Integer.getInstance(derObject).getPositiveValue();
+            }
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException(
+                "cannot extract CRL number extension from CRL", e);
+        }
+
+        // 5.2.4 (b)
+        byte[] idp = null;
+        try
+        {
+            idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException(
+                "issuing distribution point extension value could not be read",
+                e);
+        }
+
+        // 5.2.4 (d)
+
+        baseDeltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
+            .add(BigInteger.valueOf(1)));
+
+        PKIXCRLStoreSelector.Builder selBuilder = new PKIXCRLStoreSelector.Builder(baseDeltaSelect);
+
+        selBuilder.setIssuingDistributionPoint(idp);
+        selBuilder.setIssuingDistributionPointEnabled(true);
+
+        // 5.2.4 (c)
+        selBuilder.setMaxBaseCRLNumber(completeCRLNumber);
+
+        PKIXCRLStoreSelector deltaSelect = selBuilder.build();
+
+        // find delta CRLs
+        Set temp = CRL_UTIL.findCRLs(deltaSelect, validityDate, certStores, pkixCrlStores);
+
+        Set result = new HashSet();
+
+        for (Iterator it = temp.iterator(); it.hasNext(); )
+        {
+            X509CRL crl = (X509CRL)it.next();
+
+            if (isDeltaCRL(crl))
+            {
+                result.add(crl);
+            }
+        }
+
+        return result;
+    }
+
+    private static boolean isDeltaCRL(X509CRL crl)
+    {
+        Set critical = crl.getCriticalExtensionOIDs();
+
+        if (critical == null)
+        {
+            return false;
+        }
+
+        return critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+    }
+
+    /**
+     * Fetches complete CRLs according to RFC 3280.
+     *
+     * @param dp          The distribution point for which the complete CRL
+     * @param cert        The <code>X509Certificate</code> for
+     *                    which the CRL should be searched.
+     * @return A <code>Set</code> of <code>X509CRL</code>s with complete
+     *         CRLs.
+     * @throws AnnotatedException if an exception occurs while picking the CRLs
+     * or no CRLs are found.
+     */
+    protected static Set getCompleteCRLs(DistributionPoint dp, Object cert, Date validityDate, List certStores, List crlStores)
+        throws AnnotatedException, CRLNotFoundException
+    {
+        X509CRLSelector baseCrlSelect = new X509CRLSelector();
+
+        try
+        {
+            Set issuers = new HashSet();
+
+            issuers.add(X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()));
+
+            RevocationUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, baseCrlSelect);
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException(
+                "Could not get issuer information from distribution point.", e);
+        }
+
+        if (cert instanceof X509Certificate)
+        {
+            baseCrlSelect.setCertificateChecking((X509Certificate)cert);
+        }
+
+        PKIXCRLStoreSelector crlSelect = new PKIXCRLStoreSelector.Builder(baseCrlSelect).setCompleteCRLEnabled(true).build();
+
+        Set crls = CRL_UTIL.findCRLs(crlSelect, validityDate, certStores, crlStores);
+
+        checkCRLsNotEmpty(crls, cert);
+
+        return crls;
+    }
+
+    protected static Date getValidCertDateFromValidityModel(
+        PKIXExtendedParameters paramsPKIX, CertPath certPath, int index)
+        throws AnnotatedException
+    {
+        if (paramsPKIX.getValidityModel() == PKIXExtendedParameters.CHAIN_VALIDITY_MODEL)
+        {
+            // if end cert use given signing/encryption/... time
+            if (index <= 0)
+            {
+                return RevocationUtilities.getValidDate(paramsPKIX);
+                // else use time when previous cert was created
+            }
+            else
+            {
+                if (index - 1 == 0)
+                {
+                    ASN1GeneralizedTime dateOfCertgen = null;
+                    try
+                    {
+                        byte[] extBytes = ((X509Certificate)certPath.getCertificates().get(index - 1)).getExtensionValue(ISISMTTObjectIdentifiers.id_isismtt_at_dateOfCertGen.getId());
+                        if (extBytes != null)
+                        {
+                            dateOfCertgen = ASN1GeneralizedTime.getInstance(ASN1Primitive.fromByteArray(extBytes));
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "Date of cert gen extension could not be read.");
+                    }
+                    catch (IllegalArgumentException e)
+                    {
+                        throw new AnnotatedException(
+                            "Date of cert gen extension could not be read.");
+                    }
+                    if (dateOfCertgen != null)
+                    {
+                        try
+                        {
+                            return dateOfCertgen.getDate();
+                        }
+                        catch (ParseException e)
+                        {
+                            throw new AnnotatedException(
+                                "Date from date of cert gen extension could not be parsed.",
+                                e);
+                        }
+                    }
+                    return ((X509Certificate)certPath.getCertificates().get(
+                        index - 1)).getNotBefore();
+                }
+                else
+                {
+                    return ((X509Certificate)certPath.getCertificates().get(
+                        index - 1)).getNotBefore();
+                }
+            }
+        }
+        else
+        {
+            return getValidDate(paramsPKIX);
+        }
+    }
+
+    /**
+     * Return the next working key inheriting DSA parameters if necessary.
+     * <p>
+     * This methods inherits DSA parameters from the indexed certificate or
+     * previous certificates in the certificate chain to the returned
+     * <code>PublicKey</code>. The list is searched upwards, meaning the end
+     * certificate is at position 0 and previous certificates are following.
+     * </p>
+     * <p>
+     * If the indexed certificate does not contain a DSA key this method simply
+     * returns the public key. If the DSA key already contains DSA parameters
+     * the key is also only returned.
+     * </p>
+     *
+     * @param certs The certification path.
+     * @param index The index of the certificate which contains the public key
+     *              which should be extended with DSA parameters.
+     * @return The public key of the certificate in list position
+     *         <code>index</code> extended with DSA parameters if applicable.
+     * @throws AnnotatedException if DSA parameters cannot be inherited.
+     */
+    protected static PublicKey getNextWorkingKey(List certs, int index, JcaJceHelper helper)
+        throws CertPathValidatorException
+    {
+        Certificate cert = (Certificate)certs.get(index);
+        PublicKey pubKey = cert.getPublicKey();
+        if (!(pubKey instanceof DSAPublicKey))
+        {
+            return pubKey;
+        }
+        DSAPublicKey dsaPubKey = (DSAPublicKey)pubKey;
+        if (dsaPubKey.getParams() != null)
+        {
+            return dsaPubKey;
+        }
+        for (int i = index + 1; i < certs.size(); i++)
+        {
+            X509Certificate parentCert = (X509Certificate)certs.get(i);
+            pubKey = parentCert.getPublicKey();
+            if (!(pubKey instanceof DSAPublicKey))
+            {
+                throw new CertPathValidatorException(
+                    "DSA parameters cannot be inherited from previous certificate.");
+            }
+            DSAPublicKey prevDSAPubKey = (DSAPublicKey)pubKey;
+            if (prevDSAPubKey.getParams() == null)
+            {
+                continue;
+            }
+            DSAParams dsaParams = prevDSAPubKey.getParams();
+            DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(
+                dsaPubKey.getY(), dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
+            try
+            {
+                KeyFactory keyFactory = helper.createKeyFactory("DSA");
+                return keyFactory.generatePublic(dsaPubKeySpec);
+            }
+            catch (Exception exception)
+            {
+                throw new RuntimeException(exception.getMessage());
+            }
+        }
+        throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
+    }
+
+    /**
+     * Find the issuer certificates of a given certificate.
+     *
+     * @param cert       The certificate for which an issuer should be found.
+     * @return A <code>Collection</code> object containing the issuer
+     *         <code>X509Certificate</code>s. Never <code>null</code>.
+     * @throws AnnotatedException if an error occurs.
+     */
+    static Collection findIssuerCerts(
+        X509Certificate cert,
+        List<CertStore> certStores,
+        List<PKIXCertStore> pkixCertStores)
+        throws AnnotatedException
+    {
+        X509CertSelector selector = new X509CertSelector();
+
+        try
+        {
+            selector.setSubject(((X509Certificate)cert).getIssuerX500Principal().getEncoded());
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException(
+                           "Subject criteria for certificate selector to find issuer certificate could not be set.", e);
+        }
+
+        try
+        {
+            byte[] akiExtensionValue = cert.getExtensionValue(AUTHORITY_KEY_IDENTIFIER);
+            if (akiExtensionValue != null)
+            {
+                ASN1OctetString aki = ASN1OctetString.getInstance(akiExtensionValue);
+                byte[] authorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(aki.getOctets()).getKeyIdentifier();
+                if (authorityKeyIdentifier != null)
+                {
+                    selector.setSubjectKeyIdentifier(new DEROctetString(authorityKeyIdentifier).getEncoded());
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            // authority key identifier could not be retrieved from target cert, just search without it
+        }
+
+        PKIXCertStoreSelector certSelect = new PKIXCertStoreSelector.Builder(selector).build();
+        Set certs = new LinkedHashSet();
+
+        Iterator iter;
+
+        try
+        {
+            List matches = new ArrayList();
+
+            matches.addAll(RevocationUtilities.findCertificates(certSelect, certStores));
+            matches.addAll(RevocationUtilities.findCertificates(certSelect, pkixCertStores));
+
+            iter = matches.iterator();
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Issuer certificate cannot be searched.", e);
+        }
+
+        X509Certificate issuer = null;
+        while (iter.hasNext())
+        {
+            issuer = (X509Certificate)iter.next();
+            // issuer cannot be verified because possible DSA inheritance
+            // parameters are missing
+            certs.add(issuer);
+        }
+        return certs;
+    }
+
+    protected static void verifyX509Certificate(X509Certificate cert, PublicKey publicKey,
+                                                String sigProvider)
+        throws GeneralSecurityException
+    {
+        if (sigProvider == null)
+        {
+            cert.verify(publicKey);
+        }
+        else
+        {
+            cert.verify(publicKey, sigProvider);
+        }
+    }
+
+    static void checkCRLsNotEmpty(Set crls, Object cert)
+        throws CRLNotFoundException
+    {
+        if (crls.isEmpty())
+        {
+//            if (cert instanceof X509AttributeCertificate)
+//            {
+//                X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
+//
+//                throw new NoCRLFoundException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
+//            }
+//            else
+            {
+                X509Certificate xCert = (X509Certificate)cert;
+
+                throw new CRLNotFoundException("No CRLs found for issuer \"" + RFC4519Style.INSTANCE.toString(X500Name.getInstance(((X509Certificate)xCert).getIssuerX500Principal().getEncoded())) + "\"");
+            }
+        }
+    }
+
+    public static boolean isIndirectCRL(X509CRL crl)
+        throws CRLException
+    {
+        try
+        {
+            byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
+            return idp != null
+                && IssuingDistributionPoint.getInstance(ASN1OctetString.getInstance(idp).getOctets()).isIndirectCRL();
+        }
+        catch (Exception e)
+        {
+            throw new CRLException(
+                    "exception reading IssuingDistributionPoint", e);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/X509RevocationChecker.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/X509RevocationChecker.java
new file mode 100644
index 0000000..13aae31
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/X509RevocationChecker.java
@@ -0,0 +1,869 @@
+package org.bouncycastle.pkix.jcajce;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.CRL;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLSelector;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.jcajce.PKIXCRLStore;
+import org.bouncycastle.jcajce.PKIXExtendedParameters;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.Iterable;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
+/**
+ * X.509 Certificate Revocation Checker - still lacks OCSP support and support for delta CRLs.
+ */
+public class X509RevocationChecker
+    extends PKIXCertPathChecker
+{
+    /**
+     * This is the default PKIX validity model. Actually there are two variants
+     * of this: The PKIX model and the modified PKIX model. The PKIX model
+     * verifies that all involved certificates must have been valid at the
+     * current time. The modified PKIX model verifies that all involved
+     * certificates were valid at the signing time. Both are indirectly choosen
+     * with the {@link PKIXParameters#setDate(Date)} method, so this
+     * methods sets the Date when <em>all</em> certificates must have been
+     * valid.
+     */
+    public static final int PKIX_VALIDITY_MODEL = 0;
+
+    /**
+     * This model uses the following validity model. Each certificate must have
+     * been valid at the moment where is was used. That means the end
+     * certificate must have been valid at the time the signature was done. The
+     * CA certificate which signed the end certificate must have been valid,
+     * when the end certificate was signed. The CA (or Root CA) certificate must
+     * have been valid, when the CA certificate was signed and so on. So the
+     * {@link PKIXParameters#setDate(Date)} method sets the time, when
+     * the <em>end certificate</em> must have been valid. It is used e.g.
+     * in the German signature law.
+     */
+    public static final int CHAIN_VALIDITY_MODEL = 1;
+
+    public static class Builder
+    {
+        private Set<TrustAnchor> trustAnchors;
+        private List<CertStore> crlCertStores = new ArrayList<CertStore>();
+        private List<Store<CRL>> crls = new ArrayList<Store<CRL>>();
+        private boolean isCheckEEOnly;
+        private int validityModel = PKIX_VALIDITY_MODEL;
+        private Provider provider;
+        private String providerName;
+        private boolean canSoftFail;
+        private long failLogMaxTime;
+        private long failHardMaxTime;
+
+        /**
+         * Base constructor.
+         *
+         * @param trustAnchor the trust anchor our chain should start with.
+         */
+        public Builder(TrustAnchor trustAnchor)
+        {
+            this.trustAnchors = Collections.singleton(trustAnchor);
+        }
+
+        /**
+         * Base constructor.
+         *
+         * @param trustAnchors a set of potential trust anchors
+         */
+        public Builder(Set<TrustAnchor> trustAnchors)
+        {
+            this.trustAnchors = new HashSet<TrustAnchor>(trustAnchors);
+        }
+
+        /**
+         * Base constructor.
+         *
+         * @param trustStore a keystore of potential trust anchors
+         */
+        public Builder(KeyStore trustStore)
+            throws KeyStoreException
+        {
+            this.trustAnchors = new HashSet<TrustAnchor>();
+
+            for (Enumeration en = trustStore.aliases(); en.hasMoreElements(); )
+            {
+                String alias = (String)en.nextElement();
+
+                if (trustStore.isCertificateEntry(alias))
+                {
+                    trustAnchors.add(new TrustAnchor((X509Certificate)trustStore.getCertificate(alias), null));
+                }
+            }
+        }
+
+        /**
+         * Add a collection of CRLs to the checker.
+         *
+         * @param crls CRLs to be examined.
+         * @return the current builder instance.
+         */
+        public Builder addCrls(CertStore crls)
+        {
+            this.crlCertStores.add(crls);
+
+            return this;
+        }
+
+        /**
+         * Add a collection of CRLs to the checker.
+         *
+         * @param crls CRLs to be examined.
+         * @return the current builder instance.
+         */
+        public Builder addCrls(Store<CRL> crls)
+        {
+            this.crls.add(crls);
+
+            return this;
+        }
+
+        /**
+         * @param isTrue true if only end-entities should be checked, false otherwise.
+         * @return the current builder instance.
+         */
+        public Builder setCheckEndEntityOnly(boolean isTrue)
+        {
+            this.isCheckEEOnly = isTrue;
+
+            return this;
+        }
+
+        /**
+         * Configure soft failure if CRLs/OCSP not available. If maxTime is greater than zero
+         * it represents the acceptable downtime for any responders or distribution points we
+         * are trying to connect to, with downtime measured from the first failure. Initially
+         * failures will log at Level.WARNING, once maxTime is exceeded any failures will be
+         * logged as Level.SEVERE. Setting maxTime to zero will mean 1 failure will be allowed
+         * before failures are logged as severe.
+         *
+         * @param isTrue true soft failure should be enabled, false otherwise.
+         * @param maxTime the time that can pass between the first failure and the most recent.
+         * @return the current builder instance.
+         */
+        public Builder setSoftFail(boolean isTrue, long maxTime)
+        {
+            this.canSoftFail = isTrue;
+            this.failLogMaxTime = maxTime;
+            this.failHardMaxTime = -1;
+
+            return this;
+        }
+
+        /**
+         * Configure soft failure with a hard limit if CRLs/OCSP not available. If maxTime is
+         * greater than zero it represents the acceptable downtime for any responders or
+         * distribution points we are trying to connect to, with downtime measured from the
+         * first failure. Initially failures will log at Level.WARNING, once 75% of maxTime is exceeded
+         * any failures will be logged as Level.SEVERE. At maxTime any failures will be treated as hard,
+         * setting maxTime to zero will mean 1 failure will be allowed.
+         *
+         * @param isTrue true soft failure should be enabled, false otherwise.
+         * @param maxTime the time that can pass between the first failure and the most recent.
+         * @return the current builder instance.
+         */
+        public Builder setSoftFailHardLimit(boolean isTrue, long maxTime)
+        {
+            this.canSoftFail = isTrue;
+            this.failLogMaxTime = (maxTime * 3) / 4;
+            this.failHardMaxTime = maxTime;
+
+            return this;
+        }
+
+        /**
+         * Configure to use the installed provider with name ProviderName.
+         *
+         * @param provider provider to use.
+         * @return the current builder instance.
+         */
+        public Builder usingProvider(Provider provider)
+        {
+            this.provider = provider;
+
+            return this;
+        }
+
+        /**
+         * Configure to use the installed provider with name ProviderName.
+         *
+         * @param providerName name of the installed provider to use.
+         * @return the current builder instance.
+         */
+        public Builder usingProvider(String providerName)
+        {
+            this.providerName = providerName;
+
+            return this;
+        }
+
+        /**
+         * Build a revocation checker conforming to the current builder.
+         *
+         * @return a new X509RevocationChecker.
+         */
+        public X509RevocationChecker build()
+        {
+            return new X509RevocationChecker(this);
+        }
+    }
+
+    private static Logger LOG = Logger.getLogger(X509RevocationChecker.class.getName());
+    private static final Map<GeneralName, WeakReference<X509CRL>> crlCache = Collections.synchronizedMap(
+                                                        new WeakHashMap<GeneralName, WeakReference<X509CRL>>());
+
+    private final Map<X500Principal, Long> failures = new HashMap<X500Principal, Long>();
+    private final Set<TrustAnchor> trustAnchors;
+    private final boolean isCheckEEOnly;
+    private final List<Store<CRL>> crls;
+    private final List<CertStore> crlCertStores;
+    private final PKIXJcaJceHelper helper;
+    private final boolean canSoftFail;
+    private final long failLogMaxTime;
+    private final long failHardMaxTime;
+
+    private X500Principal workingIssuerName;
+    private PublicKey workingPublicKey;
+    private X509Certificate signingCert;
+
+    private X509RevocationChecker(Builder bldr)
+    {
+        this.crls = new ArrayList<Store<CRL>>(bldr.crls);
+        this.crlCertStores = new ArrayList<CertStore>(bldr.crlCertStores);
+        this.isCheckEEOnly = bldr.isCheckEEOnly;
+        this.trustAnchors = bldr.trustAnchors;
+        this.canSoftFail = bldr.canSoftFail;
+        this.failLogMaxTime = bldr.failLogMaxTime;
+        this.failHardMaxTime = bldr.failHardMaxTime;
+
+        if (bldr.provider != null)
+        {
+            this.helper = new PKIXProviderJcaJceHelper(bldr.provider);
+        }
+        else if (bldr.providerName != null)
+        {
+            helper = new PKIXNamedJcaJceHelper(bldr.providerName);
+        }
+        else
+        {
+            helper = new PKIXDefaultJcaJceHelper();
+        }
+    }
+
+    public void init(boolean forward)
+        throws CertPathValidatorException
+    {
+        if (forward)
+        {
+            throw new IllegalArgumentException("forward processing not supported");
+        }
+        this.workingIssuerName = null;
+    }
+
+    public boolean isForwardCheckingSupported()
+    {
+        return false;
+    }
+
+    public Set<String> getSupportedExtensions()
+    {
+        return null;
+    }
+
+    public void check(Certificate certificate, Collection<String> collection)
+        throws CertPathValidatorException
+    {
+        X509Certificate cert = (X509Certificate)certificate;
+
+        if (isCheckEEOnly && cert.getBasicConstraints() != -1)
+        {
+            this.workingIssuerName = cert.getSubjectX500Principal();
+            this.workingPublicKey = cert.getPublicKey();
+            this.signingCert = cert;
+            
+            return;
+        }
+
+        TrustAnchor trustAnchor = null;
+
+        if (workingIssuerName == null)
+        {
+            this.workingIssuerName = cert.getIssuerX500Principal();
+
+            for (Iterator it = trustAnchors.iterator(); it.hasNext(); )
+            {
+                TrustAnchor anchor = (TrustAnchor)it.next();
+
+                if (workingIssuerName.equals(anchor.getCA())
+                    || workingIssuerName.equals(anchor.getTrustedCert().getSubjectX500Principal()))
+                {
+                    trustAnchor = anchor;
+                }
+            }
+
+            if (trustAnchor == null)
+            {
+                throw new CertPathValidatorException("no trust anchor found for " + workingIssuerName);
+            }
+            
+            this.signingCert = trustAnchor.getTrustedCert();
+            this.workingPublicKey = signingCert.getPublicKey();
+        }
+
+        PKIXParameters baseParams;
+        List<X500Principal> issuerList = new ArrayList<X500Principal>();
+
+        try
+        {
+            baseParams = new PKIXParameters(trustAnchors);
+
+            baseParams.setRevocationEnabled(false);
+            baseParams.setDate(new Date());
+            
+            for (int i = 0; i != crlCertStores.size(); i++)
+            {
+                if (LOG.isLoggable(Level.INFO))
+                {
+                    addIssuers(issuerList, crlCertStores.get(i));
+                }
+                baseParams.addCertStore(crlCertStores.get(i));
+            }
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new RuntimeException("error setting up baseParams: " + e.getMessage());
+        }
+
+        PKIXExtendedParameters.Builder pkixParamsBldr = new PKIXExtendedParameters.Builder(baseParams);
+
+        for (int i = 0; i != crls.size(); i++)
+        {
+            if (LOG.isLoggable(Level.INFO))
+            {
+                addIssuers(issuerList, crls.get(i));
+            }
+            pkixParamsBldr.addCRLStore(new LocalCRLStore(crls.get(i)));
+        }
+
+        if (issuerList.isEmpty())
+        {
+            LOG.log(Level.INFO, "configured with 0 pre-loaded CRLs");
+        }
+        else
+        {
+            if (LOG.isLoggable(Level.FINE))
+            {
+                for (int i = 0; i != issuerList.size(); i++)
+                {
+                    LOG.log(Level.FINE, "configuring with CRL for issuer \"" + issuerList.get(i) + "\"");
+                }
+            }
+            else
+            {
+                LOG.log(Level.INFO, "configured with " + issuerList.size() + " pre-loaded CRLs");
+            }
+        }
+        
+        try
+        {
+            checkCRLs(pkixParamsBldr.build(), cert, baseParams.getDate(), signingCert, workingPublicKey, new ArrayList(), helper);
+        }
+        catch (AnnotatedException e)
+        {
+            throw new CertPathValidatorException(e.getMessage(), e.getCause());
+        }
+        catch (CRLNotFoundException e)
+        {
+            if (cert.getExtensionValue(Extension.cRLDistributionPoints.getId()) != null)
+            {
+                CRL crl = null;
+                try
+                {
+                    crl = downloadCRLs(cert.getIssuerX500Principal(), baseParams.getDate(), RevocationUtilities.getExtensionValue(cert, Extension.cRLDistributionPoints), helper);
+                }
+                catch(AnnotatedException e1)
+                {
+                    throw new CertPathValidatorException(e.getMessage(), e.getCause());
+                }
+
+                if (crl != null)
+                {
+                    try
+                    {
+                        pkixParamsBldr.addCRLStore(new LocalCRLStore(
+                            new CollectionStore<CRL>(Collections.singleton(crl))));
+                        checkCRLs(pkixParamsBldr.build(), cert, new Date(), signingCert, workingPublicKey, new ArrayList(), helper);
+                    }
+                    catch(AnnotatedException e1)
+                    {
+                        throw new CertPathValidatorException(e.getMessage(), e.getCause());
+                    }
+                }
+                else
+                {
+                    if (canSoftFail)
+                    {
+                        X500Principal issuer = cert.getIssuerX500Principal();
+
+                        Long initial = failures.get(issuer);
+                        if (initial != null)
+                        {
+                             long period = System.currentTimeMillis() - initial.longValue();
+                             if (failHardMaxTime != -1 && failHardMaxTime < period)
+                             {
+                                 throw e;
+                             }
+                             if (period < failLogMaxTime)
+                             {
+                                 LOG.log(Level.WARNING, "soft failing for issuer: \"" + issuer + "\"");
+                             }
+                             else
+                             {
+                                 LOG.log(Level.SEVERE, "soft failing for issuer: \"" + issuer + "\"");
+                             }
+                        }
+                        else
+                        {
+                            failures.put(issuer, System.currentTimeMillis());
+                        }
+                    }
+                    else
+                    {
+                        throw e;
+                    }
+                }
+            }
+            else
+            {
+                throw e;
+            }
+        }
+
+        this.signingCert = cert;
+        this.workingPublicKey = cert.getPublicKey();
+        this.workingIssuerName = cert.getSubjectX500Principal();
+    }
+
+    private void addIssuers(final List<X500Principal> issuerList, CertStore certStore)
+        throws CertStoreException
+    {
+        certStore.getCRLs(new X509CRLSelector()
+        {
+            public boolean match(CRL crl)
+            {
+                if (!(crl instanceof X509CRL))
+                {
+                    return false;
+                }
+
+                issuerList.add(((X509CRL)crl).getIssuerX500Principal());
+
+                return false;
+            }
+        });
+    }
+
+    private void addIssuers(final List<X500Principal> issuerList, Store<CRL> certStore)
+    {
+        certStore.getMatches(new Selector<CRL>()
+        {
+            public boolean match(CRL crl)
+            {
+                if (!(crl instanceof X509CRL))
+                {
+                    return false;
+                }
+
+                issuerList.add(((X509CRL)crl).getIssuerX500Principal());
+
+                return false;
+            }
+
+            public Object clone()
+            {
+                return this;
+            }
+        });
+    }
+
+    private CRL downloadCRLs(X500Principal issuer, Date currentDate, ASN1Primitive crlDpPrimitive, JcaJceHelper helper)
+    {
+        CRLDistPoint crlDp = CRLDistPoint.getInstance(crlDpPrimitive);
+        DistributionPoint[] points = crlDp.getDistributionPoints();
+
+        for (int i = 0; i != points.length; i++)
+        {
+            DistributionPoint dp = points[i];
+
+            DistributionPointName dpn = dp.getDistributionPoint();
+            if (dpn.getType() == DistributionPointName.FULL_NAME)
+            {
+                GeneralName[] names = GeneralNames.getInstance(dpn.getName()).getNames();
+
+                for (int n = 0; n != names.length; n++)
+                {
+                    GeneralName name = names[n];
+                    if (name.getTagNo() == GeneralName.uniformResourceIdentifier)
+                    {
+                        X509CRL crl;
+
+                        WeakReference<X509CRL> crlRef = crlCache.get(name);
+                        if (crlRef != null)
+                        {
+                            crl = crlRef.get();
+                            if (crl != null
+                                && !currentDate.before(crl.getThisUpdate())
+                                && !currentDate.after(crl.getNextUpdate()))
+                            {
+                                return crl;
+                            }
+                            crlCache.remove(name); // delete expired/out-of-range entry
+                        }
+
+                        URL url = null;
+                        try
+                        {
+                            url = new URL(name.getName().toString());
+            
+                            CertificateFactory certFact = helper.createCertificateFactory("X.509");
+
+                            InputStream urlStream = url.openStream();
+
+                            crl = (X509CRL)certFact.generateCRL(new BufferedInputStream(urlStream));
+
+                            urlStream.close();
+
+                            LOG.log(Level.INFO, "downloaded CRL from CrlDP " + url + " for issuer \"" + issuer + "\"");
+
+                            crlCache.put(name, new WeakReference<X509CRL>(crl));
+
+                            return crl;
+                        }
+                        catch (Exception e)
+                        {
+                            if (LOG.isLoggable(Level.FINE))
+                            {
+                                LOG.log(Level.FINE, "CrlDP " + url + " ignored: " + e.getMessage(), e);
+                            }
+                            else
+                            {
+                                LOG.log(Level.INFO, "CrlDP " + url + " ignored: " + e.getMessage());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+    
+    protected static final String[] crlReasons = new String[]{
+        "unspecified",
+        "keyCompromise",
+        "cACompromise",
+        "affiliationChanged",
+        "superseded",
+        "cessationOfOperation",
+        "certificateHold",
+        "unknown",
+        "removeFromCRL",
+        "privilegeWithdrawn",
+        "aACompromise"};
+
+    static List<PKIXCRLStore> getAdditionalStoresFromCRLDistributionPoint(CRLDistPoint crldp, Map<GeneralName, PKIXCRLStore> namedCRLStoreMap)
+        throws AnnotatedException
+    {
+        if (crldp != null)
+        {
+            DistributionPoint dps[] = null;
+            try
+            {
+                dps = crldp.getDistributionPoints();
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "could not read distribution points could not be read", e);
+            }
+            List<PKIXCRLStore> stores = new ArrayList<PKIXCRLStore>();
+
+            for (int i = 0; i < dps.length; i++)
+            {
+                DistributionPointName dpn = dps[i].getDistributionPoint();
+                // look for URIs in fullName
+                if (dpn != null)
+                {
+                    if (dpn.getType() == DistributionPointName.FULL_NAME)
+                    {
+                        GeneralName[] genNames = GeneralNames.getInstance(
+                            dpn.getName()).getNames();
+
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            PKIXCRLStore store = namedCRLStoreMap.get(genNames[j]);
+                            if (store != null)
+                            {
+                                stores.add(store);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return stores;
+        }
+        else
+        {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+
+    /**
+     * Checks a certificate if it is revoked.
+     *
+     * @param paramsPKIX       PKIX parameters.
+     * @param cert             Certificate to check if it is revoked.
+     * @param validDate        The date when the certificate revocation status should be
+     *                         checked.
+     * @param sign             The issuer certificate of the certificate <code>cert</code>.
+     * @param workingPublicKey The public key of the issuer certificate <code>sign</code>.
+     * @param certPathCerts    The certificates of the certification path.
+     * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+     * or some error occurs.
+     */
+    protected void checkCRLs(
+        PKIXExtendedParameters paramsPKIX,
+        X509Certificate cert,
+        Date validDate,
+        X509Certificate sign,
+        PublicKey workingPublicKey,
+        List certPathCerts,
+        PKIXJcaJceHelper helper)
+        throws AnnotatedException, CertPathValidatorException
+    {
+        AnnotatedException lastException = null;
+        CRLDistPoint crldp = null;
+        try
+        {
+            crldp = CRLDistPoint.getInstance(RevocationUtilities.getExtensionValue(cert, Extension.cRLDistributionPoints));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("cannot read CRL distribution point extension", e);
+        }
+
+        PKIXExtendedParameters.Builder paramsBldr = new PKIXExtendedParameters.Builder(paramsPKIX);
+        try
+        {
+            List extras = getAdditionalStoresFromCRLDistributionPoint(crldp, paramsPKIX.getNamedCRLStoreMap());
+            for (Iterator it = extras.iterator(); it.hasNext(); )
+            {
+                paramsBldr.addCRLStore((PKIXCRLStore)it.next());
+            }
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException(
+                "no additional CRL locations could be decoded from CRL distribution point extension", e);
+        }
+        CertStatus certStatus = new CertStatus();
+        ReasonsMask reasonsMask = new ReasonsMask();
+        PKIXExtendedParameters finalParams = paramsBldr.build();
+
+        boolean validCrlFound = false;
+        // for each distribution point
+        if (crldp != null)
+        {
+            DistributionPoint dps[] = null;
+            try
+            {
+                dps = crldp.getDistributionPoints();
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException("cannot read distribution points", e);
+            }
+            if (dps != null)
+            {
+                for (int i = 0; i < dps.length && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons(); i++)
+                {
+                    try
+                    {
+                        RFC3280CertPathUtilities.checkCRL(dps[i], finalParams, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, certPathCerts, helper);
+                        validCrlFound = true;
+                    }
+                    catch (AnnotatedException e)
+                    {
+                        lastException = e;
+                    }
+                }
+            }
+        }
+
+        /*
+         * If the revocation status has not been determined, repeat the process
+         * above with any available CRLs not specified in a distribution point
+         * but issued by the certificate issuer.
+         */
+
+        if (certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons())
+        {
+            try
+            {
+                /*
+                 * assume a DP with both the reasons and the cRLIssuer fields
+                 * omitted and a distribution point name of the certificate
+                 * issuer.
+                 */
+                X500Principal issuer = cert.getIssuerX500Principal();
+
+                DistributionPoint dp = new DistributionPoint(new DistributionPointName(0, new GeneralNames(
+                    new GeneralName(GeneralName.directoryName, X500Name.getInstance(issuer.getEncoded())))), null, null);
+                PKIXExtendedParameters paramsPKIXClone = (PKIXExtendedParameters)paramsPKIX.clone();
+                RFC3280CertPathUtilities.checkCRL(dp, paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask,
+                    certPathCerts, helper);
+                validCrlFound = true;
+            }
+            catch (AnnotatedException e)
+            {
+                lastException = e;
+            }
+        }
+
+        if (!validCrlFound)
+        {
+            if (lastException instanceof AnnotatedException)
+            { 
+                throw new CRLNotFoundException("no valid CRL found", lastException);
+            }
+
+            throw new CRLNotFoundException("no valid CRL found");
+        }
+        if (certStatus.getCertStatus() != CertStatus.UNREVOKED)
+        {
+            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+            df.setTimeZone(TimeZone.getTimeZone("UTC"));
+            String message = "certificate [issuer=\"" + cert.getIssuerX500Principal() + "\",serialNumber="
+                                           + cert.getSerialNumber() + ",subject=\"" + cert.getSubjectX500Principal() + "\"] revoked after " + df.format(certStatus.getRevocationDate());
+            message += ", reason: " + crlReasons[certStatus.getCertStatus()];
+            throw new AnnotatedException(message);
+        }
+        if (!reasonsMask.isAllReasons() && certStatus.getCertStatus() == CertStatus.UNREVOKED)
+        {
+            certStatus.setCertStatus(CertStatus.UNDETERMINED);
+        }
+        if (certStatus.getCertStatus() == CertStatus.UNDETERMINED)
+        {
+            throw new AnnotatedException("certificate status could not be determined");
+        }
+    }
+
+    public Object clone()
+    {
+        return this;
+    }
+
+    private class LocalCRLStore<T extends CRL>
+        implements PKIXCRLStore, Iterable<CRL>
+    {
+        private Collection<CRL> _local;
+
+        /**
+         * Basic constructor.
+         *
+         * @param collection - initial contents for the store, this is copied.
+         */
+        public LocalCRLStore(
+            Store<CRL> collection)
+        {
+            _local = new ArrayList<CRL>(collection.getMatches(null));
+        }
+
+        /**
+         * Return the matches in the collection for the passed in selector.
+         *
+         * @param selector the selector to match against.
+         * @return a possibly empty collection of matching objects.
+         */
+        public Collection getMatches(Selector selector)
+        {
+            if (selector == null)
+            {
+                return new ArrayList<CRL>(_local);
+            }
+            else
+            {
+                List<CRL> col = new ArrayList<CRL>();
+                Iterator<CRL> iter = _local.iterator();
+
+                while (iter.hasNext())
+                {
+                    CRL obj = iter.next();
+
+                    if (selector.match(obj))
+                    {
+                        col.add(obj);
+                    }
+                }
+
+                return col;
+            }
+        }
+
+        public Iterator<CRL> iterator()
+        {
+            return getMatches(null).iterator();
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/test/RevocationTest.java b/bcpkix/src/main/java/org/bouncycastle/pkix/test/RevocationTest.java
new file mode 100644
index 0000000..077ebdf
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/test/RevocationTest.java
@@ -0,0 +1,535 @@
+package org.bouncycastle.pkix.test;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.CRL;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.PolicyNode;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pkix.jcajce.X509RevocationChecker;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.encoders.Base64;
+
+public class RevocationTest
+    extends TestCase
+{
+    public static byte[] rootCertBin = Base64.decode(
+        "MIIBqzCCARQCAQEwDQYJKoZIhvcNAQEFBQAwHjEcMBoGA1UEAxMTVGVzdCBDQSBDZXJ0aWZpY2F0ZTAeFw0wODA5MDQwNDQ1MDhaFw0wODA5MTEwNDQ1MDhaMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMRLUjhPe4YUdLo6EcjKcWUOG7CydFTH53Pr1lWjOkbmszYDpkhCTT9LOsI+disk18nkBxSl8DAHTqV+VxtuTPt64iyi10YxyDeep+DwZG/f8cVQv97U3hA9cLurZ2CofkMLGr6JpSGCMZ9FcstcTdHB4lbErIJ54YqfF4pNOs4/AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAgyrTEFY7ALpeY59jL6xFOLpuPqoBOWrUWv6O+zy5BCU0qiX71r3BpigtxRj+DYcfLIM9FNERDoHu3TthD3nwYWUBtFX8N0QUJIdJabxqAMhLjSC744koiFpCYse5Ye3ZvEdFwDzgAQsJTp5eFGgTZPkPzcdhkFJ2p9+OWs+cb24=");
+
+
+    static byte[] interCertBin = Base64.decode(
+        "MIICSzCCAbSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlMB4XDTA4MDkwNDA0NDUwOFoXDTA4MDkxMTA0NDUwOFowKDEmMCQGA1UEAxMdVGVzdCBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAISS9OOZ2wxzdWny9aVvk4Joq+dwSJ+oqvHUxX3PflZyuiLiCBUOUE4q59dGKdtNX5fIfwyK3cpV0e73Y/0fwfM3m9rOWFrCKOhfeswNTes0w/2PqPVVDDsF/nj7NApuqXwioeQlgTL251RDF4sVoxXqAU7lRkcqwZt3mwqS4KTJAgMBAAGjgY4wgYswRgYDVR0jBD8wPYAUhv8BOT27EB9JaCccJD4YASPP5XWhIqQgMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGWCAQEwHQYDVR0OBBYEFL/IwAGOkHzaQyPZegy79CwM5oTFMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4GBAE4TRgUz4sUvZyVdZxqV+XyNRnqXAeLOOqFGYv2D96tQrS+zjd0elVlT6lFrtchZdOmmX7R6/H/tjMWMcTBICZyRYrvK8cCAmDOI+EIdq5p6lj2Oq6Pbw/wruojAqNrpaR6IkwNpWtdOSSupv4IJL+YU9q2YFTh4R1j3tOkPoFGr");
+
+    static byte[] finalCertBin = Base64.decode(
+        "MIICRjCCAa+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAoMSYwJAYDVQQDEx1UZXN0IEludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZTAeFw0wODA5MDQwNDQ1MDhaFw0wODA5MTEwNDQ1MDhaMB8xHTAbBgNVBAMTFFRlc3QgRW5kIENlcnRpZmljYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChpUeo0tPYywWKiLlbWKNJBcCpSaLSlaZ+4+yer1AxI5yJIVHP6SAlBghlbD5Qne5ImnN/15cz1xwYAiul6vGKJkVPlFEe2Mr+g/J/WJPQQPsjbZ1G+vxbAwXEDA4KaQrnpjRZFq+CdKHwOjuPLYS/MYQNgdIvDVEQcTbPQ8GaiQIDAQABo4GIMIGFMEYGA1UdIwQ/MD2AFL/IwAGOkHzaQyPZegy79CwM5oTFoSKkIDAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlggEBMB0GA1UdDgQWBBSVkw+VpqBf3zsLc/9GdkK9TzHPwDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQBLv/0bVDjzTs/y1vN3FUiZNknEbzupIZduTuXJjqv/vBX+LDPjUfu/+iOCXOSKoRn6nlOWhwB1z6taG2usQkFG8InMkRcPREi2uVgFdhJ/1C3dAWhsdlubjdL926bftXvxnx/koDzyrePW5U96RlOQM2qLvbaky2Giz6hrc3Wl+w==");
+    public static byte[] rootCrlBin = Base64.decode(
+        "MIIBYjCBzAIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlFw0wODA5MDQwNDQ1MDhaFw0wODA5MDQwNzMxNDhaMCIwIAIBAhcNMDgwOTA0MDQ0NTA4WjAMMAoGA1UdFQQDCgEJoFYwVDBGBgNVHSMEPzA9gBSG/wE5PbsQH0loJxwkPhgBI8/ldaEipCAwHjEcMBoGA1UEAxMTVGVzdCBDQSBDZXJ0aWZpY2F0ZYIBATAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOBgQCAbaFCo0BNG4AktVf6jjBLeawP1u0ELYkOCEGvYZE0mBpQ+OvFg7subZ6r3lRIj030nUli28sPFtu5ZQMBNcpE4nS1ziF44RfT3Lp5UgHx9x17Krz781iEyV+7zU8YxYMY9wULD+DCuK294kGKIssVNbmTYXZatBNoXQN5CLIocA==");
+    static byte[] interCrlBin = Base64.decode(
+        "MIIBbDCB1gIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDEx1UZXN0IEludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZRcNMDgwOTA0MDQ0NTA4WhcNMDgwOTA0MDczMTQ4WjAiMCACAQIXDTA4MDkwNDA0NDUwOFowDDAKBgNVHRUEAwoBCaBWMFQwRgYDVR0jBD8wPYAUv8jAAY6QfNpDI9l6DLv0LAzmhMWhIqQgMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGWCAQEwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADgYEAEVCr5TKs5yguGgLH+dBzmSPoeSIWJFLsgWwJEit/iUDJH3dgYmaczOcGxIDtbYYHLWIHM+P2YRyQz3MEkCXEgm/cx4y7leAmux5l+xQWgmxFPz+197vaphPeCZo+B7V1CWtm518gcq4mrs9ovfgNqgyFj7KGjcBpWdJE32KMt50=");
+
+    private byte[] AC_PR = Base64.decode(
+        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFU1RDQ0F6R2dBd0lC"
+            + "QWdJQkJUQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+            + "bHNaV2x5WVRBZUZ3MHdNakEwTURReE9UTTVNREJhRncwd05UQTBNRFF5DQpN"
+            + "elU1TURCYU1HRXhDekFKQmdOVkJBWVRBa0pTTVJNd0VRWURWUVFLRXdwSlEx"
+            + "QXRRbkpoYzJsc01UMHdPd1lEDQpWUVFERXpSQmRYUnZjbWxrWVdSbElFTmxj"
+            + "blJwWm1sallXUnZjbUVnWkdFZ1VISmxjMmxrWlc1amFXRWdaR0VnDQpVbVZ3"
+            + "ZFdKc2FXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJD"
+            + "Z0tDQVFFQXMwc0t5NGsrDQp6b016aldyMTQxeTVYQ045UGJMZERFQXN2cjZ4"
+            + "Z0NCN1l5bEhIQ1NBYmpGR3dOQ0R5NlVxN1h0VjZ6UHdIMXpGDQpFWENlS3Jm"
+            + "UUl5YXBXSEZ4V1VKajBMblFrY1RZM1FOR1huK0JuVk9EVTZDV3M1c3NoZktH"
+            + "RXZyVlQ1Z214V1NmDQp4OFlsdDgzY1dwUE1QZzg3VDlCaHVIbHQzazh2M2Ev"
+            + "NmRPbmF2dytOYTAyZExBaDBlNzZqcCtQUS9LK0pHZlBuDQphQjVVWURrZkd0"
+            + "em5uTTNBV01tY3VJK0o0ek5OMDZaa3ZnbDFsdEo2UU1qcnZEUFlSak9ndDlT"
+            + "cklpY1NmbEo4DQptVDdHWGRRaXJnQUNXc3g1QURBSklRK253TU1vNHlyTUtx"
+            + "SlFhNFFDMHhhT0QvdkdVcG9SaDQzT0FTZFp3c3YvDQpPWFlybmVJeVAwVCs4"
+            + "UUlEQVFBQm80RzNNSUcwTUQwR0ExVWRId1EyTURRd01xQXdvQzZHTEdoMGRI"
+            + "QTZMeTloDQpZM0poYVhvdWFXTndZbkpoYzJsc0xtZHZkaTVpY2k5TVExSmhZ"
+            + "M0poYVhvdVkzSnNNQklHQTFVZElBUUxNQWt3DQpCd1lGWUV3QkFRRXdIUVlE"
+            + "VlIwT0JCWUVGREpUVFlKNE9TWVB5T09KZkVMZXhDaHppK2hiTUI4R0ExVWRJ"
+            + "d1FZDQpNQmFBRklyNjhWZUVFUk0xa0VMNlYwbFVhUTJreFBBM01BNEdBMVVk"
+            + "RHdFQi93UUVBd0lCQmpBUEJnTlZIUk1CDQpBZjhFQlRBREFRSC9NQTBHQ1Nx"
+            + "R1NJYjNEUUVCQlFVQUE0SUJBUUJRUFNoZ1lidnFjaWV2SDVVb3ZMeXhkbkYr"
+            + "DQpFcjlOeXF1SWNkMnZ3Y0N1SnpKMkQ3WDBUcWhHQ0JmUEpVVkdBVWorS0NP"
+            + "SDFCVkgva1l1OUhsVHB1MGtKWFBwDQpBQlZkb2hJUERqRHhkbjhXcFFSL0Yr"
+            + "ejFDaWtVcldIMDR4eTd1N1p6UUpLSlBuR0loY1FpOElyRm1PYkllMEc3DQpY"
+            + "WTZPTjdPRUZxY21KTFFHWWdtRzFXMklXcytQd1JwWTdENGhLVEFoVjFSNkVv"
+            + "amE1L3BPcmVDL09kZXlQWmVxDQo1SUZTOUZZZk02U0Npd2hrK3l2Q1FHbVo0"
+            + "YzE5SjM0ZjVFYkRrK1NQR2tEK25EQ0E3L3VMUWNUMlJURE14SzBaDQpuZlo2"
+            + "Nm1Sc0ZjcXRGaWdScjVFcmtKZDdoUVV6eHNOV0VrNzJEVUFIcVgvNlNjeWtt"
+            + "SkR2V0plSUpqZlcNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==");
+
+    private byte[] AC_RAIZ_ICPBRASIL = Base64.decode(
+        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFdURDQ0E2Q2dBd0lC"
+            + "QWdJQkJEQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+            + "bHNaV2x5WVRBZUZ3MHdNVEV4TXpBeE1qVTRNREJhRncweE1URXhNekF5DQpN"
+            + "elU1TURCYU1JRzBNUXN3Q1FZRFZRUUdFd0pDVWpFVE1CRUdBMVVFQ2hNS1NV"
+            + "TlFMVUp5WVhOcGJERTlNRHNHDQpBMVVFQ3hNMFNXNXpkR2wwZFhSdklFNWhZ"
+            + "Mmx2Ym1Gc0lHUmxJRlJsWTI1dmJHOW5hV0VnWkdFZ1NXNW1iM0p0DQpZV05o"
+            + "YnlBdElFbFVTVEVSTUE4R0ExVUVCeE1JUW5KaGMybHNhV0V4Q3pBSkJnTlZC"
+            + "QWdUQWtSR01URXdMd1lEDQpWUVFERXloQmRYUnZjbWxrWVdSbElFTmxjblJw"
+            + "Wm1sallXUnZjbUVnVW1GcGVpQkNjbUZ6YVd4bGFYSmhNSUlCDQpJakFOQmdr"
+            + "cWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd1BNdWR3WC9odm0r"
+            + "VWgyYi9sUUFjSFZBDQppc2FtYUxrV2Rrd1A5L1MvdE9LSWdSckw2T3krWklH"
+            + "bE9VZGQ2dVl0azlNYS8zcFVwZ2NmTkFqMHZZbTVnc3lqDQpRbzllbXNjK3g2"
+            + "bTRWV3drOWlxTVpTQ0s1RVFrQXEvVXQ0bjdLdUxFMStnZGZ0d2RJZ3hmVXNQ"
+            + "dDRDeU5yWTUwDQpRVjU3S00yVVQ4eDVycm16RWpyN1RJQ0dwU1VBbDJnVnFl"
+            + "NnhhaWkrYm1ZUjFRcm1XYUJTQUc1OUxya3Jqcll0DQpiUmhGYm9VRGUxREsr"
+            + "NlQ4czVMNms4Yzhva3BiSHBhOXZlTXp0RFZDOXNQSjYwTVdYaDZhblZLbzFV"
+            + "Y0xjYlVSDQp5RWVOdlpuZVZSS0FBVTZvdXdkakR2d2xzYUt5ZEZLd2VkMFRv"
+            + "UTQ3Ym1VS2djbSt3VjNlVFJrMzZVT25Ud0lEDQpBUUFCbzRIU01JSFBNRTRH"
+            + "QTFVZElBUkhNRVV3UXdZRllFd0JBUUF3T2pBNEJnZ3JCZ0VGQlFjQ0FSWXNh"
+            + "SFIwDQpjRG92TDJGamNtRnBlaTVwWTNCaWNtRnphV3d1WjI5MkxtSnlMMFJR"
+            + "UTJGamNtRnBlaTV3WkdZd1BRWURWUjBmDQpCRFl3TkRBeW9EQ2dMb1lzYUhS"
+            + "MGNEb3ZMMkZqY21GcGVpNXBZM0JpY21GemFXd3VaMjkyTG1KeUwweERVbUZq"
+            + "DQpjbUZwZWk1amNtd3dIUVlEVlIwT0JCWUVGSXI2OFZlRUVSTTFrRUw2VjBs"
+            + "VWFRMmt4UEEzTUE4R0ExVWRFd0VCDQovd1FGTUFNQkFmOHdEZ1lEVlIwUEFR"
+            + "SC9CQVFEQWdFR01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQVpBNWMxDQpV"
+            + "L2hnSWg2T2NnTEFmaUpnRldwdm1EWldxbFYzMC9iSEZwajhpQm9iSlNtNXVE"
+            + "cHQ3VGlyWWgxVXhlM2ZRYUdsDQpZakplKzl6ZCtpelBSYkJxWFBWUUEzNEVY"
+            + "Y3drNHFwV3VmMWhIcmlXZmRyeDhBY3FTcXI2Q3VRRndTcjc1Rm9zDQpTemx3"
+            + "REFEYTcwbVQ3d1pqQW1RaG5aeDJ4SjZ3ZldsVDlWUWZTLy9KWWVJYzdGdWUy"
+            + "Sk5MZDAwVU9TTU1haUsvDQp0NzllbktOSEVBMmZ1cEgzdkVpZ2Y1RWg0YlZB"
+            + "TjVWb2hyVG02TVk1M3g3WFFaWnIxTUU3YTU1bEZFblNlVDB1DQptbE9BalIy"
+            + "bUFidlNNNVg1b1NaTnJtZXRkenlUajJmbENNOENDN01MYWIwa2tkbmdSSWxV"
+            + "QkdIRjEvUzVubVBiDQpLKzlBNDZzZDMzb3FLOG44DQotLS0tLUVORCBDRVJU"
+            + "SUZJQ0FURS0tLS0tDQo=");
+
+    private byte[] schefer = Base64.decode(
+        "MIIEnDCCBAWgAwIBAgICIPAwDQYJKoZIhvcNAQEEBQAwgcAxCzAJBgNVBAYT"
+            + "AkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1MDA4IFdpZXNiYWRl"
+            + "bjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAYBgNVBAsTEVNDSFVG"
+            + "QSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBCZW51dHplciBTZXJ2"
+            + "aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+            + "ZGUwHhcNMDQwMzMwMTEwODAzWhcNMDUwMzMwMTEwODAzWjCBnTELMAkGA1UE"
+            + "BhMCREUxCjAIBgNVBAcTASAxIzAhBgNVBAoTGlNIUyBJbmZvcm1hdGlvbnNz"
+            + "eXN0ZW1lIEFHMRwwGgYDVQQLExM2MDAvMDU5NDktNjAwLzA1OTQ5MRgwFgYD"
+            + "VQQDEw9TY2hldHRlciBTdGVmYW4xJTAjBgkqhkiG9w0BCQEWFlN0ZWZhbi5T"
+            + "Y2hldHRlckBzaHMuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJD0"
+            + "95Bi76fkAMjJNTGPDiLPHmZXNsmakngDeS0juzKMeJA+TjXFouhYh6QyE4Bl"
+            + "Nf18fT4mInlgLefwf4t6meIWbiseeTo7VQdM+YrbXERMx2uHsRcgZMsiMYHM"
+            + "kVfYMK3SMJ4nhCmZxrBkoTRed4gXzVA1AA8YjjTqMyyjvt4TAgMBAAGjggHE"
+            + "MIIBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDALBgNVHQ8EBAMC"
+            + "BNAwOQYJYIZIAYb4QgENBCwWKlplcnRpZmlrYXQgbnVyIGZ1ZXIgU0NIVUZB"
+            + "LU9ubGluZSBndWVsdGlnLjAdBgNVHQ4EFgQUXReirhBfg0Yhf6MsBWoo/nPa"
+            + "hGwwge0GA1UdIwSB5TCB4oAUf2UyCaBV9JUeG9lS1Yo6OFBUdEKhgcakgcMw"
+            + "gcAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1"
+            + "MDA4IFdpZXNiYWRlbjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAY"
+            + "BgNVBAsTEVNDSFVGQSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBC"
+            + "ZW51dHplciBTZXJ2aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNj"
+            + "aHVmYS1vbmxpbmUuZGWCAQAwIQYDVR0RBBowGIEWU3RlZmFuLlNjaGV0dGVy"
+            + "QHNocy5kZTAmBgNVHRIEHzAdgRt6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+            + "ZGUwDQYJKoZIhvcNAQEEBQADgYEAWzZtN9XQ9uyrFXqSy3hViYwV751+XZr0"
+            + "YH5IFhIS+9ixNAu8orP3bxqTaMhpwoU7T/oSsyGGSkb3fhzclgUADbA2lrOI"
+            + "GkeB/m+FArTwRbwpqhCNTwZywOp0eDosgPjCX1t53BB/m/2EYkRiYdDGsot0"
+            + "kQPOVGSjQSQ4+/D+TM8=");
+
+    // circular dependency certificates
+    private static final byte[] sampleTrust = Base64.decode(
+        "MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT" +
+            "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i" +
+            "YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG" +
+            "EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg" +
+            "R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9" +
+            "9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq" +
+            "fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv" +
+            "iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU" +
+            "1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+" +
+            "bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW" +
+            "MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA" +
+            "ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l" +
+            "uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn" +
+            "Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS" +
+            "tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF" +
+            "PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un" +
+            "hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV" +
+            "5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==");
+
+    private static final byte[] sampleCA = Base64.decode(
+        "MIIETTCCAzWgAwIBAgIDAjpxMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT" +
+            "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i" +
+            "YWwgQ0EwHhcNMTMxMjExMjM0NTUxWhcNMjIwNTIwMjM0NTUxWjBCMQswCQYDVQQG" +
+            "EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSUmFwaWRTU0wg" +
+            "U0hBMjU2IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1jBEgEu" +
+            "l9h9GKrIwuWF4hdsYC7JjTEFORoGmFbdVNcRjFlbPbFUrkshhTIWX1SG5tmx2GCJ" +
+            "a1i+ctqgAEJ2sSdZTM3jutRc2aZ/uyt11UZEvexAXFm33Vmf8Wr3BvzWLxmKlRK6" +
+            "msrVMNI4/Bk7WxU7NtBDTdFlodSLwWBBs9ZwF8w5wJwMoD23ESJOztmpetIqYpyg" +
+            "C04q18NhWoXdXBC5VD0tA/hJ8LySt7ecMcfpuKqCCwW5Mc0IW7siC/acjopVHHZD" +
+            "dvDibvDfqCl158ikh4tq8bsIyTYYZe5QQ7hdctUoOeFTPiUs2itP3YqeUFDgb5rE" +
+            "1RkmiQF1cwmbOwIDAQABo4IBSjCCAUYwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwR" +
+            "fap9ZbjKzE4wHQYDVR0OBBYEFJfCJ1CewsnsDIgyyHyt4qYBT9pvMBIGA1UdEwEB" +
+            "/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDYGA1UdHwQvMC0wK6ApoCeGJWh0" +
+            "dHA6Ly9nMS5zeW1jYi5jb20vY3Jscy9ndGdsb2JhbC5jcmwwLwYIKwYBBQUHAQEE" +
+            "IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vZzIuc3ltY2IuY29tMEwGA1UdIARFMEMw" +
+            "QQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0" +
+            "LmNvbS9yZXNvdXJjZXMvY3BzMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1h" +
+            "bnRlY1BLSS0xLTU2OTANBgkqhkiG9w0BAQsFAAOCAQEANevhiyBWlLp6vXmp9uP+" +
+            "bji0MsGj21hWID59xzqxZ2nVeRQb9vrsYPJ5zQoMYIp0TKOTKqDwUX/N6fmS/Zar" +
+            "RfViPT9gRlATPSATGC6URq7VIf5Dockj/lPEvxrYrDrK3maXI67T30pNcx9vMaJR" +
+            "BBZqAOv5jUOB8FChH6bKOvMoPF9RrNcKRXdLDlJiG9g4UaCSLT+Qbsh+QJ8gRhVd" +
+            "4FB84XavXu0R0y8TubglpK9YCa81tGJUheNI3rzSkHp6pIQNo0LyUcDUrVNlXWz4" +
+            "Px8G8k/Ll6BKWcZ40egDuYVtLLrhX7atKz4lecWLVtXjCYDqwSfC2Q7sRwrp0Mr8" +
+            "2A==");
+
+    // Tau Ceti Email Cert.
+    private static final byte[] sampleEE = Base64.decode(
+            "MIIF5DCCBMygAwIBAgIQXWymKNy5PxuC4PCrhdImEDANBgkqhkiG9w0BAQsFADBC" +
+            "MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS" +
+            "UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE2MTEyODAwMDAwMFoXDTE5MDEyNzIzNTk1" +
+            "OVowHjEcMBoGA1UEAwwTbWFpbC50YXVjZXRpLm9yZy5hdTCCASIwDQYJKoZIhvcN" +
+            "AQEBBQADggEPADCCAQoCggEBAPK3JUkZfsxNIuZmLLgZuJCDmWbi3KVEi4YTjpSm" +
+            "X3S+aZzO/QenA+den98fUFDIgch0X+S5mlvKRhdQuaJrtb5Y+W4QGieur9uQrind" +
+            "8CP7/eu+lMD1UUbwcYosHX13eQ+zM6Z6TcjPXBgK79QWuKLIvOm1Xxqy4+c9EtFk" +
+            "72555AOEjPS7PGZsOUBkoIWqp5p0Ryl+ZZ+DumZxNsggWgKBXL8eYL4uQVCAUvTY" +
+            "I1sfNQvSYm/ACk4LvQHNIYPxD2eOycu9xttxfG6VBOLLwHrZUqmIgwu+XY0NcO+W" +
+            "gowFtVD01R+jyVNMpnFxGovVbncym+0z71jP3cI93laO8TECAwEAAaOCAvgwggL0" +
+            "MB4GA1UdEQQXMBWCE21haWwudGF1Y2V0aS5vcmcuYXUwCQYDVR0TBAIwADArBgNV" +
+            "HR8EJDAiMCCgHqAchhpodHRwOi8vZ3Auc3ltY2IuY29tL2dwLmNybDBvBgNVHSAE" +
+            "aDBmMGQGBmeBDAECATBaMCoGCCsGAQUFBwIBFh5odHRwczovL3d3dy5yYXBpZHNz" +
+            "bC5jb20vbGVnYWwwLAYIKwYBBQUHAgIwIAweaHR0cHM6Ly93d3cucmFwaWRzc2wu" +
+            "Y29tL2xlZ2FsMB8GA1UdIwQYMBaAFJfCJ1CewsnsDIgyyHyt4qYBT9pvMA4GA1Ud" +
+            "DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwVwYIKwYB" +
+            "BQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vZ3Auc3ltY2QuY29tMCYGCCsG" +
+            "AQUFBzAChhpodHRwOi8vZ3Auc3ltY2IuY29tL2dwLmNydDCCAX4GCisGAQQB1nkC" +
+            "BAIEggFuBIIBagFoAHYA3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswA" +
+            "AAFYqH/T8QAABAMARzBFAiEA06gAEejY34PZqiYmMsVR4UmD6cJg4j7l6NcbIfVi" +
+            "aN0CICR9s94moCy9qgE63TZfsW+dHB3bcJL0Smxjo2+h4LCEAHYA7ku9t3XOYLrh" +
+            "Qmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFYqH/UOgAABAMARzBFAiEAu42gWW4w" +
+            "9t+CSry8h8xXuveO/f0fdqo/fswaHa/L9ecCIGPueAD/ydOIkjskpnFkeNcHdXVa" +
+            "a18AR8pzjW/IdMI+AHYAvHjh38X2PGhGSTNNoQ+hXwl5aSAJwIG08/aRfz7ZuKUA" +
+            "AAFYqH/U7wAABAMARzBFAiEAiQrwaLoNvmFlNLapDYN18gA09iIAvtfAM0noB35a" +
+            "wK8CIEjk9DPQthhMTtqDUA0LthHiLLeRIjlw9G7o3+4/a/A9MA0GCSqGSIb3DQEB" +
+            "CwUAA4IBAQB1/JjAkaEFcQFeihxJvGc4DpbucdB0OfmQrkjH5HvSYi/5xlp+BOxM" +
+            "es32KSI6CBiLhZviz3JVW05Zgz8tCEoV1D6kfmNQNQPXW958vO4QU88EPmbPo7fg" +
+            "Hb38Xv1BesjNN7R7S/nS80hFFU1UsspsrfRJnEMshkD4Xrt8644g+5VqQGxeN0WZ" +
+            "LkG40sYhBmVHwYBKIfefk8Erzxk58Fzfx4cIZZuIEqmVZVjuXGCmFzsW8StanBPP" +
+            "8Vyr5e9TEEGbsEyjpibgzLqrphtSpBsN4OphPYWtFzQpgq09wqLkLkhEHp+EvwPN" +
+            "gUt3Qm/EwLuDb+X5uVOqKWyP4PAlxmAr");
+
+    static boolean initialized = false;
+
+    static KeyPair trustKp;
+    static KeyPair caKp;
+    static KeyPair eeKp;
+
+    // initialise CertStore
+    static X509Certificate trustCert;
+    static X509Certificate caCert;
+    static X509Certificate eeCert;
+    static X509Certificate eeCertWithDistPoint;
+
+    static X509CRL trustCrl;
+    static X509CRL caCrl;
+    
+    public void setUp()
+        throws Exception
+    {
+        if(!initialized)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+            KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
+
+            kpGen.initialize(2048);
+
+            trustKp = kpGen.generateKeyPair();
+            caKp = kpGen.generateKeyPair();
+            eeKp = kpGen.generateKeyPair();
+
+            // initialise CertStore
+            trustCert = TestUtil.makeTrustAnchor(trustKp, "CN=Trust Anchor");
+            caCert = TestUtil.makeCaCertificate(trustCert, trustKp.getPrivate(), caKp.getPublic(), "CN=CA Cert");
+            eeCert = TestUtil.makeEeCertificate(false, caCert, caKp.getPrivate(), eeKp.getPublic(), "CN=End Entity");
+            eeCertWithDistPoint = TestUtil.makeEeCertificate(true, caCert, caKp.getPrivate(), eeKp.getPublic(), "CN=End Entity");
+            trustCrl = TestUtil.makeCrl(trustCert, trustKp.getPrivate(), BigInteger.valueOf(100));
+            caCrl = TestUtil.makeCrl(caCert, caKp.getPrivate(), BigInteger.valueOf(100));
+
+            initialized = true;
+        }
+    }
+
+    public void testValidPath()
+        throws Exception
+    {
+        List list = new ArrayList();
+        list.add(trustCert);
+        list.add(caCert);
+        list.add(eeCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(trustCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(eeCert);
+        certchain.add(caCert);
+
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(trustCert, null));
+
+        List<CRL> crls = new ArrayList<CRL>();
+        crls.add(trustCrl);
+        crls.add(caCrl);
+
+        X509RevocationChecker revocationChecker = new X509RevocationChecker
+            .Builder(new TrustAnchor(trustCert, null))
+            .addCrls(new CollectionStore<CRL>(crls))
+            .build();
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        param.addCertPathChecker(revocationChecker);
+
+        PKIXCertPathValidatorResult result =
+            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+        PolicyNode policyTree = result.getPolicyTree();
+        PublicKey subjectPublicKey = result.getPublicKey();
+
+        if (!subjectPublicKey.equals(eeCert.getPublicKey()))
+        {
+            fail("wrong public key returned");
+        }
+    }
+
+    public void testEndEntityOnly()
+        throws Exception
+    {
+        List list = new ArrayList();
+    
+        list.add(caCert);
+        list.add(eeCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(trustCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(eeCert);
+        certchain.add(caCert);
+
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(trustCert, null));
+
+        List<CRL> crls = new ArrayList<CRL>();
+        crls.add(caCrl);
+
+        X509RevocationChecker revocationChecker = new X509RevocationChecker
+            .Builder(new TrustAnchor(trustCert, null))
+            .setCheckEndEntityOnly(true)
+            .addCrls(new CollectionStore<CRL>(crls))
+            .build();
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        param.addCertPathChecker(revocationChecker);
+
+        PKIXCertPathValidatorResult result =
+            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+        PolicyNode policyTree = result.getPolicyTree();
+        PublicKey subjectPublicKey = result.getPublicKey();
+
+        if (!subjectPublicKey.equals(eeCert.getPublicKey()))
+        {
+            fail("wrong public key returned");
+        }
+    }
+
+    public void testRevokedEndEntityOnly()
+        throws Exception
+    {
+        List list = new ArrayList();
+
+        list.add(caCert);
+        list.add(eeCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(trustCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(eeCert);
+        certchain.add(caCert);
+
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(trustCert, null));
+
+        List<CRL> crls = new ArrayList<CRL>();
+        crls.add(TestUtil.makeCrl(caCert, caKp.getPrivate(), eeCert.getSerialNumber()));
+
+        X509RevocationChecker revocationChecker = new X509RevocationChecker
+            .Builder(new TrustAnchor(trustCert, null))
+            .setCheckEndEntityOnly(true)
+            .addCrls(new CollectionStore<CRL>(crls))
+            .build();
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        param.addCertPathChecker(revocationChecker);
+
+        try
+        {
+            PKIXCertPathValidatorResult result =
+                (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+            fail("no exception");
+        }
+        catch (CertPathValidatorException e)
+        {
+            assertTrue(e.getMessage().startsWith("certificate [issuer=\"CN=CA Cert\",serialNumber=3,subject=\"CN=End Entity\"] revoked"));
+            assertTrue(e.getMessage().endsWith(", reason: privilegeWithdrawn"));
+        }
+    }
+
+    public void testRevokedEndEntityWithSoftFailure()
+        throws Exception
+    {
+        List list = new ArrayList();
+
+        list.add(caCert);
+        list.add(eeCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(trustCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(eeCertWithDistPoint);
+        certchain.add(caCert);
+
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(trustCert, null));
+
+        List<CRL> crls = new ArrayList<CRL>();
+        crls.add(TestUtil.makeCrl(caCert, caKp.getPrivate(), eeCert.getSerialNumber()));
+
+        X509RevocationChecker revocationChecker = new X509RevocationChecker
+            .Builder(new TrustAnchor(trustCert, null))
+            .setCheckEndEntityOnly(true)
+            .setSoftFailHardLimit(true, 0)
+            .build();
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        param.addCertPathChecker(revocationChecker);
+
+        PKIXCertPathValidatorResult result =
+                (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+
+        // should fail on the second attempt.
+        try
+        {
+            result =
+                (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+            fail("no exception");
+        }
+        catch (CertPathValidatorException e)
+        {
+            assertTrue(e.getMessage().equals("No CRLs found for issuer \"cn=CA Cert\""));
+        }
+    }
+
+    public void testRevokedWithCRLDistPointEndEntityOnly()
+        throws Exception
+    {
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", "BC");
+        List list = new ArrayList();
+
+        X509Certificate trustCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(sampleTrust));
+        Certificate caCert = certFact.generateCertificate(new ByteArrayInputStream(sampleCA));
+        Certificate eeCert = certFact.generateCertificate(new ByteArrayInputStream(sampleEE));
+
+        list.add(caCert);
+        list.add(eeCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(trustCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(eeCert);
+        certchain.add(caCert);
+
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(trustCert, null));
+
+        List<CRL> crls = new ArrayList<CRL>();
+       // crls.add(TestUtil.makeCrl(caCert, caKp.getPrivate(), eeCert.getSerialNumber()));
+
+        X509RevocationChecker revocationChecker = new X509RevocationChecker
+            .Builder(new TrustAnchor(trustCert, null))
+            .setCheckEndEntityOnly(true)
+            .addCrls(new CollectionStore<CRL>(crls))
+            .usingProvider("BC")
+            .build();
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        param.addCertPathChecker(revocationChecker);
+
+        try
+        {
+            PKIXCertPathValidatorResult result =
+                (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+
+        }
+        catch (CertPathValidatorException e)
+        {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/test/TestUtil.java b/bcpkix/src/main/java/org/bouncycastle/pkix/test/TestUtil.java
new file mode 100644
index 0000000..d4886eb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkix/test/TestUtil.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.pkix.test;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.cert.X509v1CertificateBuilder;
+import org.bouncycastle.cert.X509v2CRLBuilder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
+import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public class TestUtil
+{
+    public static BigInteger       serialNumber = BigInteger.ONE;
+
+    private static BigInteger allocateSerialNumber()
+    {
+        BigInteger _tmp = serialNumber;
+        serialNumber = serialNumber.add(BigInteger.ONE);
+        return _tmp;
+    }
+
+    public static X509Certificate makeTrustAnchor(KeyPair kp, String name)
+        throws GeneralSecurityException, IOException, OperatorCreationException
+    {
+        X509v1CertificateBuilder v1CertGen = new JcaX509v1CertificateBuilder(
+            new X500Name(name),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Name(name),
+            kp.getPublic());
+
+        JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC");
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC")
+            .getCertificate(v1CertGen.build(contentSignerBuilder.build(kp.getPrivate())));
+
+        cert.checkValidity(new Date());
+        cert.verify(kp.getPublic());
+
+        return cert;
+    }
+
+    public static X509Certificate makeCaCertificate(X509Certificate issuer, PrivateKey issuerKey, PublicKey subjectKey, String subject)
+        throws GeneralSecurityException, IOException, OperatorCreationException
+    {
+        X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(
+            issuer.getSubjectX500Principal(),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Principal(subject),
+            subjectKey);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        v3CertGen.addExtension(
+            Extension.subjectKeyIdentifier,
+            false,
+            extUtils.createSubjectKeyIdentifier(subjectKey));
+
+        v3CertGen.addExtension(
+            Extension.authorityKeyIdentifier,
+            false,
+            extUtils.createAuthorityKeyIdentifier(issuer));
+
+        v3CertGen.addExtension(
+            Extension.basicConstraints,
+            false,
+            new BasicConstraints(0));
+
+        JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC");
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC")
+            .getCertificate(v3CertGen.build(contentSignerBuilder.build(issuerKey)));
+
+        cert.checkValidity(new Date());
+        cert.verify(issuer.getPublicKey());
+
+        return cert;
+    }
+
+    public static X509Certificate makeEeCertificate(boolean withDistPoint, X509Certificate issuer, PrivateKey issuerKey, PublicKey subjectKey, String subject)
+        throws GeneralSecurityException, IOException, OperatorCreationException
+    {
+        X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(
+            issuer.getSubjectX500Principal(),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Principal(subject),
+            subjectKey);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        v3CertGen.addExtension(
+            Extension.subjectKeyIdentifier,
+            false,
+            extUtils.createSubjectKeyIdentifier(subjectKey));
+
+        v3CertGen.addExtension(
+            Extension.authorityKeyIdentifier,
+            false,
+            extUtils.createAuthorityKeyIdentifier(issuer));
+
+        v3CertGen.addExtension(
+            Extension.basicConstraints,
+            false,
+            new BasicConstraints(false));
+
+        if (withDistPoint)
+        {
+            v3CertGen.addExtension(
+                Extension.cRLDistributionPoints,
+                false,
+                new DERSequence());
+        }
+
+        JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC");
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC")
+            .getCertificate(v3CertGen.build(contentSignerBuilder.build(issuerKey)));
+
+        cert.checkValidity(new Date());
+        cert.verify(issuer.getPublicKey());
+
+        return cert;
+    }
+
+    public static X509CRL makeCrl(X509Certificate issuer, PrivateKey sigKey, BigInteger revoked)
+        throws Exception
+    {
+        Date now = new Date();
+        X509v2CRLBuilder crlGen = new JcaX509v2CRLBuilder(issuer.getSubjectX500Principal(), now);
+        JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
+
+        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
+
+        crlGen.addCRLEntry(revoked, now, CRLReason.privilegeWithdrawn);
+
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(issuer));
+
+        return new JcaX509CRLConverter().setProvider("BC").getCRL(crlGen.build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build(sigKey)));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/ArchiveTimeStampValidationException.java b/bcpkix/src/main/java/org/bouncycastle/tsp/ArchiveTimeStampValidationException.java
new file mode 100644
index 0000000..12be0e1
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/ArchiveTimeStampValidationException.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.tsp;
+
+/**
+ * Exception thrown if an Archive TimeStamp according to RFC4998 fails to containsHashValue.
+ * <p>
+ * {@see <a href="https://tools.ietf.org/html/rfc4998">RFC4998</a>}
+ */
+
+public class ArchiveTimeStampValidationException
+    extends Exception
+{
+
+    public ArchiveTimeStampValidationException(final String message)
+    {
+        super(message);
+    }
+}
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/DataGroup.java b/bcpkix/src/main/java/org/bouncycastle/tsp/DataGroup.java
new file mode 100644
index 0000000..5f022e0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/DataGroup.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.tsp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Representation of data groups according to the description provided in RFC4998.
+ * <p>
+ * Such data groups represent a set of one or more data objects (e.g. electronic documents) for
+ * which an Evidence Record should be generated.
+ */
+public class DataGroup
+{
+    private List<byte[]> dataObjects;
+    private byte[] groupHash;
+    private TreeSet<byte[]> hashes;
+
+    public DataGroup(final List<byte[]> dataObjects)
+    {
+        this.dataObjects = dataObjects;
+    }
+
+    public DataGroup(final byte[] dataObject)
+    {
+        this.dataObjects = new ArrayList();
+        dataObjects.add(dataObject);
+    }
+
+    /**
+     * Generates hashes for all the data objects included in the data group.
+     *
+     * @param digestCalculator the {@link DigestCalculator} to use for computing the hashes
+     * @return the set of hashes, in ascending order
+     */
+    public TreeSet<byte[]> getHashes(DigestCalculator digestCalculator)
+    {
+        return getHashes(digestCalculator, null);
+    }
+
+    /**
+     * Generates hashes for all the data objects included in the data group.
+     *
+     * @param digestCalculator the {@link DigestCalculator} to use for computing the hashes
+     * @param ha a preceding hash, can be null.
+     * @return the set of hashes, in ascending order
+     */
+    private TreeSet<byte[]> getHashes(
+        final DigestCalculator digestCalculator,
+        final byte[] ha)
+    {
+        if (hashes == null)
+        {
+            hashes = new TreeSet(new ByteArrayComparator());
+
+            for (int i = 0; i != dataObjects.size(); i++)
+            {
+                byte[] dataObject = (byte[])dataObjects.get(i);
+                if (ha != null)
+                {
+                    hashes.add(calcDigest(digestCalculator, Arrays.concatenate(calcDigest(digestCalculator, dataObject), ha)));
+                }
+                else
+                {
+                    hashes.add(calcDigest(digestCalculator, dataObject));
+                }
+            }
+        }
+
+        return hashes;
+    }
+
+    /**
+     * Generates a hash for the whole DataGroup.
+     *
+     * @param digestCalculator the {@link DigestCalculator} to use for computing the hash
+     * @return a hash that is representative of the whole DataGroup
+     */
+    public byte[] getHash(DigestCalculator digestCalculator)
+    {
+        if (groupHash == null)
+        {
+            TreeSet<byte[]> hashes = getHashes(digestCalculator);
+
+            if (hashes.size() > 1)
+            {
+                byte[] concat = new byte[0];
+                Iterator<byte[]> iterator = hashes.iterator();
+
+                while (iterator.hasNext())
+                {
+                    concat = Arrays.concatenate(concat, (byte[])iterator.next());
+                }
+
+                groupHash = calcDigest(digestCalculator, concat);
+            }
+            else
+            {
+                groupHash = (byte[])hashes.first();
+            }
+        }
+
+        return groupHash;
+    }
+
+    /**
+     * Comparator for byte arrays
+     */
+    private class ByteArrayComparator
+        implements Comparator
+    {
+        public int compare(Object l, Object r)
+        {
+            byte[] left = (byte[])l;
+            byte[] right = (byte[])r;
+
+            int len = left.length < right.length ? left.length : right.length;
+
+            for (int i = 0; i != len; i++)
+            {
+                int a = (left[i] & 0xff);
+                int b = (right[i] & 0xff);
+
+                if (a != b)
+                {
+                    return a - b;
+                }
+            }
+
+            return left.length - right.length;
+        }
+    }
+
+    static byte[] calcDigest(DigestCalculator digCalc, byte[] data)
+    {
+        try
+        {
+            OutputStream dOut = digCalc.getOutputStream();
+
+            dOut.write(data);
+
+            dOut.close();
+
+            return digCalc.getDigest();
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("digest calculator failure: " + e.getMessage());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeProcessor.java b/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeProcessor.java
new file mode 100644
index 0000000..96b9924
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeProcessor.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.tsp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.tsp.PartialHashtree;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Arrays;
+
+public class PartialHashTreeProcessor
+{
+    private final byte[][] values;
+
+    public PartialHashTreeProcessor(PartialHashtree tree)
+    {
+        this.values = tree.getValues();
+    }
+
+    /**
+     * Compute a hash over the whole partialHashTree:
+     * - Concatenate all the hashes contained in the partial hash tree;
+     * - Generate a hash over the concatenated hashes, using a provided {@link DigestCalculator}.
+     *
+     * @param digestCalculator the {@link DigestCalculator} to use in order to generate the hash
+     * @return a hash value that is representative of the whole partial hash tree.
+     */
+    public byte[] getHash(DigestCalculator digestCalculator)
+    {
+        if (values.length == 1)
+        {
+            return values[0];
+        }
+
+        try
+        {
+            OutputStream dOut = digestCalculator.getOutputStream();
+
+            for (int i = 1; i != values.length; i++)
+            {
+                dOut.write(values[i]);
+            }
+
+            return digestCalculator.getDigest();
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("calculator failed: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Checks whether a PartialHashtree (RFC4998) contains a given hash.
+     *
+     * @param hash            the hash to check
+     * @throws PartialHashTreeVerificationException if the hash is not present in the
+     * PartialHashtree
+     */
+    public void verifyContainsHash(final byte[] hash)
+        throws PartialHashTreeVerificationException
+    {
+        if (!containsHash(hash))
+        {
+            throw new PartialHashTreeVerificationException("calculated hash is not present in " + "partial hash tree");
+        }
+    }
+
+    /**
+     * Checks whether a PartialHashtree (RFC4998) contains a given hash.
+     *
+     * @param hash            the hash to check
+     * @return true if the hash is present within the PartialHashtree's set of values, false
+     * otherwise.
+     */
+    public boolean containsHash(final byte[] hash)
+    {
+        for (int i = 1; i != values.length; i++)
+        {
+            if (Arrays.areEqual(hash, values[i]))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeVerificationException.java b/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeVerificationException.java
new file mode 100644
index 0000000..4b74174
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/PartialHashTreeVerificationException.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.tsp;
+
+public class PartialHashTreeVerificationException extends Exception {
+
+    public PartialHashTreeVerificationException(final String message)
+    {
+        super(message);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPAlgorithms.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPAlgorithms.java
index e8b26ad..780b79d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPAlgorithms.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPAlgorithms.java
@@ -6,9 +6,11 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 
 /**
@@ -30,6 +32,12 @@
     public static final ASN1ObjectIdentifier RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256;
     
     public static final ASN1ObjectIdentifier GOST3411 = CryptoProObjectIdentifiers.gostR3411;
-    
-    public static final Set    ALLOWED = new HashSet(Arrays.asList(new ASN1ObjectIdentifier[] { GOST3411, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD128, RIPEMD160, RIPEMD256 }));
+
+    public static final ASN1ObjectIdentifier GOST3411_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256;
+
+    public static final ASN1ObjectIdentifier GOST3411_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512;
+
+    public static final ASN1ObjectIdentifier SM3 = GMObjectIdentifiers.sm3;
+
+    public static final Set    ALLOWED = new HashSet(Arrays.asList(new ASN1ObjectIdentifier[] { SM3, GOST3411, GOST3411_2012_256, GOST3411_2012_512, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD128, RIPEMD160, RIPEMD256 }));
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
index d757071..3e179ae 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
@@ -17,9 +17,11 @@
 import org.bouncycastle.asn1.cms.AttributeTable;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import org.bouncycastle.asn1.x509.Extension;
@@ -53,6 +55,9 @@
         digestLengths.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), Integers.valueOf(20));
         digestLengths.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), Integers.valueOf(32));
         digestLengths.put(CryptoProObjectIdentifiers.gostR3411.getId(), Integers.valueOf(32));
+        digestLengths.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.getId(), Integers.valueOf(32));
+        digestLengths.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId(), Integers.valueOf(64));
+        digestLengths.put(GMObjectIdentifiers.sm3.getId(), Integers.valueOf(32));
 
         digestNames.put(PKCSObjectIdentifiers.md5.getId(), "MD5");
         digestNames.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1");
@@ -69,6 +74,9 @@
         digestNames.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160");
         digestNames.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256");
         digestNames.put(CryptoProObjectIdentifiers.gostR3411.getId(), "GOST3411");
+        digestNames.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.getId(), "GOST3411-2012-256");
+        digestNames.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId(), "GOST3411-2012-512");
+        digestNames.put(GMObjectIdentifiers.sm3.getId(), "SM3");
     }
 
      /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java
index 7d13510..255cbc2 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java
@@ -4,11 +4,15 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DLSequence;
 import org.bouncycastle.asn1.cmp.PKIFailureInfo;
 import org.bouncycastle.asn1.cmp.PKIFreeText;
 import org.bouncycastle.asn1.cmp.PKIStatus;
 import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.tsp.TimeStampResp;
 import org.bouncycastle.util.Arrays;
@@ -58,6 +62,24 @@
         this(readTimeStampResp(in));
     }
 
+    TimeStampResponse(DLSequence dlSequence)
+        throws TSPException, IOException
+    {
+        try
+        {
+            resp = TimeStampResp.getInstance(dlSequence);
+            timeStampToken = new TimeStampToken(ContentInfo.getInstance(dlSequence.getObjectAt(1)));
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new TSPException("malformed timestamp response: " + e, e);
+        }
+        catch (ClassCastException e)
+        {
+            throw new TSPException("malformed timestamp response: " + e, e);
+        }
+    }
+
     private static TimeStampResp readTimeStampResp(
         InputStream in) 
         throws IOException, TSPException
@@ -186,4 +208,16 @@
     {
         return resp.getEncoded();
     }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded(String encoding) throws IOException
+    {
+        if (ASN1Encoding.DL.equals(encoding))
+        {
+            return new DLSequence(new ASN1Encodable[] { resp.getStatus(), timeStampToken.toCMSSignedData().toASN1Structure() }).getEncoded(encoding);
+        }
+        return resp.getEncoded(encoding);
+    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
index f6b0ffb..163b924 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
@@ -7,12 +7,14 @@
 import java.util.Iterator;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.DLSequence;
 import org.bouncycastle.asn1.cmp.PKIFailureInfo;
 import org.bouncycastle.asn1.cmp.PKIFreeText;
 import org.bouncycastle.asn1.cmp.PKIStatus;
@@ -271,11 +273,9 @@
                     "Timestamp token received cannot be converted to ContentInfo", e);
         }
 
-        TimeStampResp resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo);
-
         try
         {
-            return new TimeStampResponse(resp);
+            return new TimeStampResponse(new DLSequence(new ASN1Encodable[] { pkiStatusInfo.toASN1Primitive(), tstTokenContentInfo.toASN1Primitive() }));
         }
         catch (IOException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
index 8c7c4a8..c7fef3f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
@@ -3,7 +3,9 @@
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.Security;
 import java.security.cert.X509Certificate;
 import java.text.SimpleDateFormat;
@@ -11,13 +13,14 @@
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.SimpleTimeZone;
 
 import junit.framework.TestCase;
-import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERUTF8String;
@@ -30,21 +33,30 @@
 import org.bouncycastle.asn1.ess.SigningCertificateV2;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.IssuerSerial;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
 import org.bouncycastle.cms.CMSAttributeTableGenerationException;
 import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
 import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
 import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
 import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.test.CMSTestUtil;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
@@ -114,6 +126,78 @@
         additionalExtensionTest(origKP.getPrivate(), origCert, certs);
     }
 
+    public void testCertOrdering()
+        throws Exception
+    {
+        List            certList = new ArrayList();
+
+        String _origDN   = "O=Bouncy Castle, C=AU";
+        KeyPair _origKP   = CMSTestUtil.makeKeyPair();
+        X509Certificate _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _origKP, _origDN);
+
+        String _signDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
+        KeyPair _signKP   = CMSTestUtil.makeKeyPair();
+        X509Certificate _signCert = TSPTestUtil.makeCertificate(_signKP, _signDN, _origKP, _origDN);
+
+        KeyPair _signDsaKP   = CMSTestUtil.makeDsaKeyPair();
+        X509Certificate _signDsaCert = CMSTestUtil.makeCertificate(_signDsaKP, _signDN, _origKP, _origDN);
+
+        certList.add(_origCert);
+        certList.add(_signDsaCert);
+        certList.add(_signCert);
+
+        Store      certs = new JcaCertStore(certList);
+
+        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
+            new JcaSimpleSignerInfoGeneratorBuilder().build("SHA1withRSA", _signKP.getPrivate(), _signCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2"));
+
+        tsTokenGen.addCertificates(certs);
+
+        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
+        
+        reqGen.setCertReq(true);
+
+        TimeStampRequest request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
+
+        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
+
+        TimeStampResponse initResp = tsRespGen.generateGrantedResponse(request, new BigInteger("23"), new Date());
+
+        // original CMS SignedData object
+        CMSSignedData sd = initResp.getTimeStampToken().toCMSSignedData();
+
+        certs = sd.getCertificates();
+        Iterator it = certs.getMatches(null).iterator();
+
+        assertEquals(new JcaX509CertificateHolder(_origCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signDsaCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signCert), it.next());
+
+        // definite-length
+        TimeStampResponse dlResp = new TimeStampResponse(initResp.getEncoded(ASN1Encoding.DL));
+
+        sd = dlResp.getTimeStampToken().toCMSSignedData();
+
+        certs = sd.getCertificates();
+        it = certs.getMatches(null).iterator();
+
+        assertEquals(new JcaX509CertificateHolder(_origCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signDsaCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signCert), it.next());
+
+        // convert to DER - the default encoding
+        TimeStampResponse derResp = new TimeStampResponse(initResp.getEncoded());
+
+        sd = derResp.getTimeStampToken().toCMSSignedData();
+
+        certs = sd.getCertificates();
+        it = certs.getMatches(null).iterator();
+
+        assertEquals(new JcaX509CertificateHolder(_origCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signCert), it.next());
+        assertEquals(new JcaX509CertificateHolder(_signDsaCert), it.next());
+    }
+
     private void basicTest(
         PrivateKey privateKey,
         X509Certificate cert,
@@ -143,6 +227,76 @@
         assertNotNull("no signingCertificate attribute found", table.get(PKCSObjectIdentifiers.id_aa_signingCertificate));
     }
 
+    public void testSM2withSM3()
+        throws Exception
+    {
+        //
+         // set up the keys
+         //
+         PrivateKey privKey;
+         PublicKey pubKey;
+
+         try
+         {
+             KeyPairGenerator g = KeyPairGenerator.getInstance("EC", "BC");
+
+             g.initialize(new ECNamedCurveGenParameterSpec("sm2p256v1"));
+
+             KeyPair p = g.generateKeyPair();
+
+             privKey = p.getPrivate();
+             pubKey = p.getPublic();
+         }
+         catch (Exception e)
+         {
+             fail("error setting up keys - " + e.toString());
+             return;
+         }
+
+        //
+        // extensions
+        //
+
+        //
+        // create the certificate - version 1
+        //
+
+        ContentSigner sigGen = new JcaContentSignerBuilder("SM3withSM2").setProvider(BC).build(privKey);
+        JcaX509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
+            new X500Name("CN=Test"),
+            BigInteger.valueOf(1),
+            new Date(System.currentTimeMillis() - 50000),
+            new Date(System.currentTimeMillis() + 50000),
+            new X500Name("CN=Test"),
+            pubKey);
+
+        certGen.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping));
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
+            new JcaSimpleSignerInfoGeneratorBuilder().build("SM3withSM2", privKey, cert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2"));
+
+       // tsTokenGen.addCertificates(certs);
+
+        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
+        TimeStampRequest request = reqGen.generate(TSPAlgorithms.SM3, new byte[32], BigInteger.valueOf(100));
+
+        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
+
+        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date());
+
+        tsResp = new TimeStampResponse(tsResp.getEncoded());
+
+        TimeStampToken tsToken = tsResp.getTimeStampToken();
+
+        tsToken.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert));
+
+        AttributeTable table = tsToken.getSignedAttributes();
+
+        assertNotNull("no signingCertificate attribute found", table.get(PKCSObjectIdentifiers.id_aa_signingCertificate));
+    }
+
     private void resolutionTest(
         PrivateKey privateKey,
         X509Certificate cert,
diff --git a/bcprov/src/main/java/org/bouncycastle/LICENSE.java b/bcprov/src/main/java/org/bouncycastle/LICENSE.java
index 5973087..db8c6f1 100644
--- a/bcprov/src/main/java/org/bouncycastle/LICENSE.java
+++ b/bcprov/src/main/java/org/bouncycastle/LICENSE.java
@@ -5,7 +5,7 @@
 /**
  * The Bouncy Castle License
  *
- * Copyright (c) 2000-2016 The Legion Of The Bouncy Castle Inc. (http://www.bouncycastle.org)
+ * Copyright (c) 2000-2019 The Legion Of The Bouncy Castle Inc. (http://www.bouncycastle.org)
  * <p>
  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
@@ -25,8 +25,8 @@
  */
 public class LICENSE
 {
-    public static String licenseText =
-      "Copyright (c) 2000-2016 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) "
+    public static final String licenseText =
+      "Copyright (c) 2000-2019 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) "
       + Strings.lineSeparator()
       + Strings.lineSeparator()
       + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software "
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
index c67e42f..770af15 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
@@ -3,9 +3,10 @@
 import java.io.IOException;
 
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
 
 /**
- * Base class for an application specific object
+ * Base class for an ASN.1 ApplicationSpecific object
  */
 public abstract class ASN1ApplicationSpecific
     extends ASN1Primitive
@@ -194,25 +195,19 @@
         //
         if (tagNo == 0x1f)
         {
-            tagNo = 0;
-
             int b = input[index++] & 0xff;
 
             // X.690-0207 8.1.2.4.2
             // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
             if ((b & 0x7f) == 0) // Note: -1 will pass
             {
-                throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
+                throw new IOException("corrupted stream - invalid high tag number found");
             }
 
-            while ((b >= 0) && ((b & 0x80) != 0))
+            while ((b & 0x80) != 0)
             {
-                tagNo |= (b & 0x7f);
-                tagNo <<= 7;
                 b = input[index++] & 0xff;
             }
-
-//            tagNo |= (b & 0x7f);
         }
 
         byte[] tmp = new byte[input.length - index + 1];
@@ -223,4 +218,29 @@
 
         return tmp;
     }
+
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        if (isConstructed())
+        {
+            sb.append("CONSTRUCTED ");
+        }
+        sb.append("APPLICATION ");
+        sb.append(Integer.toString(getApplicationTag()));
+        sb.append("]");
+        // @todo content encoding somehow?
+        if (this.octets != null)
+        {
+            sb.append(" #");
+            sb.append(Hex.toHexString(this.octets));
+        }
+        else
+        {
+            sb.append(" #null");
+        }
+        sb.append(" ");
+        return sb.toString();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
index 8816b2b..422bccf 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
@@ -3,7 +3,7 @@
 import java.io.IOException;
 
 /**
- * Interface to parse ASN.1 application specific objects.
+ * Interface to parse ASN.1 ApplicationSpecific objects.
  */
 public interface ASN1ApplicationSpecificParser
     extends ASN1Encodable, InMemoryRepresentable
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
index 513d4e5..e1aba65 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
@@ -58,6 +58,7 @@
             return 0;
         }
 
+
         int bits = 1;
 
         while (((val <<= 1) & 0xFF) != 0)
@@ -135,7 +136,7 @@
     {
         StringBuffer          buf = new StringBuffer("#");
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
+        ASN1OutputStream aOut = new ASN1OutputStream(bOut);
 
         try
         {
@@ -216,7 +217,7 @@
     }
 
     protected boolean asn1Equals(
-        ASN1Primitive  o)
+        ASN1Primitive o)
     {
         if (!(o instanceof ASN1BitString))
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
index 49372be..6f5d1fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
@@ -14,7 +14,6 @@
  * <li> {@link ASN1Boolean#getInstance(boolean) ASN1Boolean.getInstance(boolean)}</li>
  * <li> {@link ASN1Boolean#getInstance(int) ASN1Boolean.getInstance(int)}</li>
  * </ul>
- * </p>
  */
 public class ASN1Boolean
     extends ASN1Primitive
@@ -28,7 +27,7 @@
     public static final ASN1Boolean TRUE  = new ASN1Boolean(true);
 
     /**
-     * return a boolean from the passed in object.
+     * Return a boolean from the passed in object.
      *
      * @param obj an ASN1Boolean or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -59,7 +58,7 @@
     }
 
     /**
-     * return an ASN1Boolean from the passed in boolean.
+     * Return an ASN1Boolean from the passed in boolean.
      * @param value true or false depending on the ASN1Boolean wanted.
      * @return an ASN1Boolean instance.
      */
@@ -70,7 +69,7 @@
     }
 
     /**
-     * return an ASN1Boolean from the passed in value.
+     * Return an ASN1Boolean from the passed in value.
      * @param value non-zero (true) or zero (false) depending on the ASN1Boolean wanted.
      * @return an ASN1Boolean instance.
      */
@@ -81,7 +80,7 @@
     }
 
     /**
-     * return a Boolean from a tagged object.
+     * Return a Boolean from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
index 3ca8890..4d92dd8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
@@ -8,7 +8,6 @@
  * If you use this interface your class should also implement the getInstance()
  * pattern which takes a tag object and the tagging mode used.
  * </p>
- * <hr>
  * <p><b>X.690</b></p>
  * <p><b>8: Basic encoding rules</b></p>
  * <p><b>8.13 Encoding of a choice value </b></p>
@@ -16,11 +15,11 @@
  * The encoding of a choice value shall be the same as the encoding of a value of the chosen type.
  * <blockquote>
  * NOTE 1 &mdash; The encoding may be primitive or constructed depending on the chosen type.
- * <br />
+ * </blockquote>
+ * <blockquote>
  * NOTE 2 &mdash; The tag used in the identifier octets is the tag of the chosen type,
  * as specified in the ASN.1 definition of the choice type.
  * </blockquote>
- * </p>
  */
 public interface ASN1Choice
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
index 2828541..0971748 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -4,7 +4,7 @@
 import java.util.Vector;
 
 /**
- * Mutable class for building ASN.1 constructed objects.
+ * Mutable class for building ASN.1 constructed objects such as SETs or SEQUENCEs.
  */
 public class ASN1EncodableVector
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
index ca192f3..aa89eb5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Properties;
 
 /**
  * Class representing the ASN.1 ENUMERATED type.
@@ -99,13 +100,9 @@
     public ASN1Enumerated(
         byte[]   bytes)
     {
-        if (bytes.length > 1)
+        if (!Properties.isOverrideSet("org.bouncycastle.asn1.allow_unsafe_integer"))
         {
-            if (bytes[0] == 0 && (bytes[1] & 0x80) == 0)
-            {
-                throw new IllegalArgumentException("malformed enumerated");
-            }
-            if (bytes[0] == (byte)0xff && (bytes[1] & 0x80) != 0)
+            if (ASN1Integer.isMalformed(bytes))
             {
                 throw new IllegalArgumentException("malformed enumerated");
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java
new file mode 100644
index 0000000..7db80ff
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java
@@ -0,0 +1,292 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * Class representing the DER-type External
+ */
+public abstract class ASN1External
+    extends ASN1Primitive
+{
+    protected ASN1ObjectIdentifier directReference;
+    protected ASN1Integer indirectReference;
+    protected ASN1Primitive dataValueDescriptor;
+    protected int encoding;
+    protected ASN1Primitive externalContent;
+
+    /**
+     * Construct an EXTERNAL object, the input encoding vector must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or
+     */
+    public ASN1External(ASN1EncodableVector vector)
+    {
+        int offset = 0;
+
+        ASN1Primitive enc = getObjFromVector(vector, offset);
+        if (enc instanceof ASN1ObjectIdentifier)
+        {
+            directReference = (ASN1ObjectIdentifier)enc;
+            offset++;
+            enc = getObjFromVector(vector, offset);
+        }
+        if (enc instanceof ASN1Integer)
+        {
+            indirectReference = (ASN1Integer) enc;
+            offset++;
+            enc = getObjFromVector(vector, offset);
+        }
+        if (!(enc instanceof ASN1TaggedObject))
+        {
+            dataValueDescriptor = (ASN1Primitive) enc;
+            offset++;
+            enc = getObjFromVector(vector, offset);
+        }
+
+        if (vector.size() != offset + 1)
+        {
+            throw new IllegalArgumentException("input vector too large");
+        }
+
+        if (!(enc instanceof ASN1TaggedObject))
+        {
+            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+        }
+        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
+        setEncoding(obj.getTagNo());
+        externalContent = obj.getObject();
+    }
+
+    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+    {
+        if (v.size() <= index)
+        {
+            throw new IllegalArgumentException("too few objects in input vector");
+        }
+
+        return v.get(index).toASN1Primitive();
+    }
+
+    /**
+     * Creates a new instance of External
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param externalData The external data in its encoded form.
+     */
+    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    {
+        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+    }
+
+    /**
+     * Creates a new instance of External.
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param encoding The encoding to be used for the external data
+     * @param externalData The external data
+     */
+    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    {
+        setDirectReference(directReference);
+        setIndirectReference(indirectReference);
+        setDataValueDescriptor(dataValueDescriptor);
+        setEncoding(encoding);
+        setExternalContent(externalData.toASN1Primitive());
+    }
+
+    ASN1Primitive toDERObject()
+     {
+         if (this instanceof DERExternal)
+         {
+             return this;
+         }
+
+         return new DERExternal(directReference, indirectReference, dataValueDescriptor, encoding, externalContent);
+     }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode()
+    {
+        int ret = 0;
+        if (directReference != null)
+        {
+            ret = directReference.hashCode();
+        }
+        if (indirectReference != null)
+        {
+            ret ^= indirectReference.hashCode();
+        }
+        if (dataValueDescriptor != null)
+        {
+            ret ^= dataValueDescriptor.hashCode();
+        }
+        ret ^= externalContent.hashCode();
+        return ret;
+    }
+
+    boolean isConstructed()
+    {
+        return true;
+    }
+
+    int encodedLength()
+        throws IOException
+    {
+        return this.getEncoded().length;
+    }
+
+    /* (non-Javadoc)
+     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
+     */
+    boolean asn1Equals(ASN1Primitive o)
+    {
+        if (!(o instanceof ASN1External))
+        {
+            return false;
+        }
+        if (this == o)
+        {
+            return true;
+        }
+        ASN1External other = (ASN1External)o;
+        if (directReference != null)
+        {
+            if (other.directReference == null || !other.directReference.equals(directReference))  
+            {
+                return false;
+            }
+        }
+        if (indirectReference != null)
+        {
+            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
+            {
+                return false;
+            }
+        }
+        if (dataValueDescriptor != null)
+        {
+            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
+            {
+                return false;
+            }
+        }
+        return externalContent.equals(other.externalContent);
+    }
+
+    /**
+     * Returns the data value descriptor
+     * @return The descriptor
+     */
+    public ASN1Primitive getDataValueDescriptor()
+    {
+        return dataValueDescriptor;
+    }
+
+    /**
+     * Returns the direct reference of the external element
+     * @return The reference
+     */
+    public ASN1ObjectIdentifier getDirectReference()
+    {
+        return directReference;
+    }
+
+    /**
+     * Returns the encoding of the content. Valid values are
+     * <ul>
+     * <li><code>0</code> single-ASN1-type</li>
+     * <li><code>1</code> OCTET STRING</li>
+     * <li><code>2</code> BIT STRING</li>
+     * </ul>
+     * @return The encoding
+     */
+    public int getEncoding()
+    {
+        return encoding;
+    }
+    
+    /**
+     * Returns the content of this element
+     * @return The content
+     */
+    public ASN1Primitive getExternalContent()
+    {
+        return externalContent;
+    }
+    
+    /**
+     * Returns the indirect reference of this element
+     * @return The reference
+     */
+    public ASN1Integer getIndirectReference()
+    {
+        return indirectReference;
+    }
+    
+    /**
+     * Sets the data value descriptor
+     * @param dataValueDescriptor The descriptor
+     */
+    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
+    {
+        this.dataValueDescriptor = dataValueDescriptor;
+    }
+
+    /**
+     * Sets the direct reference of the external element
+     * @param directReferemce The reference
+     */
+    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
+    {
+        this.directReference = directReferemce;
+    }
+    
+    /**
+     * Sets the encoding of the content. Valid values are
+     * <ul>
+     * <li><code>0</code> single-ASN1-type</li>
+     * <li><code>1</code> OCTET STRING</li>
+     * <li><code>2</code> BIT STRING</li>
+     * </ul>
+     * @param encoding The encoding
+     */
+    private void setEncoding(int encoding)
+    {
+        if (encoding < 0 || encoding > 2)
+        {
+            throw new IllegalArgumentException("invalid encoding value: " + encoding);
+        }
+        this.encoding = encoding;
+    }
+    
+    /**
+     * Sets the content of this element
+     * @param externalContent The content
+     */
+    private void setExternalContent(ASN1Primitive externalContent)
+    {
+        this.externalContent = externalContent;
+    }
+    
+    /**
+     * Sets the indirect reference of this element
+     * @param indirectReference The reference
+     */
+    private void setIndirectReference(ASN1Integer indirectReference)
+    {
+        this.indirectReference = indirectReference;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
index 089526e..d250c0b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
@@ -16,11 +16,35 @@
  * <p>
  * The main difference between these and UTC time is a 4 digit year.
  * </p>
+ * <p>
+ * One second resolution date+time on UTC timezone (Z)
+ * with 4 digit year (valid from 0001 to 9999).
+ * </p><p>
+ * Timestamp format is:  yyyymmddHHMMSS'Z'
+ * </p><p>
+ * <h2>X.690</h2>
+ * This is what is called "restricted string",
+ * and it uses ASCII characters to encode digits and supplemental data.
+ *
+ * <h3>11: Restrictions on BER employed by both CER and DER</h3>
+ * <h4>11.7 GeneralizedTime </h4>
+ * <p>
+ * <b>11.7.1</b> The encoding shall terminate with a "Z",
+ * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
+ * GeneralizedTime.
+ * </p><p>
+ * <b>11.7.2</b> The seconds element shall always be present.
+ * </p>
+ * <p>
+ * <b>11.7.3</b> The fractional-seconds elements, if present,
+ * shall omit all trailing zeros; if the elements correspond to 0,
+ * they shall be wholly omitted, and the decimal point element also
+ * shall be omitted.
  */
 public class ASN1GeneralizedTime
     extends ASN1Primitive
 {
-    private byte[] time;
+    protected byte[] time;
 
     /**
      * return a generalized time from the passed in object
@@ -109,7 +133,7 @@
     public ASN1GeneralizedTime(
         Date time)
     {
-        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", DateUtil.EN_Locale);
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
@@ -161,7 +185,6 @@
      * </pre>
      * To read in the time and get a date which is compatible with our local
      * time zone.
-     * </p>
      * @return a String representation of the time.
      */
     public String getTime()
@@ -254,10 +277,18 @@
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
             }
-            else
+            else if (hasSeconds())
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
             }
+            else if (hasMinutes())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHH'Z'");
+            }
 
             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
         }
@@ -268,10 +299,18 @@
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
             }
-            else
+            else if (hasSeconds())
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
             }
+            else if (hasMinutes())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmz");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHz");
+            }
 
             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
         }
@@ -281,10 +320,18 @@
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
             }
-            else
+            else if (hasSeconds())
             {
                 dateF = new SimpleDateFormat("yyyyMMddHHmmss");
             }
+            else if (hasMinutes())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmm");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHH");
+            }
 
             dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
         }
@@ -320,10 +367,10 @@
             }
         }
 
-        return dateF.parse(d);
+        return DateUtil.epochAdjust(dateF.parse(d));
     }
 
-    private boolean hasFractionalSeconds()
+    protected boolean hasFractionalSeconds()
     {
         for (int i = 0; i != time.length; i++)
         {
@@ -338,6 +385,21 @@
         return false;
     }
 
+    protected boolean hasSeconds()
+    {
+        return isDigit(12) && isDigit(13);
+    }
+
+    protected boolean hasMinutes()
+    {
+        return isDigit(10) && isDigit(11);
+    }
+
+    private boolean isDigit(int pos)
+    {
+        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+    }
+
     boolean isConstructed()
     {
         return false;
@@ -357,6 +419,11 @@
         out.writeEncoded(BERTags.GENERALIZED_TIME, time);
     }
 
+    ASN1Primitive toDERObject()
+    {
+        return new DERGeneralizedTime(time);
+    }
+
     boolean asn1Equals(
         ASN1Primitive o)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
index 0c63a1a..92d8cbb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
@@ -9,7 +9,7 @@
 import org.bouncycastle.util.io.Streams;
 
 /**
- * a general purpose ASN.1 decoder - note: this class differs from the
+ * A general purpose ASN.1 decoder - note: this class differs from the
  * others in that it returns null after it has read the last object in
  * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
  * returned.
@@ -143,7 +143,7 @@
 
         if ((tag & APPLICATION) != 0)
         {
-            return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+            return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
         }
 
         if ((tag & TAGGED) != 0)
@@ -181,7 +181,7 @@
                 case SET:
                     return DERFactory.createSet(buildDEREncodableVector(defIn));
                 case EXTERNAL:
-                    return new DERExternal(buildDEREncodableVector(defIn));                
+                    return new DLExternal(buildDEREncodableVector(defIn));
                 default:
                     throw new IOException("unknown tag " + tagNo + " encountered");
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
index ab6d202..39ada8b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Properties;
 
 /**
  * Class representing the ASN.1 INTEGER type.
@@ -14,11 +15,11 @@
     private final byte[] bytes;
 
     /**
-     * return an integer from the passed in object
+     * Return an integer from the passed in object.
      *
      * @param obj an ASN1Integer or an object that can be converted into one.
-     * @throws IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Integer instance.
+     * @throws IllegalArgumentException if the object cannot be converted.
      */
     public static ASN1Integer getInstance(
         Object obj)
@@ -44,14 +45,14 @@
     }
 
     /**
-     * return an Integer from a tagged object.
+     * Return an Integer from a tagged object.
      *
      * @param obj      the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
+     * @return an ASN1Integer instance.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
-     * @return an ASN1Integer instance.
      */
     public static ASN1Integer getInstance(
         ASN1TaggedObject obj,
@@ -65,22 +66,54 @@
         }
         else
         {
-            return new ASN1Integer(ASN1OctetString.getInstance(obj.getObject()).getOctets());
+            return new ASN1Integer(ASN1OctetString.getInstance(o).getOctets());
         }
     }
 
+    /**
+     * Construct an INTEGER from the passed in long value.
+     *
+     * @param value the long representing the value desired.
+     */
     public ASN1Integer(
         long value)
     {
         bytes = BigInteger.valueOf(value).toByteArray();
     }
 
+    /**
+     * Construct an INTEGER from the passed in BigInteger value.
+     *
+     * @param value the BigInteger representing the value desired.
+     */
     public ASN1Integer(
         BigInteger value)
     {
         bytes = value.toByteArray();
     }
 
+    /**
+     * Construct an INTEGER from the passed in byte array.
+     *
+     * <p>
+     * <b>NB: Strict Validation applied by default.</b>
+     * </p>
+     * <p>
+     * It has turned out that there are still a few applications that struggle with
+     * the ASN.1 BER encoding rules for an INTEGER as described in:
+     *
+     * https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+     * Section 8.3.2.
+     * </p>
+     * <p>
+     * Users can set the 'org.bouncycastle.asn1.allow_unsafe_integer' to 'true'
+     * and a looser validation will be applied. Users must recognise that this is
+     * not ideal and may pave the way for an exploit based around a faulty encoding
+     * in the future.
+     * </p>
+     *
+     * @param bytes the byte array representing a 2's complement encoding of a BigInteger.
+     */
     public ASN1Integer(
         byte[] bytes)
     {
@@ -89,20 +122,40 @@
 
     ASN1Integer(byte[] bytes, boolean clone)
     {
-        if (bytes.length > 1)
+        // Apply loose validation, see note in public constructor ANS1Integer(byte[])
+        if (!Properties.isOverrideSet("org.bouncycastle.asn1.allow_unsafe_integer"))
         {
-            if (bytes[0] == 0 && (bytes[1] & 0x80) == 0)
-            {
-                throw new IllegalArgumentException("malformed integer");
-            }
-            if (bytes[0] == (byte)0xff && (bytes[1] & 0x80) != 0)
-            {
+            if (isMalformed(bytes))
+            {                           
                 throw new IllegalArgumentException("malformed integer");
             }
         }
         this.bytes = (clone) ? Arrays.clone(bytes) : bytes;
     }
 
+    /**
+     * Apply the correct validation for an INTEGER primitive following the BER rules.
+     *
+     * @param bytes The raw encoding of the integer.
+     * @return true if the (in)put fails this validation.
+     */
+    static boolean isMalformed(byte[] bytes)
+    {
+        if (bytes.length > 1)
+        {
+            if (bytes[0] == 0 && (bytes[1] & 0x80) == 0)
+            {
+                return true;
+            }
+            if (bytes[0] == (byte)0xff && (bytes[1] & 0x80) != 0)
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     public BigInteger getValue()
     {
         return new BigInteger(bytes);
@@ -111,6 +164,7 @@
     /**
      * in some cases positive values get crammed into a space,
      * that's not quite big enough...
+     *
      * @return the BigInteger that results from treating this ASN.1 INTEGER as unsigned.
      */
     public BigInteger getPositiveValue()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
index f39d120..7cf0765 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
@@ -1,3 +1,6 @@
+/***************************************************************/
+/******    DO NOT EDIT THIS CLASS bc-java SOURCE FILE     ******/
+/***************************************************************/
 package org.bouncycastle.asn1;
 
 import java.io.IOException;
@@ -8,6 +11,11 @@
 public abstract class ASN1Null
     extends ASN1Primitive
 {
+    ASN1Null()
+    {
+
+    }
+
     /**
      * Return an instance of ASN.1 NULL from the passed in object.
      * <p>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
index 73a9d92..809457b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
@@ -19,7 +19,7 @@
     private byte[] body;
 
     /**
-     * return an OID from the passed in object
+     * Return an OID from the passed in object
      *
      * @param obj an ASN1ObjectIdentifier or an object that can be converted into one.
      * @return an ASN1ObjectIdentifier instance, or null.
@@ -33,9 +33,14 @@
             return (ASN1ObjectIdentifier)obj;
         }
 
-        if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier)
+        if (obj instanceof ASN1Encodable)
         {
-            return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive();
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+
+            if (primitive instanceof ASN1ObjectIdentifier)
+            {
+                return (ASN1ObjectIdentifier)primitive;
+            }
         }
 
         if (obj instanceof byte[])
@@ -55,7 +60,7 @@
     }
 
     /**
-     * return an Object Identifier from a tagged object.
+     * Return an OBJECT IDENTIFIER from a tagged object.
      *
      * @param obj      the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -76,7 +81,7 @@
         }
         else
         {
-            return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets());
+            return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
         }
     }
 
@@ -217,7 +222,7 @@
     }
 
     /**
-     * Return  true if this oid is an extension of the passed in branch, stem.
+     * Return true if this oid is an extension of the passed in branch - stem.
      *
      * @param stem the arc or branch that is a possible parent.
      * @return true if the branch is on the passed in stem, false otherwise.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
index 07811d7..2df802b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
@@ -16,7 +16,6 @@
  * DER form is always primitive single OCTET STRING, while
  * BER support includes the constructed forms.
  * </p>
- * <hr>
  * <p><b>X.690</b></p>
  * <p><b>8: Basic encoding rules</b></p>
  * <p><b>8.7 Encoding of an octetstring value</b></p>
@@ -38,20 +37,19 @@
  * <p>
  * <b>8.7.3</b> The contents octets for the constructed encoding shall consist
  * of zero, one, or more encodings.
+ * </p>
  * <blockquote>
  * NOTE &mdash; Each such encoding includes identifier, length, and contents octets,
  * and may include end-of-contents octets if it is constructed.
  * </blockquote>
- * </p>
  * <p>
  * <b>8.7.3.1</b> To encode an octetstring value in this way,
  * it is segmented. Each segment shall consist of a series of
  * consecutive octets of the value. There shall be no significance
- * placed on the segment boundaries.
+ * placed on the segment boundaries.</p>
  * <blockquote>
  * NOTE &mdash; A segment may be of size zero, i.e. contain no octets.
  * </blockquote>
- * </p>
  * <p>
  * <b>8.7.3.2</b> Each encoding in the contents octets shall represent
  * a segment of the overall octetstring, the encoding arising from
@@ -59,15 +57,16 @@
  * In this recursive application, each segment is treated as if it were
  * a octetstring value. The encodings of the segments shall appear in the contents
  * octets in the order in which their octets appear in the overall value.
+ * </p>
  * <blockquote>
  * NOTE 1 &mdash; As a consequence of this recursion,
  * each encoding in the contents octets may itself
  * be primitive or constructed.
  * However, such encodings will usually be primitive.
- * <br />
+ * </blockquote>
+ * <blockquote>
  * NOTE 2 &mdash; In particular, the tags in the contents octets are always universal class, number 4.
  * </blockquote>
- * </p>
  * <p><b>9: Canonical encoding rules</b></p>
  * <p><b>9.1 Length forms</b></p>
  * <p>
@@ -96,7 +95,6 @@
  * For BIT STRING, OCTET STRING and restricted character string types,
  * the constructed form of encoding shall not be used.
  * (Contrast with 8.21.6.)
- * </p>
  */
 public abstract class ASN1OctetString
     extends ASN1Primitive
@@ -249,6 +247,6 @@
 
     public String toString()
     {
-      return "#"+ Strings.fromByteArray(Hex.encode(string));
+      return "#" + Strings.fromByteArray(Hex.encode(string));
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
index db72d6a..94b0b3d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
@@ -79,11 +79,23 @@
 
     public abstract int hashCode();
 
+    /**
+     * Return true if this objected is a CONSTRUCTED one, false otherwise.
+     * @return true if CONSTRUCTED bit set on object's tag, false otherwise.
+     */
     abstract boolean isConstructed();
 
+    /**
+     * Return the length of the encoding this object will produce.
+     * @return the length of the object's encoding.
+     * @throws IOException if the encoding length cannot be calculated.
+     */
     abstract int encodedLength() throws IOException;
 
     abstract void encode(ASN1OutputStream out) throws IOException;
 
+    /**
+     * Equality (similarity) comparison for two ASN1Primitive objects.
+     */
     abstract boolean asn1Equals(ASN1Primitive o);
 }
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
index 0ca4d8f..ea6f3d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
@@ -50,9 +50,11 @@
  *
  * <p><b>11: Restrictions on BER employed by both CER and DER</b></p>
  * <p><b>11.5 Set and sequence components with default value</b></p>
+ * <p>
  * The encoding of a set value or sequence value shall not include
  * an encoding for any component value which is equal to
  * its default value.
+ * </p>
  */
 public abstract class ASN1Sequence
     extends ASN1Primitive
@@ -103,7 +105,7 @@
     }
 
     /**
-     * Return an ASN1 sequence from a tagged object. There is a special
+     * Return an ASN1 SEQUENCE from a tagged object. There is a special
      * case here, if an object appears to have been explicitly tagged on 
      * reading but we were expecting it to be implicitly tagged in the 
      * normal course of events it indicates that we lost the surrounding
@@ -134,6 +136,8 @@
         }
         else
         {
+            ASN1Primitive o = obj.getObject();
+
             //
             // constructed object which appears to be explicitly tagged
             // when it should be implicit means we have to add the
@@ -143,18 +147,18 @@
             {
                 if (obj instanceof BERTaggedObject)
                 {
-                    return new BERSequence(obj.getObject());
+                    return new BERSequence(o);
                 }
                 else
                 {
-                    return new DLSequence(obj.getObject());
+                    return new DLSequence(o);
                 }
             }
             else
             {
-                if (obj.getObject() instanceof ASN1Sequence)
+                if (o instanceof ASN1Sequence)
                 {
-                    return (ASN1Sequence)obj.getObject();
+                    return (ASN1Sequence)o;
                 }
             }
         }
@@ -163,14 +167,14 @@
     }
 
     /**
-     * Create an empty sequence
+     * Create an empty SEQUENCE
      */
     protected ASN1Sequence()
     {
     }
 
     /**
-     * Create a sequence containing one object
+     * Create a SEQUENCE containing one object.
      * @param obj the object to be put in the SEQUENCE.
      */
     protected ASN1Sequence(
@@ -180,8 +184,8 @@
     }
 
     /**
-     * Create a sequence containing a vector of objects.
-     * @param v the vector of objects to be put in the SEQUENCE
+     * Create a SEQUENCE containing a vector of objects.
+     * @param v the vector of objects to be put in the SEQUENCE.
      */
     protected ASN1Sequence(
         ASN1EncodableVector v)
@@ -192,8 +196,9 @@
         }
     }
 
-    /*
-     * Create a sequence containing a vector of objects.
+    /**
+     * Create a SEQUENCE containing an array of objects.
+     * @param array the array of objects to be put in the SEQUENCE.
      */
     protected ASN1Sequence(
         ASN1Encodable[]   array)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
index 1f6234f..9865533 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
@@ -12,13 +12,12 @@
  * <p>
  * Note: This does not know which syntax the set is!
  * (The difference: ordering of SET elements or not ordering.)
- * <p>
+ * </p><p>
  * DER form is always definite form length fields, while
  * BER support uses indefinite form.
- * <p>
+ * </p><p>
  * The CER form support does not exist.
- * <p>
- * <hr>
+ * </p><p>
  * <h2>X.690</h2>
  * <h3>8: Basic encoding rules</h3>
  * <h4>8.11 Encoding of a set value </h4>
@@ -29,7 +28,7 @@
  * ASN.1 definition of the set type, in an order chosen by the sender,
  * unless the type was referenced with the keyword
  * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
- * <p>
+ * </p><p>
  * <b>8.11.3</b> The encoding of a data value may, but need not,
  * be present for a type which was referenced with the keyword
  * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
@@ -38,13 +37,14 @@
  * and places no constraints on the order during transfer
  * </blockquote>
  * <h4>8.12 Encoding of a set-of value</h4>
- * <b>8.12.1</b> The encoding of a set-of value shall be constructed.
  * <p>
+ * <b>8.12.1</b> The encoding of a set-of value shall be constructed.
+ * </p><p>
  * <b>8.12.2</b> The text of 8.10.2 applies:
  * <i>The contents octets shall consist of zero,
  * one or more complete encodings of data values from the type listed in
  * the ASN.1 definition.</i>
- * <p>
+ * </p><p>
  * <b>8.12.3</b> The order of data values need not be preserved by
  * the encoding and subsequent decoding.
  *
@@ -175,6 +175,8 @@
         }
         else
         {
+            ASN1Primitive o = obj.getObject();
+
             //
             // constructed object which appears to be explicitly tagged
             // and it's really implicit means we have to add the
@@ -184,27 +186,27 @@
             {
                 if (obj instanceof BERTaggedObject)
                 {
-                    return new BERSet(obj.getObject());
+                    return new BERSet(o);
                 }
                 else
                 {
-                    return new DLSet(obj.getObject());
+                    return new DLSet(o);
                 }
             }
             else
             {
-                if (obj.getObject() instanceof ASN1Set)
+                if (o instanceof ASN1Set)
                 {
-                    return (ASN1Set)obj.getObject();
+                    return (ASN1Set)o;
                 }
 
                 //
                 // in this case the parser returns a sequence, convert it
                 // into a set.
                 //
-                if (obj.getObject() instanceof ASN1Sequence)
+                if (o instanceof ASN1Sequence)
                 {
-                    ASN1Sequence s = (ASN1Sequence)obj.getObject();
+                    ASN1Sequence s = (ASN1Sequence)o;
 
                     if (obj instanceof BERTaggedObject)
                     {
@@ -226,7 +228,7 @@
     }
 
     /**
-     * create a sequence containing one object
+     * Create a SET containing one object
      * @param obj object to be added to the SET.
      */
     protected ASN1Set(
@@ -236,7 +238,7 @@
     }
 
     /**
-     * create a sequence containing a vector of objects.
+     * Create a SET containing a vector of objects.
      * @param v a vector of objects to make up the SET.
      * @param doSort true if should be sorted DER style, false otherwise.
      */
@@ -255,8 +257,10 @@
         }
     }
 
-    /*
-     * create a sequence containing a vector of objects.
+    /**
+     * Create a SET containing an array of objects.
+     * @param array an array of objects to make up the SET.
+     * @param doSort true if should be sorted DER style, false otherwise.
      */
     protected ASN1Set(
         ASN1Encodable[]   array,
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
index a4bb370..a103429 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -172,7 +172,7 @@
 
             if ((tag & BERTags.APPLICATION) != 0)
             {
-                return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+                return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
             }
 
             if ((tag & BERTags.TAGGED) != 0)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java
index 3754440..0c265bb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java
@@ -1,7 +1,7 @@
 package org.bouncycastle.asn1;
 
 /**
- * General interface implemented by ASN.1 STRING objects.
+ * General interface implemented by ASN.1 STRING objects for extracting the content String.
  */
 public interface ASN1String
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
index 808f478..66ac4aa 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -173,7 +173,7 @@
     }
 
     /**
-     * return whatever was following the tag.
+     * Return whatever was following the tag.
      * <p>
      * Note: tagged objects are generally context dependent if you're
      * trying to extract a tagged object you should be going via the
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
index a681dc9..660d9a2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -2,11 +2,26 @@
 
 import java.io.IOException;
 
+/**
+ * Interface for the parsing of a generic tagged ASN.1 object.
+ */
 public interface ASN1TaggedObjectParser
     extends ASN1Encodable, InMemoryRepresentable
 {
-    public int getTagNo();
-    
-    public ASN1Encodable getObjectParser(int tag, boolean isExplicit)
+    /**
+     * Return the tag number associated with the underlying tagged object.
+     * @return the object's tag number.
+     */
+    int getTagNo();
+
+    /**
+     * Return a parser for the actual object tagged.
+     *
+     * @param tag the primitive tag value for the object tagged originally.
+     * @param isExplicit true if the tagging was done explicitly.
+     * @return a parser for the tagged object.
+     * @throws IOException if a parser cannot be constructed.
+     */
+    ASN1Encodable getObjectParser(int tag, boolean isExplicit)
         throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
index 2c82df3..0d6034a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
@@ -15,7 +15,7 @@
  * Internal facade of {@link ASN1UTCTime}.
  * <p>
  * This datatype is valid only from 1950-01-01 00:00:00 UTC until 2049-12-31 23:59:59 UTC.
- * <p>
+ * </p>
  * <hr>
  * <p><b>X.690</b></p>
  * <p><b>11: Restrictions on BER employed by both CER and DER</b></p>
@@ -37,7 +37,7 @@
     private byte[]      time;
 
     /**
-     * return an UTC Time from the passed in object.
+     * Return an UTC Time from the passed in object.
      *
      * @param obj an ASN1UTCTime or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -67,7 +67,7 @@
     }
 
     /**
-     * return an UTC Time from a tagged object.
+     * Return an UTC Time from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -117,13 +117,13 @@
     }
 
     /**
-     * base constructor from a java.util.date object
+     * Base constructor from a java.util.date object
      * @param time the Date to build the time from.
      */
     public ASN1UTCTime(
         Date time)
     {
-        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
+        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", DateUtil.EN_Locale);
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
@@ -155,7 +155,7 @@
     }
 
     /**
-     * return the time as a date based on whatever a 2 digit year will return. For
+     * Return the time as a date based on whatever a 2 digit year will return. For
      * standardised processing use getAdjustedDate().
      *
      * @return the resulting date
@@ -166,11 +166,11 @@
     {
         SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
 
-        return dateF.parse(getTime());
+        return DateUtil.epochAdjust(dateF.parse(getTime()));
     }
 
     /**
-     * return the time as an adjusted date
+     * Return the time as an adjusted date
      * in the range of 1950 - 2049.
      *
      * @return a date in the range of 1950 to 2049.
@@ -181,13 +181,13 @@
     {
         SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
 
-        dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
-
-        return dateF.parse(getAdjustedTime());
+        dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+        
+        return DateUtil.epochAdjust(dateF.parse(getAdjustedTime()));
     }
 
     /**
-     * return the time - always in the form of
+     * Return the time - always in the form of
      *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
      * <p>
      * Normally in a certificate we would expect "Z" rather than "GMT",
@@ -246,7 +246,7 @@
     }
 
     /**
-     * return a time string as an adjusted date with a 4 digit year. This goes
+     * Return a time string as an adjusted date with a 4 digit year. This goes
      * in the range of 1950 - 2049.
      */
     public String getAdjustedTime()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
index f8d6aa2..3608668 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
@@ -4,7 +4,7 @@
 import java.io.IOException;
 
 /**
- * An indefinite-length encoding version of an application specific object.
+ * An indefinite-length encoding version of an ASN.1 ApplicationSpecific object.
  */
 public class BERApplicationSpecific
     extends ASN1ApplicationSpecific
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
index e4904e0..c915415 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
@@ -3,7 +3,7 @@
 import java.io.IOException;
 
 /**
- * A parser for indefinite-length application specific objects.
+ * A parser for indefinite-length ASN.1 ApplicationSpecific objects.
  */
 public class BERApplicationSpecificParser
     implements ASN1ApplicationSpecificParser
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
index bc1ed44..6334934 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
@@ -5,15 +5,30 @@
 import java.util.Enumeration;
 import java.util.Vector;
 
+/**
+ * ASN.1 OctetStrings, with indefinite length rules, and <i>constructed form</i> support.
+ * <p>
+ * The Basic Encoding Rules (BER) format allows encoding using so called "<i>constructed form</i>",
+ * which DER and CER formats forbid allowing only "primitive form".
+ * </p><p>
+ * This class <b>always</b> produces the constructed form with underlying segments
+ * in an indefinite length array.  If the input wasn't the same, then this output
+ * is not faithful reproduction.
+ * </p>
+ * <p>
+ * See {@link ASN1OctetString} for X.690 encoding rules of OCTET-STRING objects.
+ * </p>
+ */
 public class BEROctetString
     extends ASN1OctetString
 {
-    private static final int MAX_LENGTH = 1000;
+    private static final int DEFAULT_LENGTH = 1000;
 
-    private ASN1OctetString[] octs;
+    private final int chunkSize;
+    private final ASN1OctetString[] octs;
 
     /**
-     * convert a vector of octet strings into a single byte string
+     * Convert a vector of octet strings into a single byte string
      */
     static private byte[] toBytes(
         ASN1OctetString[]  octs)
@@ -42,29 +57,73 @@
     }
 
     /**
+     * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
      */
     public BEROctetString(
         byte[] string)
     {
-        super(string);
+        this(string, DEFAULT_LENGTH);
     }
 
+    /**
+     * Multiple {@link ASN1OctetString} data blocks are input,
+     * the result is <i>constructed form</i>.
+     *
+     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
+     */
     public BEROctetString(
         ASN1OctetString[] octs)
     {
-        super(toBytes(octs));
-
-        this.octs = octs;
+        this(octs, DEFAULT_LENGTH);
     }
 
+    /**
+     * Create an OCTET-STRING object from a byte[]
+     * @param string the octets making up the octet string.
+     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     */
+    public BEROctetString(
+        byte[] string,
+        int    chunkSize)
+    {
+        this(string, null, chunkSize);
+    }
+
+    /**
+     * Multiple {@link ASN1OctetString} data blocks are input,
+     * the result is <i>constructed form</i>.
+     *
+     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     */
+    public BEROctetString(
+        ASN1OctetString[] octs,
+        int chunkSize)
+    {
+        this(toBytes(octs), octs, chunkSize);
+    }
+
+    private BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)
+    {
+        super(string);
+        this.octs = octs;
+        this.chunkSize = chunkSize;
+    }
+
+    /**
+     * Return a concatenated byte array of all the octets making up the constructed OCTET STRING
+     * @return the full OCTET STRING.
+     */
     public byte[] getOctets()
     {
         return string;
     }
 
     /**
-     * return the DER octets that make up this string.
+     * Return the OCTET STRINGs that make up this string.
+     *
+     * @return an Enumeration of the component OCTET STRINGs.
      */
     public Enumeration getObjects()
     {
@@ -92,17 +151,17 @@
     private Vector generateOcts()
     { 
         Vector vec = new Vector();
-        for (int i = 0; i < string.length; i += MAX_LENGTH) 
+        for (int i = 0; i < string.length; i += chunkSize)
         { 
             int end; 
 
-            if (i + MAX_LENGTH > string.length) 
+            if (i + chunkSize > string.length)
             { 
                 end = string.length; 
             } 
             else 
             { 
-                end = i + MAX_LENGTH; 
+                end = i + chunkSize;
             } 
 
             byte[] nStr = new byte[end - i]; 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
index f6459b2..22a37ed 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
@@ -4,7 +4,8 @@
 import java.io.OutputStream;
 
 /**
- * A class which writes indefinite and definite length objects,
+ * A class which writes indefinite and definite length objects. Objects which specify DER will be encoded accordingly, but DL or BER
+ * objects will be encoded as defined.
  */
 public class BEROutputStream
     extends DEROutputStream
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
index d4bfa06..3ecb146 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
@@ -4,7 +4,12 @@
 import java.util.Enumeration;
 
 /**
- * Carrier class for an indefinite-length SEQUENCE.
+ * Indefinite length SEQUENCE of objects.
+ * <p>
+ * Length field has value 0x80, and the sequence ends with two bytes of: 0x00, 0x00.
+ * </p><p>
+ * For X.690 syntax rules, see {@link ASN1Sequence}.
+ * </p>
  */
 public class BERSequence
     extends ASN1Sequence
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
index 63a276b..29ae7fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
@@ -4,7 +4,18 @@
 import java.util.Enumeration;
 
 /**
- * Carrier class for an indefinite-length SET.
+ * Indefinite length <code>SET</code> and <code>SET OF</code> constructs.
+ * <p>
+ * Note: This does not know which syntax the set is!
+ * </p><p>
+ * Length field has value 0x80, and the set ends with two bytes of: 0x00, 0x00.
+ * </p><p>
+ * For X.690 syntax rules, see {@link ASN1Set}.
+ * </p><p>
+ * In brief: Constructing this form does not sort the supplied elements,
+ * nor does the sorting happen before serialization. This is different
+ * from the way {@link DERSet} does things.
+ * </p>
  */
 public class BERSet
     extends ASN1Set
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
index 98ab0d6..39b517e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
@@ -9,28 +9,28 @@
     public static final int NULL                = 0x05;
     public static final int OBJECT_IDENTIFIER   = 0x06;
     public static final int EXTERNAL            = 0x08;
-    public static final int ENUMERATED          = 0x0a;
-    public static final int SEQUENCE            = 0x10;
+    public static final int ENUMERATED          = 0x0a; // decimal 10
+    public static final int SEQUENCE            = 0x10; // decimal 16
     public static final int SEQUENCE_OF         = 0x10; // for completeness - used to model a SEQUENCE of the same type.
-    public static final int SET                 = 0x11;
+    public static final int SET                 = 0x11; // decimal 17
     public static final int SET_OF              = 0x11; // for completeness - used to model a SET of the same type.
 
 
-    public static final int NUMERIC_STRING      = 0x12;
-    public static final int PRINTABLE_STRING    = 0x13;
-    public static final int T61_STRING          = 0x14;
-    public static final int VIDEOTEX_STRING     = 0x15;
-    public static final int IA5_STRING          = 0x16;
-    public static final int UTC_TIME            = 0x17;
-    public static final int GENERALIZED_TIME    = 0x18;
-    public static final int GRAPHIC_STRING      = 0x19;
-    public static final int VISIBLE_STRING      = 0x1a;
-    public static final int GENERAL_STRING      = 0x1b;
-    public static final int UNIVERSAL_STRING    = 0x1c;
-    public static final int BMP_STRING          = 0x1e;
-    public static final int UTF8_STRING         = 0x0c;
+    public static final int NUMERIC_STRING      = 0x12; // decimal 18
+    public static final int PRINTABLE_STRING    = 0x13; // decimal 19
+    public static final int T61_STRING          = 0x14; // decimal 20
+    public static final int VIDEOTEX_STRING     = 0x15; // decimal 21
+    public static final int IA5_STRING          = 0x16; // decimal 22
+    public static final int UTC_TIME            = 0x17; // decimal 23
+    public static final int GENERALIZED_TIME    = 0x18; // decimal 24
+    public static final int GRAPHIC_STRING      = 0x19; // decimal 25
+    public static final int VISIBLE_STRING      = 0x1a; // decimal 26
+    public static final int GENERAL_STRING      = 0x1b; // decimal 27
+    public static final int UNIVERSAL_STRING    = 0x1c; // decimal 28
+    public static final int BMP_STRING          = 0x1e; // decimal 30
+    public static final int UTF8_STRING         = 0x0c; // decimal 12
     
-    public static final int CONSTRUCTED         = 0x20;
-    public static final int APPLICATION         = 0x40;
-    public static final int TAGGED              = 0x80;
+    public static final int CONSTRUCTED         = 0x20; // decimal 32
+    public static final int APPLICATION         = 0x40; // decimal 64
+    public static final int TAGGED              = 0x80; // decimal 128
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
index e689985..c64802c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
@@ -5,7 +5,12 @@
 import org.bouncycastle.util.Arrays;
 
 /**
- * Carrier class for DER encoding BMPString object.
+ * DER BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
+ * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
+ * <p>
+ * At ISO-10646:2011 the term "BMP" has been withdrawn, and replaced by
+ * term "UCS-2".
+ * </p>
  */
 public class DERBMPString
     extends ASN1Primitive
@@ -14,7 +19,7 @@
     private final char[]  string;
 
     /**
-     * return a BMP String from the given object.
+     * Return a BMP String from the given object.
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -44,7 +49,7 @@
     }
 
     /**
-     * return a BMP String from a tagged object.
+     * Return a BMP String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -70,7 +75,7 @@
     }
 
     /**
-     * basic constructor - byte encoded string.
+     * Basic constructor - byte encoded string.
      * @param string the encoded BMP STRING to wrap.
      */
     DERBMPString(
@@ -92,7 +97,7 @@
     }
 
     /**
-     * basic constructor
+     * Basic constructor
      * @param string a String to wrap as a BMP STRING.
      */
     public DERBMPString(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
index c789d7c..8efcaf7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
@@ -3,7 +3,7 @@
 import java.io.IOException;
 
 /**
- * A BIT STRING with DER encoding.
+ * A BIT STRING with DER encoding - the first byte contains the count of padding bits included in the byte array's last byte.
  */
 public class DERBitString
     extends ASN1BitString
@@ -124,7 +124,7 @@
     }
 
     void encode(
-        ASN1OutputStream  out)
+        ASN1OutputStream out)
         throws IOException
     {
         byte[] string = derForm(data, padBits);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
index f6c45d3..480a394 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
@@ -7,61 +7,25 @@
  * Class representing the DER-type External
  */
 public class DERExternal
-    extends ASN1Primitive
+    extends ASN1External
 {
-    private ASN1ObjectIdentifier directReference;
-    private ASN1Integer indirectReference;
-    private ASN1Primitive dataValueDescriptor;
-    private int encoding;
-    private ASN1Primitive externalContent;
-    
+    /**
+     * Construct a DER EXTERNAL object, the input encoding vector must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or
+     */
     public DERExternal(ASN1EncodableVector vector)
     {
-        int offset = 0;
-
-        ASN1Primitive enc = getObjFromVector(vector, offset);
-        if (enc instanceof ASN1ObjectIdentifier)
-        {
-            directReference = (ASN1ObjectIdentifier)enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
-        }
-        if (enc instanceof ASN1Integer)
-        {
-            indirectReference = (ASN1Integer) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
-        }
-        if (!(enc instanceof ASN1TaggedObject))
-        {
-            dataValueDescriptor = (ASN1Primitive) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
-        }
-
-        if (vector.size() != offset + 1)
-        {
-            throw new IllegalArgumentException("input vector too large");
-        }
-
-        if (!(enc instanceof ASN1TaggedObject))
-        {
-            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
-        }
-        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
-        setEncoding(obj.getTagNo());
-        externalContent = obj.getObject();
+        super(vector);
     }
 
-    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
-    {
-        if (v.size() <= index)
-        {
-            throw new IllegalArgumentException("too few objects in input vector");
-        }
-
-        return v.get(index).toASN1Primitive();
-    }
     /**
      * Creates a new instance of DERExternal
      * See X.690 for more informations about the meaning of these parameters
@@ -86,38 +50,7 @@
      */
     public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
-        setDirectReference(directReference);
-        setIndirectReference(indirectReference);
-        setDataValueDescriptor(dataValueDescriptor);
-        setEncoding(encoding);
-        setExternalContent(externalData.toASN1Primitive());
-    }
-
-    /* (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
-    public int hashCode()
-    {
-        int ret = 0;
-        if (directReference != null)
-        {
-            ret = directReference.hashCode();
-        }
-        if (indirectReference != null)
-        {
-            ret ^= indirectReference.hashCode();
-        }
-        if (dataValueDescriptor != null)
-        {
-            ret ^= dataValueDescriptor.hashCode();
-        }
-        ret ^= externalContent.hashCode();
-        return ret;
-    }
-
-    boolean isConstructed()
-    {
-        return true;
+        super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
     int encodedLength()
@@ -149,146 +82,4 @@
         baos.write(obj.getEncoded(ASN1Encoding.DER));
         out.writeEncoded(BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
     }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
-     */
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERExternal))
-        {
-            return false;
-        }
-        if (this == o)
-        {
-            return true;
-        }
-        DERExternal other = (DERExternal)o;
-        if (directReference != null)
-        {
-            if (other.directReference == null || !other.directReference.equals(directReference))  
-            {
-                return false;
-            }
-        }
-        if (indirectReference != null)
-        {
-            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
-            {
-                return false;
-            }
-        }
-        if (dataValueDescriptor != null)
-        {
-            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
-            {
-                return false;
-            }
-        }
-        return externalContent.equals(other.externalContent);
-    }
-
-    /**
-     * Returns the data value descriptor
-     * @return The descriptor
-     */
-    public ASN1Primitive getDataValueDescriptor()
-    {
-        return dataValueDescriptor;
-    }
-
-    /**
-     * Returns the direct reference of the external element
-     * @return The reference
-     */
-    public ASN1ObjectIdentifier getDirectReference()
-    {
-        return directReference;
-    }
-
-    /**
-     * Returns the encoding of the content. Valid values are
-     * <ul>
-     * <li><code>0</code> single-ASN1-type</li>
-     * <li><code>1</code> OCTET STRING</li>
-     * <li><code>2</code> BIT STRING</li>
-     * </ul>
-     * @return The encoding
-     */
-    public int getEncoding()
-    {
-        return encoding;
-    }
-    
-    /**
-     * Returns the content of this element
-     * @return The content
-     */
-    public ASN1Primitive getExternalContent()
-    {
-        return externalContent;
-    }
-    
-    /**
-     * Returns the indirect reference of this element
-     * @return The reference
-     */
-    public ASN1Integer getIndirectReference()
-    {
-        return indirectReference;
-    }
-    
-    /**
-     * Sets the data value descriptor
-     * @param dataValueDescriptor The descriptor
-     */
-    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
-    {
-        this.dataValueDescriptor = dataValueDescriptor;
-    }
-
-    /**
-     * Sets the direct reference of the external element
-     * @param directReferemce The reference
-     */
-    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
-    {
-        this.directReference = directReferemce;
-    }
-    
-    /**
-     * Sets the encoding of the content. Valid values are
-     * <ul>
-     * <li><code>0</code> single-ASN1-type</li>
-     * <li><code>1</code> OCTET STRING</li>
-     * <li><code>2</code> BIT STRING</li>
-     * </ul>
-     * @param encoding The encoding
-     */
-    private void setEncoding(int encoding)
-    {
-        if (encoding < 0 || encoding > 2)
-        {
-            throw new IllegalArgumentException("invalid encoding value: " + encoding);
-        }
-        this.encoding = encoding;
-    }
-    
-    /**
-     * Sets the content of this element
-     * @param externalContent The content
-     */
-    private void setExternalContent(ASN1Primitive externalContent)
-    {
-        this.externalContent = externalContent;
-    }
-    
-    /**
-     * Sets the indirect reference of this element
-     * @param indirectReference The reference
-     */
-    private void setIndirectReference(ASN1Integer indirectReference)
-    {
-        this.indirectReference = indirectReference;
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
index 98d02e7..afdf510 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
@@ -37,7 +37,7 @@
     {
         try
         {
-            return new DERExternal(_parser.readVector());
+            return new DLExternal(_parser.readVector());
         }
         catch (IllegalArgumentException e)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
index 9addf70..52a580f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
@@ -6,7 +6,11 @@
 import org.bouncycastle.util.Strings;
 
 /**
- * Carrier class for a DER encoding GeneralString
+ * ASN.1 GENERAL-STRING data type.
+ * <p>
+ * This is an 8-bit encoded ISO 646 (ASCII) character set
+ * with optional escapes to other character sets.
+ * </p>
  */
 public class DERGeneralString 
     extends ASN1Primitive
@@ -15,7 +19,7 @@
     private final byte[] string;
 
     /**
-     * return a GeneralString from the given object.
+     * Return a GeneralString from the given object.
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -46,7 +50,7 @@
     }
 
     /**
-     * return a GeneralString from a tagged object.
+     * Return a GeneralString from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
index adee74e..1270e85 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -1,17 +1,33 @@
 package org.bouncycastle.asn1;
 
+import java.io.IOException;
 import java.util.Date;
 
+import org.bouncycastle.util.Strings;
+
 /**
  * DER Generalized time object.
+ * <h3>11: Restrictions on BER employed by both CER and DER</h3>
+ * <h4>11.7 GeneralizedTime </h4>
+ * <p>
+ * <b>11.7.1</b> The encoding shall terminate with a "Z",
+ * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
+ * GeneralizedTime.
+ * </p><p>
+ * <b>11.7.2</b> The seconds element shall always be present.
+ * </p>
+ * <p>
+ * <b>11.7.3</b> The fractional-seconds elements, if present,
+ * shall omit all trailing zeros; if the elements correspond to 0,
+ * they shall be wholly omitted, and the decimal point element also
+ * shall be omitted.
  */
 public class DERGeneralizedTime
     extends ASN1GeneralizedTime
 {
-
-    DERGeneralizedTime(byte[] bytes)
+    public DERGeneralizedTime(byte[] time)
     {
-        super(bytes);
+        super(time);
     }
 
     public DERGeneralizedTime(Date time)
@@ -24,5 +40,77 @@
         super(time);
     }
 
-    // TODO: create proper DER encoding.
+    private byte[] getDERTime()
+    {
+        if (time[time.length - 1] == 'Z')
+        {
+            if (!hasMinutes())
+            {
+                byte[] derTime = new byte[time.length + 4];
+
+                System.arraycopy(time, 0, derTime, 0, time.length - 1);
+                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, time.length - 1, 5);
+
+                return derTime;
+            }
+            else if (!hasSeconds())
+            {
+                byte[] derTime = new byte[time.length + 2];
+
+                System.arraycopy(time, 0, derTime, 0, time.length - 1);
+                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, time.length - 1, 3);
+
+                return derTime;
+            }
+            else if (hasFractionalSeconds())
+            {
+                int ind = time.length - 2;
+                while (ind > 0 && time[ind] == '0')
+                {
+                    ind--;
+                }
+
+                if (time[ind] == '.')
+                {
+                    byte[] derTime = new byte[ind + 1];
+
+                    System.arraycopy(time, 0, derTime, 0, ind);
+                    derTime[ind] = (byte)'Z';
+
+                    return derTime;
+                }
+                else
+                {
+                    byte[] derTime = new byte[ind + 2];
+
+                    System.arraycopy(time, 0, derTime, 0, ind + 1);
+                    derTime[ind + 1] = (byte)'Z';
+
+                    return derTime;
+                }
+            }
+            else
+            {
+                return time;
+            }
+        }
+        else
+        {
+            return time; // TODO: is there a better way?
+        }
+    }
+
+    int encodedLength()
+    {
+        int length = getDERTime().length;
+
+        return 1 + StreamUtil.calculateBodyLength(length) + length;
+    }
+
+    void encode(
+        ASN1OutputStream out)
+        throws IOException
+    {
+        out.writeEncoded(BERTags.GENERALIZED_TIME, getDERTime());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
index 0336e6b..d2f7a8d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
@@ -6,7 +6,10 @@
 import org.bouncycastle.util.Strings;
 
 /**
- * DER IA5String object - this is an ascii string.
+ * DER IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
  */
 public class DERIA5String
     extends ASN1Primitive
@@ -15,7 +18,7 @@
     private final byte[]  string;
 
     /**
-     * return a IA5 string from the passed in object
+     * Return an IA5 string from the passed in object
      *
      * @param obj a DERIA5String or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -45,7 +48,7 @@
     }
 
     /**
-     * return an IA5 String from a tagged object.
+     * Return an IA5 String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -71,7 +74,7 @@
     }
 
     /**
-     * basic constructor - with bytes.
+     * Basic constructor - with bytes.
      * @param string the byte encoding of the characters making up the string.
      */
     DERIA5String(
@@ -81,7 +84,7 @@
     }
 
     /**
-     * basic constructor - without validation.
+     * Basic constructor - without validation.
      * @param string the base string to use..
      */
     public DERIA5String(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
index 1eb9f45..fc2ca86 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
@@ -3,7 +3,9 @@
 import java.io.IOException;
 
 /**
- * A NULL object.
+ * An ASN.1 DER NULL object.
+ * <p>
+ * Preferably use the constant:  DERNull.INSTANCE.
  */
 public class DERNull
     extends ASN1Null
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
index ed287e5..1f82be1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
@@ -7,6 +7,13 @@
 
 /**
  * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ * ASN.1 NUMERIC-STRING object.
+ * <p>
+ * This is an ASCII string of characters {0,1,2,3,4,5,6,7,8,9} + space.
+ * <p>
+ * See X.680 section 37.2.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
  */
 public class DERNumericString
     extends ASN1Primitive
@@ -15,7 +22,7 @@
     private final byte[]  string;
 
     /**
-     * return a Numeric string from the passed in object
+     * Return a Numeric string from the passed in object
      *
      * @param obj a DERNumericString or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -45,7 +52,7 @@
     }
 
     /**
-     * return an Numeric String from a tagged object.
+     * Return an Numeric String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -71,7 +78,7 @@
     }
 
     /**
-     * basic constructor - with bytes.
+     * Basic constructor - with bytes.
      */
     DERNumericString(
         byte[]   string)
@@ -80,7 +87,7 @@
     }
 
     /**
-     * basic constructor -  without validation..
+     * Basic constructor -  without validation..
      */
     public DERNumericString(
         String   string)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
index 58be862..1c9c159 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -4,7 +4,7 @@
 import java.io.InputStream;
 
 /**
- * Parse for DER encoded OCTET STRINGS
+ * Parser for DER encoded OCTET STRINGS
  */
 public class DEROctetStringParser
     implements ASN1OctetStringParser
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
index 805ad30..a234eea 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
@@ -7,6 +7,29 @@
 
 /**
  * DER PrintableString object.
+ * <p>
+ * X.680 section 37.4 defines PrintableString character codes as ASCII subset of following characters:
+ * </p>
+ * <ul>
+ * <li>Latin capital letters: 'A' .. 'Z'</li>
+ * <li>Latin small letters: 'a' .. 'z'</li>
+ * <li>Digits: '0'..'9'</li>
+ * <li>Space</li>
+ * <li>Apostrophe: '\''</li>
+ * <li>Left parenthesis: '('</li>
+ * <li>Right parenthesis: ')'</li>
+ * <li>Plus sign: '+'</li>
+ * <li>Comma: ','</li>
+ * <li>Hyphen-minus: '-'</li>
+ * <li>Full stop: '.'</li>
+ * <li>Solidus: '/'</li>
+ * <li>Colon: ':'</li>
+ * <li>Equals sign: '='</li>
+ * <li>Question mark: '?'</li>
+ * </ul>
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
  */
 public class DERPrintableString
     extends ASN1Primitive
@@ -15,7 +38,7 @@
     private final byte[]  string;
 
     /**
-     * return a printable string from the passed in object.
+     * Return a printable string from the passed in object.
      *
      * @param obj a DERPrintableString or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -45,7 +68,7 @@
     }
 
     /**
-     * return a Printable String from a tagged object.
+     * Return a Printable String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -71,7 +94,7 @@
     }
 
     /**
-     * basic constructor - byte encoded string.
+     * Basic constructor - byte encoded string.
      */
     DERPrintableString(
         byte[]   string)
@@ -80,7 +103,7 @@
     }
 
     /**
-     * basic constructor - this does not validate the string
+     * Basic constructor - this does not validate the string
      */
     public DERPrintableString(
         String   string)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
index b631064..8efbbab 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
@@ -3,20 +3,26 @@
 import java.io.IOException;
 import java.util.Enumeration;
 
+/**
+ * Definite length SEQUENCE, encoding tells explicit number of bytes
+ * that the content of this sequence occupies.
+ * <p>
+ * For X.690 syntax rules, see {@link ASN1Sequence}.
+ */
 public class DERSequence
     extends ASN1Sequence
 {
     private int bodyLength = -1;
 
     /**
-     * create an empty sequence
+     * Create an empty sequence
      */
     public DERSequence()
     {
     }
 
     /**
-     * create a sequence containing one object
+     * Create a sequence containing one object
      * @param obj the object to go in the sequence.
      */
     public DERSequence(
@@ -26,7 +32,7 @@
     }
 
     /**
-     * create a sequence containing a vector of objects.
+     * Create a sequence containing a vector of objects.
      * @param v the vector of objects to make up the sequence.
      */
     public DERSequence(
@@ -36,7 +42,7 @@
     }
 
     /**
-     * create a sequence containing an array of objects.
+     * Create a sequence containing an array of objects.
      * @param array the array of objects to make up the sequence.
      */
     public DERSequence(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
index 1a72a0b..99d10d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
@@ -5,6 +5,13 @@
 
 /**
  * A DER encoded SET object
+ * <p>
+ * For X.690 syntax rules, see {@link ASN1Set}.
+ * </p><p>
+ * For short: Constructing this form does sort the supplied elements,
+ * and the sorting happens also before serialization (if necesssary).
+ * This is different from the way {@link BERSet},{@link DLSet} does things.
+ * </p>
  */
 public class DERSet
     extends ASN1Set
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
index c5c2913..289bfc8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
@@ -16,7 +16,7 @@
     private byte[] string;
 
     /**
-     * return a T61 string from the passed in object.
+     * Return a T61 string from the passed in object.
      *
      * @param obj a DERT61String or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -46,7 +46,7 @@
     }
 
     /**
-     * return an T61 String from a tagged object.
+     * Return an T61 String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -72,7 +72,7 @@
     }
 
     /**
-     * basic constructor - string encoded as a sequence of bytes.
+     * Basic constructor - string encoded as a sequence of bytes.
      *
      * @param string the byte encoding of the string to be wrapped.
      */
@@ -83,7 +83,7 @@
     }
 
     /**
-     * basic constructor - with string 8 bit assumed.
+     * Basic constructor - with string 8 bit assumed.
      *
      * @param string the string to be wrapped.
      */
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
index 6b70faa..487f86d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
@@ -6,7 +6,8 @@
 import org.bouncycastle.util.Arrays;
 
 /**
- * DER UniversalString object.
+ * DER UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
+ * have no way of representing this directly so we rely on byte arrays to carry these.
  */
 public class DERUniversalString
     extends ASN1Primitive
@@ -16,7 +17,7 @@
     private final byte[] string;
     
     /**
-     * return a Universal String from the passed in object.
+     * Return a Universal String from the passed in object.
      *
      * @param obj a DERUniversalString or an object that can be converted into one.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -46,7 +47,7 @@
     }
 
     /**
-     * return a Universal String from a tagged object.
+     * Return a Universal String from a tagged object.
      *
      * @param obj the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
@@ -72,7 +73,7 @@
     }
 
     /**
-     * basic constructor - byte encoded string.
+     * Basic constructor - byte encoded string.
      *
      * @param string the byte encoding of the string to be carried in the UniversalString object,
      */
@@ -94,7 +95,7 @@
         }
         catch (IOException e)
         {
-           throw new ASN1ParsingException("internal error encoding BitString");
+           throw new ASN1ParsingException("internal error encoding UniversalString");
         }
         
         byte[]    string = bOut.toByteArray();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java
new file mode 100644
index 0000000..8369492
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * A DER encoding version of an application specific object.
+ */
+public class DLApplicationSpecific
+    extends ASN1ApplicationSpecific
+{
+    DLApplicationSpecific(
+        boolean isConstructed,
+        int     tag,
+        byte[]  octets)
+    {
+        super(isConstructed, tag, octets);
+    }
+
+    /**
+     * Create an application specific object from the passed in data. This will assume
+     * the data does not represent a constructed object.
+     *
+     * @param tag the tag number for this object.
+     * @param octets the encoding of the object's body.
+     */
+    public DLApplicationSpecific(
+        int    tag,
+        byte[] octets)
+    {
+        this(false, tag, octets);
+    }
+
+    /**
+     * Create an application specific object with a tagging of explicit/constructed.
+     *
+     * @param tag the tag number for this object.
+     * @param object the object to be contained.
+     */
+    public DLApplicationSpecific(
+        int           tag,
+        ASN1Encodable object)
+        throws IOException
+    {
+        this(true, tag, object);
+    }
+
+    /**
+     * Create an application specific object with the tagging style given by the value of constructed.
+     *
+     * @param constructed true if the object is constructed.
+     * @param tag the tag number for this object.
+     * @param object the object to be contained.
+     */
+    public DLApplicationSpecific(
+        boolean      constructed,
+        int          tag,
+        ASN1Encodable object)
+        throws IOException
+    {
+        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
+    }
+
+    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
+        throws IOException
+    {
+        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DL);
+
+        if (explicit)
+        {
+            return data;
+        }
+        else
+        {
+            int lenBytes = getLengthOfHeader(data);
+            byte[] tmp = new byte[data.length - lenBytes];
+            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
+            return tmp;
+        }
+    }
+
+    /**
+     * Create an application specific object which is marked as constructed
+     *
+     * @param tagNo the tag number for this object.
+     * @param vec the objects making up the application specific object.
+     */
+    public DLApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+    {
+        super(true, tagNo, getEncodedVector(vec));
+    }
+
+    private static byte[] getEncodedVector(ASN1EncodableVector vec)
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        for (int i = 0; i != vec.size(); i++)
+        {
+            try
+            {
+                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DL));
+            }
+            catch (IOException e)
+            {
+                throw new ASN1ParsingException("malformed object: " + e, e);
+            }
+        }
+        return bOut.toByteArray();
+    }
+
+    /* (non-Javadoc)
+     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
+     */
+    void encode(ASN1OutputStream out) throws IOException
+    {
+        int classBits = BERTags.APPLICATION;
+        if (isConstructed)
+        {
+            classBits |= BERTags.CONSTRUCTED;
+        }
+
+        out.writeEncoded(classBits, tag, octets);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
index f6cb49b..a5d9cbb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
@@ -124,7 +124,7 @@
     }
 
     void encode(
-        ASN1OutputStream  out)
+        ASN1OutputStream out)
         throws IOException
     {
         byte[] string = data;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java
new file mode 100644
index 0000000..e3df250
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Class representing the Definite-Length-type External
+ */
+public class DLExternal
+    extends ASN1External
+{
+    /**
+     * Construct a Definite-Length EXTERNAL object, the input encoding vector must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or
+     */
+    public DLExternal(ASN1EncodableVector vector)
+    {
+        super(vector);
+    }
+
+    /**
+     * Creates a new instance of DERExternal
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param externalData The external data in its encoded form.
+     */
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    {
+        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+    }
+
+    /**
+     * Creates a new instance of Definite-Length External.
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param encoding The encoding to be used for the external data
+     * @param externalData The external data
+     */
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    {
+        super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
+    }
+
+    int encodedLength()
+        throws IOException
+    {
+        return this.getEncoded().length;
+    }
+
+    /* (non-Javadoc)
+     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
+     */
+    void encode(ASN1OutputStream out)
+        throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if (directReference != null)
+        {
+            baos.write(directReference.getEncoded(ASN1Encoding.DL));
+        }
+        if (indirectReference != null)
+        {
+            baos.write(indirectReference.getEncoded(ASN1Encoding.DL));
+        }
+        if (dataValueDescriptor != null)
+        {
+            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DL));
+        }
+        DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
+        baos.write(obj.getEncoded(ASN1Encoding.DL));
+        out.writeEncoded(BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java b/bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java
new file mode 100644
index 0000000..83d8bb2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.asn1;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+class DateUtil
+{
+    private static Long ZERO = longValueOf(0);
+
+    private static final Map localeCache = new HashMap();
+
+    static Locale EN_Locale = forEN();
+
+    private static Locale forEN()
+    {
+        if ("en".equalsIgnoreCase(Locale.getDefault().getLanguage()))
+        {
+            return Locale.getDefault();
+        }
+        
+        Locale[] locales = Locale.getAvailableLocales();
+        for (int i = 0; i != locales.length; i++)
+        {
+            if ("en".equalsIgnoreCase(locales[i].getLanguage()))
+            {
+                return locales[i];
+            }
+        }
+
+        return Locale.getDefault();
+    }
+
+    static Date epochAdjust(Date date)
+        throws ParseException
+    {
+        Locale locale = Locale.getDefault();
+        if (locale == null)
+        {
+            return date;
+        }
+
+        synchronized (localeCache)
+        {
+            Long adj = (Long)localeCache.get(locale);
+
+            if (adj == null)
+            {
+                SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+                long v = dateF.parse("19700101000000GMT+00:00").getTime();
+
+                if (v == 0)
+                {
+                    adj = ZERO;
+                }
+                else
+                {
+                    adj = longValueOf(v);
+                }
+
+                localeCache.put(locale, adj);
+            }
+
+            if (adj != ZERO)
+            {
+                return new Date(date.getTime() - adj.longValue());
+            }
+
+            return date;
+        }
+    }
+
+    private static Long longValueOf(long v)
+    {
+        return Long.valueOf(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
index 3785174..d7b51de 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -6,6 +6,9 @@
 
 import org.bouncycastle.util.io.Streams;
 
+/**
+ * Parse data stream of expected ASN.1 data expecting definite-length encoding..
+ */
 class DefiniteLengthInputStream
         extends LimitedInputStream
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
index d94b0bd..ab8470f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
@@ -2,6 +2,9 @@
 
 import java.io.InputStream;
 
+/**
+ * Internal use stream that allows reading of a limited number of bytes from a wrapped stream.
+ */
 abstract class LimitedInputStream
         extends InputStream
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java
index 1cc8d67..a8d4259 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java
@@ -7,5 +7,5 @@
  */
 public interface ANSSIObjectIdentifiers
 {
-    static final ASN1ObjectIdentifier FRP256v1 = new ASN1ObjectIdentifier("1.2.250.1.223.101.256.1");
+    ASN1ObjectIdentifier FRP256v1 = new ASN1ObjectIdentifier("1.2.250.1.223.101.256.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/package.html
new file mode 100644
index 0000000..f0ee6dc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for the French ANSSI EC curves.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
index e34779c..62df018 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -81,6 +81,77 @@
     public static final ASN1ObjectIdentifier sphincs256_with_SHA3_512        = sphincs256.branch("3");
 
     /**
+     * XMSS
+     */
+    public static final ASN1ObjectIdentifier xmss = bc_sig.branch("2");
+    public static final ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
+    public static final ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
+    public static final ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("3");
+    public static final ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("4");
+    public static final ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
+    public static final ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
+    public static final ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
+    public static final ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+
+    /**
+     * XMSS^MT
+     */
+    public static final ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
+    public static final ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
+    public static final ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("3");
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("4");
+    public static final ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
+    public static final ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
+
+    // old OIDs.
+    /**
+     * @deprecated use xmss_SHA256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHA256          = xmss_SHA256ph;
+    /**
+     * @deprecated use xmss_SHA512ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHA512 = xmss_SHA512ph;
+    /**
+     * @deprecated use xmss_SHAKE128ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHAKE128 = xmss_SHAKE128ph;
+    /**
+     * @deprecated use xmss_SHAKE256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHAKE256        = xmss_SHAKE256ph;
+
+    /**
+     * @deprecated use xmss_mt_SHA256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHA256          = xmss_mt_SHA256ph;
+    /**
+     * @deprecated use xmss_mt_SHA512ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHA512          = xmss_mt_SHA512ph;
+    /**
+     * @deprecated use xmss_mt_SHAKE128ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE128        = xmss_mt_SHAKE128;
+    /**
+     * @deprecated use xmss_mt_SHAKE256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE256        = xmss_mt_SHAKE256;
+
+    /**
+     * qTESLA
+     */
+    public static final ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
+    public static final ASN1ObjectIdentifier qTESLA_I = qTESLA.branch("1");
+    public static final ASN1ObjectIdentifier qTESLA_III_size = qTESLA.branch("2");
+    public static final ASN1ObjectIdentifier qTESLA_III_speed = qTESLA.branch("3");
+    public static final ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("4");
+    public static final ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("5");
+
+    /**
      * key_exchange(3) algorithms
      */
     public static final ASN1ObjectIdentifier bc_exch = bc.branch("3");
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStore.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStore.java
index ef2e773..0d84cb4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStore.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStore.java
@@ -39,6 +39,11 @@
 
     private ObjectStore(ASN1Sequence seq)
     {
+        if (seq.size() != 2)
+        {
+            throw new IllegalArgumentException("malformed sequence");
+        }
+        
         ASN1Encodable sData = seq.getObjectAt(0);
         if (sData instanceof EncryptedObjectStoreData)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStoreIntegrityCheck.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStoreIntegrityCheck.java
index 96c1b6f..29385cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStoreIntegrityCheck.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ObjectStoreIntegrityCheck.java
@@ -7,11 +7,14 @@
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
 
 /**
  * <pre>
  * ObjectStoreIntegrityCheck ::= CHOICE {
- *     PbeMacIntegrityCheck
+ *     PbkdMacIntegrityCheck
+ *     [0] EXPLICIT SignatureCheck
  * }
  * </pre>
  */
@@ -20,6 +23,7 @@
     implements ASN1Choice
 {
     public static final int PBKD_MAC_CHECK = 0;
+    public static final int SIG_CHECK = 1;
 
     private final int type;
     private final ASN1Object integrityCheck;
@@ -29,6 +33,11 @@
         this((ASN1Encodable)macIntegrityCheck);
     }
 
+    public ObjectStoreIntegrityCheck(SignatureCheck signatureCheck)
+    {
+        this(new DERTaggedObject(0, signatureCheck));
+    }
+
     private ObjectStoreIntegrityCheck(ASN1Encodable obj)
     {
         if (obj instanceof ASN1Sequence || obj instanceof  PbkdMacIntegrityCheck)
@@ -36,6 +45,11 @@
             this.type = PBKD_MAC_CHECK;
             this.integrityCheck = PbkdMacIntegrityCheck.getInstance(obj);
         }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            this.type = SIG_CHECK;
+            this.integrityCheck = SignatureCheck.getInstance(((ASN1TaggedObject)obj).getObject());
+        }
         else
         {
             throw new IllegalArgumentException("Unknown check object in integrity check.");
@@ -80,6 +94,10 @@
 
     public ASN1Primitive toASN1Primitive()
     {
+        if (integrityCheck instanceof SignatureCheck)
+        {
+            return new DERTaggedObject(0, integrityCheck);
+        }
         return integrityCheck.toASN1Primitive();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/SignatureCheck.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/SignatureCheck.java
new file mode 100644
index 0000000..1ac70b8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/SignatureCheck.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.bc;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ * SignatureCheck ::= SEQUENCE {
+ *        signatureAlgorithm   AlgorithmIdentifier,
+ *        certificates         [0] EXPLICIT Certificates OPTIONAL,
+ *        signatureValue       BIT STRING
+ * }
+ *
+ * Certificates ::= SEQUENCE OF Certificate
+ * </pre>
+ */
+public class SignatureCheck
+    extends ASN1Object
+{
+    private final AlgorithmIdentifier signatureAlgorithm;
+    private final ASN1Sequence certificates;
+    private final ASN1BitString signatureValue;
+
+    public SignatureCheck(AlgorithmIdentifier signatureAlgorithm, byte[] signature)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.certificates = null;
+        this.signatureValue = new DERBitString(Arrays.clone(signature));
+    }
+
+    public SignatureCheck(AlgorithmIdentifier signatureAlgorithm, Certificate[] certificates, byte[] signature)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.certificates = new DERSequence(certificates);
+        this.signatureValue = new DERBitString(Arrays.clone(signature));
+    }
+
+    private SignatureCheck(ASN1Sequence seq)
+    {
+        this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+        int index = 1;
+        if (seq.getObjectAt(1) instanceof ASN1TaggedObject)
+        {
+            this.certificates = ASN1Sequence.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(index++)).getObject());
+        }
+        else
+        {
+            this.certificates = null;
+        }
+        this.signatureValue = DERBitString.getInstance(seq.getObjectAt(index));
+    }
+
+    public static SignatureCheck getInstance(Object o)
+    {
+        if (o instanceof SignatureCheck)
+        {
+            return (SignatureCheck)o;
+        }
+        else if (o != null)
+        {
+            return new SignatureCheck(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public ASN1BitString getSignature()
+    {
+        return new DERBitString(Arrays.clone(signatureValue.getBytes()), signatureValue.getPadBits());
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return signatureAlgorithm;
+    }
+
+    public Certificate[] getCertificates()
+    {
+        if (certificates == null)
+        {
+            return null;
+        }
+        
+        Certificate[] certs = new Certificate[certificates.size()];
+
+        for (int i = 0; i != certs.length; i++)
+        {
+            certs[i] = Certificate.getInstance(certificates.getObjectAt(i));
+        }
+
+        return certs;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(signatureAlgorithm);
+        if (certificates != null)
+        {
+            v.add(new DERTaggedObject(0, certificates));
+        }
+        v.add(signatureValue);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java
index 315f6c4..0fe173e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java
@@ -32,4 +32,73 @@
 
     /* 0.4.0.127.0.7.1.1.4.1.6 */
     static final ASN1ObjectIdentifier ecdsa_plain_RIPEMD160 = ecdsa_plain_signatures.branch("6");
+
+	/** 0.4.0.127.0.7.1 */
+	static final ASN1ObjectIdentifier algorithm = bsi_de.branch("1");
+
+	static final ASN1ObjectIdentifier ecka_eg = id_ecc.branch("5.1");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963 OID: 0.4.0.127.0.7.1.1.5.1.1 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf = ecka_eg.branch("1");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function SHA-1
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.1 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_SHA1 = ecka_eg_X963kdf.branch("1");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function SHA224
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.2 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_SHA224 = ecka_eg_X963kdf.branch("2");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function SHA256
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.3 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_SHA256 = ecka_eg_X963kdf.branch("3");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function SHA384
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.4 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_SHA384 = ecka_eg_X963kdf.branch("4");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function SHA512
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.5 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_SHA512 = ecka_eg_X963kdf.branch("5");
+
+	/** ElGamal Elliptic Curve Key Agreement and Key Derivation according to X963
+	 * with hash function RIPEMD160
+	 * OID: 0.4.0.127.0.7.1.1.5.1.1.6 */
+	static final ASN1ObjectIdentifier ecka_eg_X963kdf_RIPEMD160 = ecka_eg_X963kdf.branch("6");
+
+	/**
+	 * 	Key Derivation Function for Session Keys
+	 */
+	static final ASN1ObjectIdentifier ecka_eg_SessionKDF = ecka_eg.branch("2");
+
+	static final ASN1ObjectIdentifier ecka_eg_SessionKDF_3DES    = ecka_eg_SessionKDF.branch("1");
+	static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES128  = ecka_eg_SessionKDF.branch("2");
+	static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES192  = ecka_eg_SessionKDF.branch("3");
+	static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES256  = ecka_eg_SessionKDF.branch("4");
+
+	/** AES encryption (CBC) and authentication (CMAC)
+	 * OID: 0.4.0.127.0.7.1.x */
+	//TODO: replace "1" with correct OID
+	//static final ASN1ObjectIdentifier aes_cbc_cmac = algorithm.branch("1");
+
+	/** AES encryption (CBC) and authentication (CMAC) with 128 bit
+	 * OID: 0.4.0.127.0.7.1.x.y1 */
+	//TODO:  replace "1" with correct OID
+	//static final ASN1ObjectIdentifier id_aes128_CBC_CMAC = aes_cbc_cmac.branch("1");
+
+
+	/** AES encryption (CBC) and authentication (CMAC) with 192 bit
+	 * OID: 0.4.0.127.0.7.1.x.y2 */
+	//TODO:  replace "1" with correct OID
+	//static final ASN1ObjectIdentifier id_aes192_CBC_CMAC = aes_cbc_cmac.branch("1");
+
+	/** AES encryption (CBC) and authentication (CMAC) with 256 bit
+	 * OID: 0.4.0.127.0.7.1.x.y3 */
+	//TODO:  replace "1" with correct OID
+	//static final ASN1ObjectIdentifier id_aes256_CBC_CMAC = aes_cbc_cmac.branch("1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PKIData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PKIData.java
index cd75ace..35180b1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PKIData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PKIData.java
@@ -31,10 +31,10 @@
         TaggedContentInfo[] cmsSequence,
         OtherMsg[] otherMsgSequence)
     {
-        this.controlSequence = controlSequence;
-        this.reqSequence = reqSequence;
-        this.cmsSequence = cmsSequence;
-        this.otherMsgSequence = otherMsgSequence;
+        this.controlSequence = copy(controlSequence);
+        this.reqSequence = copy(reqSequence);
+        this.cmsSequence = copy(cmsSequence);
+        this.otherMsgSequence = copy(otherMsgSequence);
     }
 
     private PKIData(ASN1Sequence seq)
@@ -99,21 +99,57 @@
 
     public TaggedAttribute[] getControlSequence()
     {
-        return controlSequence;
+        return copy(controlSequence);
+    }
+
+    private TaggedAttribute[] copy(TaggedAttribute[] taggedAtts)
+    {
+        TaggedAttribute[] tmp = new TaggedAttribute[taggedAtts.length];
+
+        System.arraycopy(taggedAtts, 0, tmp, 0, tmp.length);
+
+        return tmp;
     }
 
     public TaggedRequest[] getReqSequence()
     {
-        return reqSequence;
+        return copy(reqSequence);
+    }
+
+    private TaggedRequest[] copy(TaggedRequest[] taggedReqs)
+    {
+        TaggedRequest[] tmp = new TaggedRequest[taggedReqs.length];
+
+        System.arraycopy(taggedReqs, 0, tmp, 0, tmp.length);
+
+        return tmp;
     }
 
     public TaggedContentInfo[] getCmsSequence()
     {
-        return cmsSequence;
+        return copy(cmsSequence);
+    }
+
+    private TaggedContentInfo[] copy(TaggedContentInfo[] taggedConts)
+    {
+        TaggedContentInfo[] tmp = new TaggedContentInfo[taggedConts.length];
+
+        System.arraycopy(taggedConts, 0, tmp, 0, tmp.length);
+
+        return tmp;
     }
 
     public OtherMsg[] getOtherMsgSequence()
     {
-        return otherMsgSequence;
+        return copy(otherMsgSequence);
+    }
+
+    private OtherMsg[] copy(OtherMsg[] otherMsgs)
+    {
+        OtherMsg[] tmp = new OtherMsg[otherMsgs.length];
+
+        System.arraycopy(otherMsgs, 0, tmp, 0, tmp.length);
+
+        return tmp;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PendInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PendInfo.java
index 2d65efb..a0178ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PendInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/PendInfo.java
@@ -67,7 +67,7 @@
 
     public byte[] getPendToken()
     {
-        return pendToken;
+        return Arrays.clone(pendToken);
     }
 
     public ASN1GeneralizedTime getPendTime()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmc/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/package.html
new file mode 100644
index 0000000..62fe6ce
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmc/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for Certificate Management over CMS (CMC) - RFC 5272 and RFC 6402.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java
index 949ad73..e80d573 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CertifiedKeyPair.java
@@ -10,6 +10,16 @@
 import org.bouncycastle.asn1.crmf.EncryptedValue;
 import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
 
+/**
+ * <pre>
+ * CertifiedKeyPair ::= SEQUENCE {
+ *                                  certOrEncCert       CertOrEncCert,
+ *                                  privateKey      [0] EncryptedValue      OPTIONAL,
+ *                                  -- see [CRMF] for comment on encoding
+ *                                  publicationInfo [1] PKIPublicationInfo  OPTIONAL
+ *       }
+ * </pre>
+ */
 public class CertifiedKeyPair
     extends ASN1Object
 {
@@ -37,8 +47,8 @@
             }
             else
             {
-                privateKey = EncryptedValue.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1)));
-                publicationInfo = PKIPublicationInfo.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2)));
+                privateKey = EncryptedValue.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject());
+                publicationInfo = PKIPublicationInfo.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2)).getObject());
             }
         }
     }
@@ -67,8 +77,7 @@
     public CertifiedKeyPair(
         CertOrEncCert certOrEncCert,
         EncryptedValue privateKey,
-        PKIPublicationInfo  publicationInfo
-        )
+        PKIPublicationInfo  publicationInfo)
     {
         if (certOrEncCert == null)
         {
@@ -96,14 +105,8 @@
     }
 
     /**
-     * <pre>
-     * CertifiedKeyPair ::= SEQUENCE {
-     *                                  certOrEncCert       CertOrEncCert,
-     *                                  privateKey      [0] EncryptedValue      OPTIONAL,
-     *                                  -- see [CRMF] for comment on encoding
-     *                                  publicationInfo [1] PKIPublicationInfo  OPTIONAL
-     *       }
-     * </pre>
+     * Return the primitive representation of PKIPublicationInfo.
+     *
      * @return a basic ASN.1 object representation.
      */
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java
index de059c5..a7d439e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/PollReqContent.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.asn1.cmp;
 
+import java.math.BigInteger;
+
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -41,6 +43,36 @@
         this(new DERSequence(new DERSequence(certReqId)));
     }
 
+    /**
+     * Create a pollReqContent for a multiple certReqIds.
+     *
+     * @param certReqIds the certificate request IDs.
+     */
+    public PollReqContent(ASN1Integer[] certReqIds)
+    {
+        this(new DERSequence(intsToSequence(certReqIds)));
+    }
+
+    /**
+     * Create a pollReqContent for a single certReqId.
+     *
+     * @param certReqId the certificate request ID.
+     */
+    public PollReqContent(BigInteger certReqId)
+    {
+        this(new ASN1Integer(certReqId));
+    }
+
+    /**
+     * Create a pollReqContent for a multiple certReqIds.
+     *
+     * @param certReqIds the certificate request IDs.
+     */
+    public PollReqContent(BigInteger[] certReqIds)
+    {
+        this(intsToASN1(certReqIds));
+    }
+
     public ASN1Integer[][] getCertReqIds()
     {
         ASN1Integer[][] result = new ASN1Integer[content.size()][];
@@ -53,9 +85,22 @@
         return result;
     }
 
+    public BigInteger[] getCertReqIdValues()
+    {
+        BigInteger[] result = new BigInteger[content.size()];
+
+        for (int i = 0; i != result.length; i++)
+        {
+            result[i] = ASN1Integer.getInstance(
+                ASN1Sequence.getInstance(content.getObjectAt(i)).getObjectAt(0)).getValue();
+        }
+
+        return result;
+    }
+
     private static ASN1Integer[] sequenceToASN1IntegerArray(ASN1Sequence seq)
     {
-         ASN1Integer[] result = new ASN1Integer[seq.size()];
+        ASN1Integer[] result = new ASN1Integer[seq.size()];
 
         for (int i = 0; i != result.length; i++)
         {
@@ -65,6 +110,30 @@
         return result;
     }
 
+    private static DERSequence[] intsToSequence(ASN1Integer[] ids)
+    {
+        DERSequence[] result = new DERSequence[ids.length];
+
+        for (int i = 0; i != result.length; i++)
+        {
+            result[i] = new DERSequence(ids[i]);
+        }
+
+        return result;
+    }
+
+    private static ASN1Integer[] intsToASN1(BigInteger[] ids)
+    {
+        ASN1Integer[] result = new ASN1Integer[ids.length];
+
+        for (int i = 0; i != result.length; i++)
+        {
+            result[i] = new ASN1Integer(ids[i]);
+        }
+
+        return result;
+    }
+
     /**
      * <pre>
      * PollReqContent ::= SEQUENCE OF SEQUENCE {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
index e21c8a7..4ffe5ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DLSet;
 
 /**
@@ -63,6 +64,13 @@
         return null;
     }
 
+    public static Attributes getInstance(
+        ASN1TaggedObject obj,
+        boolean explicit)
+    {
+        return getInstance(ASN1Set.getInstance(obj, explicit));
+    }
+
     public Attribute[] getAttributes()
     {
         Attribute[] rv = new Attribute[attributes.size()];
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
index ce9aa4f..d8ed520 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
@@ -127,15 +127,6 @@
         return null;
     }
 
-    /**
-     * @deprecated use getEncapsulatedContentInfo()
-     */
-    public ContentInfoParser getEnapsulatedContentInfo()
-        throws IOException
-    {
-        return getEncapsulatedContentInfo();
-    }
-
     public ContentInfoParser getEncapsulatedContentInfo()
         throws IOException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
index 71c85fb..d452ef8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 
+
 /**
  * <a href="http://tools.ietf.org/html/rfc5652">RFC 5652</a> CMS attribute OID constants.
  * and <a href="http://tools.ietf.org/html/rfc6211">RFC 6211</a> Algorithm Identifier Protection Attribute.
@@ -20,16 +21,16 @@
 public interface CMSAttributes
 {
     /** PKCS#9: 1.2.840.113549.1.9.3 */
-    public static final ASN1ObjectIdentifier  contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
+    ASN1ObjectIdentifier  contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
     /** PKCS#9: 1.2.840.113549.1.9.4 */
-    public static final ASN1ObjectIdentifier  messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
+    ASN1ObjectIdentifier  messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
     /** PKCS#9: 1.2.840.113549.1.9.5 */
-    public static final ASN1ObjectIdentifier  signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
+    ASN1ObjectIdentifier  signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
     /** PKCS#9: 1.2.840.113549.1.9.6 */
-    public static final ASN1ObjectIdentifier  counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
+    ASN1ObjectIdentifier  counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
     /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
-    public static final ASN1ObjectIdentifier  contentHint = PKCSObjectIdentifiers.id_aa_contentHint;
+    ASN1ObjectIdentifier  contentHint = PKCSObjectIdentifiers.id_aa_contentHint;
 
-    public static final ASN1ObjectIdentifier  cmsAlgorithmProtect = PKCSObjectIdentifiers.id_aa_cmsAlgorithmProtect;
+    ASN1ObjectIdentifier  cmsAlgorithmProtect = PKCSObjectIdentifiers.id_aa_cmsAlgorithmProtect;
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
index 7363c81..576bee6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
@@ -53,10 +53,7 @@
         return null;
     }
 
-    /**
-     * @deprecated use getInstance()
-     */
-    public OtherKeyAttribute(
+    private OtherKeyAttribute(
         ASN1Sequence seq)
     {
         keyAttrId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
index b77b150..a8d5ed0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
@@ -31,11 +31,8 @@
         this.oriType = oriType;
         this.oriValue = oriValue;
     }
-
-    /**
-     * @deprecated use getInstance().
-     */
-    public OtherRecipientInfo(
+    
+    private OtherRecipientInfo(
         ASN1Sequence seq)
     {
         oriType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
index 5461147..d290b15 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
@@ -24,7 +24,7 @@
 
     public TimeStampTokenEvidence(TimeStampAndCRL[] timeStampAndCRLs)
     {
-        this.timeStampAndCRLs = timeStampAndCRLs;
+        this.timeStampAndCRLs = copy(timeStampAndCRLs);
     }
 
     public TimeStampTokenEvidence(TimeStampAndCRL timeStampAndCRL)
@@ -80,9 +80,18 @@
 
     public TimeStampAndCRL[] toTimeStampAndCRLArray()
     {
-        return timeStampAndCRLs;
+        return copy(timeStampAndCRLs);
     }
-    
+
+    private TimeStampAndCRL[] copy(TimeStampAndCRL[] tsAndCrls)
+    {
+        TimeStampAndCRL[] tmp = new TimeStampAndCRL[tsAndCrls.length];
+
+        System.arraycopy(tsAndCrls, 0, tmp, 0, tmp.length);
+
+        return tmp;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
index c298a7e..9db240c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
@@ -26,4 +26,11 @@
 
     /** 1.2.840.113549.1.9.16.1,21 */
     static final ASN1ObjectIdentifier id_ct_encKeyWithID = PKCSObjectIdentifiers.id_ct.branch("21");
+
+    /** 1.3.6.1.5.5.7.6 */
+    static final ASN1ObjectIdentifier id_alg    = id_pkix.branch("6");
+
+    static final ASN1ObjectIdentifier id_dh_sig_hmac_sha1 = id_alg.branch("3");
+
+    static final ASN1ObjectIdentifier id_alg_dh_pop = id_alg.branch("4");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java
index 73412e9..2d77aad 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CertTemplate.java
@@ -90,9 +90,19 @@
         return null;
     }
 
+    /**
+     * Return Version - -1 if not set.
+     *
+     * @return Version value.
+     */
     public int getVersion()
     {
-        return version.getValue().intValue();
+        if (version != null)
+        {
+            return version.getValue().intValue();
+        }
+
+        return -1;
     }
 
     public ASN1Integer getSerialNumber()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/DhSigStatic.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/DhSigStatic.java
new file mode 100644
index 0000000..a7295e5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/DhSigStatic.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.crmf;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * From RFC 2875 for Diffie-Hellman POP.
+ * <pre>
+ *     DhSigStatic ::= SEQUENCE {
+ *         IssuerAndSerial IssuerAndSerialNumber OPTIONAL,
+ *         hashValue       MessageDigest
+ *     }
+ * </pre>
+ */
+public class DhSigStatic
+    extends ASN1Object
+{
+    private final IssuerAndSerialNumber issuerAndSerial;
+    private final ASN1OctetString hashValue;
+
+    public DhSigStatic(byte[] hashValue)
+    {
+        this(null, hashValue);
+    }
+
+    public DhSigStatic(IssuerAndSerialNumber issuerAndSerial, byte[] hashValue)
+    {
+        this.issuerAndSerial = issuerAndSerial;
+        this.hashValue = new DEROctetString(Arrays.clone(hashValue));
+    }
+
+    public static DhSigStatic getInstance(Object o)
+    {
+        if (o instanceof DhSigStatic)
+        {
+            return (DhSigStatic)o;
+        }
+        else if (o != null)
+        {
+            return new DhSigStatic(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private DhSigStatic(ASN1Sequence seq)
+    {
+        if (seq.size() == 1)
+        {
+            issuerAndSerial = null;
+            hashValue = ASN1OctetString.getInstance(seq.getObjectAt(0));
+        }
+        else if (seq.size() == 2)
+        {
+            issuerAndSerial = IssuerAndSerialNumber.getInstance(seq.getObjectAt(0));
+            hashValue = ASN1OctetString.getInstance(seq.getObjectAt(1));
+        }
+        else
+        {
+            throw new IllegalArgumentException("sequence wrong length for DhSigStatic");
+        }
+    }
+
+    public IssuerAndSerialNumber getIssuerAndSerial()
+    {
+        return issuerAndSerial;
+    }
+
+    public byte[] getHashValue()
+    {
+        return Arrays.clone(hashValue.getOctets());
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (issuerAndSerial != null)
+        {
+            v.add(issuerAndSerial);
+        }
+
+        v.add(hashValue);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java
index 5ef38d4..4207217 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/EncKeyWithID.java
@@ -99,7 +99,7 @@
      *     } OPTIONAL
      * }
      * </pre>
-     * @return a DERSequence representing the value in this object.
+     * @return an ASN.1 primitive composition of this EncKeyWithID.
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java
index dba0422..a2f598a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/PKIPublicationInfo.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.asn1.crmf;
 
+import java.math.BigInteger;
+
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
@@ -7,16 +9,34 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <pre>
+ * PKIPublicationInfo ::= SEQUENCE {
+ *                  action     INTEGER {
+ *                                 dontPublish (0),
+ *                                 pleasePublish (1) },
+ *                  pubInfos  SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL }
+ * -- pubInfos MUST NOT be present if action is "dontPublish"
+ * -- (if action is "pleasePublish" and pubInfos is omitted,
+ * -- "dontCare" is assumed)
+ * </pre>
+ */
 public class PKIPublicationInfo
     extends ASN1Object
 {
+    public static final ASN1Integer dontPublish = new ASN1Integer(0);
+    public static final ASN1Integer pleasePublish = new ASN1Integer(1);
+
     private ASN1Integer action;
     private ASN1Sequence pubInfos;
 
     private PKIPublicationInfo(ASN1Sequence seq)
     {
         action = ASN1Integer.getInstance(seq.getObjectAt(0));
-        pubInfos = ASN1Sequence.getInstance(seq.getObjectAt(1));
+        if (seq.size() > 1)
+        {
+            pubInfos = ASN1Sequence.getInstance(seq.getObjectAt(1));
+        }
     }
 
     public static PKIPublicationInfo getInstance(Object o)
@@ -34,6 +54,45 @@
         return null;
     }
 
+    public PKIPublicationInfo(BigInteger action)
+    {
+        this(new ASN1Integer(action));
+    }
+
+    public PKIPublicationInfo(ASN1Integer action)
+    {
+        this.action = action;
+    }
+
+    /**
+     * Constructor with a single pubInfo, assumes pleasePublish as the action.
+     *
+     * @param pubInfo the pubInfo to be published (can be null if don't care is required).
+     */
+    public PKIPublicationInfo(SinglePubInfo pubInfo)
+    {
+        this(pubInfo != null ? new SinglePubInfo[] { pubInfo } : (SinglePubInfo[])null);
+    }
+
+    /**
+     * Constructor with multiple pubInfo, assumes pleasePublish as the action.
+     *
+     * @param pubInfos the pubInfos to be published (can be null if don't care is required).
+     */
+    public PKIPublicationInfo(SinglePubInfo[] pubInfos)
+    {
+        this.action = pleasePublish;
+
+        if (pubInfos != null)
+        {
+            this.pubInfos = new DERSequence(pubInfos);
+        }
+        else
+        {
+            this.pubInfos = null;
+        }
+    }
+
     public ASN1Integer getAction()
     {
         return action;
@@ -57,16 +116,8 @@
     }
 
     /**
-     * <pre>
-     * PKIPublicationInfo ::= SEQUENCE {
-     *                  action     INTEGER {
-     *                                 dontPublish (0),
-     *                                 pleasePublish (1) },
-     *                  pubInfos  SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL }
-     * -- pubInfos MUST NOT be present if action is "dontPublish"
-     * -- (if action is "pleasePublish" and pubInfos is omitted,
-     * -- "dontCare" is assumed)
-     * </pre>
+     * Return the primitive representation of PKIPublicationInfo.
+     *
      * @return a basic ASN.1 object representation.
      */
     public ASN1Primitive toASN1Primitive()
@@ -74,7 +125,11 @@
         ASN1EncodableVector v = new ASN1EncodableVector();
 
         v.add(action);
-        v.add(pubInfos);
+
+        if (pubInfos != null)
+        {
+            v.add(pubInfos);
+        }
 
         return new DERSequence(v);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java
index 8c9db8a..6603f06 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/POPOPrivKey.java
@@ -68,6 +68,12 @@
         return getInstance(ASN1TaggedObject.getInstance(obj, explicit));
     }
 
+    public POPOPrivKey(PKMACValue agreeMac)
+    {
+        this.tagNo = agreeMAC;
+        this.obj = agreeMac;
+    }
+
     public POPOPrivKey(SubsequentMessage msg)
     {
         this.tagNo = subsequentMessage;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java
index 0237b3a..06aa7b7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/SinglePubInfo.java
@@ -8,9 +8,25 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.GeneralName;
 
+/**
+ * <pre>
+ * SinglePubInfo ::= SEQUENCE {
+ *        pubMethod    INTEGER {
+ *           dontCare    (0),
+ *           x500        (1),
+ *           web         (2),
+ *           ldap        (3) },
+ *       pubLocation  GeneralName OPTIONAL }
+ * </pre>
+ */
 public class SinglePubInfo
     extends ASN1Object
 {
+    public static final ASN1Integer dontCare = new ASN1Integer(0);
+    public static final ASN1Integer x500 = new ASN1Integer(1);
+    public static final ASN1Integer web = new ASN1Integer(2);
+    public static final ASN1Integer ldap = new ASN1Integer(3);
+
     private ASN1Integer pubMethod;
     private GeneralName pubLocation;
 
@@ -39,21 +55,25 @@
         return null;
     }
 
+    public SinglePubInfo(ASN1Integer pubMethod, GeneralName pubLocation)
+    {
+        this.pubMethod = pubMethod;
+        this.pubLocation = pubLocation;
+    }
+
+    public ASN1Integer getPubMethod()
+    {
+        return pubMethod;
+    }
+
     public GeneralName getPubLocation()
     {
         return pubLocation;
     }
 
     /**
-     * <pre>
-     * SinglePubInfo ::= SEQUENCE {
-     *        pubMethod    INTEGER {
-     *           dontCare    (0),
-     *           x500        (1),
-     *           web         (2),
-     *           ldap        (3) },
-     *       pubLocation  GeneralName OPTIONAL }
-     * </pre>
+     * Return the primitive representation of SinglePubInfo.
+     *
      * @return a basic ASN.1 object representation.
      */
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptlib/CryptlibObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptlib/CryptlibObjectIdentifiers.java
new file mode 100644
index 0000000..88dd03f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptlib/CryptlibObjectIdentifiers.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.asn1.cryptlib;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public class CryptlibObjectIdentifiers
+{
+    public static final ASN1ObjectIdentifier cryptlib = new ASN1ObjectIdentifier("1.3.6.1.4.1.3029");
+
+    public static final ASN1ObjectIdentifier ecc = cryptlib.branch("1").branch("5");
+
+    public static final ASN1ObjectIdentifier curvey25519 = ecc.branch("1");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
index d9bf741..1299056 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
@@ -18,6 +18,9 @@
     /** Gost R3411 HMAC OID: 1.2.643.2.2.10 */
     static final ASN1ObjectIdentifier    gostR3411Hmac      = GOST_id.branch("10");
 
+    static final ASN1ObjectIdentifier    id_Gost28147_89_None_KeyWrap =  GOST_id.branch("13.0");
+    static final ASN1ObjectIdentifier    id_Gost28147_89_CryptoPro_KeyWrap =  GOST_id.branch("13.1");
+
     /** Gost R28147 OID: 1.2.643.2.2.21 */
     static final ASN1ObjectIdentifier    gostR28147_gcfb = GOST_id.branch("21");
 
@@ -101,4 +104,8 @@
     static final ASN1ObjectIdentifier    gost_ElSgDH3410_default    = GOST_id.branch("36.0");
     /** Gost R3410-ElSqDH3410-1 OID: 1.2.643.2.2.36.1 */
     static final ASN1ObjectIdentifier    gost_ElSgDH3410_1          = GOST_id.branch("36.1");
+
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_ESDH = GOST_id.branch("96");
+
+    static final ASN1ObjectIdentifier    gostR3410_2001DH = GOST_id.branch("98");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
index 19daeb0..f7520b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
@@ -5,12 +5,13 @@
 import java.util.Hashtable;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 
 /**
- * table of the available named parameters for GOST 3410-2001.
+ * table of the available named parameters for GOST 3410-2001 / 2012.
  */
 public class ECGOST3410NamedCurves
 {
@@ -27,15 +28,14 @@
             mod_p, // p
             new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
             new BigInteger("166"), // b
-            mod_q,
-            ECConstants.ONE);
+            mod_q, ECConstants.ONE);
 
         ECDomainParameters ecParams = new ECDomainParameters(
             curve,
             curve.createPoint(
                 new BigInteger("1"), // x
                 new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
-            mod_q);
+            mod_q, ECConstants.ONE);
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, ecParams);  
 
@@ -46,15 +46,14 @@
             mod_p, // p
             new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"),
             new BigInteger("166"),
-            mod_q,
-            ECConstants.ONE);
+            mod_q, ECConstants.ONE);
 
         ecParams = new ECDomainParameters(
             curve,
             curve.createPoint(
                 new BigInteger("1"), // x
                 new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
-            mod_q);
+            mod_q, ECConstants.ONE);
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, ecParams); 
 
@@ -65,15 +64,14 @@
             mod_p, // p
             new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a
             new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595"), // b
-            mod_q,
-            ECConstants.ONE);
+            mod_q, ECConstants.ONE);
 
         ecParams = new ECDomainParameters(
             curve,
             curve.createPoint(
                 new BigInteger("1"), // x
                 new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y
-            mod_q); // q
+            mod_q, ECConstants.ONE);
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, ecParams);  
 
@@ -84,15 +82,14 @@
             mod_p, // p
             new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"),
             new BigInteger("32858"),
-            mod_q,
-            ECConstants.ONE);
+            mod_q, ECConstants.ONE);
 
         ecParams = new ECDomainParameters(
             curve,
             curve.createPoint(
                 new BigInteger("0"),
                 new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")),
-            mod_q);
+            mod_q, ECConstants.ONE);
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, ecParams);  
 
@@ -102,29 +99,103 @@
             mod_p, // p
             new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a
             new BigInteger("32858"), // b
-            mod_q,
-            ECConstants.ONE);
+            mod_q, ECConstants.ONE);
 
         ecParams = new ECDomainParameters(
             curve,
             curve.createPoint(
                 new BigInteger("0"), // x
                 new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y
-            mod_q); // q
+            mod_q, ECConstants.ONE);
 
-        params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, ecParams); 
+        params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, ecParams);
+
+        //GOST34.10 2012
+        mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", 16); //p
+        mod_q = new BigInteger("400000000000000000000000000000000FD8CDDFC87B6635C115AF556C360C67", 16); //q
+        curve = new ECCurve.Fp(
+            mod_p, // p
+            new BigInteger("C2173F1513981673AF4892C23035A27CE25E2013BF95AA33B22C656F277E7335", 16), // a
+            new BigInteger("295F9BAE7428ED9CCC20E7C359A9D41A22FCCD9108E17BF7BA9337A6F8AE9513", 16), // b
+            mod_q, ECConstants.FOUR);
+
+        ecParams = new ECDomainParameters(
+            curve,
+            curve.createPoint(
+                new BigInteger("91E38443A5E82C0D880923425712B2BB658B9196932E02C78B2582FE742DAA28", 16), // x
+                new BigInteger("32879423AB1A0375895786C4BB46E9565FDE0B5344766740AF268ADB32322E5C", 16)), // y
+            mod_q, ECConstants.FOUR);
+        params.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA, ecParams);
+
+        mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p
+        mod_q = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",16); //q
+        curve = new ECCurve.Fp(
+            mod_p, // p
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",16), // a
+            new BigInteger("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",16), // b
+            mod_q, ECConstants.ONE);
+
+        ecParams = new ECDomainParameters(
+            curve,
+            curve.createPoint(
+                new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), // x
+                new BigInteger("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",16)), // y
+            mod_q, ECConstants.ONE);
+        params.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA, ecParams);
+
+        mod_p = new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F",16); //p
+        mod_q = new BigInteger("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD",16); //q
+        curve = new ECCurve.Fp(
+            mod_p, // p
+            new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C",16), // a
+            new BigInteger("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116",16), // b
+            mod_q, ECConstants.ONE);
+
+        ecParams = new ECDomainParameters(
+            curve,
+            curve.createPoint(
+                new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), // x
+                new BigInteger("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD",16)), // y
+            mod_q, ECConstants.ONE);
+        params.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB, ecParams);
+
+        mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p
+        mod_q = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED",16); //q
+        curve = new ECCurve.Fp(
+            mod_p, // p
+            new BigInteger("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3",16), // a
+            new BigInteger("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1",16), // b
+            mod_q, ECConstants.FOUR);
+
+        ecParams = new ECDomainParameters(
+            curve,
+            curve.createPoint(
+                new BigInteger("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148", 16), // x
+                new BigInteger("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F",16)), // y
+            mod_q, ECConstants.FOUR);
+        params.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC, ecParams);
+
+
 
         objIds.put("GostR3410-2001-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A);
         objIds.put("GostR3410-2001-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B);
         objIds.put("GostR3410-2001-CryptoPro-C", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C);
         objIds.put("GostR3410-2001-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA);
         objIds.put("GostR3410-2001-CryptoPro-XchB", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB);
+        objIds.put("Tc26-Gost-3410-12-256-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA);
+        objIds.put("Tc26-Gost-3410-12-512-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA);
+        objIds.put("Tc26-Gost-3410-12-512-paramSetB", RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB);
+        objIds.put("Tc26-Gost-3410-12-512-paramSetC", RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC);
 
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, "GostR3410-2001-CryptoPro-A");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, "GostR3410-2001-CryptoPro-B");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, "GostR3410-2001-CryptoPro-C");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, "GostR3410-2001-CryptoPro-XchA");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, "GostR3410-2001-CryptoPro-XchB");
+        names.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA, "Tc26-Gost-3410-12-256-paramSetA");
+        names.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA, "Tc26-Gost-3410-12-512-paramSetA");
+        names.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB, "Tc26-Gost-3410-12-512-paramSetB");
+        names.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC, "Tc26-Gost-3410-12-512-paramSetC");
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
index 368907e..909db0a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
@@ -9,6 +9,7 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.util.Arrays;
 
@@ -44,10 +45,15 @@
         return null;
     }
 
-    /**
-     * @deprecated use the getInstance() method. This constructor will vanish!
-     */
     public GOST28147Parameters(
+        byte[] iv,
+        ASN1ObjectIdentifier paramSet)
+    {
+        this.iv = new DEROctetString(iv);
+        this.paramSet = paramSet;
+    }
+
+    private GOST28147Parameters(
         ASN1Sequence  seq)
     {
         Enumeration     e = seq.getObjects();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789EncryptedKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789EncryptedKey.java
new file mode 100644
index 0000000..45e4bba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789EncryptedKey.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ * Gost28147-89-EncryptedKey ::=   SEQUENCE {
+ *       encryptedKey         Gost28147-89-Key,
+ *       maskKey              [0] IMPLICIT Gost28147-89-Key
+ *                                 OPTIONAL,
+ *       macKey               Gost28147-89-MAC
+ * }
+ * </pre>
+ */
+public class Gost2814789EncryptedKey
+    extends ASN1Object
+{
+    private final byte[] encryptedKey;
+    private final byte[] maskKey;
+    private final byte[] macKey;
+
+    private Gost2814789EncryptedKey(ASN1Sequence seq)
+    {
+        if (seq.size() == 2)
+        {
+            encryptedKey = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            macKey = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+            maskKey = null;
+        }
+        else if (seq.size() == 3)
+        {
+            encryptedKey = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            maskKey = Arrays.clone(ASN1OctetString.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1)), false).getOctets());
+            macKey = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets());
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence length: " + seq.size());
+        }
+    }
+
+    public static Gost2814789EncryptedKey getInstance(
+        Object obj)
+    {
+        if (obj instanceof Gost2814789EncryptedKey)
+        {
+            return (Gost2814789EncryptedKey)obj;
+        }
+
+        if (obj != null)
+        {
+            return new Gost2814789EncryptedKey(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public Gost2814789EncryptedKey(byte[] encryptedKey, byte[] macKey)
+    {
+        this(encryptedKey, null, macKey);
+    }
+
+    public Gost2814789EncryptedKey(byte[] encryptedKey, byte[] maskKey, byte[] macKey)
+    {
+        this.encryptedKey = Arrays.clone(encryptedKey);
+        this.maskKey = Arrays.clone(maskKey);
+        this.macKey = Arrays.clone(macKey);
+    }
+
+    public byte[] getEncryptedKey()
+    {
+        return Arrays.clone(encryptedKey);
+    }
+
+    public byte[] getMaskKey()
+    {
+        return Arrays.clone(maskKey);
+    }
+
+    public byte[] getMacKey()
+    {
+        return Arrays.clone(macKey);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new DEROctetString(encryptedKey));
+        if (maskKey != null)
+        {
+            v.add(new DERTaggedObject(false, 0, new DEROctetString(encryptedKey)));
+        }
+        v.add(new DEROctetString(macKey));
+        
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789KeyWrapParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789KeyWrapParameters.java
new file mode 100644
index 0000000..9d3376e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/Gost2814789KeyWrapParameters.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+public class Gost2814789KeyWrapParameters
+    extends ASN1Object
+{
+    private final ASN1ObjectIdentifier encryptionParamSet;
+    private final byte[] ukm;
+
+    private Gost2814789KeyWrapParameters(ASN1Sequence seq)
+    {
+        if (seq.size() == 2)
+        {
+            this.encryptionParamSet = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+            this.ukm = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+        }
+        else if (seq.size() == 1)
+        {
+            this.encryptionParamSet = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+            this.ukm = null;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence length: " + seq.size());
+        }
+    }
+
+    public static Gost2814789KeyWrapParameters getInstance(
+        Object obj)
+    {
+        if (obj instanceof Gost2814789KeyWrapParameters)
+        {
+            return (Gost2814789KeyWrapParameters)obj;
+        }
+
+        if (obj != null)
+        {
+            return new Gost2814789KeyWrapParameters(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public Gost2814789KeyWrapParameters(ASN1ObjectIdentifier encryptionParamSet)
+    {
+        this(encryptionParamSet, null);
+    }
+
+    public Gost2814789KeyWrapParameters(ASN1ObjectIdentifier encryptionParamSet, byte[] ukm)
+    {
+        this.encryptionParamSet = encryptionParamSet;
+        this.ukm = Arrays.clone(ukm);
+    }
+
+    public ASN1ObjectIdentifier getEncryptionParamSet()
+    {
+        return encryptionParamSet;
+    }
+
+    public byte[] getUkm()
+    {
+        return Arrays.clone(ukm);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(encryptionParamSet);
+        if (ukm != null)
+        {
+            v.add(new DEROctetString(ukm));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410KeyTransport.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410KeyTransport.java
new file mode 100644
index 0000000..c0d4c4c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410KeyTransport.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ *     GostR3410-KeyTransport ::= SEQUENCE {
+ *       sessionEncryptedKey   Gost28147-89-EncryptedKey,
+ *       transportParameters
+ *          [0] IMPLICIT GostR3410-TransportParameters OPTIONAL
+ *    }
+ * </pre>
+ */
+public class GostR3410KeyTransport
+    extends ASN1Object
+{
+    private final Gost2814789EncryptedKey sessionEncryptedKey;
+    private final GostR3410TransportParameters transportParameters;
+
+    private GostR3410KeyTransport(ASN1Sequence seq)
+    {
+       this.sessionEncryptedKey = Gost2814789EncryptedKey.getInstance(seq.getObjectAt(0));
+       this.transportParameters = GostR3410TransportParameters.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(1)), false);
+    }
+
+    public GostR3410KeyTransport(Gost2814789EncryptedKey sessionEncryptedKey, GostR3410TransportParameters transportParameters)
+    {
+        this.sessionEncryptedKey = sessionEncryptedKey;
+        this.transportParameters = transportParameters;
+    }
+
+    public static GostR3410KeyTransport getInstance(
+        Object obj)
+    {
+        if (obj instanceof GostR3410KeyTransport)
+        {
+            return (GostR3410KeyTransport)obj;
+        }
+
+        if (obj != null)
+        {
+            return new GostR3410KeyTransport(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public Gost2814789EncryptedKey getSessionEncryptedKey()
+    {
+        return sessionEncryptedKey;
+    }
+
+    public GostR3410TransportParameters getTransportParameters()
+    {
+        return transportParameters;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(sessionEncryptedKey);
+        if (transportParameters != null)
+        {
+            v.add(new DERTaggedObject(false, 0, transportParameters));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410TransportParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410TransportParameters.java
new file mode 100644
index 0000000..20d5fe5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GostR3410TransportParameters.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.asn1.cryptopro;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.util.Arrays;
+
+/**
+ *  <pre>
+ * GostR3410-TransportParameters ::= SEQUENCE {
+ *        encryptionParamSet   OBJECT IDENTIFIER,
+ *        ephemeralPublicKey   [0] IMPLICIT SubjectPublicKeyInfo OPTIONAL,
+ *        ukm                  OCTET STRING
+ * }
+ *  </pre>
+ */
+public class GostR3410TransportParameters
+    extends ASN1Object
+{
+    private final ASN1ObjectIdentifier encryptionParamSet;
+    private final SubjectPublicKeyInfo ephemeralPublicKey;
+    private final byte[] ukm;
+
+    public GostR3410TransportParameters(ASN1ObjectIdentifier encryptionParamSet, SubjectPublicKeyInfo ephemeralPublicKey, byte[] ukm)
+    {
+        this.encryptionParamSet = encryptionParamSet;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+        this.ukm = Arrays.clone(ukm);
+    }
+
+    private GostR3410TransportParameters(ASN1Sequence seq)
+    {
+        if (seq.size() == 2)
+        {
+            this.encryptionParamSet = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+            this.ukm = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
+            this.ephemeralPublicKey = null;
+        }
+        else if (seq.size() == 3)
+        {
+            this.encryptionParamSet = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+            this.ephemeralPublicKey = SubjectPublicKeyInfo.getInstance(
+                ASN1TaggedObject.getInstance(seq.getObjectAt(1)), false);
+            this.ukm = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence length: " + seq.size());
+        }
+    }
+
+    public static GostR3410TransportParameters getInstance(
+        Object obj)
+    {
+        if (obj instanceof GostR3410TransportParameters)
+        {
+            return (GostR3410TransportParameters)obj;
+        }
+
+        if (obj != null)
+        {
+            return new GostR3410TransportParameters(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static GostR3410TransportParameters getInstance(
+        ASN1TaggedObject    obj,
+        boolean             explicit)
+    {
+        return new GostR3410TransportParameters(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public ASN1ObjectIdentifier getEncryptionParamSet()
+    {
+        return encryptionParamSet;
+    }
+
+    public SubjectPublicKeyInfo getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+
+    public byte[] getUkm()
+    {
+        return Arrays.clone(ukm);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(encryptionParamSet);
+
+        if (ephemeralPublicKey != null)
+        {
+            v.add(new DERTaggedObject(false, 0, ephemeralPublicKey));
+        }
+
+        v.add(new DEROctetString(ukm));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java
index a1e32aa..4c8fec4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/PathProcInput.java
@@ -1,6 +1,3 @@
-/***************************************************************/
-/******    DO NOT EDIT THIS CLASS bc-java SOURCE FILE     ******/
-/***************************************************************/
 package org.bouncycastle.asn1.dvcs;
 
 import java.util.Arrays;
@@ -29,7 +26,6 @@
 public class PathProcInput
     extends ASN1Object
 {
-
     private PolicyInformation[] acceptablePolicySet;
     private boolean inhibitPolicyMapping = false;
     private boolean explicitPolicyReqd = false;
@@ -37,12 +33,12 @@
 
     public PathProcInput(PolicyInformation[] acceptablePolicySet)
     {
-        this.acceptablePolicySet = acceptablePolicySet;
+        this.acceptablePolicySet = copy(acceptablePolicySet);
     }
 
     public PathProcInput(PolicyInformation[] acceptablePolicySet, boolean inhibitPolicyMapping, boolean explicitPolicyReqd, boolean inhibitAnyPolicy)
     {
-        this.acceptablePolicySet = acceptablePolicySet;
+        this.acceptablePolicySet = copy(acceptablePolicySet);
         this.inhibitPolicyMapping = inhibitPolicyMapping;
         this.explicitPolicyReqd = explicitPolicyReqd;
         this.inhibitAnyPolicy = inhibitAnyPolicy;
@@ -153,7 +149,7 @@
 
     public PolicyInformation[] getAcceptablePolicySet()
     {
-        return acceptablePolicySet;
+        return copy(acceptablePolicySet);
     }
 
     public boolean isInhibitPolicyMapping()
@@ -185,4 +181,13 @@
     {
         this.inhibitAnyPolicy = inhibitAnyPolicy;
     }
+
+    private PolicyInformation[] copy(PolicyInformation[] policySet)
+    {
+        PolicyInformation[] rv = new PolicyInformation[policySet.length];
+
+        System.arraycopy(policySet, 0, rv, 0, rv.length);
+
+        return rv;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java
index 8cd3ae6..9b5e86d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CVCertificate.java
@@ -49,10 +49,10 @@
             ASN1Primitive tmpObj;
             while ((tmpObj = content.readObject()) != null)
             {
-                DERApplicationSpecific aSpe;
-                if (tmpObj instanceof DERApplicationSpecific)
+                ASN1ApplicationSpecific aSpe;
+                if (tmpObj instanceof ASN1ApplicationSpecific)
                 {
-                    aSpe = (DERApplicationSpecific)tmpObj;
+                    aSpe = (ASN1ApplicationSpecific)tmpObj;
                     switch (aSpe.getApplicationTag())
                     {
                     case EACTags.CERTIFICATE_CONTENT_TEMPLATE:
@@ -103,9 +103,9 @@
         ASN1Primitive obj;
         while ((obj = aIS.readObject()) != null)
         {
-            if (obj instanceof DERApplicationSpecific)
+            if (obj instanceof ASN1ApplicationSpecific)
             {
-                setPrivateData((DERApplicationSpecific)obj);
+                setPrivateData((ASN1ApplicationSpecific)obj);
             }
             else
             {
@@ -115,10 +115,10 @@
     }
 
     /**
-     * Create an iso7816Certificate structure from a DERApplicationSpecific.
+     * Create an iso7816Certificate structure from a ASN1ApplicationSpecific.
      *
-     * @param appSpe the DERApplicationSpecific object.
-     * @return the Iso7816CertificateStructure represented by the DERApplicationSpecific object.
+     * @param appSpe the ASN1ApplicationSpecific object.
+     * @return the Iso7816CertificateStructure represented by the ASN1ApplicationSpecific object.
      * @throws IOException if there is a problem parsing the data.
      */
     private CVCertificate(ASN1ApplicationSpecific appSpe)
@@ -160,7 +160,7 @@
         {
             try
             {
-                return new CVCertificate(DERApplicationSpecific.getInstance(obj));
+                return new CVCertificate(ASN1ApplicationSpecific.getInstance(obj));
             }
             catch (IOException e)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java
index 01a21a1..7bcc241 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateBody.java
@@ -17,24 +17,24 @@
  * <pre>
  *  CertificateBody ::= SEQUENCE {
  *      // version of the certificate format. Must be 0 (version 1)
- *      CertificateProfileIdentifer         DERApplicationSpecific,
+ *      CertificateProfileIdentifer         ASN1ApplicationSpecific,
  *      //uniquely identifies the issuinng CA's signature key pair
  *      // contains the iso3166-1 alpha2 encoded country code, the
  *      // name of issuer and the sequence number of the key pair.
- *      CertificationAuthorityReference        DERApplicationSpecific,
+ *      CertificationAuthorityReference        ASN1ApplicationSpecific,
  *      // stores the encoded public key
  *      PublicKey                            Iso7816PublicKey,
  *      //associates the public key contained in the certificate with a unique name
  *      // contains the iso3166-1 alpha2 encoded country code, the
  *      // name of the holder and the sequence number of the key pair.
- *      certificateHolderReference            DERApplicationSpecific,
+ *      certificateHolderReference            ASN1ApplicationSpecific,
  *      // Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write
  *      // access rights to data groups storing sensitive data
  *      certificateHolderAuthorization        Iso7816CertificateHolderAuthorization,
  *      // the date of the certificate generation
- *      CertificateEffectiveDate            DERApplicationSpecific,
+ *      CertificateEffectiveDate            ASN1ApplicationSpecific,
  *      // the date after wich the certificate expires
- *      certificateExpirationDate            DERApplicationSpecific
+ *      certificateExpirationDate            ASN1ApplicationSpecific
  *  }
  * </pre>
  */
@@ -42,13 +42,13 @@
     extends ASN1Object
 {
     ASN1InputStream seq;
-    private DERApplicationSpecific certificateProfileIdentifier;// version of the certificate format. Must be 0 (version 1)
-    private DERApplicationSpecific certificationAuthorityReference;//uniquely identifies the issuinng CA's signature key pair
+    private ASN1ApplicationSpecific certificateProfileIdentifier;// version of the certificate format. Must be 0 (version 1)
+    private ASN1ApplicationSpecific certificationAuthorityReference;//uniquely identifies the issuinng CA's signature key pair
     private PublicKeyDataObject publicKey;// stores the encoded public key
-    private DERApplicationSpecific certificateHolderReference;//associates the public key contained in the certificate with a unique name
+    private ASN1ApplicationSpecific certificateHolderReference;//associates the public key contained in the certificate with a unique name
     private CertificateHolderAuthorization certificateHolderAuthorization;// Encodes the role of the holder (i.e. CVCA, DV, IS) and assigns read/write access rights to data groups storing sensitive data
-    private DERApplicationSpecific certificateEffectiveDate;// the date of the certificate generation
-    private DERApplicationSpecific certificateExpirationDate;// the date after wich the certificate expires
+    private ASN1ApplicationSpecific certificateEffectiveDate;// the date of the certificate generation
+    private ASN1ApplicationSpecific certificateExpirationDate;// the date after wich the certificate expires
     private int certificateType = 0;// bit field of initialized data. This will tell us if the data are valid.
     private static final int CPI = 0x01;//certificate Profile Identifier
     private static final int CAR = 0x02;//certification Authority Reference
@@ -77,15 +77,15 @@
         ASN1Primitive obj;
         while ((obj = aIS.readObject()) != null)
         {
-            DERApplicationSpecific aSpe;
+            ASN1ApplicationSpecific aSpe;
 
-            if (obj instanceof DERApplicationSpecific)
+            if (obj instanceof ASN1ApplicationSpecific)
             {
-                aSpe = (DERApplicationSpecific)obj;
+                aSpe = (ASN1ApplicationSpecific)obj;
             }
             else
             {
-                throw new IOException("Not a valid iso7816 content : not a DERApplicationSpecific Object :" + EACTags.encodeTag(appSpe) + obj.getClass());
+                throw new IOException("Not a valid iso7816 content : not a ASN1ApplicationSpecific Object :" + EACTags.encodeTag(appSpe) + obj.getClass());
             }
             switch (aSpe.getApplicationTag())
             {
@@ -112,7 +112,7 @@
                 break;
             default:
                 certificateType = 0;
-                throw new IOException("Not a valid iso7816 DERApplicationSpecific tag " + aSpe.getApplicationTag());
+                throw new IOException("Not a valid iso7816 ASN1ApplicationSpecific tag " + aSpe.getApplicationTag());
             }
         }
         aIS.close();
@@ -131,7 +131,7 @@
      * @param certificateExpirationDate
      */
     public CertificateBody(
-        DERApplicationSpecific certificateProfileIdentifier,
+        ASN1ApplicationSpecific certificateProfileIdentifier,
         CertificationAuthorityReference certificationAuthorityReference,
         PublicKeyDataObject publicKey,
         CertificateHolderReference certificateHolderReference,
@@ -163,7 +163,7 @@
     /**
      * builds an Iso7816CertificateBody with an ASN1InputStream.
      *
-     * @param obj DERApplicationSpecific containing the whole body.
+     * @param obj ASN1ApplicationSpecific containing the whole body.
      * @throws IOException if the body is not valid.
      */
     private CertificateBody(ASN1ApplicationSpecific obj)
@@ -176,7 +176,7 @@
      * create a profile type Iso7816CertificateBody.
      *
      * @return return the "profile" type certificate body.
-     * @throws IOException if the DERApplicationSpecific cannot be created.
+     * @throws IOException if the ASN1ApplicationSpecific cannot be created.
      */
     private ASN1Primitive profileToASN1Object()
         throws IOException
@@ -193,7 +193,7 @@
         return new DERApplicationSpecific(EACTags.CERTIFICATE_CONTENT_TEMPLATE, v);
     }
 
-    private void setCertificateProfileIdentifier(DERApplicationSpecific certificateProfileIdentifier)
+    private void setCertificateProfileIdentifier(ASN1ApplicationSpecific certificateProfileIdentifier)
         throws IllegalArgumentException
     {
         if (certificateProfileIdentifier.getApplicationTag() == EACTags.INTERCHANGE_PROFILE)
@@ -207,7 +207,7 @@
         }
     }
 
-    private void setCertificateHolderReference(DERApplicationSpecific certificateHolderReference)
+    private void setCertificateHolderReference(ASN1ApplicationSpecific certificateHolderReference)
         throws IllegalArgumentException
     {
         if (certificateHolderReference.getApplicationTag() == EACTags.CARDHOLDER_NAME)
@@ -225,11 +225,11 @@
      * set the CertificationAuthorityReference.
      *
      * @param certificationAuthorityReference
-     *         the DERApplicationSpecific containing the CertificationAuthorityReference.
-     * @throws IllegalArgumentException if the DERApplicationSpecific is not valid.
+     *         the ASN1ApplicationSpecific containing the CertificationAuthorityReference.
+     * @throws IllegalArgumentException if the ASN1ApplicationSpecific is not valid.
      */
     private void setCertificationAuthorityReference(
-        DERApplicationSpecific certificationAuthorityReference)
+        ASN1ApplicationSpecific certificationAuthorityReference)
         throws IllegalArgumentException
     {
         if (certificationAuthorityReference.getApplicationTag() == EACTags.ISSUER_IDENTIFICATION_NUMBER)
@@ -246,7 +246,7 @@
     /**
      * set the public Key
      *
-     * @param publicKey : the DERApplicationSpecific containing the public key
+     * @param publicKey : the ASN1ApplicationSpecific containing the public key
      * @throws java.io.IOException
      */
     private void setPublicKey(PublicKeyDataObject publicKey)
@@ -259,7 +259,7 @@
      * create a request type Iso7816CertificateBody.
      *
      * @return return the "request" type certificate body.
-     * @throws IOException if the DERApplicationSpecific cannot be created.
+     * @throws IOException if the ASN1ApplicationSpecific cannot be created.
      */
     private ASN1Primitive requestToASN1Object()
         throws IOException
@@ -345,10 +345,10 @@
     /**
      * set the date of the certificate generation
      *
-     * @param ced DERApplicationSpecific containing the date of the certificate generation
+     * @param ced ASN1ApplicationSpecific containing the date of the certificate generation
      * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EFFECTIVE_DATE
      */
-    private void setCertificateEffectiveDate(DERApplicationSpecific ced)
+    private void setCertificateEffectiveDate(ASN1ApplicationSpecific ced)
         throws IllegalArgumentException
     {
         if (ced.getApplicationTag() == EACTags.APPLICATION_EFFECTIVE_DATE)
@@ -379,10 +379,10 @@
     /**
      * set the date after wich the certificate expires
      *
-     * @param ced DERApplicationSpecific containing the date after wich the certificate expires
+     * @param ced ASN1ApplicationSpecific containing the date after wich the certificate expires
      * @throws IllegalArgumentException if the tag is not Iso7816Tags.APPLICATION_EXPIRATION_DATE
      */
-    private void setCertificateExpirationDate(DERApplicationSpecific ced)
+    private void setCertificateExpirationDate(ASN1ApplicationSpecific ced)
         throws IllegalArgumentException
     {
         if (ced.getApplicationTag() == EACTags.APPLICATION_EXPIRATION_DATE)
@@ -442,7 +442,7 @@
      *
      * @return the CertificateProfileIdentifier
      */
-    public DERApplicationSpecific getCertificateProfileIdentifier()
+    public ASN1ApplicationSpecific getCertificateProfileIdentifier()
     {
         return certificateProfileIdentifier;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java
index e4b7d57..ad16921 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/CertificateHolderAuthorization.java
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.util.Hashtable;
 
+import org.bouncycastle.asn1.ASN1ApplicationSpecific;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Object;
@@ -27,7 +28,7 @@
     extends ASN1Object
 {
     ASN1ObjectIdentifier oid;
-    DERApplicationSpecific accessRights;
+    ASN1ApplicationSpecific accessRights;
     public static final ASN1ObjectIdentifier id_role_EAC = EACObjectIdentifiers.bsi_de.branch("3.1.2.1");
     public static final int CVCA = 0xC0;
     public static final int DV_DOMESTIC = 0x80;
@@ -89,9 +90,9 @@
             throw new IllegalArgumentException("no Oid in CerticateHolderAuthorization");
         }
         obj = cha.readObject();
-        if (obj instanceof DERApplicationSpecific)
+        if (obj instanceof ASN1ApplicationSpecific)
         {
-            this.accessRights = (DERApplicationSpecific)obj;
+            this.accessRights = (ASN1ApplicationSpecific)obj;
         }
         else
         {
@@ -116,12 +117,12 @@
     }
 
     /**
-     * create an Iso7816CertificateHolderAuthorization according to the {@link DERApplicationSpecific}
+     * create an Iso7816CertificateHolderAuthorization according to the {@link ASN1ApplicationSpecific}
      *
      * @param aSpe the DERApplicationSpecific containing the data
      * @throws IOException
      */
-    public CertificateHolderAuthorization(DERApplicationSpecific aSpe)
+    public CertificateHolderAuthorization(ASN1ApplicationSpecific aSpe)
         throws IOException
     {
         if (aSpe.getApplicationTag() == EACTags.CERTIFICATE_HOLDER_AUTHORIZATION_TEMPLATE)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACTags.java
index c536193..4643923 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACTags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACTags.java
@@ -166,6 +166,7 @@
                 retValue <<= 8;
 
                 currentByte = tag & 0x7F;
+                retValue |= currentByte;
                 tag >>= 7;
             }
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/Flags.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/Flags.java
index 89d4e9f..e8dfbbe 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/Flags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/Flags.java
@@ -62,7 +62,7 @@
         return joiner.toString();
     }
 
-    private class StringJoiner
+    private static class StringJoiner
     {
 
         String mSeparator;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/edec/EdECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/edec/EdECObjectIdentifiers.java
new file mode 100644
index 0000000..0ff7f38
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/edec/EdECObjectIdentifiers.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.asn1.edec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Edwards Elliptic Curve Object Identifiers (RFC 8410)
+ */
+public interface EdECObjectIdentifiers
+{
+    ASN1ObjectIdentifier id_edwards_curve_algs      = new ASN1ObjectIdentifier("1.3.101");
+
+    ASN1ObjectIdentifier id_X25519 = id_edwards_curve_algs.branch("110").intern();
+    ASN1ObjectIdentifier id_X448 = id_edwards_curve_algs.branch("111").intern();
+    ASN1ObjectIdentifier id_Ed25519 = id_edwards_curve_algs.branch("112").intern();
+    ASN1ObjectIdentifier id_Ed448 = id_edwards_curve_algs.branch("113").intern();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java b/bcprov/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
index 579fe80..f8d484b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
@@ -27,9 +27,9 @@
 public class SignerLocation
     extends ASN1Object
 {
-    private DERUTF8String   countryName;
-    private DERUTF8String   localityName;
-    private ASN1Sequence    postalAddress;
+    private DirectoryString   countryName;
+    private DirectoryString   localityName;
+    private ASN1Sequence      postalAddress;
     
     private SignerLocation(
         ASN1Sequence seq)
@@ -43,12 +43,10 @@
             switch (o.getTagNo())
             {
             case 0:
-                DirectoryString countryNameDirectoryString = DirectoryString.getInstance(o, true);
-                this.countryName = new DERUTF8String(countryNameDirectoryString.getString());
+                this.countryName = DirectoryString.getInstance(o, true);;
                 break;
             case 1:
-                DirectoryString localityNameDirectoryString = DirectoryString.getInstance(o, true);
-                this.localityName = new DERUTF8String(localityNameDirectoryString.getString());
+                this.localityName = DirectoryString.getInstance(o, true);
                 break;
             case 2:
                 if (o.isExplicit())
@@ -70,30 +68,35 @@
         }
     }
 
-    public SignerLocation(
-        DERUTF8String   countryName,
-        DERUTF8String   localityName,
-        ASN1Sequence    postalAddress)
+    private SignerLocation(
+        DirectoryString   countryName,
+        DirectoryString   localityName,
+        ASN1Sequence      postalAddress)
     {
         if (postalAddress != null && postalAddress.size() > 6)
         {
             throw new IllegalArgumentException("postal address must contain less than 6 strings");
         }
 
-        if (countryName != null)
-        {
-            this.countryName = DERUTF8String.getInstance(countryName.toASN1Primitive());
-        }
+        this.countryName = countryName;
+        this.localityName = localityName;
+        this.postalAddress = postalAddress;
+    }
 
-        if (localityName != null)
-        {
-            this.localityName = DERUTF8String.getInstance(localityName.toASN1Primitive());
-        }
+    public SignerLocation(
+        DirectoryString   countryName,
+        DirectoryString   localityName,
+        DirectoryString[] postalAddress)
+    {
+        this(countryName, localityName, new DERSequence(postalAddress));
+    }
 
-        if (postalAddress != null)
-        {
-            this.postalAddress = ASN1Sequence.getInstance(postalAddress.toASN1Primitive());
-        }
+    public SignerLocation(
+        DERUTF8String   countryName,
+        DERUTF8String   localityName,
+        ASN1Sequence    postalAddress)
+    {
+        this(DirectoryString.getInstance(countryName), DirectoryString.getInstance(localityName), postalAddress);
     }
 
     public static SignerLocation getInstance(
@@ -107,16 +110,71 @@
         return new SignerLocation(ASN1Sequence.getInstance(obj));
     }
 
-    public DERUTF8String getCountryName()
+    /**
+     * Return the countryName DirectoryString
+     *
+     * @return the countryName, null if absent.
+     */
+    public DirectoryString getCountry()
     {
         return countryName;
     }
 
-    public DERUTF8String getLocalityName()
+    /**
+     * Return the localityName DirectoryString
+     *
+     * @return the localityName, null if absent.
+     */
+    public DirectoryString getLocality()
     {
         return localityName;
     }
 
+    /**
+     * Return the postalAddress DirectoryStrings
+     *
+     * @return the postalAddress, null if absent.
+     */
+    public DirectoryString[] getPostal()
+    {
+        if (postalAddress == null)
+        {
+            return null;
+        }
+
+        DirectoryString[] dirStrings = new DirectoryString[postalAddress.size()];
+        for (int i = 0; i != dirStrings.length; i++)
+        {
+            dirStrings[i] = DirectoryString.getInstance(postalAddress.getObjectAt(i));
+        }
+
+        return dirStrings;
+    }
+
+    /**
+     * @deprecated use getCountry()
+     */
+    public DERUTF8String getCountryName()
+    {
+        if (countryName == null)
+        {
+            return null;
+        }
+        return new DERUTF8String(getCountry().getString());
+    }
+
+    /**
+     * @deprecated use getLocality()
+     */
+    public DERUTF8String getLocalityName()
+    {
+        if (localityName == null)
+        {
+            return null;
+        }
+        return new DERUTF8String(getLocality().getString());
+    }
+
     public ASN1Sequence getPostalAddress()
     {
         return postalAddress;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java b/bcprov/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
index 95c19ae..9d190a8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
@@ -3,7 +3,6 @@
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/est/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/est/package.html
new file mode 100644
index 0000000..19b82dc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/est/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for Enrollment over Secure Transport (EST) - RFC 7030.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gm/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/gm/package.html
new file mode 100644
index 0000000..54a633d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/gm/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for Chinese Standard (GM) standard curves and algorithms.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
index 9eb3885..0f82da0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
@@ -3,61 +3,109 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
 /**
- *  GNU project OID collection<p>
- *  { iso(1) identifier-organization(3) dod(6) internet(1) private(4) } == IETF defined things
+ * GNU project OID collection<p>
+ * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) } == IETF defined things
  */
 public interface GNUObjectIdentifiers
 {
-    /** 1.3.6.1.4.1.11591.1 -- used by GNU Radius */
-    public static final ASN1ObjectIdentifier GNU      = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
-    /** 1.3.6.1.4.1.11591.2 -- used by GNU PG */
-    public static final ASN1ObjectIdentifier GnuPG    = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten)
-    /** 1.3.6.1.4.1.11591.2.1 -- notation */
-    public static final ASN1ObjectIdentifier notation = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation
-    /** 1.3.6.1.4.1.11591.2.1.1 -- pkaAddress */
-    public static final ASN1ObjectIdentifier pkaAddress = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress
-    /** 1.3.6.1.4.1.11591.3 -- GNU Radar */
-    public static final ASN1ObjectIdentifier GnuRadar = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar
-    /** 1.3.6.1.4.1.11591.12 -- digestAlgorithm */
-    public static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm
-    /** 1.3.6.1.4.1.11591.12.2 -- TIGER/192 */
-    public static final ASN1ObjectIdentifier Tiger_192 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192
-    /** 1.3.6.1.4.1.11591.13 -- encryptionAlgorithm */
-    public static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm
-    /** 1.3.6.1.4.1.11591.13.2 -- Serpent */
-    public static final ASN1ObjectIdentifier Serpent = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent
-    /** 1.3.6.1.4.1.11591.13.2.1 -- Serpent-128-ECB */
-    public static final ASN1ObjectIdentifier Serpent_128_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB
-    /** 1.3.6.1.4.1.11591.13.2.2 -- Serpent-128-CBC */
-    public static final ASN1ObjectIdentifier Serpent_128_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC
-    /** 1.3.6.1.4.1.11591.13.2.3 -- Serpent-128-OFB */
-    public static final ASN1ObjectIdentifier Serpent_128_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB
-    /** 1.3.6.1.4.1.11591.13.2.4 -- Serpent-128-CFB */
-    public static final ASN1ObjectIdentifier Serpent_128_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB
-    /** 1.3.6.1.4.1.11591.13.2.21 -- Serpent-192-ECB */
-    public static final ASN1ObjectIdentifier Serpent_192_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB
-    /** 1.3.6.1.4.1.11591.13.2.22 -- Serpent-192-CCB */
-    public static final ASN1ObjectIdentifier Serpent_192_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC
-    /** 1.3.6.1.4.1.11591.13.2.23 -- Serpent-192-OFB */
-    public static final ASN1ObjectIdentifier Serpent_192_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB
-    /** 1.3.6.1.4.1.11591.13.2.24 -- Serpent-192-CFB */
-    public static final ASN1ObjectIdentifier Serpent_192_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB
-    /** 1.3.6.1.4.1.11591.13.2.41 -- Serpent-256-ECB */
-    public static final ASN1ObjectIdentifier Serpent_256_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB
-    /** 1.3.6.1.4.1.11591.13.2.42 -- Serpent-256-CBC */
-    public static final ASN1ObjectIdentifier Serpent_256_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC
-    /** 1.3.6.1.4.1.11591.13.2.43 -- Serpent-256-OFB */
-    public static final ASN1ObjectIdentifier Serpent_256_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB
-    /** 1.3.6.1.4.1.11591.13.2.44 -- Serpent-256-CFB */
-    public static final ASN1ObjectIdentifier Serpent_256_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB
+    /**
+     * 1.3.6.1.4.1.11591.1 -- used by GNU Radius
+     */
+    ASN1ObjectIdentifier GNU = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
+    /**
+     * 1.3.6.1.4.1.11591.2 -- used by GNU PG
+     */
+    ASN1ObjectIdentifier GnuPG = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten)
+    /**
+     * 1.3.6.1.4.1.11591.2.1 -- notation
+     */
+    ASN1ObjectIdentifier notation = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation
+    /**
+     * 1.3.6.1.4.1.11591.2.1.1 -- pkaAddress
+     */
+    ASN1ObjectIdentifier pkaAddress = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress
+    /**
+     * 1.3.6.1.4.1.11591.3 -- GNU Radar
+     */
+    ASN1ObjectIdentifier GnuRadar = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar
+    /**
+     * 1.3.6.1.4.1.11591.12 -- digestAlgorithm
+     */
+    ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm
+    /**
+     * 1.3.6.1.4.1.11591.12.2 -- TIGER/192
+     */
+    ASN1ObjectIdentifier Tiger_192 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192
+    /**
+     * 1.3.6.1.4.1.11591.13 -- encryptionAlgorithm
+     */
+    ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm
+    /**
+     * 1.3.6.1.4.1.11591.13.2 -- Serpent
+     */
+    ASN1ObjectIdentifier Serpent = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent
+    /**
+     * 1.3.6.1.4.1.11591.13.2.1 -- Serpent-128-ECB
+     */
+    ASN1ObjectIdentifier Serpent_128_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.2 -- Serpent-128-CBC
+     */
+    ASN1ObjectIdentifier Serpent_128_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC
+    /**
+     * 1.3.6.1.4.1.11591.13.2.3 -- Serpent-128-OFB
+     */
+    ASN1ObjectIdentifier Serpent_128_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.4 -- Serpent-128-CFB
+     */
+    ASN1ObjectIdentifier Serpent_128_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.21 -- Serpent-192-ECB
+     */
+    ASN1ObjectIdentifier Serpent_192_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.22 -- Serpent-192-CCB
+     */
+    ASN1ObjectIdentifier Serpent_192_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC
+    /**
+     * 1.3.6.1.4.1.11591.13.2.23 -- Serpent-192-OFB
+     */
+    ASN1ObjectIdentifier Serpent_192_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.24 -- Serpent-192-CFB
+     */
+    ASN1ObjectIdentifier Serpent_192_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.41 -- Serpent-256-ECB
+     */
+    ASN1ObjectIdentifier Serpent_256_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.42 -- Serpent-256-CBC
+     */
+    ASN1ObjectIdentifier Serpent_256_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC
+    /**
+     * 1.3.6.1.4.1.11591.13.2.43 -- Serpent-256-OFB
+     */
+    ASN1ObjectIdentifier Serpent_256_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB
+    /**
+     * 1.3.6.1.4.1.11591.13.2.44 -- Serpent-256-CFB
+     */
+    ASN1ObjectIdentifier Serpent_256_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB
 
-    /** 1.3.6.1.4.1.11591.14 -- CRC algorithms */
-    public static final ASN1ObjectIdentifier CRC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
-    /** 1.3.6.1.4.1.11591.14,1 -- CRC32 */
-    public static final ASN1ObjectIdentifier CRC32 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
+    /**
+     * 1.3.6.1.4.1.11591.14 -- CRC algorithms
+     */
+    ASN1ObjectIdentifier CRC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
+    /**
+     * 1.3.6.1.4.1.11591.14,1 -- CRC32
+     */
+    ASN1ObjectIdentifier CRC32 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
 
-    /** 1.3.6.1.4.1.11591.15 - ellipticCurve */
-    public static final ASN1ObjectIdentifier ellipticCurve = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.15");
+    /**
+     * 1.3.6.1.4.1.11591.15 - ellipticCurve
+     */
+    ASN1ObjectIdentifier ellipticCurve = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.15");
 
-    public static final ASN1ObjectIdentifier Ed25519   = ellipticCurve.branch("1");
+    ASN1ObjectIdentifier Ed25519 = ellipticCurve.branch("1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
index fae8762..dfee3c7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
@@ -19,19 +19,18 @@
  *   dataGroupHashValues    SEQUENCE SIZE (2..ub-DataGroups) OF DataHashGroup,
  *   ldsVersionInfo         LDSVersionInfo OPTIONAL
  *   -- if present, version MUST be v1 }
- *   
+ *
  * DigestAlgorithmIdentifier ::= AlgorithmIdentifier,
- * 
+ *
  * LDSSecurityObjectVersion :: INTEGER {V0(0)}
  * </pre>
  */
-
-public class LDSSecurityObject 
+public class LDSSecurityObject
     extends ASN1Object
-    implements ICAOObjectIdentifiers    
+    implements ICAOObjectIdentifiers
 {
     public static final int ub_DataGroups = 16;
-    
+
     private ASN1Integer version = new ASN1Integer(0);
     private AlgorithmIdentifier digestAlgorithmIdentifier;
     private DataGroupHash[] datagroupHash;
@@ -46,12 +45,12 @@
         }
         else if (obj != null)
         {
-            return new LDSSecurityObject(ASN1Sequence.getInstance(obj));            
+            return new LDSSecurityObject(ASN1Sequence.getInstance(obj));
         }
-        
+
         return null;
-    }    
-    
+    }
+
     private LDSSecurityObject(
         ASN1Sequence seq)
     {
@@ -59,14 +58,14 @@
         {
             throw new IllegalArgumentException("null or empty sequence passed.");
         }
-        
+
         Enumeration e = seq.getObjects();
 
         // version
         version = ASN1Integer.getInstance(e.nextElement());
         // digestAlgorithmIdentifier
         digestAlgorithmIdentifier = AlgorithmIdentifier.getInstance(e.nextElement());
-      
+
         ASN1Sequence datagroupHashSeq = ASN1Sequence.getInstance(e.nextElement());
 
         if (version.getValue().intValue() == 1)
@@ -74,34 +73,34 @@
             versionInfo = LDSVersionInfo.getInstance(e.nextElement());
         }
 
-        checkDatagroupHashSeqSize(datagroupHashSeq.size());        
-        
+        checkDatagroupHashSeqSize(datagroupHashSeq.size());
+
         datagroupHash = new DataGroupHash[datagroupHashSeq.size()];
-        for (int i= 0; i< datagroupHashSeq.size();i++)
+        for (int i = 0; i < datagroupHashSeq.size(); i++)
         {
             datagroupHash[i] = DataGroupHash.getInstance(datagroupHashSeq.getObjectAt(i));
         }
     }
 
     public LDSSecurityObject(
-        AlgorithmIdentifier digestAlgorithmIdentifier, 
-        DataGroupHash[]       datagroupHash)
+        AlgorithmIdentifier digestAlgorithmIdentifier,
+        DataGroupHash[] datagroupHash)
     {
         this.version = new ASN1Integer(0);
         this.digestAlgorithmIdentifier = digestAlgorithmIdentifier;
-        this.datagroupHash = datagroupHash;
-        
-        checkDatagroupHashSeqSize(datagroupHash.length);                      
-    }    
+        this.datagroupHash = copy(datagroupHash);
+
+        checkDatagroupHashSeqSize(datagroupHash.length);
+    }
 
     public LDSSecurityObject(
         AlgorithmIdentifier digestAlgorithmIdentifier,
-        DataGroupHash[]     datagroupHash,
-        LDSVersionInfo      versionInfo)
+        DataGroupHash[] datagroupHash,
+        LDSVersionInfo versionInfo)
     {
         this.version = new ASN1Integer(1);
         this.digestAlgorithmIdentifier = digestAlgorithmIdentifier;
-        this.datagroupHash = datagroupHash;
+        this.datagroupHash = copy(datagroupHash);
         this.versionInfo = versionInfo;
 
         checkDatagroupHashSeqSize(datagroupHash.length);
@@ -111,9 +110,9 @@
     {
         if ((size < 2) || (size > ub_DataGroups))
         {
-               throw new IllegalArgumentException("wrong size in DataGroupHashValues : not in (2.."+ ub_DataGroups +")");
+            throw new IllegalArgumentException("wrong size in DataGroupHashValues : not in (2.." + ub_DataGroups + ")");
         }
-    }  
+    }
 
     public int getVersion()
     {
@@ -124,10 +123,10 @@
     {
         return digestAlgorithmIdentifier;
     }
-    
+
     public DataGroupHash[] getDatagroupHash()
     {
-        return datagroupHash;
+        return copy(datagroupHash);
     }
 
     public LDSVersionInfo getVersionInfo()
@@ -138,16 +137,16 @@
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector seq = new ASN1EncodableVector();
-        
+
         seq.add(version);
         seq.add(digestAlgorithmIdentifier);
-                
+
         ASN1EncodableVector seqname = new ASN1EncodableVector();
-        for (int i = 0; i < datagroupHash.length; i++) 
+        for (int i = 0; i < datagroupHash.length; i++)
         {
             seqname.add(datagroupHash[i]);
-        }            
-        seq.add(new DERSequence(seqname));                   
+        }
+        seq.add(new DERSequence(seqname));
 
         if (versionInfo != null)
         {
@@ -156,4 +155,13 @@
 
         return new DERSequence(seq);
     }
+
+    private DataGroupHash[] copy(DataGroupHash[] dgHash)
+    {
+        DataGroupHash[] rv = new DataGroupHash[dgHash.length];
+
+        System.arraycopy(dgHash, 0, rv, 0, rv.length);
+
+        return rv;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java
index dff3d84..e7cf777 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/CertHash.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Arrays;
 
 /**
  * ISIS-MTT PROFILE: The responder may include this extension in a response to
@@ -94,7 +95,7 @@
 
     public byte[] getCertificateHash()
     {
-        return certificateHash;
+        return Arrays.clone(certificateHash);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java
index 5c12016..b2e249c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.util.Arrays;
 
 /**
  * ISIS-MTT-Optional: The certificate requested by the client by inserting the
@@ -147,9 +148,9 @@
         }
         if (publicKeyCert != null)
         {
-            return publicKeyCert;
+            return Arrays.clone(publicKeyCert);
         }
-        return attributeCert;
+        return Arrays.clone(attributeCert);
     }
     
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/iso/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/iso/package.html
new file mode 100644
index 0000000..dfd5299
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/iso/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for various ISO Standards.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java
index 99ddc5e..b47dee3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/CAST5CBCParameters.java
@@ -39,7 +39,7 @@
         this.keyLength = new ASN1Integer(keyLength);
     }
 
-    public CAST5CBCParameters(
+    private CAST5CBCParameters(
         ASN1Sequence  seq)
     {
         iv = (ASN1OctetString)seq.getObjectAt(0);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
index 1c1d276..7682b04 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
@@ -35,7 +35,7 @@
         this.iv = new DEROctetString(iv);
     }
 
-    public IDEACBCPar(
+    private IDEACBCPar(
         ASN1Sequence  seq)
     {
         if (seq.size() == 1)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index 284751e..0f0b10d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -8,64 +8,98 @@
     // Netscape
     //       iso/itu(2) joint-assign(16) us(840) uscompany(1) netscape(113730) cert-extensions(1) }
     //
-    /** Netscape cert extensions OID base: 2.16.840.1.113730.1  */
-    static final ASN1ObjectIdentifier    netscape                = new ASN1ObjectIdentifier("2.16.840.1.113730.1");
-    /** Netscape cert CertType OID: 2.16.840.1.113730.1.1  */
-    static final ASN1ObjectIdentifier    netscapeCertType        = netscape.branch("1");
-    /** Netscape cert BaseURL OID: 2.16.840.1.113730.1.2  */
-    static final ASN1ObjectIdentifier    netscapeBaseURL         = netscape.branch("2");
-    /** Netscape cert RevocationURL OID: 2.16.840.1.113730.1.3  */
-    static final ASN1ObjectIdentifier    netscapeRevocationURL   = netscape.branch("3");
-    /** Netscape cert CARevocationURL OID: 2.16.840.1.113730.1.4  */
-    static final ASN1ObjectIdentifier    netscapeCARevocationURL = netscape.branch("4");
-    /** Netscape cert RenewalURL OID: 2.16.840.1.113730.1.7  */
-    static final ASN1ObjectIdentifier    netscapeRenewalURL      = netscape.branch("7");
-    /** Netscape cert CApolicyURL OID: 2.16.840.1.113730.1.8  */
-    static final ASN1ObjectIdentifier    netscapeCApolicyURL     = netscape.branch("8");
-    /** Netscape cert SSLServerName OID: 2.16.840.1.113730.1.12  */
-    static final ASN1ObjectIdentifier    netscapeSSLServerName   = netscape.branch("12");
-    /** Netscape cert CertComment OID: 2.16.840.1.113730.1.13  */
-    static final ASN1ObjectIdentifier    netscapeCertComment     = netscape.branch("13");
-    
+    /**
+     * Netscape cert extensions OID base: 2.16.840.1.113730.1
+     */
+    ASN1ObjectIdentifier netscape = new ASN1ObjectIdentifier("2.16.840.1.113730.1");
+    /**
+     * Netscape cert CertType OID: 2.16.840.1.113730.1.1
+     */
+    ASN1ObjectIdentifier netscapeCertType = netscape.branch("1");
+    /**
+     * Netscape cert BaseURL OID: 2.16.840.1.113730.1.2
+     */
+    ASN1ObjectIdentifier netscapeBaseURL = netscape.branch("2");
+    /**
+     * Netscape cert RevocationURL OID: 2.16.840.1.113730.1.3
+     */
+    ASN1ObjectIdentifier netscapeRevocationURL = netscape.branch("3");
+    /**
+     * Netscape cert CARevocationURL OID: 2.16.840.1.113730.1.4
+     */
+    ASN1ObjectIdentifier netscapeCARevocationURL = netscape.branch("4");
+    /**
+     * Netscape cert RenewalURL OID: 2.16.840.1.113730.1.7
+     */
+    ASN1ObjectIdentifier netscapeRenewalURL = netscape.branch("7");
+    /**
+     * Netscape cert CApolicyURL OID: 2.16.840.1.113730.1.8
+     */
+    ASN1ObjectIdentifier netscapeCApolicyURL = netscape.branch("8");
+    /**
+     * Netscape cert SSLServerName OID: 2.16.840.1.113730.1.12
+     */
+    ASN1ObjectIdentifier netscapeSSLServerName = netscape.branch("12");
+    /**
+     * Netscape cert CertComment OID: 2.16.840.1.113730.1.13
+     */
+    ASN1ObjectIdentifier netscapeCertComment = netscape.branch("13");
+
     //
     // Verisign
     //       iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) }
     //
-    /** Verisign OID base: 2.16.840.1.113733.1 */
-    static final ASN1ObjectIdentifier   verisign                = new ASN1ObjectIdentifier("2.16.840.1.113733.1");
+    /**
+     * Verisign OID base: 2.16.840.1.113733.1
+     */
+    ASN1ObjectIdentifier verisign = new ASN1ObjectIdentifier("2.16.840.1.113733.1");
 
-    /** Verisign CZAG (Country,Zip,Age,Gender) Extension OID: 2.16.840.1.113733.1.6.3 */
-    static final ASN1ObjectIdentifier   verisignCzagExtension   = verisign.branch("6.3");
+    /**
+     * Verisign CZAG (Country,Zip,Age,Gender) Extension OID: 2.16.840.1.113733.1.6.3
+     */
+    ASN1ObjectIdentifier verisignCzagExtension = verisign.branch("6.3");
 
-    static final ASN1ObjectIdentifier   verisignPrivate_6_9     = verisign.branch("6.9");
-    static final ASN1ObjectIdentifier   verisignOnSiteJurisdictionHash = verisign.branch("6.11");
-    static final ASN1ObjectIdentifier   verisignBitString_6_13   = verisign.branch("6.13");
+    ASN1ObjectIdentifier verisignPrivate_6_9 = verisign.branch("6.9");
+    ASN1ObjectIdentifier verisignOnSiteJurisdictionHash = verisign.branch("6.11");
+    ASN1ObjectIdentifier verisignBitString_6_13 = verisign.branch("6.13");
 
-    /** Verisign D&amp;B D-U-N-S number Extension OID: 2.16.840.1.113733.1.6.15 */
-    static final ASN1ObjectIdentifier   verisignDnbDunsNumber   = verisign.branch("6.15");
+    /**
+     * Verisign D&amp;B D-U-N-S number Extension OID: 2.16.840.1.113733.1.6.15
+     */
+    ASN1ObjectIdentifier verisignDnbDunsNumber = verisign.branch("6.15");
 
-    static final ASN1ObjectIdentifier   verisignIssStrongCrypto = verisign.branch("8.1");
+    ASN1ObjectIdentifier verisignIssStrongCrypto = verisign.branch("8.1");
 
     //
     // Novell
     //       iso/itu(2) country(16) us(840) organization(1) novell(113719)
     //
-    /** Novell OID base: 2.16.840.1.113719 */
-    static final ASN1ObjectIdentifier    novell                  = new ASN1ObjectIdentifier("2.16.840.1.113719");
-    /** Novell SecurityAttribs OID: 2.16.840.1.113719.1.9.4.1 */
-    static final ASN1ObjectIdentifier    novellSecurityAttribs   = novell.branch("1.9.4.1");
+    /**
+     * Novell OID base: 2.16.840.1.113719
+     */
+    ASN1ObjectIdentifier novell = new ASN1ObjectIdentifier("2.16.840.1.113719");
+    /**
+     * Novell SecurityAttribs OID: 2.16.840.1.113719.1.9.4.1
+     */
+    ASN1ObjectIdentifier novellSecurityAttribs = novell.branch("1.9.4.1");
 
     //
     // Entrust
     //       iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7)
     //
-    /** NortelNetworks Entrust OID base: 1.2.840.113533.7 */
-    static final ASN1ObjectIdentifier    entrust                 = new ASN1ObjectIdentifier("1.2.840.113533.7");
-    /** NortelNetworks Entrust VersionExtension OID: 1.2.840.113533.7.65.0 */
-    static final ASN1ObjectIdentifier    entrustVersionExtension = entrust.branch("65.0");
+    /**
+     * NortelNetworks Entrust OID base: 1.2.840.113533.7
+     */
+    ASN1ObjectIdentifier entrust = new ASN1ObjectIdentifier("1.2.840.113533.7");
+    /**
+     * NortelNetworks Entrust VersionExtension OID: 1.2.840.113533.7.65.0
+     */
+    ASN1ObjectIdentifier entrustVersionExtension = entrust.branch("65.0");
 
-    /** cast5CBC OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) nt(113533) nsn(7) algorithms(66) 10} SEE RFC 2984 */
-    ASN1ObjectIdentifier    cast5CBC = entrust.branch("66.10");
+    /**
+     * cast5CBC OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) nt(113533) nsn(7) algorithms(66) 10} SEE RFC 2984
+     */
+    ASN1ObjectIdentifier cast5CBC = entrust.branch("66.10");
 
     //
     // Ascom
@@ -92,4 +126,13 @@
     ASN1ObjectIdentifier id_blake2b256 = blake2.branch("1.8");
     ASN1ObjectIdentifier id_blake2b384 = blake2.branch("1.12");
     ASN1ObjectIdentifier id_blake2b512 = blake2.branch("1.16");
+
+    ASN1ObjectIdentifier id_blake2s128 = blake2.branch("2.4");
+    ASN1ObjectIdentifier id_blake2s160 = blake2.branch("2.5");
+    ASN1ObjectIdentifier id_blake2s224 = blake2.branch("2.7");
+    ASN1ObjectIdentifier id_blake2s256 = blake2.branch("2.8");
+
+    //
+    // Scrypt
+    ASN1ObjectIdentifier id_scrypt = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.4.11");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/ScryptParams.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/ScryptParams.java
new file mode 100644
index 0000000..010d23a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/ScryptParams.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.asn1.misc;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * RFC 7914 scrypt parameters.
+ *
+ * <pre>
+ * scrypt-params ::= SEQUENCE {
+ *      salt OCTET STRING,
+ *      costParameter INTEGER (1..MAX),
+ *      blockSize INTEGER (1..MAX),
+ *      parallelizationParameter INTEGER (1..MAX),
+ *      keyLength INTEGER (1..MAX) OPTIONAL
+ * }
+ * </pre>
+ */
+public class ScryptParams
+    extends ASN1Object
+{
+    private final byte[] salt;
+    private final BigInteger costParameter;
+    private final BigInteger blockSize;
+    private final BigInteger parallelizationParameter;
+    private final BigInteger keyLength;
+
+    public ScryptParams(byte[] salt, int costParameter, int blockSize, int parallelizationParameter)
+    {
+        this(salt, BigInteger.valueOf(costParameter), BigInteger.valueOf(blockSize), BigInteger.valueOf(parallelizationParameter), null);
+    }
+
+    public ScryptParams(byte[] salt, int costParameter, int blockSize, int parallelizationParameter, int keyLength)
+    {
+        this(salt, BigInteger.valueOf(costParameter), BigInteger.valueOf(blockSize), BigInteger.valueOf(parallelizationParameter), BigInteger.valueOf(keyLength));
+    }
+    
+    /**
+     * Base constructor.
+     *
+     * @param salt salt value
+     * @param costParameter specifies the CPU/Memory cost parameter N
+     * @param blockSize block size parameter r
+     * @param parallelizationParameter parallelization parameter
+     * @param keyLength length of key to be derived (in octects)
+     */
+    public ScryptParams(byte[] salt, BigInteger costParameter, BigInteger blockSize, BigInteger parallelizationParameter, BigInteger keyLength)
+    {
+        this.salt = Arrays.clone(salt);
+        this.costParameter = costParameter;
+        this.blockSize = blockSize;
+        this.parallelizationParameter = parallelizationParameter;
+        this.keyLength = keyLength;
+    }
+
+    public static ScryptParams getInstance(
+        Object  o)
+    {
+        if (o instanceof ScryptParams)
+        {
+            return (ScryptParams)o;
+        }
+        else if (o != null)
+        {
+            return new ScryptParams(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private ScryptParams(ASN1Sequence seq)
+    {
+        if (seq.size() != 4 && seq.size() != 5)
+        {
+            throw new IllegalArgumentException("invalid sequence: size = " + seq.size());
+        }
+
+        this.salt = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+        this.costParameter = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
+        this.blockSize = ASN1Integer.getInstance(seq.getObjectAt(2)).getValue();
+        this.parallelizationParameter = ASN1Integer.getInstance(seq.getObjectAt(3)).getValue();
+
+        if (seq.size() == 5)
+        {
+            this.keyLength = ASN1Integer.getInstance(seq.getObjectAt(4)).getValue();
+        }
+        else
+        {
+            this.keyLength = null;
+        }
+    }
+
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
+
+    public BigInteger getCostParameter()
+    {
+        return costParameter;
+    }
+
+    public BigInteger getBlockSize()
+    {
+        return blockSize;
+    }
+
+    public BigInteger getParallelizationParameter()
+    {
+        return parallelizationParameter;
+    }
+
+    /**
+     * Return the length in octets for the derived key.
+     *
+     * @return length for key to be derived (in octets)
+     */
+    public BigInteger getKeyLength()
+    {
+        return keyLength;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new DEROctetString(salt));
+        v.add(new ASN1Integer(costParameter));
+        v.add(new ASN1Integer(blockSize));
+        v.add(new ASN1Integer(parallelizationParameter));
+        if (keyLength != null)
+        {
+            v.add(new ASN1Integer(keyLength));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java
index 14d6534..2bc06cc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
- *  <pre>
+ * <pre>
  *  SignedPublicKeyAndChallenge ::= SEQUENCE {
  *    publicKeyAndChallenge PublicKeyAndChallenge,
  *    signatureAlgorithm AlgorithmIdentifier,
@@ -20,7 +20,7 @@
     extends ASN1Object
 {
     private final PublicKeyAndChallenge pubKeyAndChal;
-    private final ASN1Sequence          pkacSeq;
+    private final ASN1Sequence pkacSeq;
 
     public static SignedPublicKeyAndChallenge getInstance(Object obj)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java
index 4ef8148..c687f3c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java
@@ -4,55 +4,55 @@
 
 public interface NSRIObjectIdentifiers
 {
-    static final ASN1ObjectIdentifier   nsri                = new ASN1ObjectIdentifier("1.2.410.200046");
+    ASN1ObjectIdentifier nsri = new ASN1ObjectIdentifier("1.2.410.200046");
 
-    static final ASN1ObjectIdentifier   id_algorithm        = nsri.branch("1");
+    ASN1ObjectIdentifier id_algorithm = nsri.branch("1");
 
-    static final ASN1ObjectIdentifier   id_sea              = id_algorithm.branch("1");
-    static final ASN1ObjectIdentifier   id_pad              = id_algorithm.branch("2");
+    ASN1ObjectIdentifier id_sea = id_algorithm.branch("1");
+    ASN1ObjectIdentifier id_pad = id_algorithm.branch("2");
 
-    static final ASN1ObjectIdentifier   id_pad_null         = id_algorithm.branch("0");
-    static final ASN1ObjectIdentifier   id_pad_1            = id_algorithm.branch("1");
+    ASN1ObjectIdentifier id_pad_null = id_algorithm.branch("0");
+    ASN1ObjectIdentifier id_pad_1 = id_algorithm.branch("1");
 
-    static final ASN1ObjectIdentifier   id_aria128_ecb      = id_sea.branch("1");
-    static final ASN1ObjectIdentifier   id_aria128_cbc      = id_sea.branch("2");
-    static final ASN1ObjectIdentifier   id_aria128_cfb      = id_sea.branch("3");
-    static final ASN1ObjectIdentifier   id_aria128_ofb      = id_sea.branch("4");
-    static final ASN1ObjectIdentifier   id_aria128_ctr      = id_sea.branch("5");
+    ASN1ObjectIdentifier id_aria128_ecb = id_sea.branch("1");
+    ASN1ObjectIdentifier id_aria128_cbc = id_sea.branch("2");
+    ASN1ObjectIdentifier id_aria128_cfb = id_sea.branch("3");
+    ASN1ObjectIdentifier id_aria128_ofb = id_sea.branch("4");
+    ASN1ObjectIdentifier id_aria128_ctr = id_sea.branch("5");
 
-    static final ASN1ObjectIdentifier   id_aria192_ecb      = id_sea.branch("6");
-    static final ASN1ObjectIdentifier   id_aria192_cbc      = id_sea.branch("7");
-    static final ASN1ObjectIdentifier   id_aria192_cfb      = id_sea.branch("8");
-    static final ASN1ObjectIdentifier   id_aria192_ofb      = id_sea.branch("9");
-    static final ASN1ObjectIdentifier   id_aria192_ctr      = id_sea.branch("10");
+    ASN1ObjectIdentifier id_aria192_ecb = id_sea.branch("6");
+    ASN1ObjectIdentifier id_aria192_cbc = id_sea.branch("7");
+    ASN1ObjectIdentifier id_aria192_cfb = id_sea.branch("8");
+    ASN1ObjectIdentifier id_aria192_ofb = id_sea.branch("9");
+    ASN1ObjectIdentifier id_aria192_ctr = id_sea.branch("10");
 
-    static final ASN1ObjectIdentifier   id_aria256_ecb      = id_sea.branch("11");
-    static final ASN1ObjectIdentifier   id_aria256_cbc      = id_sea.branch("12");
-    static final ASN1ObjectIdentifier   id_aria256_cfb      = id_sea.branch("13");
-    static final ASN1ObjectIdentifier   id_aria256_ofb      = id_sea.branch("14");
-    static final ASN1ObjectIdentifier   id_aria256_ctr      = id_sea.branch("15");
+    ASN1ObjectIdentifier id_aria256_ecb = id_sea.branch("11");
+    ASN1ObjectIdentifier id_aria256_cbc = id_sea.branch("12");
+    ASN1ObjectIdentifier id_aria256_cfb = id_sea.branch("13");
+    ASN1ObjectIdentifier id_aria256_ofb = id_sea.branch("14");
+    ASN1ObjectIdentifier id_aria256_ctr = id_sea.branch("15");
 
-    static final ASN1ObjectIdentifier   id_aria128_cmac     = id_sea.branch("21");
-    static final ASN1ObjectIdentifier   id_aria192_cmac     = id_sea.branch("22");
-    static final ASN1ObjectIdentifier   id_aria256_cmac     = id_sea.branch("23");
+    ASN1ObjectIdentifier id_aria128_cmac = id_sea.branch("21");
+    ASN1ObjectIdentifier id_aria192_cmac = id_sea.branch("22");
+    ASN1ObjectIdentifier id_aria256_cmac = id_sea.branch("23");
 
-    static final ASN1ObjectIdentifier   id_aria128_ocb2     = id_sea.branch("31");
-    static final ASN1ObjectIdentifier   id_aria192_ocb2     = id_sea.branch("32");
-    static final ASN1ObjectIdentifier   id_aria256_ocb2     = id_sea.branch("33");
+    ASN1ObjectIdentifier id_aria128_ocb2 = id_sea.branch("31");
+    ASN1ObjectIdentifier id_aria192_ocb2 = id_sea.branch("32");
+    ASN1ObjectIdentifier id_aria256_ocb2 = id_sea.branch("33");
 
-    static final ASN1ObjectIdentifier   id_aria128_gcm      = id_sea.branch("34");
-    static final ASN1ObjectIdentifier   id_aria192_gcm      = id_sea.branch("35");
-    static final ASN1ObjectIdentifier   id_aria256_gcm      = id_sea.branch("36");
+    ASN1ObjectIdentifier id_aria128_gcm = id_sea.branch("34");
+    ASN1ObjectIdentifier id_aria192_gcm = id_sea.branch("35");
+    ASN1ObjectIdentifier id_aria256_gcm = id_sea.branch("36");
 
-    static final ASN1ObjectIdentifier   id_aria128_ccm      = id_sea.branch("37");
-    static final ASN1ObjectIdentifier   id_aria192_ccm      = id_sea.branch("38");
-    static final ASN1ObjectIdentifier   id_aria256_ccm      = id_sea.branch("39");
+    ASN1ObjectIdentifier id_aria128_ccm = id_sea.branch("37");
+    ASN1ObjectIdentifier id_aria192_ccm = id_sea.branch("38");
+    ASN1ObjectIdentifier id_aria256_ccm = id_sea.branch("39");
 
-    static final ASN1ObjectIdentifier   id_aria128_kw       = id_sea.branch("40");
-    static final ASN1ObjectIdentifier   id_aria192_kw       = id_sea.branch("41");
-    static final ASN1ObjectIdentifier   id_aria256_kw       = id_sea.branch("42");
+    ASN1ObjectIdentifier id_aria128_kw = id_sea.branch("40");
+    ASN1ObjectIdentifier id_aria192_kw = id_sea.branch("41");
+    ASN1ObjectIdentifier id_aria256_kw = id_sea.branch("42");
 
-    static final ASN1ObjectIdentifier   id_aria128_kwp      = id_sea.branch("43");
-    static final ASN1ObjectIdentifier   id_aria192_kwp      = id_sea.branch("44");
-    static final ASN1ObjectIdentifier   id_aria256_kwp      = id_sea.branch("45");
+    ASN1ObjectIdentifier id_aria128_kwp = id_sea.branch("43");
+    ASN1ObjectIdentifier id_aria192_kwp = id_sea.branch("44");
+    ASN1ObjectIdentifier id_aria256_kwp = id_sea.branch("45");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nsri/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/package.html
new file mode 100644
index 0000000..bd43ca0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes algorithms from the Korean National Security Research Institute.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
index 0b1db32..e471149 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
@@ -13,7 +13,7 @@
     implements ASN1Choice
 {
     private int             tagNo;
-    private ASN1Encodable   value;
+    private ASN1Encodable    value;
 
     /**
      * create a CertStatus object with a tag of zero.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
index ea4779b..b7cae33 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
@@ -45,12 +45,21 @@
     public AuthenticatedSafe(
         ContentInfo[]       info)
     {
-        this.info = info;
+        this.info = copy(info);
     }
 
     public ContentInfo[] getContentInfo()
     {
-        return info;
+        return copy(info);
+    }
+
+    private ContentInfo[] copy(ContentInfo[] infos)
+    {
+        ContentInfo[] tmp = new ContentInfo[infos.length];
+
+        System.arraycopy(infos, 0, tmp, 0, tmp.length);
+
+        return tmp;
     }
 
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
index e0f5efd..7a250ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -35,8 +35,6 @@
     extends ASN1Object
 {
     ASN1Sequence                data;
-    ASN1ObjectIdentifier bagId;
-    ASN1Primitive bagValue;
 
     public static EncryptedData getInstance(
          Object  obj)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
index 848f4fc..eeaa48d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
@@ -13,6 +13,12 @@
     private AlgorithmIdentifier algId;
 
     public EncryptionScheme(
+        ASN1ObjectIdentifier objectId)
+    {
+        this.algId = new AlgorithmIdentifier(objectId);
+    }
+
+    public EncryptionScheme(
         ASN1ObjectIdentifier objectId,
         ASN1Encodable parameters)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
index 63fa2e4..593373f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
@@ -42,11 +42,11 @@
     {
         this.digInfo = DigestInfo.getInstance(seq.getObjectAt(0));
 
-        this.salt = Arrays.clone(((ASN1OctetString)seq.getObjectAt(1)).getOctets());
+        this.salt = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
 
         if (seq.size() == 3)
         {
-            this.iterationCount = ((ASN1Integer)seq.getObjectAt(2)).getValue();
+            this.iterationCount = ASN1Integer.getInstance(seq.getObjectAt(2)).getValue();
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index bb81a4f..db5c026 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -10,35 +10,35 @@
 public interface PKCSObjectIdentifiers
 {
     /** PKCS#1: 1.2.840.113549.1.1 */
-    static final ASN1ObjectIdentifier    pkcs_1                    = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
+    ASN1ObjectIdentifier    pkcs_1                    = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
     /** PKCS#1: 1.2.840.113549.1.1.1 */
-    static final ASN1ObjectIdentifier    rsaEncryption             = pkcs_1.branch("1");
+    ASN1ObjectIdentifier    rsaEncryption             = pkcs_1.branch("1");
     /** PKCS#1: 1.2.840.113549.1.1.2 */
-    static final ASN1ObjectIdentifier    md2WithRSAEncryption      = pkcs_1.branch("2");
+    ASN1ObjectIdentifier    md2WithRSAEncryption      = pkcs_1.branch("2");
     /** PKCS#1: 1.2.840.113549.1.1.3 */
-    static final ASN1ObjectIdentifier    md4WithRSAEncryption      = pkcs_1.branch("3");
+    ASN1ObjectIdentifier    md4WithRSAEncryption      = pkcs_1.branch("3");
     /** PKCS#1: 1.2.840.113549.1.1.4 */
-    static final ASN1ObjectIdentifier    md5WithRSAEncryption      = pkcs_1.branch("4");
+    ASN1ObjectIdentifier    md5WithRSAEncryption      = pkcs_1.branch("4");
     /** PKCS#1: 1.2.840.113549.1.1.5 */
-    static final ASN1ObjectIdentifier    sha1WithRSAEncryption     = pkcs_1.branch("5");
+    ASN1ObjectIdentifier    sha1WithRSAEncryption     = pkcs_1.branch("5");
     /** PKCS#1: 1.2.840.113549.1.1.6 */
-    static final ASN1ObjectIdentifier    srsaOAEPEncryptionSET     = pkcs_1.branch("6");
+    ASN1ObjectIdentifier    srsaOAEPEncryptionSET     = pkcs_1.branch("6");
     /** PKCS#1: 1.2.840.113549.1.1.7 */
-    static final ASN1ObjectIdentifier    id_RSAES_OAEP             = pkcs_1.branch("7");
+    ASN1ObjectIdentifier    id_RSAES_OAEP             = pkcs_1.branch("7");
     /** PKCS#1: 1.2.840.113549.1.1.8 */
-    static final ASN1ObjectIdentifier    id_mgf1                   = pkcs_1.branch("8");
+    ASN1ObjectIdentifier    id_mgf1                   = pkcs_1.branch("8");
     /** PKCS#1: 1.2.840.113549.1.1.9 */
-    static final ASN1ObjectIdentifier    id_pSpecified             = pkcs_1.branch("9");
+    ASN1ObjectIdentifier    id_pSpecified             = pkcs_1.branch("9");
     /** PKCS#1: 1.2.840.113549.1.1.10 */
-    static final ASN1ObjectIdentifier    id_RSASSA_PSS             = pkcs_1.branch("10");
+    ASN1ObjectIdentifier    id_RSASSA_PSS             = pkcs_1.branch("10");
     /** PKCS#1: 1.2.840.113549.1.1.11 */
-    static final ASN1ObjectIdentifier    sha256WithRSAEncryption   = pkcs_1.branch("11");
+    ASN1ObjectIdentifier    sha256WithRSAEncryption   = pkcs_1.branch("11");
     /** PKCS#1: 1.2.840.113549.1.1.12 */
-    static final ASN1ObjectIdentifier    sha384WithRSAEncryption   = pkcs_1.branch("12");
+    ASN1ObjectIdentifier    sha384WithRSAEncryption   = pkcs_1.branch("12");
     /** PKCS#1: 1.2.840.113549.1.1.13 */
-    static final ASN1ObjectIdentifier    sha512WithRSAEncryption   = pkcs_1.branch("13");
+    ASN1ObjectIdentifier    sha512WithRSAEncryption   = pkcs_1.branch("13");
     /** PKCS#1: 1.2.840.113549.1.1.14 */
-    static final ASN1ObjectIdentifier    sha224WithRSAEncryption   = pkcs_1.branch("14");
+    ASN1ObjectIdentifier    sha224WithRSAEncryption   = pkcs_1.branch("14");
     /** PKCS#1: 1.2.840.113549.1.1.15 */
     ASN1ObjectIdentifier    sha512_224WithRSAEncryption   = pkcs_1.branch("15");
     /** PKCS#1: 1.2.840.113549.1.1.16 */
@@ -49,159 +49,159 @@
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 }
     //
     /** PKCS#3: 1.2.840.113549.1.3 */
-    static final ASN1ObjectIdentifier    pkcs_3                  = new ASN1ObjectIdentifier("1.2.840.113549.1.3");
+    ASN1ObjectIdentifier    pkcs_3                  = new ASN1ObjectIdentifier("1.2.840.113549.1.3");
     /** PKCS#3: 1.2.840.113549.1.3.1 */
-    static final ASN1ObjectIdentifier    dhKeyAgreement          = pkcs_3.branch("1");
+    ASN1ObjectIdentifier    dhKeyAgreement          = pkcs_3.branch("1");
 
     //
     // pkcs-5 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
     //
     /** PKCS#5: 1.2.840.113549.1.5 */
-    static final ASN1ObjectIdentifier    pkcs_5                  = new ASN1ObjectIdentifier("1.2.840.113549.1.5");
+    ASN1ObjectIdentifier    pkcs_5                  = new ASN1ObjectIdentifier("1.2.840.113549.1.5");
 
     /** PKCS#5: 1.2.840.113549.1.5.1 */
-    static final ASN1ObjectIdentifier    pbeWithMD2AndDES_CBC    = pkcs_5.branch("1");
+    ASN1ObjectIdentifier    pbeWithMD2AndDES_CBC    = pkcs_5.branch("1");
     /** PKCS#5: 1.2.840.113549.1.5.4 */
-    static final ASN1ObjectIdentifier    pbeWithMD2AndRC2_CBC    = pkcs_5.branch("4");
+    ASN1ObjectIdentifier    pbeWithMD2AndRC2_CBC    = pkcs_5.branch("4");
     /** PKCS#5: 1.2.840.113549.1.5.3 */
-    static final ASN1ObjectIdentifier    pbeWithMD5AndDES_CBC    = pkcs_5.branch("3");
+    ASN1ObjectIdentifier    pbeWithMD5AndDES_CBC    = pkcs_5.branch("3");
     /** PKCS#5: 1.2.840.113549.1.5.6 */
-    static final ASN1ObjectIdentifier    pbeWithMD5AndRC2_CBC    = pkcs_5.branch("6");
+    ASN1ObjectIdentifier    pbeWithMD5AndRC2_CBC    = pkcs_5.branch("6");
     /** PKCS#5: 1.2.840.113549.1.5.10 */
-    static final ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
+    ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
     /** PKCS#5: 1.2.840.113549.1.5.11 */
-    static final ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
+    ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
     /** PKCS#5: 1.2.840.113549.1.5.13 */
-    static final ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
+    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
     /** PKCS#5: 1.2.840.113549.1.5.12 */
-    static final ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
+    ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) 3 }
     //
     /**  1.2.840.113549.3 */
-    static final ASN1ObjectIdentifier    encryptionAlgorithm     = new ASN1ObjectIdentifier("1.2.840.113549.3");
+    ASN1ObjectIdentifier    encryptionAlgorithm     = new ASN1ObjectIdentifier("1.2.840.113549.3");
 
     /**  1.2.840.113549.3.7 */
-    static final ASN1ObjectIdentifier    des_EDE3_CBC            = encryptionAlgorithm.branch("7");
+    ASN1ObjectIdentifier    des_EDE3_CBC            = encryptionAlgorithm.branch("7");
     /**  1.2.840.113549.3.2 */
-    static final ASN1ObjectIdentifier    RC2_CBC                 = encryptionAlgorithm.branch("2");
+    ASN1ObjectIdentifier    RC2_CBC                 = encryptionAlgorithm.branch("2");
     /**  1.2.840.113549.3.4 */
-    static final ASN1ObjectIdentifier    rc4                     = encryptionAlgorithm.branch("4");
+    ASN1ObjectIdentifier    rc4                     = encryptionAlgorithm.branch("4");
 
     //
     // object identifiers for digests
     //
     /**  1.2.840.113549.2 */
-    static final ASN1ObjectIdentifier    digestAlgorithm        = new ASN1ObjectIdentifier("1.2.840.113549.2");
+    ASN1ObjectIdentifier    digestAlgorithm        = new ASN1ObjectIdentifier("1.2.840.113549.2");
     //
     // md2 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
     //
     /**  1.2.840.113549.2.2 */
-    static final ASN1ObjectIdentifier    md2                    = digestAlgorithm.branch("2");
+    ASN1ObjectIdentifier    md2                    = digestAlgorithm.branch("2");
 
     //
     // md4 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 4}
     //
     /**  1.2.840.113549.2.4 */
-    static final ASN1ObjectIdentifier    md4                    = digestAlgorithm.branch("4");
+    ASN1ObjectIdentifier    md4                    = digestAlgorithm.branch("4");
 
     //
     // md5 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
     //
     /**  1.2.840.113549.2.5 */
-    static final ASN1ObjectIdentifier    md5                    = digestAlgorithm.branch("5");
+    ASN1ObjectIdentifier    md5                    = digestAlgorithm.branch("5");
 
     /**  1.2.840.113549.2.7 */
-    static final ASN1ObjectIdentifier    id_hmacWithSHA1        = digestAlgorithm.branch("7").intern();
+    ASN1ObjectIdentifier    id_hmacWithSHA1        = digestAlgorithm.branch("7").intern();
     /**  1.2.840.113549.2.8 */
-    static final ASN1ObjectIdentifier    id_hmacWithSHA224      = digestAlgorithm.branch("8").intern();
+    ASN1ObjectIdentifier    id_hmacWithSHA224      = digestAlgorithm.branch("8").intern();
     /**  1.2.840.113549.2.9 */
-    static final ASN1ObjectIdentifier    id_hmacWithSHA256      = digestAlgorithm.branch("9").intern();
+    ASN1ObjectIdentifier    id_hmacWithSHA256      = digestAlgorithm.branch("9").intern();
     /**  1.2.840.113549.2.10 */
-    static final ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10").intern();
+    ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10").intern();
     /**  1.2.840.113549.2.11 */
-    static final ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11").intern();
+    ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11").intern();
 
     //
     // pkcs-7 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
     //
     /** pkcs#7: 1.2.840.113549.1.7 */
-    static final ASN1ObjectIdentifier    pkcs_7                  = new ASN1ObjectIdentifier("1.2.840.113549.1.7").intern();
+    ASN1ObjectIdentifier    pkcs_7                  = new ASN1ObjectIdentifier("1.2.840.113549.1.7").intern();
     /** PKCS#7: 1.2.840.113549.1.7.1 */
-    static final ASN1ObjectIdentifier    data                    = new ASN1ObjectIdentifier("1.2.840.113549.1.7.1").intern();
+    ASN1ObjectIdentifier    data                    = new ASN1ObjectIdentifier("1.2.840.113549.1.7.1").intern();
     /** PKCS#7: 1.2.840.113549.1.7.2 */
-    static final ASN1ObjectIdentifier    signedData              = new ASN1ObjectIdentifier("1.2.840.113549.1.7.2").intern();
+    ASN1ObjectIdentifier    signedData              = new ASN1ObjectIdentifier("1.2.840.113549.1.7.2").intern();
     /** PKCS#7: 1.2.840.113549.1.7.3 */
-    static final ASN1ObjectIdentifier    envelopedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.3").intern();
+    ASN1ObjectIdentifier    envelopedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.3").intern();
     /** PKCS#7: 1.2.840.113549.1.7.4 */
-    static final ASN1ObjectIdentifier    signedAndEnvelopedData  = new ASN1ObjectIdentifier("1.2.840.113549.1.7.4").intern();
+    ASN1ObjectIdentifier    signedAndEnvelopedData  = new ASN1ObjectIdentifier("1.2.840.113549.1.7.4").intern();
     /** PKCS#7: 1.2.840.113549.1.7.5 */
-    static final ASN1ObjectIdentifier    digestedData            = new ASN1ObjectIdentifier("1.2.840.113549.1.7.5").intern();
+    ASN1ObjectIdentifier    digestedData            = new ASN1ObjectIdentifier("1.2.840.113549.1.7.5").intern();
     /** PKCS#7: 1.2.840.113549.1.7.76 */
-    static final ASN1ObjectIdentifier    encryptedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.6").intern();
+    ASN1ObjectIdentifier    encryptedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.6").intern();
 
     //
     // pkcs-9 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
     //
     /** PKCS#9: 1.2.840.113549.1.9 */
-    static final ASN1ObjectIdentifier    pkcs_9                  = new ASN1ObjectIdentifier("1.2.840.113549.1.9");
+    ASN1ObjectIdentifier    pkcs_9                  = new ASN1ObjectIdentifier("1.2.840.113549.1.9");
 
     /** PKCS#9: 1.2.840.113549.1.9.1 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_emailAddress        = pkcs_9.branch("1").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_emailAddress        = pkcs_9.branch("1").intern();
     /** PKCS#9: 1.2.840.113549.1.9.2 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_unstructuredName    = pkcs_9.branch("2").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_unstructuredName    = pkcs_9.branch("2").intern();
     /** PKCS#9: 1.2.840.113549.1.9.3 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_contentType         = pkcs_9.branch("3").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_contentType         = pkcs_9.branch("3").intern();
     /** PKCS#9: 1.2.840.113549.1.9.4 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_messageDigest       = pkcs_9.branch("4").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_messageDigest       = pkcs_9.branch("4").intern();
     /** PKCS#9: 1.2.840.113549.1.9.5 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_signingTime         = pkcs_9.branch("5").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_signingTime         = pkcs_9.branch("5").intern();
     /** PKCS#9: 1.2.840.113549.1.9.6 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_counterSignature    = pkcs_9.branch("6").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_counterSignature    = pkcs_9.branch("6").intern();
     /** PKCS#9: 1.2.840.113549.1.9.7 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_challengePassword   = pkcs_9.branch("7").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_challengePassword   = pkcs_9.branch("7").intern();
     /** PKCS#9: 1.2.840.113549.1.9.8 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_unstructuredAddress = pkcs_9.branch("8").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_unstructuredAddress = pkcs_9.branch("8").intern();
     /** PKCS#9: 1.2.840.113549.1.9.9 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_extendedCertificateAttributes = pkcs_9.branch("9").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_extendedCertificateAttributes = pkcs_9.branch("9").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.13 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_signingDescription = pkcs_9.branch("13").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_signingDescription = pkcs_9.branch("13").intern();
     /** PKCS#9: 1.2.840.113549.1.9.14 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_extensionRequest   = pkcs_9.branch("14").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_extensionRequest   = pkcs_9.branch("14").intern();
     /** PKCS#9: 1.2.840.113549.1.9.15 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15").intern();
     /** PKCS#9: 1.2.840.113549.1.9.16 */
-    static final ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16").intern();
+    ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.20 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20").intern();
     /** PKCS#9: 1.2.840.113549.1.9.21 */
-    static final ASN1ObjectIdentifier    pkcs_9_at_localKeyId    = pkcs_9.branch("21").intern();
+    ASN1ObjectIdentifier    pkcs_9_at_localKeyId    = pkcs_9.branch("21").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.22.1
      * @deprecated use x509Certificate instead */
-    static final ASN1ObjectIdentifier    x509certType            = pkcs_9.branch("22.1");
+    ASN1ObjectIdentifier    x509certType            = pkcs_9.branch("22.1");
 
     /** PKCS#9: 1.2.840.113549.1.9.22 */
-    static final ASN1ObjectIdentifier    certTypes               = pkcs_9.branch("22");
+    ASN1ObjectIdentifier    certTypes               = pkcs_9.branch("22");
     /** PKCS#9: 1.2.840.113549.1.9.22.1 */
-    static final ASN1ObjectIdentifier    x509Certificate         = certTypes.branch("1").intern();
+    ASN1ObjectIdentifier    x509Certificate         = certTypes.branch("1").intern();
     /** PKCS#9: 1.2.840.113549.1.9.22.2 */
-    static final ASN1ObjectIdentifier    sdsiCertificate         = certTypes.branch("2").intern();
+    ASN1ObjectIdentifier    sdsiCertificate         = certTypes.branch("2").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.23 */
-    static final ASN1ObjectIdentifier    crlTypes                = pkcs_9.branch("23");
+    ASN1ObjectIdentifier    crlTypes                = pkcs_9.branch("23");
     /** PKCS#9: 1.2.840.113549.1.9.23.1 */
-    static final ASN1ObjectIdentifier    x509Crl                 = crlTypes.branch("1").intern();
+    ASN1ObjectIdentifier    x509Crl                 = crlTypes.branch("1").intern();
 
     /** RFC 6211 -  id-aa-cmsAlgorithmProtect OBJECT IDENTIFIER ::= {
             iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
@@ -212,38 +212,38 @@
     // SMIME capability sub oids.
     //
     /** PKCS#9: 1.2.840.113549.1.9.15.1 -- smime capability */
-    static final ASN1ObjectIdentifier    preferSignedData        = pkcs_9.branch("15.1");
+    ASN1ObjectIdentifier    preferSignedData        = pkcs_9.branch("15.1");
     /** PKCS#9: 1.2.840.113549.1.9.15.2 -- smime capability  */
-    static final ASN1ObjectIdentifier    canNotDecryptAny        = pkcs_9.branch("15.2");
+    ASN1ObjectIdentifier    canNotDecryptAny        = pkcs_9.branch("15.2");
     /** PKCS#9: 1.2.840.113549.1.9.15.3 -- smime capability  */
-    static final ASN1ObjectIdentifier    sMIMECapabilitiesVersions = pkcs_9.branch("15.3");
+    ASN1ObjectIdentifier    sMIMECapabilitiesVersions = pkcs_9.branch("15.3");
 
     //
     // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)}
     //
     /** PKCS#9: 1.2.840.113549.1.9.16.1 -- smime ct */
-    static final ASN1ObjectIdentifier    id_ct = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1");
+    ASN1ObjectIdentifier    id_ct = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1");
 
     /** PKCS#9: 1.2.840.113549.1.9.16.1.2 -- smime ct authData */
-    static final ASN1ObjectIdentifier    id_ct_authData          = id_ct.branch("2");
+    ASN1ObjectIdentifier    id_ct_authData          = id_ct.branch("2");
     /** PKCS#9: 1.2.840.113549.1.9.16.1.4 -- smime ct TSTInfo*/
-    static final ASN1ObjectIdentifier    id_ct_TSTInfo           = id_ct.branch("4");
+    ASN1ObjectIdentifier    id_ct_TSTInfo           = id_ct.branch("4");
     /** PKCS#9: 1.2.840.113549.1.9.16.1.9 -- smime ct compressedData */
-    static final ASN1ObjectIdentifier    id_ct_compressedData    = id_ct.branch("9");
+    ASN1ObjectIdentifier    id_ct_compressedData    = id_ct.branch("9");
     /** PKCS#9: 1.2.840.113549.1.9.16.1.23 -- smime ct authEnvelopedData */
-    static final ASN1ObjectIdentifier    id_ct_authEnvelopedData = id_ct.branch("23");
+    ASN1ObjectIdentifier    id_ct_authEnvelopedData = id_ct.branch("23");
     /** PKCS#9: 1.2.840.113549.1.9.16.1.31 -- smime ct timestampedData*/
-    static final ASN1ObjectIdentifier    id_ct_timestampedData   = id_ct.branch("31");
+    ASN1ObjectIdentifier    id_ct_timestampedData   = id_ct.branch("31");
 
 
     /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */
-    static final ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
+    ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */
-    static final ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
+    ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
     /**
      * <pre>
-     * -- RSA-KEM Key Transport Algorithm
+     * -- RSA-KEM Key Transport Algorithm  RFC 5990
      *
      * id-rsa-kem OID ::= {
      *      iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
@@ -251,114 +251,114 @@
      *   }
      * </pre>
      */
-    static final ASN1ObjectIdentifier id_rsa_KEM              = id_alg.branch("14");
+    ASN1ObjectIdentifier id_rsa_KEM              = id_alg.branch("14");
 
     //
     // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)}
     //
     /** PKCS#9: 1.2.840.113549.1.9.16.6 -- smime cti */
-    static final ASN1ObjectIdentifier    id_cti = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.6");
+    ASN1ObjectIdentifier    id_cti = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.6");
     
     /** PKCS#9: 1.2.840.113549.1.9.16.6.1 -- smime cti proofOfOrigin */
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfOrigin   = id_cti.branch("1");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfOrigin   = id_cti.branch("1");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.2 -- smime cti proofOfReceipt*/
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfReceipt  = id_cti.branch("2");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfReceipt  = id_cti.branch("2");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.3 -- smime cti proofOfDelivery */
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfDelivery = id_cti.branch("3");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfDelivery = id_cti.branch("3");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.4 -- smime cti proofOfSender */
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfSender   = id_cti.branch("4");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfSender   = id_cti.branch("4");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.5 -- smime cti proofOfApproval */
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfApproval = id_cti.branch("5");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfApproval = id_cti.branch("5");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.6 -- smime cti proofOfCreation */
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfCreation = id_cti.branch("6");
+    ASN1ObjectIdentifier    id_cti_ets_proofOfCreation = id_cti.branch("6");
     
     //
     // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)}
     //
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2 - smime attributes */
-    static final ASN1ObjectIdentifier    id_aa = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.2");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2 - smime attributes */
+    ASN1ObjectIdentifier    id_aa = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.2");
 
 
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.1 -- smime attribute receiptRequest */
-    static final ASN1ObjectIdentifier id_aa_receiptRequest = id_aa.branch("1");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.1 -- smime attribute receiptRequest */
+    ASN1ObjectIdentifier id_aa_receiptRequest = id_aa.branch("1");
     
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
-    static final ASN1ObjectIdentifier id_aa_contentHint      = id_aa.branch("4"); // See RFC 2634
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.5 */
-    static final ASN1ObjectIdentifier id_aa_msgSigDigest     = id_aa.branch("5");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.10 */
-    static final ASN1ObjectIdentifier id_aa_contentReference = id_aa.branch("10");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
+    ASN1ObjectIdentifier id_aa_contentHint      = id_aa.branch("4"); // See RFC 2634
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.5 */
+    ASN1ObjectIdentifier id_aa_msgSigDigest     = id_aa.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.10 */
+    ASN1ObjectIdentifier id_aa_contentReference = id_aa.branch("10");
     /*
      * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11}
      * 
      */
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.11 */
-    static final ASN1ObjectIdentifier id_aa_encrypKeyPref        = id_aa.branch("11");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.12 */
-    static final ASN1ObjectIdentifier id_aa_signingCertificate   = id_aa.branch("12");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.47 */
-    static final ASN1ObjectIdentifier id_aa_signingCertificateV2 = id_aa.branch("47");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.11 */
+    ASN1ObjectIdentifier id_aa_encrypKeyPref        = id_aa.branch("11");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.12 */
+    ASN1ObjectIdentifier id_aa_signingCertificate   = id_aa.branch("12");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.47 */
+    ASN1ObjectIdentifier id_aa_signingCertificateV2 = id_aa.branch("47");
 
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.7 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
-    static final ASN1ObjectIdentifier id_aa_contentIdentifier = id_aa.branch("7"); // See RFC 2634
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.7 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
+    ASN1ObjectIdentifier id_aa_contentIdentifier = id_aa.branch("7"); // See RFC 2634
 
     /*
      * RFC 3126
      */
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.14 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.14 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14");
     
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.15 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.16 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.17 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.18 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.15 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.16 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.17 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.18 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18");
     /** PKCS#9: 1.2.840.113549.1.9.16.6.2.19 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.20 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.21 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.22 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.23 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.24 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.25 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.26 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26");
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.27 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
-    static final ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27");
+    ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.20 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.21 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.22 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.23 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.24 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.25 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.26 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.27 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
+    ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27");
 
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.37 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.5">RFC 4108</a> */
-    static final ASN1ObjectIdentifier id_aa_decryptKeyID = id_aa.branch("37");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.37 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.5">RFC 4108</a> */
+    ASN1ObjectIdentifier id_aa_decryptKeyID = id_aa.branch("37");
 
-    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.38 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.6">RFC 4108</a> */
-    static final ASN1ObjectIdentifier id_aa_implCryptoAlgs = id_aa.branch("38");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.38 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.6">RFC 4108</a> */
+    ASN1ObjectIdentifier id_aa_implCryptoAlgs = id_aa.branch("38");
 
     /** PKCS#9: 1.2.840.113549.1.9.16.2.54 <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
-    static final ASN1ObjectIdentifier id_aa_asymmDecryptKeyID = id_aa.branch("54");
+    ASN1ObjectIdentifier id_aa_asymmDecryptKeyID = id_aa.branch("54");
 
     /** PKCS#9: 1.2.840.113549.1.9.16.2.43   <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
-    static final ASN1ObjectIdentifier id_aa_implCompressAlgs = id_aa.branch("43");
+    ASN1ObjectIdentifier id_aa_implCompressAlgs = id_aa.branch("43");
     /** PKCS#9: 1.2.840.113549.1.9.16.2.40   <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
-    static final ASN1ObjectIdentifier id_aa_communityIdentifiers = id_aa.branch("40");
+    ASN1ObjectIdentifier id_aa_communityIdentifiers = id_aa.branch("40");
 
     /** @deprecated use id_aa_ets_sigPolicyId instead */
-    static final ASN1ObjectIdentifier id_aa_sigPolicyId    = id_aa_ets_sigPolicyId;
+    ASN1ObjectIdentifier id_aa_sigPolicyId    = id_aa_ets_sigPolicyId;
     /** @deprecated use id_aa_ets_commitmentType instead */
-    static final ASN1ObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType;
+    ASN1ObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType;
     /** @deprecated use id_aa_ets_signerLocation instead */
-    static final ASN1ObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation;
+    ASN1ObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation;
     /** @deprecated use id_aa_ets_otherSigCert instead */
-    static final ASN1ObjectIdentifier id_aa_otherSigCert   = id_aa_ets_otherSigCert;
+    ASN1ObjectIdentifier id_aa_otherSigCert   = id_aa_ets_otherSigCert;
     
     /**
      * id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
@@ -368,61 +368,61 @@
     final String id_spq = "1.2.840.113549.1.9.16.5";
 
     /** SMIME SPQ URI:     1.2.840.113549.1.9.16.5.1 */
-    static final ASN1ObjectIdentifier id_spq_ets_uri     = new ASN1ObjectIdentifier(id_spq + ".1");
+    ASN1ObjectIdentifier id_spq_ets_uri     = new ASN1ObjectIdentifier(id_spq + ".1");
     /** SMIME SPQ UNOTICE: 1.2.840.113549.1.9.16.5.2 */
-    static final ASN1ObjectIdentifier id_spq_ets_unotice = new ASN1ObjectIdentifier(id_spq + ".2");
+    ASN1ObjectIdentifier id_spq_ets_unotice = new ASN1ObjectIdentifier(id_spq + ".2");
 
     //
     // pkcs-12 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
     //
     /** PKCS#12: 1.2.840.113549.1.12 */
-    static final ASN1ObjectIdentifier   pkcs_12                  = new ASN1ObjectIdentifier("1.2.840.113549.1.12");
+    ASN1ObjectIdentifier   pkcs_12                  = new ASN1ObjectIdentifier("1.2.840.113549.1.12");
     /** PKCS#12: 1.2.840.113549.1.12.10.1 */
-    static final ASN1ObjectIdentifier   bagtypes                 = pkcs_12.branch("10.1");
+    ASN1ObjectIdentifier   bagtypes                 = pkcs_12.branch("10.1");
 
     /** PKCS#12: 1.2.840.113549.1.12.10.1.1 */
-    static final ASN1ObjectIdentifier    keyBag                  = bagtypes.branch("1");
+    ASN1ObjectIdentifier    keyBag                  = bagtypes.branch("1");
     /** PKCS#12: 1.2.840.113549.1.12.10.1.2 */
-    static final ASN1ObjectIdentifier    pkcs8ShroudedKeyBag     = bagtypes.branch("2");
+    ASN1ObjectIdentifier    pkcs8ShroudedKeyBag     = bagtypes.branch("2");
     /** PKCS#12: 1.2.840.113549.1.12.10.1.3 */
-    static final ASN1ObjectIdentifier    certBag                 = bagtypes.branch("3");
+    ASN1ObjectIdentifier    certBag                 = bagtypes.branch("3");
     /** PKCS#12: 1.2.840.113549.1.12.10.1.4 */
-    static final ASN1ObjectIdentifier    crlBag                  = bagtypes.branch("4");
+    ASN1ObjectIdentifier    crlBag                  = bagtypes.branch("4");
     /** PKCS#12: 1.2.840.113549.1.12.10.1.5 */
-    static final ASN1ObjectIdentifier    secretBag               = bagtypes.branch("5");
+    ASN1ObjectIdentifier    secretBag               = bagtypes.branch("5");
     /** PKCS#12: 1.2.840.113549.1.12.10.1.6 */
-    static final ASN1ObjectIdentifier    safeContentsBag         = bagtypes.branch("6");
+    ASN1ObjectIdentifier    safeContentsBag         = bagtypes.branch("6");
 
     /** PKCS#12: 1.2.840.113549.1.12.1 */
-    static final ASN1ObjectIdentifier    pkcs_12PbeIds           = pkcs_12.branch("1");
+    ASN1ObjectIdentifier    pkcs_12PbeIds           = pkcs_12.branch("1");
 
     /** PKCS#12: 1.2.840.113549.1.12.1.1 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC4          = pkcs_12PbeIds.branch("1");
+    ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC4          = pkcs_12PbeIds.branch("1");
     /** PKCS#12: 1.2.840.113549.1.12.1.2 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC4           = pkcs_12PbeIds.branch("2");
+    ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC4           = pkcs_12PbeIds.branch("2");
     /** PKCS#12: 1.2.840.113549.1.12.1.3 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd3_KeyTripleDES_CBC = pkcs_12PbeIds.branch("3");
+    ASN1ObjectIdentifier    pbeWithSHAAnd3_KeyTripleDES_CBC = pkcs_12PbeIds.branch("3");
     /** PKCS#12: 1.2.840.113549.1.12.1.4 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd2_KeyTripleDES_CBC = pkcs_12PbeIds.branch("4");
+    ASN1ObjectIdentifier    pbeWithSHAAnd2_KeyTripleDES_CBC = pkcs_12PbeIds.branch("4");
     /** PKCS#12: 1.2.840.113549.1.12.1.5 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC2_CBC      = pkcs_12PbeIds.branch("5");
+    ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC2_CBC      = pkcs_12PbeIds.branch("5");
     /** PKCS#12: 1.2.840.113549.1.12.1.6 */
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC2_CBC       = pkcs_12PbeIds.branch("6");
+    ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC2_CBC       = pkcs_12PbeIds.branch("6");
 
     /**
      * PKCS#12: 1.2.840.113549.1.12.1.6
      * @deprecated use pbeWithSHAAnd40BitRC2_CBC
      */
-    static final ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
+    ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
 
     /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
-    static final ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
+    ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
-    static final ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
+    ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
-    static final ASN1ObjectIdentifier    id_alg_ESDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.5");
+    ASN1ObjectIdentifier    id_alg_ESDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.5");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
-    static final ASN1ObjectIdentifier    id_alg_SSDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.10");
+    ASN1ObjectIdentifier    id_alg_SSDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.10");
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
index 7885a79..ce7e075 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
@@ -22,7 +22,7 @@
     private Pfx(
         ASN1Sequence   seq)
     {
-        BigInteger  version = ((ASN1Integer)seq.getObjectAt(0)).getValue();
+        BigInteger  version = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();
         if (version.intValue() != 3)
         {
             throw new IllegalArgumentException("wrong version for PFX PDU");
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
index 7f02e70..85700b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -4,9 +4,9 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -14,27 +14,65 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.BigIntegers;
 
+/**
+ * RFC 5958
+ *
+ * <pre>
+ *  [IMPLICIT TAGS]
+ *
+ *  OneAsymmetricKey ::= SEQUENCE {
+ *      version                   Version,
+ *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+ *      privateKey                PrivateKey,
+ *      attributes            [0] Attributes OPTIONAL,
+ *      ...,
+ *      [[2: publicKey        [1] PublicKey OPTIONAL ]],
+ *      ...
+ *  }
+ *
+ *  PrivateKeyInfo ::= OneAsymmetricKey
+ *
+ *  Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)
+ *
+ *  PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ *                                     { PUBLIC-KEY,
+ *                                       { PrivateKeyAlgorithms } }
+ *
+ *  PrivateKey ::= OCTET STRING
+ *                     -- Content varies based on type of key.  The
+ *                     -- algorithm identifier dictates the format of
+ *                     -- the key.
+ *
+ *  PublicKey ::= BIT STRING
+ *                     -- Content varies based on type of key.  The
+ *                     -- algorithm identifier dictates the format of
+ *                     -- the key.
+ *
+ *  Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+ *  </pre>
+ */
 public class PrivateKeyInfo
     extends ASN1Object
 {
-    private ASN1OctetString         privKey;
-    private AlgorithmIdentifier     algId;
-    private ASN1Set                 attributes;
+    private ASN1Integer version;
+    private AlgorithmIdentifier privateKeyAlgorithm;
+    private ASN1OctetString privateKey;
+    private ASN1Set attributes;
+    private ASN1BitString publicKey;
 
-    public static PrivateKeyInfo getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static PrivateKeyInfo getInstance(ASN1TaggedObject obj, boolean explicit)
     {
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
 
-    public static PrivateKeyInfo getInstance(
-        Object  obj)
+    public static PrivateKeyInfo getInstance(Object obj)
     {
         if (obj instanceof PrivateKeyInfo)
         {
@@ -47,118 +85,165 @@
 
         return null;
     }
-        
-    public PrivateKeyInfo(
-        AlgorithmIdentifier algId,
-        ASN1Encodable       privateKey)
-        throws IOException
+
+    private static int getVersionValue(ASN1Integer version)
     {
-        this(algId, privateKey, null);
+        BigInteger bigValue = version.getValue();
+        if (bigValue.compareTo(BigIntegers.ZERO) < 0 || bigValue.compareTo(BigIntegers.ONE) > 0)
+        {
+            throw new IllegalArgumentException("invalid version for private key info");
+        }
+        return bigValue.intValue();
     }
 
     public PrivateKeyInfo(
-        AlgorithmIdentifier algId,
-        ASN1Encodable       privateKey,
-        ASN1Set             attributes)
+        AlgorithmIdentifier privateKeyAlgorithm,
+        ASN1Encodable privateKey)
         throws IOException
     {
-        this.privKey = new DEROctetString(privateKey.toASN1Primitive().getEncoded(ASN1Encoding.DER));
-        this.algId = algId;
+        this(privateKeyAlgorithm, privateKey, null, null);
+    }
+
+    public PrivateKeyInfo(
+        AlgorithmIdentifier privateKeyAlgorithm,
+        ASN1Encodable privateKey,
+        ASN1Set attributes)
+        throws IOException
+    {
+        this(privateKeyAlgorithm, privateKey, attributes, null);
+    }
+
+    public PrivateKeyInfo(
+        AlgorithmIdentifier privateKeyAlgorithm,
+        ASN1Encodable privateKey,
+        ASN1Set attributes,
+        byte[] publicKey)
+        throws IOException
+    {
+        this.version = new ASN1Integer(publicKey != null ? BigIntegers.ONE : BigIntegers.ZERO);
+        this.privateKeyAlgorithm = privateKeyAlgorithm;
+        this.privateKey = new DEROctetString(privateKey);
         this.attributes = attributes;
+        this.publicKey = publicKey == null ? null : new DERBitString(publicKey);
     }
 
-    /**
-     * @deprecated use PrivateKeyInfo.getInstance()
-     * @param seq
-     */
-    public PrivateKeyInfo(
-        ASN1Sequence  seq)
+    private PrivateKeyInfo(ASN1Sequence seq)
     {
         Enumeration e = seq.getObjects();
 
-        BigInteger  version = ((ASN1Integer)e.nextElement()).getValue();
-        if (version.intValue() != 0)
-        {
-            throw new IllegalArgumentException("wrong version for private key info");
-        }
+        this.version = ASN1Integer.getInstance(e.nextElement());
 
-        algId = AlgorithmIdentifier.getInstance(e.nextElement());
-        privKey = ASN1OctetString.getInstance(e.nextElement());
-        
-        if (e.hasMoreElements())
+        int versionValue = getVersionValue(version);
+
+        this.privateKeyAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+        this.privateKey = ASN1OctetString.getInstance(e.nextElement());
+
+        int lastTag = -1;
+        while (e.hasMoreElements())
         {
-           attributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
+            ASN1TaggedObject tagged = (ASN1TaggedObject)e.nextElement();
+
+            int tag = tagged.getTagNo();
+            if (tag <= lastTag)
+            {
+                throw new IllegalArgumentException("invalid optional field in private key info");
+            }
+
+            lastTag = tag;
+
+            switch (tag)
+            {
+            case 0:
+            {
+                this.attributes = ASN1Set.getInstance(tagged, false);
+                break;
+            }
+            case 1:
+            {
+                if (versionValue < 1)
+                {
+                    throw new IllegalArgumentException("'publicKey' requires version v2(1) or later");
+                }
+
+                this.publicKey = DERBitString.getInstance(tagged, false);
+                break;
+            }
+            default:
+            {
+                throw new IllegalArgumentException("unknown optional field in private key info");
+            }
+            }
         }
     }
 
-    public AlgorithmIdentifier getPrivateKeyAlgorithm()
-    {
-        return algId;
-    }
-        /**
-          * @deprecated use getPrivateKeyAlgorithm()
-     */
-    public AlgorithmIdentifier getAlgorithmId()
-    {
-        return algId;
-    }
-
-    public ASN1Encodable parsePrivateKey()
-        throws IOException
-    {
-        return ASN1Primitive.fromByteArray(privKey.getOctets());
-    }
-
-    /**
-          * @deprecated use parsePrivateKey()
-     */
-    public ASN1Primitive getPrivateKey()
-    {
-        try
-        {
-            return parsePrivateKey().toASN1Primitive();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException("unable to parse private key");
-        }
-    }
-    
     public ASN1Set getAttributes()
     {
         return attributes;
     }
 
+    public AlgorithmIdentifier getPrivateKeyAlgorithm()
+    {
+        return privateKeyAlgorithm;
+    }
+
+    public ASN1Encodable parsePrivateKey()
+        throws IOException
+    {
+        return ASN1Primitive.fromByteArray(privateKey.getOctets());
+    }
+
     /**
-     * write out an RSA private key with its associated information
-     * as described in PKCS8.
-     * <pre>
-     *      PrivateKeyInfo ::= SEQUENCE {
-     *                              version Version,
-     *                              privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
-     *                              privateKey PrivateKey,
-     *                              attributes [0] IMPLICIT Attributes OPTIONAL 
-     *                          }
-     *      Version ::= INTEGER {v1(0)} (v1,...)
+     * Return true if a public key is present, false otherwise.
      *
-     *      PrivateKey ::= OCTET STRING
-     *
-     *      Attributes ::= SET OF Attribute
-     * </pre>
+     * @return true if public included, otherwise false.
      */
+    public boolean hasPublicKey()
+    {
+        return publicKey != null;
+    }
+
+    /**
+     * for when the public key is an encoded object - if the bitstring
+     * can't be decoded this routine throws an IOException.
+     *
+     * @return the public key as an ASN.1 primitive.
+     * @throws IOException - if the bit string doesn't represent a DER
+     * encoded object.
+     */
+    public ASN1Encodable parsePublicKey()
+        throws IOException
+    {
+        return publicKey == null ? null : ASN1Primitive.fromByteArray(publicKey.getOctets());
+    }
+
+    /**
+     * for when the public key is raw bits.
+     *
+     * @return the public key as the raw bit string...
+     */
+    public ASN1BitString getPublicKeyData()
+    {
+        return publicKey;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
 
-        v.add(new ASN1Integer(0));
-        v.add(algId);
-        v.add(privKey);
+        v.add(version);
+        v.add(privateKeyAlgorithm);
+        v.add(privateKey);
 
         if (attributes != null)
         {
             v.add(new DERTaggedObject(false, 0, attributes));
         }
-        
+
+        if (publicKey != null)
+        {
+            v.add(new DERTaggedObject(false, 1, publicKey));
+        }
+
         return new DERSequence(v);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/RosstandartObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/RosstandartObjectIdentifiers.java
index f4dfe43..888b40d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/RosstandartObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/RosstandartObjectIdentifiers.java
@@ -16,13 +16,31 @@
 
     static final ASN1ObjectIdentifier id_tc26_hmac_gost_3411_12_512 = id_tc26.branch("1.4.2");
 
-    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_256_paramSetA = id_tc26.branch("2.1.1.1");
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_256 = id_tc26.branch("1.1.1");
 
-    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetA = id_tc26.branch("2.1.2.1");
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512 = id_tc26.branch("1.1.2");
 
-    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetB = id_tc26.branch("2.1.2.2");
+    static final ASN1ObjectIdentifier id_tc26_signwithdigest_gost_3410_12_256 = id_tc26.branch("1.3.2");
 
-    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetC = id_tc26.branch("2.1.2.3");
+    static final ASN1ObjectIdentifier id_tc26_signwithdigest_gost_3410_12_512 = id_tc26.branch("1.3.3");
+
+    static final ASN1ObjectIdentifier id_tc26_agreement = id_tc26.branch("1.6");
+
+    static final ASN1ObjectIdentifier id_tc26_agreement_gost_3410_12_256 = id_tc26_agreement.branch("1");
+
+    static final ASN1ObjectIdentifier id_tc26_agreement_gost_3410_12_512  = id_tc26_agreement.branch("2");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_256_paramSet = id_tc26.branch("2.1.1");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_256_paramSetA = id_tc26_gost_3410_12_256_paramSet.branch("1");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSet = id_tc26.branch("2.1.2");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetA = id_tc26_gost_3410_12_512_paramSet.branch("1");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetB = id_tc26_gost_3410_12_512_paramSet.branch("2");
+
+    static final ASN1ObjectIdentifier id_tc26_gost_3410_12_512_paramSetC = id_tc26_gost_3410_12_512_paramSet.branch("3");
 
     static final ASN1ObjectIdentifier id_tc26_gost_28147_param_Z = id_tc26.branch("2.5.1.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/package.html
new file mode 100644
index 0000000..9d1b842
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/rosstandart/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes algorithms from the Russian Federal Agency on Technical Regulating and Metrology - Rosstandart.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
index 1ddc17d..bb215be 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
@@ -9,6 +9,8 @@
  *  ellipticCurve OBJECT IDENTIFIER ::= {
  *        iso(1) identified-organization(3) certicom(132) curve(0)
  *  }
+ *  secg-scheme OBJECT IDENTIFIER ::= {
+ *     iso(1) identified-organization(3) certicom(132) schemes(1) }
  * </pre>
  */
 public interface SECObjectIdentifiers
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java
index eec29e6..79d693a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/smime/SMIMEAttributes.java
@@ -5,6 +5,6 @@
 
 public interface SMIMEAttributes
 {
-    public static final ASN1ObjectIdentifier  smimeCapabilities = PKCSObjectIdentifiers.pkcs_9_at_smimeCapabilities;
-    public static final ASN1ObjectIdentifier  encrypKeyPref = PKCSObjectIdentifiers.id_aa_encrypKeyPref;
+    ASN1ObjectIdentifier  smimeCapabilities = PKCSObjectIdentifiers.pkcs_9_at_smimeCapabilities;
+    ASN1ObjectIdentifier  encrypKeyPref = PKCSObjectIdentifiers.id_aa_encrypKeyPref;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
index 121cc2f..4879180 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
@@ -307,27 +307,27 @@
 
     static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
     {
-        objIds.put(name, oid);
+        objIds.put(Strings.toLowerCase(name), oid);
         names.put(oid, name);
         curves.put(oid, holder);
     }
 
     static
     {
-        defineCurve("brainpoolp160r1", TeleTrusTObjectIdentifiers.brainpoolP160r1, brainpoolP160r1);
-        defineCurve("brainpoolp160t1", TeleTrusTObjectIdentifiers.brainpoolP160t1, brainpoolP160t1);
-        defineCurve("brainpoolp192r1", TeleTrusTObjectIdentifiers.brainpoolP192r1, brainpoolP192r1);
-        defineCurve("brainpoolp192t1", TeleTrusTObjectIdentifiers.brainpoolP192t1, brainpoolP192t1);
-        defineCurve("brainpoolp224r1", TeleTrusTObjectIdentifiers.brainpoolP224r1, brainpoolP224r1);
-        defineCurve("brainpoolp224t1", TeleTrusTObjectIdentifiers.brainpoolP224t1, brainpoolP224t1);
-        defineCurve("brainpoolp256r1", TeleTrusTObjectIdentifiers.brainpoolP256r1, brainpoolP256r1);
-        defineCurve("brainpoolp256t1", TeleTrusTObjectIdentifiers.brainpoolP256t1, brainpoolP256t1);
-        defineCurve("brainpoolp320r1", TeleTrusTObjectIdentifiers.brainpoolP320r1, brainpoolP320r1);
-        defineCurve("brainpoolp320t1", TeleTrusTObjectIdentifiers.brainpoolP320t1, brainpoolP320t1);
-        defineCurve("brainpoolp384r1", TeleTrusTObjectIdentifiers.brainpoolP384r1, brainpoolP384r1);
-        defineCurve("brainpoolp384t1", TeleTrusTObjectIdentifiers.brainpoolP384t1, brainpoolP384t1);
-        defineCurve("brainpoolp512r1", TeleTrusTObjectIdentifiers.brainpoolP512r1, brainpoolP512r1);
-        defineCurve("brainpoolp512t1", TeleTrusTObjectIdentifiers.brainpoolP512t1, brainpoolP512t1);
+        defineCurve("brainpoolP160r1", TeleTrusTObjectIdentifiers.brainpoolP160r1, brainpoolP160r1);
+        defineCurve("brainpoolP160t1", TeleTrusTObjectIdentifiers.brainpoolP160t1, brainpoolP160t1);
+        defineCurve("brainpoolP192r1", TeleTrusTObjectIdentifiers.brainpoolP192r1, brainpoolP192r1);
+        defineCurve("brainpoolP192t1", TeleTrusTObjectIdentifiers.brainpoolP192t1, brainpoolP192t1);
+        defineCurve("brainpoolP224r1", TeleTrusTObjectIdentifiers.brainpoolP224r1, brainpoolP224r1);
+        defineCurve("brainpoolP224t1", TeleTrusTObjectIdentifiers.brainpoolP224t1, brainpoolP224t1);
+        defineCurve("brainpoolP256r1", TeleTrusTObjectIdentifiers.brainpoolP256r1, brainpoolP256r1);
+        defineCurve("brainpoolP256t1", TeleTrusTObjectIdentifiers.brainpoolP256t1, brainpoolP256t1);
+        defineCurve("brainpoolP320r1", TeleTrusTObjectIdentifiers.brainpoolP320r1, brainpoolP320r1);
+        defineCurve("brainpoolP320t1", TeleTrusTObjectIdentifiers.brainpoolP320t1, brainpoolP320t1);
+        defineCurve("brainpoolP384r1", TeleTrusTObjectIdentifiers.brainpoolP384r1, brainpoolP384r1);
+        defineCurve("brainpoolP384t1", TeleTrusTObjectIdentifiers.brainpoolP384t1, brainpoolP384t1);
+        defineCurve("brainpoolP512r1", TeleTrusTObjectIdentifiers.brainpoolP512r1, brainpoolP512r1);
+        defineCurve("brainpoolP512t1", TeleTrusTObjectIdentifiers.brainpoolP512t1, brainpoolP512t1);
     }
 
     public static X9ECParameters getByName(
@@ -388,8 +388,8 @@
      * contained in this structure.
      */
     public static Enumeration getNames()
-    {
-        return objIds.keys();
+    {    // we need to use names so we get the mixed case names.
+        return names.elements();
     }
 
     public static ASN1ObjectIdentifier getOID(short curvesize, boolean twisted)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/ASN1IntegerTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/ASN1IntegerTest.java
new file mode 100644
index 0000000..c9eaed4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/ASN1IntegerTest.java
@@ -0,0 +1,356 @@
+package org.bouncycastle.asn1.test;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.util.Properties;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ASN1IntegerTest
+    extends SimpleTest
+{
+    private static final byte[] suspectKey = Base64.decode(
+        "MIGJAoGBAHNc+iExm94LUrJdPSJ4QJ9tDRuvaNmGVHpJ4X7a5zKI02v+2E7RotuiR2MHDJfVJkb9LUs2kb3XBlyENhtMLsbeH+3Muy3" +
+            "hGDlh/mLJSh1s4c5jDKBRYOHom7Uc8wP0P2+zBCA+OEdikNDFBaP5PbR2Xq9okG2kPh35M2quAiMTAgMBAAE=");
+
+    public String getName()
+    {
+        return "ASN1Integer";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+
+        ASN1Sequence.getInstance(suspectKey);
+
+        testValidEncodingSingleByte();
+        testValidEncodingMultiByte();
+        testInvalidEncoding_00();
+        testInvalidEncoding_ff();
+        testInvalidEncoding_00_32bits();
+        testInvalidEncoding_ff_32bits();
+        //testLooseInvalidValidEncoding_FF_32B();
+        //testLooseInvalidValidEncoding_zero_32B();
+        testLooseValidEncoding_zero_32BAligned();
+        testLooseValidEncoding_FF_32BAligned();
+        testLooseValidEncoding_FF_32BAligned_1not0();
+        testLooseValidEncoding_FF_32BAligned_2not0();
+        testOversizedEncoding();
+        
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+
+        new ASN1Integer(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e"));
+
+        new ASN1Enumerated(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e"));
+
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        
+        try
+        {
+            new ASN1Integer(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b"));
+
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+
+        isTrue(!Properties.setThreadOverride("org.bouncycastle.asn1.allow_unsafe_integer", true));
+        
+        new ASN1Integer(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b"));
+
+        isTrue(Properties.removeThreadOverride("org.bouncycastle.asn1.allow_unsafe_integer"));
+
+        try
+        {
+            ASN1Sequence.getInstance(suspectKey);
+
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        { 
+            isEquals("test 1", "failed to construct sequence from byte[]: corrupted stream detected", e.getMessage());
+        }
+
+        try
+        {
+            new ASN1Integer(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e"));
+
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+
+        try
+        {
+            new ASN1Enumerated(Hex.decode("ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e"));
+
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed enumerated", e.getMessage());
+        }
+    }
+
+    /**
+     * Ensure existing single byte behavior.
+     */
+    public void testValidEncodingSingleByte()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Without property, single byte.
+        //
+        byte[] rawInt = Hex.decode("10");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(i.getValue().intValue(), 16);
+
+        //
+        // With property set.
+        //
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+
+        rawInt = Hex.decode("10");
+        i = new ASN1Integer(rawInt);
+        isEquals(i.getValue().intValue(), 16);
+
+    }
+
+    public void testValidEncodingMultiByte()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Without property, single byte.
+        //
+        byte[] rawInt = Hex.decode("10FF");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(i.getValue().intValue(), 4351);
+
+        //
+        // With property set.
+        //
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+
+        rawInt = Hex.decode("10FF");
+        i = new ASN1Integer(rawInt);
+        isEquals(i.getValue().intValue(), 4351);
+
+    }
+
+    public void testInvalidEncoding_00()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        try
+        {
+            byte[] rawInt = Hex.decode("0010FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            isEquals(i.getValue().intValue(), 4351);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    public void testInvalidEncoding_ff()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        try
+        {
+            byte[] rawInt = Hex.decode("FF81FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    public void testInvalidEncoding_00_32bits()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Check what would pass loose validation fails outside of loose validation.
+        //
+        try
+        {
+            byte[] rawInt = Hex.decode("0000000010FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            isEquals(i.getValue().intValue(), 4351);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    public void testInvalidEncoding_ff_32bits()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Check what would pass loose validation fails outside of loose validation.
+        //
+        try
+        {
+            byte[] rawInt = Hex.decode("FFFFFFFF01FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    /*
+     Unfortunately it turns out that integers stored without sign bits that are assumed to be
+     unsigned.. this means a string of FF may occur and then the user will call getPositiveValue().
+     Sigh..
+    public void testLooseInvalidValidEncoding_zero_32B()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should still fail as loose validation only permits 3 leading 0x00 bytes.
+        //
+        try
+        {
+            System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+            byte[] rawInt = Hex.decode("0000000010FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    public void testLooseInvalidValidEncoding_FF_32B()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should still fail as loose validation only permits 3 leading 0xFF bytes.
+        //
+        try
+        {
+            System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+            byte[] rawInt = Hex.decode("FFFFFFFF10FF");
+            ASN1Integer i = new ASN1Integer(rawInt);
+            fail("Expecting illegal argument exception.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+    */
+
+    public void testLooseValidEncoding_zero_32BAligned()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should pass as loose validation permits 3 leading 0x00 bytes.
+        //
+
+        System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+        byte[] rawInt = Hex.decode("00000010FF000000");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(72997666816L, i.getValue().longValue());
+
+    }
+
+    public void testLooseValidEncoding_FF_32BAligned()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should pass as loose validation permits 3
+
+        System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+        byte[] rawInt = Hex.decode("FFFFFF10FF000000");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(-1026513960960L, i.getValue().longValue());
+
+    }
+
+    public void testLooseValidEncoding_FF_32BAligned_1not0()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should pass as loose validation permits 3 leading 0xFF bytes.
+        //
+
+        System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+        byte[] rawInt = Hex.decode("FFFEFF10FF000000");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(-282501490671616L, i.getValue().longValue());
+
+    }
+
+    public void testLooseValidEncoding_FF_32BAligned_2not0()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should pass as loose validation permits 3 leading 0xFF bytes.
+        //
+
+        System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+        byte[] rawInt = Hex.decode("FFFFFE10FF000000");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(-2126025588736L, i.getValue().longValue());
+    }
+
+    public void testOversizedEncoding()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "false");
+        //
+        // Should pass as loose validation permits 3 leading 0xFF bytes.
+        //
+
+        System.getProperties().put("org.bouncycastle.asn1.allow_unsafe_integer", "true");
+        byte[] rawInt = Hex.decode("FFFFFFFE10FF000000000000");
+        ASN1Integer i = new ASN1Integer(rawInt);
+        isEquals(new BigInteger(Hex.decode("FFFFFFFE10FF000000000000")), i.getValue());
+
+        rawInt = Hex.decode("FFFFFFFFFE10FF000000000000");
+        try
+        {
+            new ASN1Integer(rawInt);
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("malformed integer", e.getMessage());
+        }
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new ASN1IntegerTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/CMCPublicationInfoTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/CMCPublicationInfoTest.java
index b6682f2..3cf5ef1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/CMCPublicationInfoTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/CMCPublicationInfoTest.java
@@ -4,12 +4,12 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.cmc.CMCPublicationInfo;
 import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
+import org.bouncycastle.asn1.crmf.SinglePubInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -33,10 +33,7 @@
             secureRandom.nextBytes(hashes[i]);
         }
 
-        PKIPublicationInfo pinfo = PKIPublicationInfo.getInstance(
-            new DERSequence(
-                new ASN1Encodable[]{new ASN1Integer(1L),  new DERSequence(new ASN1Integer(0L)) })
-        );
+        PKIPublicationInfo pinfo = new PKIPublicationInfo(new SinglePubInfo(SinglePubInfo.dontCare, null));
 
         CMCPublicationInfo cmcPublicationInfo = new CMCPublicationInfo(testIA,hashes,pinfo);
         byte[] b = cmcPublicationInfo.getEncoded();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/CertifiedKeyPairTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/CertifiedKeyPairTest.java
new file mode 100644
index 0000000..c3b0dbf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/CertifiedKeyPairTest.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.asn1.test;
+
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.cmp.CMPCertificate;
+import org.bouncycastle.asn1.cmp.CertOrEncCert;
+import org.bouncycastle.asn1.cmp.CertifiedKeyPair;
+import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CertifiedKeyPairTest
+    extends SimpleTest
+{
+    byte[]  cert1 = Base64.decode(
+           "MIIDXjCCAsegAwIBAgIBBzANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
+         + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
+         + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
+         + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
+         + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU2MjFaFw0wMTA2"
+         + "MDIwNzU2MjFaMIG4MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
+         + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
+         + "dGQxFzAVBgNVBAsTDldlYnNlcnZlciBUZWFtMR0wGwYDVQQDExR3d3cyLmNvbm5l"
+         + "Y3Q0LmNvbS5hdTEoMCYGCSqGSIb3DQEJARYZd2VibWFzdGVyQGNvbm5lY3Q0LmNv"
+         + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArvDxclKAhyv7Q/Wmr2re"
+         + "Gw4XL9Cnh9e+6VgWy2AWNy/MVeXdlxzd7QAuc1eOWQkGQEiLPy5XQtTY+sBUJ3AO"
+         + "Rvd2fEVJIcjf29ey7bYua9J/vz5MG2KYo9/WCHIwqD9mmG9g0xLcfwq/s8ZJBswE"
+         + "7sb85VU+h94PTvsWOsWuKaECAwEAAaN3MHUwJAYDVR0RBB0wG4EZd2VibWFzdGVy"
+         + "QGNvbm5lY3Q0LmNvbS5hdTA6BglghkgBhvhCAQ0ELRYrbW9kX3NzbCBnZW5lcmF0"
+         + "ZWQgY3VzdG9tIHNlcnZlciBjZXJ0aWZpY2F0ZTARBglghkgBhvhCAQEEBAMCBkAw"
+         + "DQYJKoZIhvcNAQEEBQADgYEAotccfKpwSsIxM1Hae8DR7M/Rw8dg/RqOWx45HNVL"
+         + "iBS4/3N/TO195yeQKbfmzbAA2jbPVvIvGgTxPgO1MP4ZgvgRhasaa0qCJCkWvpM4"
+         + "yQf33vOiYQbpv4rTwzU8AmRlBG45WdjyNIigGV+oRc61aKCTnLq7zB8N3z1TF/bF"
+         + "5/8=");
+
+    byte[] encEncryptedValue = Hex.decode(
+        "30820145a11d060960864801650304010204109ed75dc2111f006e0ea707583" +
+            "daa49898241001fad2520dec6122c51f9f292fc96de9adb881a2101a49155de" +
+            "3e4b04a4699ee517d7a7623679812f62e0fc996854d89df2daa6850862f11e4" +
+            "f1751768e8a1a8da30d06092a864886f70d01010105000381d100bb1084782a" +
+            "3b326390ce1096b44eda81e89b24e117c22b197a0df3ff3d181a5e3d96f30f6" +
+            "a7f8b545733a867f27f299ff3c2c0ec64bcdca18f566a5e3be893e4842a7442" +
+            "184a4d147066515d8bcb9aa7d8e6655937e393b2c45186119bf0869702fc58a" +
+            "ae8a983ce5b54cf5273bcd2e5273e219e2947e41446612c8cf8f4d9e1ede52d" +
+            "25e00d505485083ea8359f7767c0ae66ff47894f9d621459f50f60e0376059a" +
+            "6a3b6fe7caca1c13274cf549f6721cf9f3654462687c7392a1c0efea2f393d9" +
+            "4a5d33b829de8bd521c7205069db");
+
+    public void performTest()
+        throws Exception
+    {
+        CertOrEncCert certOrEncCert = new CertOrEncCert(new CMPCertificate(Certificate.getInstance(cert1)));
+
+        CertifiedKeyPair ckp = new CertifiedKeyPair(certOrEncCert);
+
+        isEquals(certOrEncCert, ckp.getCertOrEncCert());
+        isTrue(null == ckp.getPrivateKey());
+        isTrue(null == ckp.getPublicationInfo());
+
+        encEqualTest(ckp);
+
+        PKIPublicationInfo pubInfo = new PKIPublicationInfo(PKIPublicationInfo.dontPublish);
+        ckp = new CertifiedKeyPair(certOrEncCert, null, pubInfo);
+
+        isEquals(certOrEncCert, ckp.getCertOrEncCert());
+        isTrue(null == ckp.getPrivateKey());
+        isEquals(pubInfo, ckp.getPublicationInfo());
+
+        encEqualTest(ckp);
+
+        EncryptedValue encValue = EncryptedValue.getInstance(encEncryptedValue);
+
+        ckp = new CertifiedKeyPair(certOrEncCert, encValue, null);
+
+        isEquals(certOrEncCert, ckp.getCertOrEncCert());
+        isEquals(encValue, ckp.getPrivateKey());
+        isTrue(null == ckp.getPublicationInfo());
+
+        encEqualTest(ckp);
+
+        ckp = new CertifiedKeyPair(certOrEncCert, encValue, pubInfo);
+
+        isEquals(certOrEncCert, ckp.getCertOrEncCert());
+        isEquals(encValue, ckp.getPrivateKey());
+        isEquals(pubInfo, ckp.getPublicationInfo());
+
+        encEqualTest(ckp);
+    }
+
+    private void encEqualTest(CertifiedKeyPair ckp)
+        throws IOException
+    {
+        byte[] b = ckp.getEncoded();
+
+        CertifiedKeyPair ckpResult = CertifiedKeyPair.getInstance(b);
+
+        isEquals(ckp, ckpResult);
+    }
+
+    public String getName()
+    {
+        return "CertifiedKeyPairTest";
+    }
+
+    public static void main(String[] args) {
+        runTest(new CertifiedKeyPairTest());
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/DERApplicationSpecificTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/DERApplicationSpecificTest.java
index c39522a..44d21d9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/DERApplicationSpecificTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/DERApplicationSpecificTest.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.test;
 
+import org.bouncycastle.asn1.ASN1ApplicationSpecific;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -92,7 +93,7 @@
     {
         testTaggedObject();
 
-        DERApplicationSpecific appSpec = (DERApplicationSpecific)ASN1Primitive.fromByteArray(sampleData);
+        ASN1ApplicationSpecific appSpec = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(sampleData);
 
         if (1 != appSpec.getApplicationTag())
         {
@@ -115,8 +116,7 @@
             fail("implicit read back failed");
         }
 
-        DERApplicationSpecific certObj = (DERApplicationSpecific)
-        ASN1Primitive.fromByteArray(certData);
+        ASN1ApplicationSpecific certObj = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(certData);
 
         if (!certObj.isConstructed() || certObj.getApplicationTag() != 33)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/DhSigStaticTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/DhSigStaticTest.java
new file mode 100644
index 0000000..656ce99
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/DhSigStaticTest.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1.test;
+
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.crmf.DhSigStatic;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class DhSigStaticTest
+    extends SimpleTest
+{
+
+
+    public void performTest()
+        throws Exception
+    {
+        // Test correct encode / decode
+
+        // Test encode and decode from Long and from other instance of DhSigStatic
+        DhSigStatic dhS = new DhSigStatic(new byte[20]);
+        instanceTest(dhS);
+
+        dhS = new DhSigStatic(new IssuerAndSerialNumber(new X500Name("CN=Test"), BigInteger.valueOf(20)), new byte[20]);
+        instanceTest(dhS);
+
+        dhS = DhSigStatic.getInstance(new DERSequence(new DEROctetString(Hex.decode("0102030405060708090a"))));
+
+        isTrue(Arrays.areEqual(Hex.decode("0102030405060708090a"), dhS.getHashValue()));
+
+        try
+        {
+            dhS = DhSigStatic.getInstance(new DERSequence(
+                new ASN1Encodable[] {
+                    new DEROctetString(Hex.decode("0102030405060708090a")),
+                    new DEROctetString(Hex.decode("0102030405060708090a")),
+                    new DEROctetString(Hex.decode("0102030405060708090a")) }));
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals(e.getMessage(), "sequence wrong length for DhSigStatic", e.getMessage());
+        }
+    }
+
+    private void instanceTest(DhSigStatic bpd)
+        throws IOException
+    {
+        byte[] b = bpd.getEncoded();
+        DhSigStatic resBpd = DhSigStatic.getInstance(b);
+        isTrue("hash check failed", areEqual(bpd.getHashValue(), resBpd.getHashValue()));
+        isEquals("issuerAndSerial failed", bpd.getIssuerAndSerial(), resBpd.getIssuerAndSerial());
+    }
+
+    public String getName()
+    {
+        return "DhSigStaticTest";
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        runTest(new DhSigStaticTest());
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/GeneralizedTimeTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/GeneralizedTimeTest.java
index 3a86370..4663484 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/GeneralizedTimeTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/GeneralizedTimeTest.java
@@ -5,6 +5,7 @@
 import java.util.SimpleTimeZone;
 import java.util.TimeZone;
 
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.DERGeneralizedTime;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -110,6 +111,40 @@
         "20020122022220.000Z"
     };
 
+    String[] derMzOutput = {
+        "20020122122220Z",
+        "20020122122220Z",
+        "20020122222220Z",
+        "20020122122220Z",
+        "20020122122220.1Z",
+        "20020122122220.1Z",
+        "20020122222220.1Z",
+        "20020122122220.1Z",
+        "20020122122220.01Z",
+        "20020122122220.01Z",
+        "20020122222220.01Z",
+        "20020122122220.01Z",
+        "20020122122220.001Z",
+        "20020122122220.001Z",
+        "20020122222220.001Z",
+        "20020122122220.001Z",
+        "20020122122220Z",
+        "20020122122220Z",
+        "20020122222220Z",
+        "20020122122220Z",
+        "20020122022220Z"
+    };
+
+    String[] truncOutput = {
+         "200201221222Z",
+         "2002012212Z"
+     };
+
+     String[] derTruncOutput = {
+         "20020122122200Z",
+         "20020122120000Z"
+     };
+
     public String getName()
     {
         return "GeneralizedTime";
@@ -124,7 +159,7 @@
 
         for (int i = 0; i != input.length; i++)
         {
-            DERGeneralizedTime    t = new DERGeneralizedTime(input[i]);
+            ASN1GeneralizedTime    t = new ASN1GeneralizedTime(input[i]);
 
             if (output[i].indexOf('G') > 0)   // don't check local time the same way
             {
@@ -153,13 +188,33 @@
 
         for (int i = 0; i != input.length; i++)
         {
-            DERGeneralizedTime    t = new DERGeneralizedTime(input[i]);
+            ASN1GeneralizedTime    t = new ASN1GeneralizedTime(input[i]);
 
             if (!dateF.format(t.getDate()).equals(mzOutput[i]))
             {
                 fail("failed long date conversion test");
             }
         }
+
+        for (int i = 0; i != mzOutput.length; i++)
+        {
+            ASN1GeneralizedTime    t = new DERGeneralizedTime(mzOutput[i]);
+
+            if (!areEqual(t.getEncoded(), new ASN1GeneralizedTime(derMzOutput[i]).getEncoded()))
+            {
+                fail("der encoding wrong");
+            }
+        }
+
+        for (int i = 0; i != truncOutput.length; i++)
+        {
+            DERGeneralizedTime    t = new DERGeneralizedTime(truncOutput[i]);
+
+            if (!areEqual(t.getEncoded(), new ASN1GeneralizedTime(derTruncOutput[i]).getEncoded()))
+            {
+                fail("trunc der encoding wrong");
+            }
+        }
     }
 
     private String calculateGMTOffset(Date date)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/GenerationTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/GenerationTest.java
index 1f819b3..bf34fc7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/GenerationTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/GenerationTest.java
@@ -6,8 +6,6 @@
 import java.math.BigInteger;
 import java.text.ParseException;
 import java.util.Date;
-import java.util.Hashtable;
-import java.util.Vector;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
@@ -41,8 +39,6 @@
 import org.bouncycastle.asn1.x509.V1TBSCertificateGenerator;
 import org.bouncycastle.asn1.x509.V2TBSCertListGenerator;
 import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.util.Arrays;
@@ -171,18 +167,11 @@
         //
         // add extensions
         //
-        Vector          order = new Vector();
-        Hashtable       extensions = new Hashtable();
-
-        order.addElement(X509Extension.authorityKeyIdentifier);
-        order.addElement(X509Extension.subjectKeyIdentifier);
-        order.addElement(X509Extension.keyUsage);
-
-        extensions.put(X509Extension.authorityKeyIdentifier, new X509Extension(true, new DEROctetString(createAuthorityKeyId(info, new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"), 2))));
-        extensions.put(X509Extension.subjectKeyIdentifier, new X509Extension(true, new DEROctetString(new SubjectKeyIdentifier(getDigest(info)))));
-        extensions.put(X509Extension.keyUsage, new X509Extension(false, new DEROctetString(new KeyUsage(KeyUsage.dataEncipherment))));
-
-        X509Extensions  ex = new X509Extensions(order, extensions);
+        Extensions ex = new Extensions(new Extension[] {
+            new Extension(Extension.authorityKeyIdentifier, true, new DEROctetString(createAuthorityKeyId(info, new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"), 2))),
+            new Extension(Extension.subjectKeyIdentifier, true, new DEROctetString(new SubjectKeyIdentifier(getDigest(info)))),
+            new Extension(Extension.keyUsage, false, new DEROctetString(new KeyUsage(KeyUsage.dataEncipherment)))
+        });
 
         gen.setExtensions(ex);
 
@@ -250,14 +239,9 @@
         //
         // add extensions
         //
-        Vector          order = new Vector();
-        Hashtable       extensions = new Hashtable();
 
-        order.addElement(X509Extension.subjectAlternativeName);
-
-        extensions.put(X509Extension.subjectAlternativeName, new X509Extension(true, new DEROctetString(new GeneralNames(new GeneralName(new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"))))));
-
-        X509Extensions  ex = new X509Extensions(order, extensions);
+        Extensions ex = new Extensions(new Extension(Extension.subjectAlternativeName, true,
+            new DEROctetString(new GeneralNames(new GeneralName(new X500Name("CN=AU,O=Bouncy Castle,OU=Test 2"))))));
 
         gen.setExtensions(ex);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/LocaleTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/LocaleTest.java
new file mode 100644
index 0000000..89ee7e0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/LocaleTest.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.asn1.test;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class LocaleTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "LocaleTest";
+    }
+
+    private void doTestLocale(Locale l)
+        throws Exception
+    {
+        long time = 1538063166000L;
+        String timeString = "180927154606GMT+00:00";
+        String longTimeString = "20180927154606Z";
+
+        Locale.setDefault(l);
+
+        isTrue(time == new DERUTCTime(timeString).getAdjustedDate().getTime());
+        isTrue(time == new DERGeneralizedTime(longTimeString).getDate().getTime());
+
+        isTrue(time == new DERUTCTime(new Date(time)).getAdjustedDate().getTime());
+        isTrue(time == new DERGeneralizedTime(new Date(time)).getDate().getTime());
+
+        Date d = new Date();
+
+        isTrue((d.getTime() - (d.getTime() % 1000)) == new DERUTCTime(d).getAdjustedDate().getTime());
+        isTrue((d.getTime() - (d.getTime() % 1000)) == new DERGeneralizedTime(d).getDate().getTime());
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        Locale defLocale = Locale.getDefault();
+
+        Locale list[] = DateFormat.getAvailableLocales();
+        for (int i = 0; i != list.length; i++)
+        {
+            doTestLocale(list[i]);
+        }
+
+        Locale.setDefault(defLocale);
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new LocaleTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/MiscTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/MiscTest.java
index 8dbb852..f4dc225 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/MiscTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/MiscTest.java
@@ -25,26 +25,6 @@
 public class MiscTest
     extends SimpleTest
 {
-    private boolean isSameAs(
-        byte[]  a,
-        byte[]  b)
-    {
-        if (a.length != b.length)
-        {
-            return false;
-        }
-        
-        for (int i = 0; i != a.length; i++)
-        {
-            if (a[i] != b[i])
-            {
-                return false;
-            }
-        }
-        
-        return true;
-    }
-
     public void shouldFailOnExtraData()
         throws Exception
     {
@@ -132,15 +112,12 @@
             aOut.writeObject(values[i]);
         }
 
-        ASN1Primitive[] readValues = new ASN1Primitive[values.length];
-
-        if (!isSameAs(bOut.toByteArray(), data))
+        if (!areEqual(bOut.toByteArray(), data))
         {
             fail("Failed data check");
         }
 
-        ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
-        ASN1InputStream aIn = new ASN1InputStream(bIn);
+        ASN1InputStream aIn = new ASN1InputStream(bOut.toByteArray());
 
         for (int i = 0; i != values.length; i++)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/PKIPublicationInfoTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/PKIPublicationInfoTest.java
new file mode 100644
index 0000000..6f1a2bb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/PKIPublicationInfoTest.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.test;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
+import org.bouncycastle.asn1.crmf.SinglePubInfo;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.util.test.SimpleTest;
+
+
+public class PKIPublicationInfoTest
+    extends SimpleTest
+{
+    public static void main(String[] args)
+    {
+        runTest(new PKIPublicationInfoTest());
+    }
+
+    public String getName()
+    {
+        return "PKIPublicationInfoTest";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        PKIPublicationInfo pkiPubInfo = new PKIPublicationInfo(PKIPublicationInfo.dontPublish);
+
+        isEquals(PKIPublicationInfo.dontPublish, pkiPubInfo.getAction());
+
+        encEqualTest(pkiPubInfo);
+
+        pkiPubInfo = new PKIPublicationInfo(PKIPublicationInfo.dontPublish.getValue());
+
+        isEquals(PKIPublicationInfo.dontPublish, pkiPubInfo.getAction());
+
+        encEqualTest(pkiPubInfo);
+
+        SinglePubInfo singlePubInfo1 = new SinglePubInfo(SinglePubInfo.x500, new GeneralName(new X500Name("CN=TEST")));
+        pkiPubInfo = new PKIPublicationInfo(singlePubInfo1);
+
+        isEquals(PKIPublicationInfo.pleasePublish, pkiPubInfo.getAction());
+        isEquals(1, pkiPubInfo.getPubInfos().length);
+        isEquals(singlePubInfo1, pkiPubInfo.getPubInfos()[0]);
+        
+        encEqualTest(pkiPubInfo);
+
+        SinglePubInfo singlePubInfo2 = new SinglePubInfo(SinglePubInfo.x500, new GeneralName(new X500Name("CN=BLOOT")));
+
+        pkiPubInfo = new PKIPublicationInfo(new SinglePubInfo[] { singlePubInfo1, singlePubInfo2 });
+
+        isEquals(PKIPublicationInfo.pleasePublish, pkiPubInfo.getAction());
+        isEquals(2, pkiPubInfo.getPubInfos().length);
+        isEquals(singlePubInfo1, pkiPubInfo.getPubInfos()[0]);
+        isEquals(singlePubInfo2, pkiPubInfo.getPubInfos()[1]);
+        
+        encEqualTest(pkiPubInfo);
+
+        pkiPubInfo = new PKIPublicationInfo((SinglePubInfo)null);
+
+        isEquals(PKIPublicationInfo.pleasePublish, pkiPubInfo.getAction());
+        isTrue(null == pkiPubInfo.getPubInfos());
+
+        encEqualTest(pkiPubInfo);
+
+        pkiPubInfo = new PKIPublicationInfo((SinglePubInfo[])null);
+
+        isEquals(PKIPublicationInfo.pleasePublish, pkiPubInfo.getAction());
+        isTrue(null == pkiPubInfo.getPubInfos());
+
+        encEqualTest(pkiPubInfo);
+    }
+
+    private void encEqualTest(PKIPublicationInfo pubInfo)
+        throws IOException
+    {
+        byte[] b = pubInfo.getEncoded();
+
+        PKIPublicationInfo pubInfoResult = PKIPublicationInfo.getInstance(b);
+
+        isEquals(pubInfo, pubInfoResult);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/PollReqContentTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/PollReqContentTest.java
new file mode 100644
index 0000000..1b81f77
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/PollReqContentTest.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.asn1.test;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.cmp.PollReqContent;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class PollReqContentTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "PollReqContentTest";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        BigInteger one = BigInteger.valueOf(1), two = BigInteger.valueOf(2);
+        BigInteger[] ids = new BigInteger[] { one, two };
+
+        PollReqContent c = new PollReqContent(ids);
+
+        ASN1Integer[][] vs = c.getCertReqIds();
+
+        isTrue(vs.length == 2);
+        for (int i = 0; i != vs.length; i++)
+        {
+            isTrue(vs[i].length == 1);
+            isTrue(vs[i][0].getValue().equals(ids[i]));
+        }
+
+        BigInteger[] values = c.getCertReqIdValues();
+
+        isTrue(values.length == 2);
+        for (int i = 0; i != values.length; i++)
+        {
+            isTrue(values[i].equals(ids[i]));
+        }
+
+        c = new PollReqContent(two);
+        vs = c.getCertReqIds();
+
+        isTrue(vs.length == 1);
+
+        isTrue(vs[0].length == 1);
+        isTrue(vs[0][0].getValue().equals(two));
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new PollReqContentTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/PrivateKeyInfoTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/PrivateKeyInfoTest.java
new file mode 100644
index 0000000..5ee0445
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/PrivateKeyInfoTest.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.asn1.test;
+
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class PrivateKeyInfoTest
+    extends SimpleTest
+{
+    private static final byte[] priv = Base64.decode(
+        "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC");
+
+    private static final byte[] privWithPub = Base64.decode(
+        "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC" +
+            "oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB" +
+            "Z9w7lshQhqowtrbLDFw4rXAxZuE=");
+
+
+    public String getName()
+    {
+        return "PrivateKeyInfoTest";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        PrivateKeyInfo privInfo1 = PrivateKeyInfo.getInstance(priv);
+
+        isTrue(!privInfo1.hasPublicKey());
+
+        PrivateKeyInfo privInfo2 = new PrivateKeyInfo(privInfo1.getPrivateKeyAlgorithm(), privInfo1.parsePrivateKey());
+
+        isTrue("enc 1 failed", areEqual(priv, privInfo2.getEncoded()));
+
+        privInfo1 = PrivateKeyInfo.getInstance(privWithPub);
+
+        isTrue(privInfo1.hasPublicKey());
+
+        privInfo2 = new PrivateKeyInfo(privInfo1.getPrivateKeyAlgorithm(), privInfo1.parsePrivateKey(), privInfo1.getAttributes(), privInfo1.getPublicKeyData().getOctets());
+
+        isTrue("enc 2 failed", areEqual(privWithPub, privInfo2.getEncoded()));
+    }
+
+    public static void main(
+        String[]    args)
+    {
+        runTest(new PrivateKeyInfoTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/RegressionTest.java
index f4a9eff..2106cf0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/RegressionTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/RegressionTest.java
@@ -10,6 +10,7 @@
         new EqualsAndHashCodeTest(),
         new TagTest(),
         new SetTest(),
+        new ASN1IntegerTest(),
         new DERUTF8StringTest(),
         new CertificateTest(),
         new GenerationTest(),
@@ -71,7 +72,13 @@
         new GeneralNameTest(),
         new ObjectIdentifierTest(),
         new RFC4519Test(),
-        new PolicyConstraintsTest()
+        new PolicyConstraintsTest(),
+        new PollReqContentTest(),
+        new DhSigStaticTest(),
+        new PKIPublicationInfoTest(),
+        new CertifiedKeyPairTest(),
+        new PrivateKeyInfoTest(),
+        new LocaleTest()
     };
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/SignerLocationUnitTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/SignerLocationUnitTest.java
index cbb45c3..29dcd06 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/SignerLocationUnitTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/SignerLocationUnitTest.java
@@ -9,6 +9,7 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.DERUTF8String;
 import org.bouncycastle.asn1.esf.SignerLocation;
+import org.bouncycastle.asn1.x500.DirectoryString;
 import org.bouncycastle.util.test.SimpleTest;
 
 public class SignerLocationUnitTest 
@@ -26,17 +27,17 @@
         
         SignerLocation sl = new SignerLocation(countryName, null, null);
 
-        checkConstruction(sl, countryName, null, null);
+        checkConstruction(sl, DirectoryString.getInstance(countryName), null, null);
 
         DERUTF8String localityName = new DERUTF8String("Melbourne");
         
         sl = new SignerLocation(null, localityName, null);
 
-        checkConstruction(sl, null, localityName, null);
+        checkConstruction(sl, null, DirectoryString.getInstance(localityName), null);
         
         sl = new SignerLocation(countryName, localityName, null);
 
-        checkConstruction(sl, countryName, localityName, null);
+        checkConstruction(sl, DirectoryString.getInstance(countryName), DirectoryString.getInstance(localityName), null);
         
         ASN1EncodableVector v = new ASN1EncodableVector();
         
@@ -51,11 +52,11 @@
         
         sl = new SignerLocation(countryName, null, postalAddress);
         
-        checkConstruction(sl, countryName, null, postalAddress);
+        checkConstruction(sl, DirectoryString.getInstance(countryName), null, postalAddress);
         
         sl = new SignerLocation(countryName, localityName, postalAddress);
         
-        checkConstruction(sl, countryName, localityName, postalAddress);
+        checkConstruction(sl, DirectoryString.getInstance(countryName), DirectoryString.getInstance(localityName), postalAddress);
         
         sl = SignerLocation.getInstance(null);
         
@@ -126,8 +127,8 @@
 
     private void checkConstruction(
         SignerLocation sl,
-        DERUTF8String  countryName,
-        DERUTF8String  localityName,
+        DirectoryString  countryName,
+        DirectoryString  localityName,
         ASN1Sequence   postalAddress) 
         throws IOException
     {
@@ -148,8 +149,8 @@
     
     private void checkValues(
         SignerLocation sl,
-        DERUTF8String  countryName,
-        DERUTF8String  localityName,
+        DirectoryString countryName,
+        DirectoryString  localityName,
         ASN1Sequence   postalAddress)
     {
         if (countryName != null)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/TagTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/TagTest.java
index 90489a7..f06c87c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/TagTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/TagTest.java
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 
+import org.bouncycastle.asn1.ASN1ApplicationSpecific;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1TaggedObject;
@@ -39,11 +40,11 @@
     {
         ASN1InputStream aIn = new ASN1InputStream(longTagged);
 
-        DERApplicationSpecific app = (DERApplicationSpecific)aIn.readObject();
+        ASN1ApplicationSpecific app = (ASN1ApplicationSpecific)aIn.readObject();
         
         aIn = new ASN1InputStream(app.getContents());
 
-        app = (DERApplicationSpecific)aIn.readObject();
+        app = (ASN1ApplicationSpecific)aIn.readObject();
 
         aIn = new ASN1InputStream(app.getContents());
 
@@ -77,14 +78,14 @@
 
         aIn = new ASN1InputStream(longAppSpecificTag);
 
-        app = (DERApplicationSpecific)aIn.readObject();
+        app = (ASN1ApplicationSpecific)aIn.readObject();
 
         if (app.getApplicationTag() != 97)
         {
             fail("incorrect tag number read");
         }
 
-        app = (DERApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
+        app = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
 
         if (app.getApplicationTag() != 97)
         {
@@ -96,7 +97,7 @@
         {
             int testTag = sr.nextInt() >>> (1 + (sr.nextInt() >>> 1) % 26);
             app = new DERApplicationSpecific(testTag, new byte[]{ 1 });
-            app = (DERApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
+            app = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(app.getEncoded());
 
             if (app.getApplicationTag() != testTag)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/X500NameTest.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/X500NameTest.java
index 2822f8c..663e71f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/X500NameTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/X500NameTest.java
@@ -159,7 +159,8 @@
         // compatibility encoding
         testEncodingGeneralizedTime(BCStyle.DATE_OF_BIRTH, "20020122122220Z");
         testEncodingUTF8String(BCStyle.CN, "Mörsky");
-
+        testEncodingUTF8String(BCStyle.ORGANIZATION_IDENTIFIER, "Mörsky");
+        
         //
         // composite
         //
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/test/X9Test.java b/bcprov/src/main/java/org/bouncycastle/asn1/test/X9Test.java
index d5b7f4b..3fc3e14 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/test/X9Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/test/X9Test.java
@@ -40,7 +40,7 @@
                     "H0ywPWp1CjDCUBAtSYhxfZuhWrbT4DFQB9c3QWj/40cbYKhXaGoZR107+i/wQfA2doro4Yu5LPzw" +
                     "BclJqixtlIU9DmYLv4VLHJUF/pWgIef///////////////f///l13rQbOmBXw8QyFGUmVRAgEBBC" +
                     "UwIwIBAQQeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU");
- 
+
     private void encodePublicKey()
         throws Exception
     {
@@ -80,17 +80,17 @@
         }
 
         ASN1Primitive           o = ASN1Primitive.fromByteArray(namedPub);
-        
+
         if (!info.equals(o))
         {
             fail("failed public named equality");
         }
-        
+
         //
         // explicit curve parameters
         //
         params = new X962Parameters(ecP);
-        
+
         info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
 
         if (!areEqual(info.getEncoded(), expPub))
@@ -99,13 +99,13 @@
         }
 
         o = ASN1Primitive.fromByteArray(expPub);
-        
+
         if (!info.equals(o))
         {
             fail("failed public explicit equality");
         }
     }
-    
+
     private void encodePrivateKey()
         throws Exception
     {
@@ -130,14 +130,14 @@
         {
             fail("failed private named equality");
         }
-        
+
         //
         // explicit curve parameters
         //
         ecP = X962NamedCurves.getByOID(X9ObjectIdentifiers.prime239v3);
 
         params = new X962Parameters(ecP);
-        
+
         info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params),
             new ECPrivateKey(ecP.getN().bitLength(), BigInteger.valueOf(20)));
 
@@ -147,13 +147,13 @@
         }
 
         o = ASN1Primitive.fromByteArray(expPriv);
-        
+
         if (!info.equals(o))
         {
             fail("failed private explicit equality");
         }
     }
-    
+
     public void performTest()
         throws Exception
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStamp.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStamp.java
new file mode 100644
index 0000000..8095d53
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStamp.java
@@ -0,0 +1,210 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.Attributes;
+import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.cms.SignedData;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Implementation of the Archive Timestamp type defined in RFC4998.
+ * {@see <a href="https://tools.ietf.org/html/rfc4998">RFC 4998</a>}
+ * <p>
+ * ASN.1 Archive Timestamp
+ * <p>
+ * ArchiveTimeStamp ::= SEQUENCE {
+ * digestAlgorithm [Ø] AlgorithmIdentifier OPTIONAL,
+ * attributes      [1] Attributes OPTIONAL,
+ * reducedHashtree [2] SEQUENCE OF PartialHashtree OPTIONAL,
+ * timeStamp       ContentInfo}
+ * <p>
+ * PartialHashtree ::= SEQUENCE OF OCTET STRING
+ * <p>
+ * Attributes ::= SET SIZE (1..MAX) OF Attribute
+ */
+public class ArchiveTimeStamp
+    extends ASN1Object
+{
+    private AlgorithmIdentifier digestAlgorithm;
+    private Attributes attributes;
+    private ASN1Sequence reducedHashTree;
+    private ContentInfo timeStamp;
+
+    /**
+     * Return an ArchiveTimestamp from the given object.
+     *
+     * @param obj the object we want converted.
+     * @return an ArchiveTimestamp instance, or null.
+     * @throws IllegalArgumentException if the object cannot be converted.
+     */
+    public static ArchiveTimeStamp getInstance(final Object obj)
+    {
+        if (obj instanceof ArchiveTimeStamp)
+        {
+            return (ArchiveTimeStamp)obj;
+        }
+        else if (obj != null)
+        {
+            return new ArchiveTimeStamp(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ArchiveTimeStamp(
+        AlgorithmIdentifier digestAlgorithm,
+        PartialHashtree[] reducedHashTree,
+        ContentInfo timeStamp)
+    {
+        this.digestAlgorithm = digestAlgorithm;
+        this.reducedHashTree = new DERSequence(reducedHashTree);
+        this.timeStamp = timeStamp;
+    }
+
+    public ArchiveTimeStamp(
+        AlgorithmIdentifier digestAlgorithm,
+        Attributes attributes,
+        PartialHashtree[] reducedHashTree,
+        ContentInfo timeStamp)
+    {
+        this.digestAlgorithm = digestAlgorithm;
+        this.attributes = attributes;
+        this.reducedHashTree = new DERSequence(reducedHashTree);
+        this.timeStamp = timeStamp;
+    }
+
+    public ArchiveTimeStamp(
+        ContentInfo timeStamp)
+    {
+        this.timeStamp = timeStamp;
+    }
+
+    private ArchiveTimeStamp(final ASN1Sequence sequence)
+    {
+        if (sequence.size() < 1 || sequence.size() > 4)
+        {
+            throw new IllegalArgumentException("wrong sequence size in constructor: " + sequence.size());
+        }
+
+        for (int i = 0; i < sequence.size() - 1; i++)
+        {
+            Object obj = sequence.getObjectAt(i);
+
+            if (obj instanceof ASN1TaggedObject)
+            {
+                ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(obj);
+
+                switch (taggedObject.getTagNo())
+                {
+                case 0:
+                    digestAlgorithm = AlgorithmIdentifier.getInstance(taggedObject, false);
+                    break;
+                case 1:
+                    attributes = Attributes.getInstance(taggedObject, false);
+                    break;
+                case 2:
+                    reducedHashTree = ASN1Sequence.getInstance(taggedObject, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("invalid tag no in constructor: "
+                        + taggedObject.getTagNo());
+                }
+            }
+        }
+
+        timeStamp = ContentInfo.getInstance(sequence.getObjectAt(sequence.size() - 1));
+    }
+
+    public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+    {
+        if (digestAlgorithm != null)
+        {
+            return digestAlgorithm;
+        }
+        else
+        {
+            if (timeStamp.getContentType().equals(CMSObjectIdentifiers.signedData))
+            {
+                SignedData tsData = SignedData.getInstance(timeStamp.getContent());
+                if (tsData.getEncapContentInfo().getContentType().equals(PKCSObjectIdentifiers.id_ct_TSTInfo))
+                {
+                    TSTInfo tstData = TSTInfo.getInstance(tsData.getEncapContentInfo());
+
+                    return tstData.getMessageImprint().getHashAlgorithm();
+                }
+                else
+                {
+                    throw new IllegalStateException("cannot parse time stamp");
+                }
+            }
+            else
+            {
+                throw new IllegalStateException("cannot identify algorithm identifier for digest");
+            }
+        }
+    }
+
+    /**
+     * Return the contents of the digestAlgorithm field - null if not set.
+     *
+     * @return the contents of the digestAlgorithm field, or null if not set.
+     */
+    public AlgorithmIdentifier getDigestAlgorithm()
+    {
+        return digestAlgorithm;
+    }
+
+    public PartialHashtree[] getReducedHashTree()
+    {
+        if (reducedHashTree == null)
+        {
+           return null;
+        }
+
+        PartialHashtree[] rv = new PartialHashtree[reducedHashTree.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = PartialHashtree.getInstance(reducedHashTree.getObjectAt(i));
+        }
+
+        return rv;
+    }
+
+    public ContentInfo getTimeStamp()
+    {
+        return timeStamp;
+    }
+    
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (digestAlgorithm != null)
+        {
+            v.add(new DERTaggedObject(false, 0, digestAlgorithm));
+        }
+
+        if (attributes != null)
+        {
+            v.add(new DERTaggedObject(false, 1, attributes));
+        }
+
+        if (reducedHashTree != null)
+        {
+            v.add(new DERTaggedObject(false, 2, reducedHashTree));
+        }
+
+        v.add(timeStamp);
+
+        return new DERSequence(v);
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampChain.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampChain.java
new file mode 100644
index 0000000..f96948b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampChain.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.asn1.tsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Implementation of ArchiveTimeStampChain type, as defined in RFC4998 and RFC6283.
+ * <p>
+ * An ArchiveTimeStampChain corresponds to a SEQUENCE OF ArchiveTimeStamps, and has the following
+ * ASN.1 Syntax:
+ * <p>
+ * ArchiveTimeStampChain ::= SEQUENCE OF ArchiveTimeStamp
+ */
+public class ArchiveTimeStampChain
+    extends ASN1Object
+{
+    private ASN1Sequence archiveTimestamps;
+
+    /**
+     * Return an ArchiveTimeStampChain from the given object.
+     *
+     * @param obj the object we want converted.
+     * @return an ArchiveTimeStampChain instance, or null.
+     * @throws IllegalArgumentException if the object cannot be converted.
+     */
+    public static ArchiveTimeStampChain getInstance(final Object obj)
+    {
+        if (obj instanceof ArchiveTimeStampChain)
+        {
+            return (ArchiveTimeStampChain)obj;
+        }
+        else if (obj != null)
+        {
+            return new ArchiveTimeStampChain(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ArchiveTimeStampChain(ArchiveTimeStamp archiveTimeStamp)
+    {
+        archiveTimestamps = new DERSequence(archiveTimeStamp);
+    }
+
+    public ArchiveTimeStampChain(ArchiveTimeStamp[] archiveTimeStamps)
+    {
+        archiveTimestamps = new DERSequence(archiveTimeStamps);
+    }
+
+    private ArchiveTimeStampChain(final ASN1Sequence sequence)
+    {
+        final ASN1EncodableVector vector = new ASN1EncodableVector();
+        final Enumeration objects = sequence.getObjects();
+
+        while (objects.hasMoreElements())
+        {
+            vector.add(ArchiveTimeStamp.getInstance(objects.nextElement()));
+        }
+
+        archiveTimestamps = new DERSequence(vector);
+    }
+
+    public ArchiveTimeStamp[] getArchiveTimestamps()
+    {
+        ArchiveTimeStamp[] rv = new ArchiveTimeStamp[archiveTimestamps.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = ArchiveTimeStamp.getInstance(archiveTimestamps.getObjectAt(i));
+        }
+
+        return rv;
+    }
+
+    /**
+     * Adds an {@link ArchiveTimeStamp} object to the archive timestamp chain.
+     *
+     * @param archiveTimeStamp the {@link ArchiveTimeStamp} to add.
+     * @return returns the modified chain.
+     */
+    public ArchiveTimeStampChain append(final ArchiveTimeStamp archiveTimeStamp)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (int i = 0; i != archiveTimestamps.size(); i++)
+        {
+            v.add(archiveTimestamps.getObjectAt(i));
+        }
+
+        v.add(archiveTimeStamp);
+
+        return new ArchiveTimeStampChain(new DERSequence(v));
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return archiveTimestamps;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampSequence.java
new file mode 100644
index 0000000..15af812
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampSequence.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.asn1.tsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * Implementation of ArchiveTimeStampSequence type, as defined in RFC4998.
+ * <p>
+ * An ArchiveTimeStampSequence corresponds to a SEQUENCE OF ArchiveTimeStampChains and has the
+ * following ASN.1 Syntax:
+ * <p>
+ * ArchiveTimeStampSequence ::= SEQUENCE OF ArchiveTimeStampChain
+ */
+public class ArchiveTimeStampSequence
+    extends ASN1Object
+{
+    private ASN1Sequence archiveTimeStampChains;
+
+    /**
+     * Return an ArchiveTimestampSequence from the given object.
+     *
+     * @param obj the object we want converted.
+     * @return an ArchiveTimeStampSequence instance, or null.
+     * @throws IllegalArgumentException if the object cannot be converted.
+     */
+    public static ArchiveTimeStampSequence getInstance(final Object obj)
+    {
+        if (obj instanceof ArchiveTimeStampChain)
+        {
+            return (ArchiveTimeStampSequence)obj;
+        }
+        else if (obj != null)
+        {
+            return new ArchiveTimeStampSequence(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    private ArchiveTimeStampSequence(final ASN1Sequence sequence)
+        throws IllegalArgumentException
+    {
+        final ASN1EncodableVector vector = new ASN1EncodableVector();
+        Enumeration objects = sequence.getObjects();
+
+        while (objects.hasMoreElements())
+        {
+            vector.add(ArchiveTimeStampChain.getInstance(objects.nextElement()));
+        }
+
+        this.archiveTimeStampChains = new DERSequence(vector);
+    }
+
+    public ArchiveTimeStampSequence(ArchiveTimeStampChain archiveTimeStampChain)
+    {
+        this.archiveTimeStampChains = new DERSequence(archiveTimeStampChain);
+    }
+
+    public ArchiveTimeStampSequence(ArchiveTimeStampChain[] archiveTimeStampChains)
+    {
+        this.archiveTimeStampChains = new DERSequence(archiveTimeStampChains);
+    }
+
+    /**
+     * Returns the sequence of ArchiveTimeStamp chains that compose the ArchiveTimeStamp sequence.
+     *
+     * @return the {@link ASN1Sequence} containing the ArchiveTimeStamp chains.
+     */
+    public ArchiveTimeStampChain[] getArchiveTimeStampChains()
+    {
+        ArchiveTimeStampChain[] rv = new ArchiveTimeStampChain[archiveTimeStampChains.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = ArchiveTimeStampChain.getInstance(archiveTimeStampChains.getObjectAt(i));
+        }
+
+        return rv;
+    }
+
+    public int size()
+    {
+        return archiveTimeStampChains.size();
+    }
+
+    /**
+     * Adds an {@link ArchiveTimeStampChain} to the ArchiveTimeStamp sequence.
+     *
+     * @param chain the {@link ArchiveTimeStampChain} to add
+     * @return returns the modified sequence.
+     */
+    public ArchiveTimeStampSequence append(ArchiveTimeStampChain chain) {
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (int i = 0; i != archiveTimeStampChains.size(); i++)
+        {
+            v.add(archiveTimeStampChains.getObjectAt(i));
+        }
+
+        v.add(chain);
+
+        return new ArchiveTimeStampSequence(new DERSequence(v));
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return archiveTimeStampChains;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/CryptoInfos.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/CryptoInfos.java
new file mode 100644
index 0000000..2cc02f4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/CryptoInfos.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cms.Attribute;
+
+/**
+ * Implementation of the CryptoInfos element defined in RFC 4998:
+ * <p>
+ * CryptoInfos ::= SEQUENCE SIZE (1..MAX) OF Attribute
+ */
+public class CryptoInfos
+    extends ASN1Object
+{
+    private ASN1Sequence attributes;
+
+    public static CryptoInfos getInstance(final Object obj)
+    {
+        if (obj instanceof CryptoInfos)
+        {
+            return (CryptoInfos)obj;
+        }
+        else if (obj != null)
+        {
+            return new CryptoInfos(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static CryptoInfos getInstance(
+        ASN1TaggedObject obj,
+        boolean explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    private CryptoInfos(final ASN1Sequence attributes)
+    {
+        this.attributes = attributes;
+    }
+
+    public CryptoInfos(Attribute[] attrs)
+    {
+        attributes = new DERSequence(attrs);
+    }
+
+    public Attribute[] getAttributes()
+    {
+        Attribute[] rv = new Attribute[attributes.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = Attribute.getInstance(attributes.getObjectAt(i));
+        }
+
+        return rv;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return attributes;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EncryptionInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EncryptionInfo.java
new file mode 100644
index 0000000..edae856
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EncryptionInfo.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+
+/**
+ * Implementation of the EncryptionInfo element defined in RFC 4998:
+ * <p>
+ * 1988 ASN.1 EncryptionInfo
+ * <p>
+ * EncryptionInfo       ::=     SEQUENCE {
+ * encryptionInfoType     OBJECT IDENTIFIER,
+ * encryptionInfoValue    ANY DEFINED BY encryptionInfoType
+ * }
+ * <p>
+ * 1997-ASN.1 EncryptionInfo
+ * <p>
+ * EncryptionInfo       ::=     SEQUENCE {
+ * encryptionInfoType   ENCINFO-TYPE.&id
+ * ({SupportedEncryptionAlgorithms}),
+ * encryptionInfoValue  ENCINFO-TYPE.&Type
+ * ({SupportedEncryptionAlgorithms}{@encryptionInfoType})
+ * }
+ * <p>
+ * ENCINFO-TYPE ::= TYPE-IDENTIFIER
+ * <p>
+ * SupportedEncryptionAlgorithms ENCINFO-TYPE ::= {...}
+ */
+public class EncryptionInfo
+    extends ASN1Object
+{
+
+    /**
+     * The OID for EncryptionInfo type.
+     */
+    private ASN1ObjectIdentifier encryptionInfoType;
+
+    /**
+     * The value of EncryptionInfo
+     */
+    private ASN1Encodable encryptionInfoValue;
+
+    public static EncryptionInfo getInstance(final ASN1Object obj)
+    {
+        if (obj instanceof EncryptionInfo)
+        {
+            return (EncryptionInfo)obj;
+        }
+        else if (obj != null)
+        {
+            return new EncryptionInfo(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static EncryptionInfo getInstance(
+        ASN1TaggedObject obj,
+        boolean explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    private EncryptionInfo(ASN1Sequence sequence)
+    {
+        if (sequence.size() != 2)
+        {
+            throw new IllegalArgumentException("wrong sequence size in constructor: " + sequence.size());
+        }
+
+        this.encryptionInfoType = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));
+        this.encryptionInfoValue = sequence.getObjectAt(1);
+    }
+
+    public EncryptionInfo(ASN1ObjectIdentifier encryptionInfoType,
+                          ASN1Encodable encryptionInfoValue)
+    {
+        this.encryptionInfoType = encryptionInfoType;
+        this.encryptionInfoValue = encryptionInfoValue;
+    }
+
+    private EncryptionInfo()
+    {
+
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+
+        final ASN1EncodableVector v = new ASN1EncodableVector();
+        v.add(encryptionInfoType);
+        v.add(encryptionInfoValue);
+
+        return ASN1Sequence.getInstance(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EvidenceRecord.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EvidenceRecord.java
new file mode 100644
index 0000000..49bfa78
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/EvidenceRecord.java
@@ -0,0 +1,244 @@
+package org.bouncycastle.asn1.tsp;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * <a href="https://tools.ietf.org/html/rfc4998">RFC 4998</a>:
+ * Evidence Record Syntax (ERS)
+ * <p>
+ * <pre>
+ * EvidenceRecord ::= SEQUENCE {
+ *   version                   INTEGER { v1(1) } ,
+ *   digestAlgorithms          SEQUENCE OF AlgorithmIdentifier,
+ *   cryptoInfos               [0] CryptoInfos OPTIONAL,
+ *   encryptionInfo            [1] EncryptionInfo OPTIONAL,
+ *   archiveTimeStampSequence  ArchiveTimeStampSequence
+ * }
+ *
+ * CryptoInfos ::= SEQUENCE SIZE (1..MAX) OF Attribute
+ * </pre>
+ */
+public class EvidenceRecord
+    extends ASN1Object
+{
+
+    /**
+     * ERS {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) ltans(11)
+     * id-mod(0) id-mod-ers88(2) id-mod-ers88-v1(1) }
+     */
+    private static final ASN1ObjectIdentifier OID = new ASN1ObjectIdentifier("1.3.6.1.5.5.11.0.2.1");
+
+    private ASN1Integer version = new ASN1Integer(1);
+    private ASN1Sequence digestAlgorithms;
+    private CryptoInfos cryptoInfos;
+    private EncryptionInfo encryptionInfo;
+    private ArchiveTimeStampSequence archiveTimeStampSequence;
+
+    /**
+     * Return an EvidenceRecord from the given object.
+     *
+     * @param obj the object we want converted.
+     * @return an EvidenceRecord instance, or null.
+     * @throws IllegalArgumentException if the object cannot be converted.
+     */
+    public static EvidenceRecord getInstance(final Object obj)
+    {
+        if (obj instanceof EvidenceRecord)
+        {
+            return (EvidenceRecord)obj;
+        }
+        else if (obj != null)
+        {
+            return new EvidenceRecord(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    private EvidenceRecord(
+        EvidenceRecord evidenceRecord,
+        ArchiveTimeStampSequence replacementSequence,
+        ArchiveTimeStamp newChainTimeStamp)
+    {
+        this.version = evidenceRecord.version;
+
+        // check the list of digest algorithms is correct.
+        if (newChainTimeStamp != null)
+        {
+            AlgorithmIdentifier algId = newChainTimeStamp.getDigestAlgorithmIdentifier();
+            final ASN1EncodableVector vector = new ASN1EncodableVector();
+            final Enumeration enumeration = evidenceRecord.digestAlgorithms.getObjects();
+            boolean found = false;
+
+            while (enumeration.hasMoreElements())
+            {
+                final AlgorithmIdentifier algorithmIdentifier = AlgorithmIdentifier.getInstance(
+                                                                            enumeration.nextElement());
+                vector.add(algorithmIdentifier);
+
+                if (algorithmIdentifier.equals(algId))
+                {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found)
+            {
+                vector.add(algId);
+                this.digestAlgorithms = new DERSequence(vector);
+            }
+            else
+            {
+                this.digestAlgorithms = evidenceRecord.digestAlgorithms;
+            }
+        }
+        else
+        {
+            this.digestAlgorithms = evidenceRecord.digestAlgorithms;
+        }
+
+        this.cryptoInfos = evidenceRecord.cryptoInfos;
+        this.encryptionInfo = evidenceRecord.encryptionInfo;
+        this.archiveTimeStampSequence = replacementSequence;
+    }
+
+    public EvidenceRecord(
+        AlgorithmIdentifier[] digestAlgorithms,
+        CryptoInfos cryptoInfos,
+        EncryptionInfo encryptionInfo,
+        ArchiveTimeStampSequence archiveTimeStampSequence)
+    {
+        this.digestAlgorithms = new DERSequence(digestAlgorithms);
+        this.cryptoInfos = cryptoInfos;
+        this.encryptionInfo = encryptionInfo;
+        this.archiveTimeStampSequence = archiveTimeStampSequence;
+    }
+
+    private EvidenceRecord(final ASN1Sequence sequence)
+    {
+        if (sequence.size() < 3 && sequence.size() > 5)
+        {
+            throw new IllegalArgumentException("wrong sequence size in constructor: " + sequence.size());
+        }
+
+        final ASN1Integer versionNumber = ASN1Integer.getInstance(sequence.getObjectAt(0));
+        if (!versionNumber.getValue().equals(BigIntegers.ONE))
+        {
+            throw new IllegalArgumentException("incompatible version");
+        }
+        else
+        {
+            this.version = versionNumber;
+        }
+
+        this.digestAlgorithms = ASN1Sequence.getInstance(sequence.getObjectAt(1));
+        for (int i = 2; i != sequence.size() - 1; i++)
+        {
+            ASN1Encodable object = sequence.getObjectAt(i);
+
+            if (object instanceof ASN1TaggedObject)
+            {
+                ASN1TaggedObject asn1TaggedObject = (ASN1TaggedObject)object;
+                switch (asn1TaggedObject.getTagNo())
+                {
+                case 0:
+                    cryptoInfos = CryptoInfos.getInstance(asn1TaggedObject, false);
+                    break;
+                case 1:
+                    encryptionInfo = EncryptionInfo.getInstance(asn1TaggedObject, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("unknown tag in getInstance: " + asn1TaggedObject.getTagNo());
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("unknown object in getInstance: " +
+                    object.getClass().getName());
+            }
+        }
+        archiveTimeStampSequence = ArchiveTimeStampSequence.getInstance(sequence.getObjectAt(sequence.size() - 1));
+    }
+
+    public AlgorithmIdentifier[] getDigestAlgorithms()
+    {
+        AlgorithmIdentifier[] rv = new AlgorithmIdentifier[digestAlgorithms.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = AlgorithmIdentifier.getInstance(digestAlgorithms.getObjectAt(i));
+        }
+
+        return rv;
+    }
+
+    public ArchiveTimeStampSequence getArchiveTimeStampSequence()
+    {
+        return archiveTimeStampSequence;
+    }
+
+    /**
+     * Return a new EvidenceRecord with an added ArchiveTimeStamp
+     *
+     * @param ats         the archive timestamp to add
+     * @param newChain states whether this new archive timestamp must be added as part of a
+     *                    new sequence (i.e. in the case of hashtree renewal) or not (i.e. in the case of timestamp
+     *                    renewal)
+     * @return the new EvidenceRecord
+     */
+    public EvidenceRecord addArchiveTimeStamp(final ArchiveTimeStamp ats, final boolean newChain)
+    {
+        if (newChain)
+        {
+            ArchiveTimeStampChain chain = new ArchiveTimeStampChain(ats);
+            
+            return new EvidenceRecord(this, archiveTimeStampSequence.append(chain), ats);
+        }
+        else
+        {
+            ArchiveTimeStampChain[] chains = archiveTimeStampSequence.getArchiveTimeStampChains();
+
+            chains[chains.length - 1] = chains[chains.length - 1].append(ats);
+            return new EvidenceRecord(this, new ArchiveTimeStampSequence(chains), null);
+        }
+    }
+
+    public String toString()
+    {
+        return ("EvidenceRecord: Oid(" + OID + ")");
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        final ASN1EncodableVector vector = new ASN1EncodableVector();
+
+        vector.add(version);
+        vector.add(digestAlgorithms);
+
+        if (null != cryptoInfos)
+        {
+            vector.add(cryptoInfos);
+        }
+        if (null != encryptionInfo)
+        {
+            vector.add(encryptionInfo);
+        }
+
+        vector.add(archiveTimeStampSequence);
+
+        return new DERSequence(vector);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/PartialHashtree.java b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/PartialHashtree.java
new file mode 100644
index 0000000..51a5ac1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/PartialHashtree.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.tsp;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of PartialHashtree, as defined in RFC 4998.
+ * <p>
+ * The ASN.1 notation for a PartialHashTree is:
+ * <p>
+ * PartialHashtree ::= SEQUENCE OF OCTET STRING
+ */
+public class PartialHashtree
+    extends ASN1Object
+{
+    /**
+     * Hash values that constitute the hash tree, as ASN.1 Octet Strings.
+     */
+    private ASN1Sequence values;
+
+    /**
+     * Return a PartialHashtree from the given object.
+     *
+     * @param obj the object we want converted.
+     * @return a PartialHashtree instance, or null.
+     * @throws IllegalArgumentException if the object cannot be converted.
+     */
+    public static PartialHashtree getInstance(final Object obj)
+    {
+        if (obj instanceof PartialHashtree)
+        {
+            return (PartialHashtree)obj;
+        }
+        else if (obj != null)
+        {
+            return new PartialHashtree(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    private PartialHashtree(final ASN1Sequence values)
+    {
+        for (int i = 0; i != values.size(); i++)
+        {
+            if (!(values.getObjectAt(i) instanceof DEROctetString))
+            {
+                throw new IllegalArgumentException("unknown object in constructor: " + values
+                    .getObjectAt(i).getClass().getName());
+            }
+        }
+        this.values = values;
+    }
+
+    public PartialHashtree(byte[] values)
+    {
+        this(new byte[][] { values });
+    }
+
+    public PartialHashtree(byte[][] values)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (int i = 0; i != values.length; i++)
+        {
+            v.add(new DEROctetString(Arrays.clone(values[i])));
+        }
+
+        this.values = new DERSequence(v);
+    }
+
+    public byte[][] getValues()
+    {
+        byte[][] rv = new byte[values.size()][];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = Arrays.clone(ASN1OctetString.getInstance(values.getObjectAt(i)).getOctets());
+        }
+
+        return rv;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return values;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
index 0c02ce8..b18953a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
@@ -12,7 +12,7 @@
     private static final BigInteger ZERO = BigInteger.valueOf(0);
     private static final BigInteger ONE = BigInteger.valueOf(1);
 
-    public static final ECDomainParameters[] params = new ECDomainParameters[10];
+    static final ECDomainParameters[] params = new ECDomainParameters[10];
     static final ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[10];
 
     //All named curves have the following oid format: 1.2.804.2.1.1.1.1.3.1.1.2.X
@@ -98,8 +98,8 @@
         String oidStr = oid.getId();
         if (oidStr.startsWith(oidBase))
         {
-            int index = Integer.parseInt(oidStr.substring(oidStr.length() - 1));
-            return params[index];
+            int index = Integer.parseInt(oidStr.substring(oidStr.lastIndexOf('.') + 1));
+            return (index >= 0 && index < params.length) ? params[index] : null;
         }
         return null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java
index 2d6145b..101d797 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145Params.java
@@ -56,12 +56,12 @@
 
     public byte[] getDKE()
     {
-        return dke;
+        return Arrays.clone(dke);
     }
 
     public static byte[] getDefaultDKE()
     {
-        return DEFAULT_DKE;
+        return Arrays.clone(DEFAULT_DKE);
     }
 
     public ASN1ObjectIdentifier getNamedCurve()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
index ccdb34e..d0d4a8d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
@@ -20,4 +20,89 @@
     static final ASN1ObjectIdentifier dstu4145le = UaOid.branch("1.3.1.1");
     /** DSTU4145 Big Endian presentation.  OID: 1.2.804.2.1.1.1.1.3.1.1.1 */
     static final ASN1ObjectIdentifier dstu4145be = UaOid.branch("1.3.1.1.1.1");
+    
+    /** DSTU7564 256-bit digest presentation. */
+    static final ASN1ObjectIdentifier dstu7564digest_256 = UaOid.branch("1.2.2.1");
+    /** DSTU7564 384-bit digest presentation. */
+    static final ASN1ObjectIdentifier dstu7564digest_384 = UaOid.branch("1.2.2.2");
+    /** DSTU7564 512-bit digest presentation. */
+    static final ASN1ObjectIdentifier dstu7564digest_512 = UaOid.branch("1.2.2.3");
+
+    /** DSTU7564 256-bit mac presentation. */
+    static final ASN1ObjectIdentifier dstu7564mac_256 = UaOid.branch("1.2.2.4");
+    /** DSTU7564 384-bit mac presentation. */
+    static final ASN1ObjectIdentifier dstu7564mac_384 = UaOid.branch("1.2.2.5");
+    /** DSTU7564 512-bit mac presentation. */
+    static final ASN1ObjectIdentifier dstu7564mac_512 = UaOid.branch("1.2.2.6");
+
+
+    /** DSTU7624 in ECB mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ecb_128 = UaOid.branch("1.1.3.1.1");
+    /** DSTU7624 in ECB mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ecb_256 = UaOid.branch("1.1.3.1.2");
+    /** DSTU7624 in ECB mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ecb_512 = UaOid.branch("1.1.3.1.3");
+
+    /** DSTU7624 in CTR mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ctr_128 = UaOid.branch("1.1.3.2.1");
+    /** DSTU7624 in CTR mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ctr_256 = UaOid.branch("1.1.3.2.2");
+    /** DSTU7624 in CTR mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ctr_512 = UaOid.branch("1.1.3.2.3");
+
+    /** DSTU7624 in CFB mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cfb_128 = UaOid.branch("1.1.3.3.1");
+    /** DSTU7624 in CFB mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cfb_256 = UaOid.branch("1.1.3.3.2");
+    /** DSTU7624 in CFB mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cfb_512 = UaOid.branch("1.1.3.3.3");
+
+    /** DSTU7624 in MAC mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cmac_128 = UaOid.branch("1.1.3.4.1");
+    /** DSTU7624 in MAC mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cmac_256 = UaOid.branch("1.1.3.4.2");
+    /** DSTU7624 in MAC mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cmac_512 = UaOid.branch("1.1.3.4.3");
+
+    /** DSTU7624 in CBC mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cbc_128 = UaOid.branch("1.1.3.5.1");
+    /** DSTU7624 in CBC mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cbc_256 = UaOid.branch("1.1.3.5.2");
+    /** DSTU7624 in CBC mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624cbc_512 = UaOid.branch("1.1.3.5.3");
+
+    /** DSTU7624 in OFB mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ofb_128 = UaOid.branch("1.1.3.6.1");
+    /** DSTU7624 in OFB mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ofb_256 = UaOid.branch("1.1.3.6.2");
+    /** DSTU7624 in OFB mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ofb_512 = UaOid.branch("1.1.3.6.3");
+
+    /** DSTU7624 in GMAC (GCM witout encryption) mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624gmac_128 = UaOid.branch("1.1.3.7.1");
+    /** DSTU7624 in GMAC (GCM witout encryption) mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624gmac_256 = UaOid.branch("1.1.3.7.2");
+    /** DSTU7624 in GMAC (GCM witout encryption) mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624gmac_512 = UaOid.branch("1.1.3.7.3");
+
+    /** DSTU7624 in CCM mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ccm_128 = UaOid.branch("1.1.3.8.1");
+    /** DSTU7624 in CCM mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ccm_256 = UaOid.branch("1.1.3.8.2");
+    /** DSTU7624 in CCM mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624ccm_512 = UaOid.branch("1.1.3.8.3");
+
+    /** DSTU7624 in XTS mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624xts_128 = UaOid.branch("1.1.3.9.1");
+    /** DSTU7624 in XTS mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624xts_256 = UaOid.branch("1.1.3.9.2");
+    /** DSTU7624 in XTS mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624xts_512 = UaOid.branch("1.1.3.9.3");
+
+    /** DSTU7624 in key wrap (KW) mode with 128 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624kw_128 = UaOid.branch("1.1.3.10.1");
+    /** DSTU7624 in key wrap (KW) mode with 256 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624kw_256 = UaOid.branch("1.1.3.10.2");
+    /** DSTU7624 in key wrap (KW) mode with 512 bit block/key presentation */
+    static final ASN1ObjectIdentifier dstu7624kw_512 = UaOid.branch("1.1.3.10.3");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
index 59c961f..f251702 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.asn1.ASN1Boolean;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1External;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -25,16 +26,17 @@
 import org.bouncycastle.asn1.DERApplicationSpecific;
 import org.bouncycastle.asn1.DERBMPString;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERExternal;
 import org.bouncycastle.asn1.DERGraphicString;
 import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERPrintableString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.DERT61String;
 import org.bouncycastle.asn1.DERUTF8String;
 import org.bouncycastle.asn1.DERVideotexString;
 import org.bouncycastle.asn1.DERVisibleString;
+import org.bouncycastle.asn1.DLApplicationSpecific;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -147,11 +149,14 @@
             {
                 buf.append("BER Set");
             }
-            else
+            else if (obj instanceof DERSet)
             {
                 buf.append("DER Set");
             }
-
+            else
+            {
+                buf.append("Set");
+            }
             buf.append(nl);
 
             while (e.hasMoreElements())
@@ -268,14 +273,18 @@
         {
             buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
         }
+        else if (obj instanceof DLApplicationSpecific)
+        {
+            buf.append(outputApplicationSpecific("", indent, verbose, obj, nl));
+        }
         else if (obj instanceof ASN1Enumerated)
         {
             ASN1Enumerated en = (ASN1Enumerated) obj;
             buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
         }
-        else if (obj instanceof DERExternal)
+        else if (obj instanceof ASN1External)
         {
-            DERExternal ext = (DERExternal) obj;
+            ASN1External ext = (ASN1External) obj;
             buf.append(indent + "External " + nl);
             String          tab = indent + TAB;
             if (ext.getDirectReference() != null)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
index 6a1b318..c84bd0e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
@@ -108,7 +108,7 @@
      * <pre>
      * RelativeDistinguishedName ::=
      *                     SET OF AttributeTypeAndValue
-     *
+
      * AttributeTypeAndValue ::= SEQUENCE {
      *        type     AttributeType,
      *        value    AttributeValue }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
index 67c5cd1..bcdfa33 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
@@ -130,7 +130,7 @@
         X500NameStyle style,
         RDN[]         rDNs)
     {
-        this.rdns = rDNs;
+        this.rdns = copy(rDNs);
         this.style = style;
     }
 
@@ -247,6 +247,15 @@
         return tmp;
     }
 
+    private RDN[] copy(RDN[] rdns)
+    {
+        RDN[] tmp = new RDN[rdns.length];
+
+        System.arraycopy(rdns, 0, tmp, 0, tmp.length);
+
+        return tmp;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         return new DERSequence(rdns);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
index 2ef24d3..8d2129a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -150,6 +150,12 @@
      */
     public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
 
+
+    /**
+     * id-at-organizationIdentifier
+     */
+    public static final ASN1ObjectIdentifier ORGANIZATION_IDENTIFIER = X509ObjectIdentifiers.id_at_organizationIdentifier;
+
     /**
      * Email address (RSA PKCS#9 extension) - IA5String.
      * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
@@ -222,6 +228,7 @@
         DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
         DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
         DefaultSymbols.put(NAME, "Name");
+        DefaultSymbols.put(ORGANIZATION_IDENTIFIER, "organizationIdentifier");
 
         DefaultLookUp.put("c", C);
         DefaultLookUp.put("o", O);
@@ -257,6 +264,7 @@
         DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
         DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
         DefaultLookUp.put("name", NAME);
+        DefaultLookUp.put("organizationidentifier", ORGANIZATION_IDENTIFIER);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
index 54eaa32..b55d9a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
@@ -21,7 +21,7 @@
     {
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
-
+    
     public static AlgorithmIdentifier getInstance(
         Object  obj)
     {
@@ -59,7 +59,7 @@
             throw new IllegalArgumentException("Bad sequence size: "
                     + seq.size());
         }
-
+        
         algorithm = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
 
         if (seq.size() == 2)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index 302ba03..d84ad4c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -15,6 +15,7 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.util.encoders.Hex;
 
 /**
  * The AuthorityKeyIdentifier object.
@@ -224,6 +225,6 @@
 
     public String toString()
     {
-        return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")");
+        return ("AuthorityKeyIdentifier: KeyID(" + ((keyidentifier != null) ? Hex.toHexString(this.keyidentifier.getOctets()) : "null") + ")");
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
index dd3422f..14bc2e2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
@@ -20,6 +20,11 @@
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
 
+    public static CRLDistPoint fromExtensions(Extensions extensions)
+    {
+        return CRLDistPoint.getInstance(extensions.getExtensionParsedValue(Extension.cRLDistributionPoints));
+    }
+
     public static CRLDistPoint getInstance(
         Object  obj)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
index 7f4476b..560b464 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
@@ -60,7 +60,7 @@
     public CertificatePolicies(
         PolicyInformation[] policyInformation)
     {
-        this.policyInformation = policyInformation;
+        this.policyInformation = copyPolicyInfo(policyInformation);
     }
 
     private CertificatePolicies(
@@ -76,9 +76,14 @@
 
     public PolicyInformation[] getPolicyInformation()
     {
-        PolicyInformation[] tmp = new PolicyInformation[policyInformation.length];
+        return copyPolicyInfo(policyInformation);
+    }
 
-        System.arraycopy(policyInformation, 0, tmp, 0, policyInformation.length);
+    private PolicyInformation[] copyPolicyInfo(PolicyInformation[] policyInfo)
+    {
+        PolicyInformation[] tmp = new PolicyInformation[policyInfo.length];
+
+        System.arraycopy(policyInfo, 0, tmp, 0, policyInfo.length);
 
         return tmp;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
index fd17f1b..4b040a7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
 
 /**
  * The DigestInfo object.
@@ -51,7 +52,7 @@
         AlgorithmIdentifier  algId,
         byte[]               digest)
     {
-        this.digest = digest;
+        this.digest = Arrays.clone(digest);
         this.algId = algId;
     }
 
@@ -71,7 +72,7 @@
 
     public byte[] getDigest()
     {
-        return digest;
+        return Arrays.clone(digest);
     }
 
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
index 6508f93..30a16f3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
@@ -56,6 +56,11 @@
         {
             Extension ext = Extension.getInstance(e.nextElement());
 
+            if (extensions.containsKey(ext.getExtnId()))
+            {
+                throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+            }
+            
             extensions.put(ext.getExtnId(), ext);
             ordering.addElement(ext.getExtnId());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
index 405f6e4..52e0c36 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
@@ -56,7 +56,7 @@
     public GeneralNames(
         GeneralName[]  names)
     {
-        this.names = names;
+        this.names = copy(names);
     }
 
     private GeneralNames(
@@ -72,9 +72,14 @@
 
     public GeneralName[] getNames()
     {
-        GeneralName[] tmp = new GeneralName[names.length];
+        return copy(names);
+    }
 
-        System.arraycopy(names, 0, tmp, 0, names.length);
+    private GeneralName[] copy(GeneralName[] nms)
+    {
+        GeneralName[] tmp = new GeneralName[nms.length];
+
+        System.arraycopy(nms, 0, tmp, 0, tmp.length);
 
         return tmp;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java
new file mode 100644
index 0000000..eb652f7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java
@@ -0,0 +1,92 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * The OtherName object.
+ * <pre>
+ * OtherName ::= SEQUENCE {
+ *      type-id    OBJECT IDENTIFIER,
+ *      value      [0] EXPLICIT ANY DEFINED BY type-id }
+ * </pre>
+ */
+public class OtherName
+    extends ASN1Object
+{
+    private final ASN1ObjectIdentifier typeID;
+    private final ASN1Encodable value;
+
+    /**
+     * OtherName factory method.
+     * @param obj the object used to construct an instance of <code>
+     * OtherName</code>. It must be an instance of <code>OtherName
+     * </code> or <code>ASN1Sequence</code>.
+     * @return the instance of <code>OtherName</code> built from the
+     * supplied object.
+     * @throws java.lang.IllegalArgumentException if the object passed
+     * to the factory is not an instance of <code>OtherName</code> or something that
+     * can be converted into an appropriate <code>ASN1Sequence</code>.
+     */
+    public static OtherName getInstance(
+        Object obj)
+    {
+
+        if (obj instanceof OtherName)
+        {
+            return (OtherName)obj;
+        }
+        else if (obj != null)
+        {
+            return new OtherName(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * Base constructor.
+     * @param typeID the type of the other name.
+     * @param value the ANY object that represents the value.
+     */
+    public OtherName(
+        ASN1ObjectIdentifier typeID,
+        ASN1Encodable value)
+    {
+        this.typeID = typeID;
+        this.value  = value;
+    }
+
+    private OtherName(ASN1Sequence seq)
+    {
+        this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
+    }
+
+    public ASN1ObjectIdentifier getTypeID()
+    {
+        return typeID;
+    }
+
+    public ASN1Encodable getValue()
+    {
+        return value;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(typeID);
+        v.add(new DERTaggedObject(true, 0, value));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
index 0f15dae..d360609 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -15,6 +16,7 @@
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
 
 public class PKIXNameConstraintValidator
     implements NameConstraintValidator
@@ -29,6 +31,8 @@
 
     private Set excludedSubtreesIP = new HashSet();
 
+    private Set excludedSubtreesOtherName = new HashSet();
+
     private Set permittedSubtreesDN;
 
     private Set permittedSubtreesDNS;
@@ -39,6 +43,8 @@
 
     private Set permittedSubtreesIP;
 
+    private Set permittedSubtreesOtherName;
+
     public PKIXNameConstraintValidator()
     {
     }
@@ -54,6 +60,9 @@
     {
         switch (name.getTagNo())
         {
+        case GeneralName.otherName:
+            checkPermittedOtherName(permittedSubtreesOtherName, OtherName.getInstance(name.getName()));
+            break;
         case GeneralName.rfc822Name:
             checkPermittedEmail(permittedSubtreesEmail,
                 extractNameAsString(name));
@@ -73,6 +82,9 @@
             byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
 
             checkPermittedIP(permittedSubtreesIP, ip);
+            break;
+        default:
+            throw new IllegalStateException("Unknown tag encountered: " + name.getTagNo());
         }
     }
 
@@ -88,6 +100,9 @@
     {
         switch (name.getTagNo())
         {
+        case GeneralName.otherName:
+            checkExcludedOtherName(excludedSubtreesOtherName, OtherName.getInstance(name.getName()));
+            break;
         case GeneralName.rfc822Name:
             checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
             break;
@@ -106,6 +121,9 @@
             byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
 
             checkExcludedIP(excludedSubtreesIP, ip);
+            break;
+        default:
+            throw new IllegalStateException("Unknown tag encountered: " + name.getTagNo());
         }
     }
 
@@ -141,8 +159,13 @@
             Map.Entry entry = (Map.Entry)it.next();
 
             // go through all subtree groups
-            switch (((Integer)entry.getKey()).intValue())
+            int nameType = ((Integer)entry.getKey()).intValue();
+            switch (nameType)
             {
+            case GeneralName.otherName:
+                permittedSubtreesOtherName = intersectOtherName(permittedSubtreesOtherName,
+                    (Set)entry.getValue());
+                break;
             case GeneralName.rfc822Name:
                 permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
                     (Set)entry.getValue());
@@ -162,6 +185,9 @@
             case GeneralName.iPAddress:
                 permittedSubtreesIP = intersectIP(permittedSubtreesIP,
                     (Set)entry.getValue());
+                break;
+            default:
+                throw new IllegalStateException("Unknown tag encountered: " + nameType);
             }
         }
     }
@@ -170,6 +196,9 @@
     {
         switch (nameType)
         {
+        case GeneralName.otherName:
+            permittedSubtreesOtherName = new HashSet();
+            break;
         case GeneralName.rfc822Name:
             permittedSubtreesEmail = new HashSet();
             break;
@@ -184,6 +213,9 @@
             break;
         case GeneralName.iPAddress:
             permittedSubtreesIP = new HashSet();
+            break;
+        default:
+            throw new IllegalStateException("Unknown tag encountered: " + nameType);
         }
     }
 
@@ -198,6 +230,10 @@
 
         switch (base.getTagNo())
         {
+        case GeneralName.otherName:
+            excludedSubtreesOtherName = unionOtherName(excludedSubtreesOtherName,
+                OtherName.getInstance(base.getName()));
+            break;
         case GeneralName.rfc822Name:
             excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
                 extractNameAsString(base));
@@ -218,6 +254,8 @@
             excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString
                 .getInstance(base.getName()).getOctets());
             break;
+        default:
+            throw new IllegalStateException("Unknown tag encountered: " + base.getTagNo());
         }
     }
 
@@ -228,11 +266,13 @@
             + hashCollection(excludedSubtreesEmail)
             + hashCollection(excludedSubtreesIP)
             + hashCollection(excludedSubtreesURI)
+            + hashCollection(excludedSubtreesOtherName)
             + hashCollection(permittedSubtreesDN)
             + hashCollection(permittedSubtreesDNS)
             + hashCollection(permittedSubtreesEmail)
             + hashCollection(permittedSubtreesIP)
-            + hashCollection(permittedSubtreesURI);
+            + hashCollection(permittedSubtreesURI)
+            + hashCollection(permittedSubtreesOtherName);
     }
 
     public boolean equals(Object o)
@@ -247,11 +287,13 @@
             && collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
             && collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
             && collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
+            && collectionsAreEqual(constraintValidator.excludedSubtreesOtherName, excludedSubtreesOtherName)
             && collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
             && collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
             && collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
             && collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
-            && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
+            && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesOtherName, permittedSubtreesOtherName);
     }
 
     public String toString()
@@ -283,6 +325,11 @@
             temp += "IP:\n";
             temp += stringifyIPCollection(permittedSubtreesIP) + "\n";
         }
+        if (permittedSubtreesOtherName != null)
+        {
+            temp += "OtherName:\n";
+            temp += stringifyOtherNameCollection(permittedSubtreesOtherName) + "\n";
+        }
         temp += "excluded:\n";
         if (!excludedSubtreesDN.isEmpty())
         {
@@ -309,6 +356,11 @@
             temp += "IP:\n";
             temp += stringifyIPCollection(excludedSubtreesIP) + "\n";
         }
+        if (!excludedSubtreesOtherName.isEmpty())
+        {
+            temp += "OtherName:\n";
+            temp += stringifyOtherNameCollection(excludedSubtreesOtherName) + "\n";
+        }
         return temp;
     }
 
@@ -474,6 +526,25 @@
         }
     }
 
+    private Set intersectOtherName(Set permitted, Set otherNames)
+    {
+        Set intersect = new HashSet(permitted);
+
+        intersect.retainAll(otherNames);
+
+        return intersect;
+    }
+
+
+    private Set unionOtherName(Set permitted, OtherName otherName)
+    {
+        Set union = new HashSet(permitted);
+
+        union.add(otherName);
+
+        return union;
+    }
+
     private Set intersectEmail(Set permitted, Set emails)
     {
         Set intersect = new HashSet();
@@ -774,6 +845,52 @@
             "Subject email address is not from a permitted subtree.");
     }
 
+    private void checkPermittedOtherName(Set permitted, OtherName name)
+        throws NameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            OtherName str = ((OtherName)it.next());
+
+            if (otherNameIsConstrained(name, str))
+            {
+                return;
+            }
+        }
+
+        throw new NameConstraintValidatorException(
+            "Subject OtherName is not from a permitted subtree.");
+    }
+
+    private void checkExcludedOtherName(Set excluded, OtherName name)
+        throws NameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            OtherName str = OtherName.getInstance(it.next());
+
+            if (otherNameIsConstrained(name, str))
+            {
+                throw new NameConstraintValidatorException(
+                    "OtherName is from an excluded subtree.");
+            }
+        }
+    }
+
     private void checkExcludedEmail(Set excluded, String email)
         throws NameConstraintValidatorException
     {
@@ -899,6 +1016,16 @@
         return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
     }
 
+    private boolean otherNameIsConstrained(OtherName name, OtherName constraint)
+    {
+        if (constraint.equals(name))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
     private boolean emailIsConstrained(String email, String constraint)
     {
         String sub = email.substring(email.indexOf('@') + 1);
@@ -1887,34 +2014,73 @@
      */
     private String stringifyIP(byte[] ip)
     {
-        String temp = "";
+        StringBuilder temp = new StringBuilder();
         for (int i = 0; i < ip.length / 2; i++)
         {
-            temp += Integer.toString(ip[i] & 0x00FF) + ".";
+            if (temp.length() > 0)
+            {
+                temp.append(".");
+            }
+            temp.append(Integer.toString(ip[i] & 0x00FF));
         }
-        temp = temp.substring(0, temp.length() - 1);
-        temp += "/";
+
+        temp.append("/");
+        boolean first = true;
         for (int i = ip.length / 2; i < ip.length; i++)
         {
-            temp += Integer.toString(ip[i] & 0x00FF) + ".";
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                temp.append(".");
+            }
+            temp.append(Integer.toString(ip[i] & 0x00FF));
         }
-        temp = temp.substring(0, temp.length() - 1);
-        return temp;
+
+        return temp.toString();
     }
 
     private String stringifyIPCollection(Set ips)
     {
-        String temp = "";
-        temp += "[";
+        StringBuilder temp = new StringBuilder();
+        temp.append("[");
         for (Iterator it = ips.iterator(); it.hasNext(); )
         {
-            temp += stringifyIP((byte[])it.next()) + ",";
+            if (temp.length() > 1)
+            {
+                temp.append(",");
+            }
+            temp.append(stringifyIP((byte[])it.next()));
         }
-        if (temp.length() > 1)
+        temp.append("]");
+        return temp.toString();
+    }
+
+    private String stringifyOtherNameCollection(Set otherNames)
+    {
+        StringBuilder temp = new StringBuilder();
+        temp.append("[");
+        for (Iterator it = otherNames.iterator(); it.hasNext(); )
         {
-            temp = temp.substring(0, temp.length() - 1);
+            if (temp.length() > 1)
+            {
+                temp.append(",");
+            }
+            OtherName name = OtherName.getInstance(it.next());
+            temp.append(name.getTypeID().getId());
+            temp.append(":");
+            try
+            {
+                temp.append(Hex.toHexString(name.getValue().toASN1Primitive().getEncoded()));
+            }
+            catch (IOException e)
+            {
+                temp.append(e.toString());
+            }
         }
-        temp += "]";
-        return temp;
+        temp.append("]");
+        return temp.toString();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
index 0938a94..97c0f14 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -5,7 +5,6 @@
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
@@ -14,7 +13,7 @@
 import org.bouncycastle.asn1.DERSequence;
 
 /**
- * The object that contains the public key stored in a certficate.
+ * The object that contains the public key stored in a certificate.
  * <p>
  * The getEncoded() method in the public keys in the JCE produces a DER
  * encoded one of these.
@@ -107,9 +106,7 @@
     public ASN1Primitive parsePublicKey()
         throws IOException
     {
-        ASN1InputStream         aIn = new ASN1InputStream(keyData.getOctets());
-
-        return aIn.readObject();
+        return ASN1Primitive.fromByteArray(keyData.getOctets());
     }
 
     /**
@@ -124,9 +121,7 @@
     public ASN1Primitive getPublicKey()
         throws IOException
     {
-        ASN1InputStream         aIn = new ASN1InputStream(keyData.getOctets());
-
-        return aIn.readObject();
+        return ASN1Primitive.fromByteArray(keyData.getOctets());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
index f7f6005..3d5e1be 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
@@ -1,12 +1,13 @@
 package org.bouncycastle.asn1.x509;
 
+import java.math.BigInteger;
+
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x500.X500Name;
 
 /**
@@ -87,6 +88,22 @@
             version = new ASN1Integer(0);
         }
 
+        boolean isV1 = false;
+        boolean isV2 = false;
+ 
+        if (version.getValue().equals(BigInteger.valueOf(0)))
+        {
+            isV1 = true;
+        }
+        else if (version.getValue().equals(BigInteger.valueOf(1)))
+        {
+            isV2 = true;
+        }
+        else if (!version.getValue().equals(BigInteger.valueOf(2)))
+        {
+            throw new IllegalArgumentException("version number not recognised");
+        }
+
         serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
 
         signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
@@ -107,7 +124,13 @@
         //
         subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
 
-        for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+        int extras = seq.size() - (seqStart + 6) - 1;
+        if (extras != 0 && isV1)
+        {
+            throw new IllegalArgumentException("version 1 certificate contains extra data");
+        }
+        
+        while (extras > 0)
         {
             ASN1TaggedObject extra = (ASN1TaggedObject)seq.getObjectAt(seqStart + 6 + extras);
 
@@ -120,8 +143,16 @@
                 subjectUniqueId = DERBitString.getInstance(extra, false);
                 break;
             case 3:
+                if (isV2)
+                {
+                    throw new IllegalArgumentException("version 2 certificate cannot contain extensions");
+                }
                 extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown tag encountered in structure: " + extra.getTagNo());
             }
+            extras--;
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
index 2c5d920..e7bdedc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -29,6 +29,7 @@
  * <p>
  * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
  * will parse them, but you really shouldn't be creating new ones.
+ * @deprecated use TBSCertificate
  */
 public class TBSCertificateStructure
     extends ASN1Object
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
index 029a66a..f6523c5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
@@ -79,8 +79,8 @@
            }
            else
            {
-               noticeRef = null;
                explicitText = DisplayText.getInstance(as.getObjectAt(0));
+               noticeRef = null;
            }
        }
        else if (as.size() == 0)       // neither field set!
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index e58220e..af218bc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -23,6 +23,8 @@
     /** Subject RDN components: name = 2.5.4.41 */
     static final ASN1ObjectIdentifier    id_at_name              = new ASN1ObjectIdentifier("2.5.4.41").intern();
 
+    static final ASN1ObjectIdentifier    id_at_organizationIdentifier = new ASN1ObjectIdentifier("2.5.4.97").intern();
+
     /**
      * id-SHA1 OBJECT IDENTIFIER ::=    
      *   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java
index e23f1b8..0555190 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java
@@ -19,7 +19,7 @@
  *       p                INTEGER,           -- odd prime, p=jq +1
  *       g                INTEGER,           -- generator, g
  *       q                INTEGER,           -- factor of p-1
- *       j                INTEGER OPTIONAL,  -- subgroup factor, j>= 2
+ *       j                INTEGER OPTIONAL,  -- subgroup factor, j &gt;= 2
  *       validationParams  ValidationParams OPTIONAL
  *    }
  * </pre>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
index 0bd665d..bc57b9b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -6,9 +6,12 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.anssi.ANSSINamedCurves;
 import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.gm.GMNamedCurves;
 import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.sec.SECNamedCurves;
 import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.crypto.params.ECDomainParameters;
 
 /**
  * A general class that reads all X9.62 style EC curve tables.
@@ -47,6 +50,16 @@
             ecP = ANSSINamedCurves.getByName(name);
         }
 
+        if (ecP == null)
+        {
+            ecP = fromDomainParameters(ECGOST3410NamedCurves.getByName(name));
+        }
+
+        if (ecP == null)
+        {
+            ecP = GMNamedCurves.getByName(name);
+        }
+
         return ecP;
     }
 
@@ -81,6 +94,16 @@
             oid = ANSSINamedCurves.getOID(name);
         }
 
+        if (oid == null)
+        {
+            oid = ECGOST3410NamedCurves.getOID(name);
+        }
+
+        if (oid == null)
+        {
+            oid = GMNamedCurves.getOID(name);
+        }
+
         return oid;
     }
 
@@ -94,7 +117,7 @@
     public static String getName(
         ASN1ObjectIdentifier oid)
     {
-        String name = NISTNamedCurves.getName(oid);
+        String name = X962NamedCurves.getName(oid);
 
         if (name == null)
         {
@@ -103,12 +126,17 @@
 
         if (name == null)
         {
+            name = NISTNamedCurves.getName(oid);
+        }
+
+        if (name == null)
+        {
             name = TeleTrusTNamedCurves.getName(oid);
         }
 
         if (name == null)
         {
-            name = X962NamedCurves.getName(oid);
+            name = ANSSINamedCurves.getName(oid);
         }
 
         if (name == null)
@@ -116,6 +144,16 @@
             name = ECGOST3410NamedCurves.getName(oid);
         }
 
+        if (name == null)
+        {
+            name = GMNamedCurves.getName(oid);
+        }
+
+        if (name == null)
+        {
+            name = CustomNamedCurves.getName(oid);
+        }
+
         return name;
     }
 
@@ -148,6 +186,16 @@
             ecP = ANSSINamedCurves.getByOID(oid);
         }
 
+        if (ecP == null)
+        {
+            ecP = fromDomainParameters(ECGOST3410NamedCurves.getByOID(oid));
+        }
+
+        if (ecP == null)
+        {
+            ecP = GMNamedCurves.getByOID(oid);
+        }
+
         return ecP;
     }
 
@@ -165,6 +213,8 @@
         addEnumeration(v, NISTNamedCurves.getNames());
         addEnumeration(v, TeleTrusTNamedCurves.getNames());
         addEnumeration(v, ANSSINamedCurves.getNames());
+        addEnumeration(v, ECGOST3410NamedCurves.getNames());
+        addEnumeration(v, GMNamedCurves.getNames());
 
         return v.elements();
     }
@@ -178,4 +228,9 @@
             v.addElement(e.nextElement());
         }
     }
+
+    private static X9ECParameters fromDomainParameters(ECDomainParameters dp)
+    {
+        return dp == null ? null : new X9ECParameters(dp.getCurve(), dp.getG(), dp.getN(), dp.getH(), dp.getSeed());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java
index 159ec83..1c03d95 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/KeySpecificInfo.java
@@ -92,7 +92,7 @@
     /**
      * Return an ASN.1 primitive representation of this object.
      *
-     * @return a DERSequence containing the parameter values.
+     * @return a DERSequence containing the KeySpecificInfo values.
      */
     public ASN1Primitive toASN1Primitive()
     {
@@ -103,4 +103,4 @@
 
         return new DERSequence(v);
     }
-}
\ No newline at end of file
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
index 022c018..0296834 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
@@ -23,7 +23,7 @@
             BigInteger h = BigInteger.valueOf(1);
 
             ECCurve cFp192v1 = new ECCurve.Fp(
-                new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+                new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16),
                 new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
                 new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16),
                 n, h);
@@ -45,7 +45,7 @@
             BigInteger h = BigInteger.valueOf(1);
 
             ECCurve cFp192v2 = new ECCurve.Fp(
-                new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+                new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16),
                 new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
                 new BigInteger("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953", 16),
                 n, h);
@@ -67,7 +67,7 @@
             BigInteger h = BigInteger.valueOf(1);
 
             ECCurve cFp192v3 = new ECCurve.Fp(
-                new BigInteger("6277101735386680763835789423207666416083908700390324961279"),
+                new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16),
                 new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
                 new BigInteger("22123dc2395a05caa7423daeccc94760a7d462256bd56916", 16),
                 n, h);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
index f1bac2b..3838a23 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
@@ -13,6 +13,7 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Arrays;
 
 /**
  * ASN.1 def for Elliptic-Curve Curve structure. See
@@ -29,9 +30,7 @@
     public X9Curve(
         ECCurve     curve)
     {
-        this.curve = curve;
-        this.seed = null;
-        setFieldIdentifier();
+        this(curve, null);
     }
 
     public X9Curve(
@@ -39,25 +38,25 @@
         byte[]      seed)
     {
         this.curve = curve;
-        this.seed = seed;
+        this.seed = Arrays.clone(seed);
         setFieldIdentifier();
     }
 
     public X9Curve(
         X9FieldID     fieldID,
+        BigInteger    order,
+        BigInteger    cofactor,
         ASN1Sequence  seq)
-    {
-        // TODO Is it possible to get the order(n) and cofactor(h) too?
-
+    {   
         fieldIdentifier = fieldID.getIdentifier();
         if (fieldIdentifier.equals(prime_field))
-        {
-            BigInteger      p = ((ASN1Integer)fieldID.getParameters()).getValue();
-            X9FieldElement  x9A = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(0));
-            X9FieldElement  x9B = new X9FieldElement(p, (ASN1OctetString)seq.getObjectAt(1));
-            curve = new ECCurve.Fp(p, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger());
+        {   
+            BigInteger p = ((ASN1Integer)fieldID.getParameters()).getValue();
+            BigInteger A = new BigInteger(1, ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());      
+            BigInteger B = new BigInteger(1, ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());      
+            curve = new ECCurve.Fp(p, A, B, order, cofactor);
         }
-        else if (fieldIdentifier.equals(characteristic_two_field)) 
+        else if (fieldIdentifier.equals(characteristic_two_field))
         {
             // Characteristic two field
             ASN1Sequence parameters = ASN1Sequence.getInstance(fieldID.getParameters());
@@ -70,11 +69,11 @@
             int k2 = 0;
             int k3 = 0;
 
-            if (representation.equals(tpBasis)) 
+            if (representation.equals(tpBasis))
             {
                 // Trinomial basis representation
                 k1 = ASN1Integer.getInstance(parameters.getObjectAt(2)).getValue().intValue();
-            }
+            }   
             else if (representation.equals(ppBasis))
             {
                 // Pentanomial basis representation
@@ -82,25 +81,25 @@
                 k1 = ASN1Integer.getInstance(pentanomial.getObjectAt(0)).getValue().intValue();
                 k2 = ASN1Integer.getInstance(pentanomial.getObjectAt(1)).getValue().intValue();
                 k3 = ASN1Integer.getInstance(pentanomial.getObjectAt(2)).getValue().intValue();
-            }
+            }   
             else
             {
                 throw new IllegalArgumentException("This type of EC basis is not implemented");
-            }
-            X9FieldElement x9A = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(0));
-            X9FieldElement x9B = new X9FieldElement(m, k1, k2, k3, (ASN1OctetString)seq.getObjectAt(1));
-            curve = new ECCurve.F2m(m, k1, k2, k3, x9A.getValue().toBigInteger(), x9B.getValue().toBigInteger());
-        }
+            }   
+            BigInteger A = new BigInteger(1, ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());      
+            BigInteger B = new BigInteger(1, ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());      
+            curve = new ECCurve.F2m(m, k1, k2, k3, A, B, order, cofactor);
+        }   
         else
         {
             throw new IllegalArgumentException("This type of ECCurve is not implemented");
-        }
+        }   
 
         if (seq.size() == 3)
         {
-            seed = ((DERBitString)seq.getObjectAt(2)).getBytes();
-        }
-    }
+            seed = Arrays.clone(((DERBitString)seq.getObjectAt(2)).getBytes());
+        }   
+    }   
 
     private void setFieldIdentifier()
     {
@@ -125,7 +124,7 @@
 
     public byte[]   getSeed()
     {
-        return seed;
+        return Arrays.clone(seed);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
index 8516365..f02404a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -13,6 +13,7 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.field.PolynomialExtensionField;
+import org.bouncycastle.util.Arrays;
 
 /**
  * ASN.1 def for Elliptic-Curve ECParameters structure. See
@@ -35,34 +36,35 @@
         ASN1Sequence  seq)
     {
         if (!(seq.getObjectAt(0) instanceof ASN1Integer)
-           || !((ASN1Integer)seq.getObjectAt(0)).getValue().equals(ONE))
+            || !((ASN1Integer)seq.getObjectAt(0)).getValue().equals(ONE))
         {
             throw new IllegalArgumentException("bad version in X9ECParameters");
         }
 
-        X9Curve     x9c = new X9Curve(
-                        X9FieldID.getInstance(seq.getObjectAt(1)),
-                        ASN1Sequence.getInstance(seq.getObjectAt(2)));
+        this.n = ((ASN1Integer)seq.getObjectAt(4)).getValue();
+
+        if (seq.size() == 6)
+        {
+            this.h = ((ASN1Integer)seq.getObjectAt(5)).getValue();
+        }
+
+        X9Curve x9c = new X9Curve(
+            X9FieldID.getInstance(seq.getObjectAt(1)), n, h,
+            ASN1Sequence.getInstance(seq.getObjectAt(2)));
 
         this.curve = x9c.getCurve();
         Object p = seq.getObjectAt(3);
 
         if (p instanceof X9ECPoint)
         {
-            this.g = ((X9ECPoint)p);
+            this.g = (X9ECPoint)p;
         }
         else
         {
             this.g = new X9ECPoint(curve, (ASN1OctetString)p);
         }
 
-        this.n = ((ASN1Integer)seq.getObjectAt(4)).getValue();
         this.seed = x9c.getSeed();
-
-        if (seq.size() == 6)
-        {
-            this.h = ((ASN1Integer)seq.getObjectAt(5)).getValue();
-        }
     }
 
     public static X9ECParameters getInstance(Object obj)
@@ -127,7 +129,7 @@
         this.g = g;
         this.n = n;
         this.h = h;
-        this.seed = seed;
+        this.seed = Arrays.clone(seed);
 
         if (ECAlgorithms.isFpCurve(curve))
         {
@@ -178,7 +180,7 @@
 
     public byte[] getSeed()
     {
-        return seed;
+        return Arrays.clone(seed);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
index 96130c6..2dd8ff1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.asn1.x9;
 
+/**
+ * A holding class that allows for X9ECParameters to be lazily constructed.
+ */
 public abstract class X9ECParametersHolder
 {
     private X9ECParameters params;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
index 95fdc67..b503784 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
@@ -9,7 +9,7 @@
 import org.bouncycastle.util.Arrays;
 
 /**
- * class for describing an ECPoint as a DER object.
+ * Class for describing an ECPoint as a DER object.
  */
 public class X9ECPoint
     extends ASN1Object
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
index 13fe772..4cba82d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
@@ -22,17 +22,23 @@
     {
         this.f = f;
     }
-    
+
+    /**
+     * @deprecated Will be removed
+     */
     public X9FieldElement(BigInteger p, ASN1OctetString s)
     {
         this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets())));
     }
-    
+
+    /**
+     * @deprecated Will be removed
+     */
     public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s)
     {
         this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets())));
     }
-    
+
     public ECFieldElement getValue()
     {
         return f;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
index 1317b4a..ce6f86d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
@@ -4,7 +4,7 @@
 
 /**
  *
- * X9.62
+ * Object identifiers for the various X9 standards.
  * <pre>
  * ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
  *                                    us(840) ansi-x962(10045) }
@@ -13,129 +13,129 @@
 public interface X9ObjectIdentifiers
 {
     /** Base OID: 1.2.840.10045 */
-    static final ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045");
+    ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045");
 
     /** OID: 1.2.840.10045.1 */
-    static final ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1");
+    ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1");
 
     /** OID: 1.2.840.10045.1.1 */
-    static final ASN1ObjectIdentifier prime_field = id_fieldType.branch("1");
+    ASN1ObjectIdentifier prime_field = id_fieldType.branch("1");
 
     /** OID: 1.2.840.10045.1.2 */
-    static final ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2");
+    ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2");
 
     /** OID: 1.2.840.10045.1.2.3.1 */
-    static final ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1");
+    ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1");
 
     /** OID: 1.2.840.10045.1.2.3.2 */
-    static final ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2");
+    ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2");
 
     /** OID: 1.2.840.10045.1.2.3.3 */
-    static final ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3");
+    ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3");
 
     /** OID: 1.2.840.10045.4 */
-    static final ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4");
+    ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4");
 
     /** OID: 1.2.840.10045.4.1 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA1 = id_ecSigType.branch("1");
+    ASN1ObjectIdentifier ecdsa_with_SHA1 = id_ecSigType.branch("1");
 
     /** OID: 1.2.840.10045.2 */
-    static final ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2");
+    ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2");
 
     /** OID: 1.2.840.10045.2.1 */
-    static final ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1");
+    ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1");
 
     /** OID: 1.2.840.10045.4.3 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3");
+    ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3");
 
     /** OID: 1.2.840.10045.4.3.1 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1");
+    ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1");
 
     /** OID: 1.2.840.10045.4.3.2 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2");
+    ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2");
 
     /** OID: 1.2.840.10045.4.3.3 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3");
+    ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3");
 
     /** OID: 1.2.840.10045.4.3.4 */
-    static final ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4");
+    ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4");
 
     /**
      * Named curves base
      * <p>
      * OID: 1.2.840.10045.3
      */
-    static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3");
+    ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3");
 
     /**
      * Two Curves
      * <p>
      * OID: 1.2.840.10045.3.0
      */
-    static final ASN1ObjectIdentifier  cTwoCurve = ellipticCurve.branch("0");
+    ASN1ObjectIdentifier  cTwoCurve = ellipticCurve.branch("0");
 
     /** Two Curve c2pnb163v1, OID: 1.2.840.10045.3.0.1 */
-    static final ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1");
+    ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1");
     /** Two Curve c2pnb163v2, OID: 1.2.840.10045.3.0.2 */
-    static final ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2");
+    ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2");
     /** Two Curve c2pnb163v3, OID: 1.2.840.10045.3.0.3 */
-    static final ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3");
+    ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3");
     /** Two Curve c2pnb176w1, OID: 1.2.840.10045.3.0.4 */
-    static final ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4");
+    ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4");
     /** Two Curve c2tnb191v1, OID: 1.2.840.10045.3.0.5 */
-    static final ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5");
+    ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5");
     /** Two Curve c2tnb191v2, OID: 1.2.840.10045.3.0.6 */
-    static final ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6");
+    ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6");
     /** Two Curve c2tnb191v3, OID: 1.2.840.10045.3.0.7 */
-    static final ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7");
+    ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7");
     /** Two Curve c2onb191v4, OID: 1.2.840.10045.3.0.8 */
-    static final ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8");
+    ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8");
     /** Two Curve c2onb191v5, OID: 1.2.840.10045.3.0.9 */
-    static final ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9");
+    ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9");
     /** Two Curve c2pnb208w1, OID: 1.2.840.10045.3.0.10 */
-    static final ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10");
+    ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10");
     /** Two Curve c2tnb239v1, OID: 1.2.840.10045.3.0.11 */
-    static final ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11");
+    ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11");
     /** Two Curve c2tnb239v2, OID: 1.2.840.10045.3.0.12 */
-    static final ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12");
+    ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12");
     /** Two Curve c2tnb239v3, OID: 1.2.840.10045.3.0.13 */
-    static final ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13");
+    ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13");
     /** Two Curve c2onb239v4, OID: 1.2.840.10045.3.0.14 */
-    static final ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14");
+    ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14");
     /** Two Curve c2onb239v5, OID: 1.2.840.10045.3.0.15 */
-    static final ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15");
+    ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15");
     /** Two Curve c2pnb272w1, OID: 1.2.840.10045.3.0.16 */
-    static final ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16");
+    ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16");
     /** Two Curve c2pnb304w1, OID: 1.2.840.10045.3.0.17 */
-    static final ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17");
+    ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17");
     /** Two Curve c2tnb359v1, OID: 1.2.840.10045.3.0.18 */
-    static final ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18");
+    ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18");
     /** Two Curve c2pnb368w1, OID: 1.2.840.10045.3.0.19 */
-    static final ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19");
+    ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19");
     /** Two Curve c2tnb431r1, OID: 1.2.840.10045.3.0.20 */
-    static final ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20");
+    ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20");
 
     /**
      * Prime Curves
      * <p>
      * OID: 1.2.840.10045.3.1
      */
-    static final ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1");
+    ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1");
 
     /** Prime Curve prime192v1, OID: 1.2.840.10045.3.1.1 */
-    static final ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1");
+    ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1");
     /** Prime Curve prime192v2, OID: 1.2.840.10045.3.1.2 */
-    static final ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2");
+    ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2");
     /** Prime Curve prime192v3, OID: 1.2.840.10045.3.1.3 */
-    static final ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3");
+    ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3");
     /** Prime Curve prime239v1, OID: 1.2.840.10045.3.1.4 */
-    static final ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4");
+    ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4");
     /** Prime Curve prime239v2, OID: 1.2.840.10045.3.1.5 */
-    static final ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5");
+    ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5");
     /** Prime Curve prime239v3, OID: 1.2.840.10045.3.1.6 */
-    static final ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6");
+    ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6");
     /** Prime Curve prime256v1, OID: 1.2.840.10045.3.1.7 */
-    static final ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7");
+    ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7");
 
     /**
      * DSA
@@ -145,7 +145,7 @@
      * </pre>
      * Base OID: 1.2.840.10040.4.1
      */
-    static final ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1");
+    ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1");
 
     /**
      * <pre>
@@ -154,26 +154,26 @@
      * </pre>
      * OID: 1.2.840.10040.4.3
      */
-    static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3");
+    ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3");
 
     /**
      * X9.63 - Signature Specification
      * <p>
      * Base OID: 1.3.133.16.840.63.0
      */
-    static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0");
+    ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0");
     /** OID: 1.3.133.16.840.63.0.2 */
-    static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme      = x9_63_scheme.branch("2");
+    ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme      = x9_63_scheme.branch("2");
     /** OID: 1.3.133.16.840.63.0.3 */
-    static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3");
+    ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3");
     /** OID: 1.3.133.16.840.63.0.16 */
-    static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme           = x9_63_scheme.branch("16");
+    ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme           = x9_63_scheme.branch("16");
 
     /**
      * X9.42
      */
 
-    static final ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046");
+    ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046");
 
     /**
      * Diffie-Hellman
@@ -184,26 +184,26 @@
      * </pre>
      * OID: 1.2.840.10046.2.1
      */
-    static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1");
+    ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1");
 
     /** X9.42 schemas base OID: 1.2.840.10046.3 */
-    static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3");
+    ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3");
     /** X9.42 dhStatic OID: 1.2.840.10046.3.1 */
-    static final ASN1ObjectIdentifier dhStatic        = x9_42_schemes.branch("1");
+    ASN1ObjectIdentifier dhStatic        = x9_42_schemes.branch("1");
     /** X9.42 dhEphem OID: 1.2.840.10046.3.2 */
-    static final ASN1ObjectIdentifier dhEphem         = x9_42_schemes.branch("2");
+    ASN1ObjectIdentifier dhEphem         = x9_42_schemes.branch("2");
     /** X9.42 dhOneFlow OID: 1.2.840.10046.3.3 */
-    static final ASN1ObjectIdentifier dhOneFlow       = x9_42_schemes.branch("3");
+    ASN1ObjectIdentifier dhOneFlow       = x9_42_schemes.branch("3");
     /** X9.42 dhHybrid1 OID: 1.2.840.10046.3.4 */
-    static final ASN1ObjectIdentifier dhHybrid1       = x9_42_schemes.branch("4");
+    ASN1ObjectIdentifier dhHybrid1       = x9_42_schemes.branch("4");
     /** X9.42 dhHybrid2 OID: 1.2.840.10046.3.5 */
-    static final ASN1ObjectIdentifier dhHybrid2       = x9_42_schemes.branch("5");
+    ASN1ObjectIdentifier dhHybrid2       = x9_42_schemes.branch("5");
     /** X9.42 dhHybridOneFlow OID: 1.2.840.10046.3.6 */
-    static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6");
+    ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6");
     /** X9.42 MQV2 OID: 1.2.840.10046.3.7 */
-    static final ASN1ObjectIdentifier mqv2            = x9_42_schemes.branch("7");
+    ASN1ObjectIdentifier mqv2            = x9_42_schemes.branch("7");
     /** X9.42 MQV1 OID: 1.2.840.10046.3.8 */
-    static final ASN1ObjectIdentifier mqv1            = x9_42_schemes.branch("8");
+    ASN1ObjectIdentifier mqv1            = x9_42_schemes.branch("8");
 
     /**
      * X9.44
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java
new file mode 100644
index 0000000..8a62a8d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto;
+
+import java.security.Permission;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Permissions that need to be configured if a SecurityManager is used.
+ */
+public class CryptoServicesPermission
+    extends Permission
+{
+    /**
+     * Enable the setting of global configuration properties. This permission implies THREAD_LOCAL_CONFIG
+     */
+    public static final String GLOBAL_CONFIG = "globalConfig";
+
+    /**
+     * Enable the setting of thread local configuration properties.
+     */
+    public static final String THREAD_LOCAL_CONFIG = "threadLocalConfig";
+
+    /**
+     * Enable the setting of the default SecureRandom.
+     */
+    public static final String DEFAULT_RANDOM = "defaultRandomConfig";
+
+    private final Set<String> actions = new HashSet<String>();
+
+    public CryptoServicesPermission(String name)
+    {
+        super(name);
+
+        this.actions.add(name);
+    }
+
+    public boolean implies(Permission permission)
+    {
+        if (permission instanceof CryptoServicesPermission)
+        {
+            CryptoServicesPermission other = (CryptoServicesPermission)permission;
+
+            if (this.getName().equals(other.getName()))
+            {
+                return true;
+            }
+
+            if (this.actions.containsAll(other.actions))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean equals(Object obj)
+    {
+        if (obj instanceof CryptoServicesPermission)
+        {
+            CryptoServicesPermission other = (CryptoServicesPermission)obj;
+
+            if (this.actions.equals(other.actions))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return actions.hashCode();
+    }
+
+    public String getActions()
+    {
+        return actions.toString();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java
new file mode 100644
index 0000000..4448be0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java
@@ -0,0 +1,417 @@
+package org.bouncycastle.crypto;
+
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHValidationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * Basic registrar class for providing defaults for cryptography services in this module.
+ */
+public final class CryptoServicesRegistrar
+{
+    private static final Permission CanSetDefaultProperty = new CryptoServicesPermission(CryptoServicesPermission.GLOBAL_CONFIG);
+    private static final Permission CanSetThreadProperty = new CryptoServicesPermission(CryptoServicesPermission.THREAD_LOCAL_CONFIG);
+    private static final Permission CanSetDefaultRandom = new CryptoServicesPermission(CryptoServicesPermission.DEFAULT_RANDOM);
+
+    private static final ThreadLocal<Map<String, Object[]>> threadProperties = new ThreadLocal<Map<String, Object[]>>();
+    private static final Map<String, Object[]> globalProperties = Collections.synchronizedMap(new HashMap<String, Object[]>());
+
+    private static volatile SecureRandom defaultSecureRandom;
+
+    static
+    {
+        // default domain parameters for DSA and Diffie-Hellman
+
+        DSAParameters def512Params = new DSAParameters(
+            new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e17", 16),
+            new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16),
+            new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4", 16),
+            new DSAValidationParameters(Hex.decode("b869c82b35d70e1b1ff91b28e37a62ecdc34409b"), 123));
+
+        DSAParameters def768Params = new DSAParameters(
+            new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5" +
+                           "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
+                           "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
+                           "ee3688c11a8c56ab127a3daf", 16),
+            new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16),
+            new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7" +
+                           "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
+                           "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
+                           "7064f316933a346d3f529252", 16),
+            new DSAValidationParameters(Hex.decode("77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399"), 263));
+
+        DSAParameters def1024Params = new DSAParameters(
+            new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80" +
+                            "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
+                            "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
+                            "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
+                            "f3ae2b61d72aeff22203199dd14801c7", 16),
+            new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16),
+            new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b" +
+                            "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
+                            "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
+                            "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
+                            "928b665e807b552564014c3bfecf492a", 16),
+            new DSAValidationParameters(Hex.decode("8d5155894229d5e689ee01e6018a237e2cae64cd"), 92));
+
+        DSAParameters def2048Params = new DSAParameters(
+            new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
+                            "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                            "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                            "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                            "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                            "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                            "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                            "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                            "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                            "a406cb0b", 16),
+            new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
+            new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
+                            "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                            "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                            "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                            "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                            "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                            "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                            "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                            "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                            "78693c7a", 16),
+            new DSAValidationParameters(Hex.decode("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
+
+        localSetGlobalProperty(Property.DSA_DEFAULT_PARAMS, def512Params, def768Params, def1024Params, def2048Params);
+        localSetGlobalProperty(Property.DH_DEFAULT_PARAMS, toDH(def512Params), toDH(def768Params), toDH(def1024Params), toDH(def2048Params));
+    }
+
+    private CryptoServicesRegistrar()
+    {
+
+    }
+
+    /**
+     * Return the default source of randomness.
+     *
+     * @return the default SecureRandom
+     * @throws IllegalStateException if no source of randomness has been provided.
+     */
+    public static SecureRandom getSecureRandom()
+    {
+        if (defaultSecureRandom == null)
+        {
+            return new SecureRandom();
+        }
+        
+        return defaultSecureRandom;
+    }
+
+    /**
+     * Set a default secure random to be used where none is otherwise provided.
+     *
+     * @param secureRandom the SecureRandom to use as the default.
+     */
+    public static void setSecureRandom(SecureRandom secureRandom)
+    {
+        checkPermission(CanSetDefaultRandom);
+
+        defaultSecureRandom = secureRandom;
+    }
+
+    /**
+     * Return the default value for a particular property if one exists. The look up is done on the thread's local
+     * configuration first and then on the global configuration in no local configuration exists.
+     *
+     * @param property the property to look up.
+     * @param <T> the type to be returned
+     * @return null if the property is not set, the default value otherwise,
+     */
+    public static <T> T getProperty(Property property)
+    {
+        Object[] values = lookupProperty(property);
+
+        if (values != null)
+        {
+            return (T)values[0];
+        }
+
+        return null;
+    }
+
+    private static Object[] lookupProperty(Property property)
+    {
+        Map<String, Object[]> properties = threadProperties.get();
+        Object[] values;
+
+        if (properties == null || !properties.containsKey(property.name))
+        {
+            values = globalProperties.get(property.name);
+        }
+        else
+        {
+            values = properties.get(property.name);
+        }
+        return values;
+    }
+
+    /**
+     * Return an array representing the current values for a sized property such as DH_DEFAULT_PARAMS or
+     * DSA_DEFAULT_PARAMS.
+     *
+     * @param property the name of the property to look up.
+     * @param <T> the base type of the array to be returned.
+     * @return null if the property is not set, an array of the current values otherwise.
+     */
+    public static <T> T[] getSizedProperty(Property property)
+    {
+        Object[] values = lookupProperty(property);
+
+        if (values == null)
+        {
+            return null;
+        }
+
+        return (T[])values.clone();
+    }
+
+    /**
+     * Return the value for a specific size for a sized property such as DH_DEFAULT_PARAMS or
+     * DSA_DEFAULT_PARAMS.
+     *
+     * @param property the name of the property to look up.
+     * @param size the size (in bits) of the defining value in the property type.
+     * @param <T> the type of the value to be returned.
+     * @return the current value for the size, null if there is no value set,
+     */
+    public static <T> T getSizedProperty(Property property, int size)
+    {
+        Object[] values = lookupProperty(property);
+
+        if (values == null)
+        {
+            return null;
+        }
+
+        if (property.type.isAssignableFrom(DHParameters.class))
+        {
+            for (int i = 0; i != values.length; i++)
+            {
+                DHParameters params = (DHParameters)values[i];
+
+                if (params.getP().bitLength() == size)
+                {
+                    return (T)params;
+                }
+            }
+        }
+        else if (property.type.isAssignableFrom(DSAParameters.class))
+        {
+            for (int i = 0; i != values.length; i++)
+            {
+                DSAParameters params = (DSAParameters)values[i];
+
+                if (params.getP().bitLength() == size)
+                {
+                    return (T)params;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Set the value of the the passed in property on the current thread only. More than
+     * one value can be passed in for a sized property. If more than one value is provided the
+     * first value in the argument list becomes the default value.
+     *
+     * @param property the name of the property to set.
+     * @param propertyValue the values to assign to the property.
+     * @param <T> the base type of the property value.
+     */
+    public static <T> void setThreadProperty(Property property, T... propertyValue)
+    {
+        checkPermission(CanSetThreadProperty);
+
+        if (!property.type.isAssignableFrom(propertyValue[0].getClass()))
+        {
+            throw new IllegalArgumentException("Bad property value passed");
+        }
+
+        localSetThread(property, propertyValue.clone());
+    }
+
+    /**
+     * Set the value of the the passed in property globally in the JVM. More than
+     * one value can be passed in for a sized property. If more than one value is provided the
+     * first value in the argument list becomes the default value.
+     *
+     * @param property the name of the property to set.
+     * @param propertyValue the values to assign to the property.
+     * @param <T> the base type of the property value.
+     */
+    public static <T> void setGlobalProperty(Property property, T... propertyValue)
+    {
+        checkPermission(CanSetDefaultProperty);
+
+        localSetGlobalProperty(property, propertyValue.clone());
+    }
+
+    private static <T> void localSetThread(Property property, T[] propertyValue)
+    {
+        Map<String, Object[]> properties = threadProperties.get();
+
+        if (properties == null)
+        {
+            properties = new HashMap<String, Object[]>();
+            threadProperties.set(properties);
+        }
+
+        properties.put(property.name, propertyValue);
+    }
+
+    private static <T> void localSetGlobalProperty(Property property, T... propertyValue)
+    {
+        if (!property.type.isAssignableFrom(propertyValue[0].getClass()))
+        {
+            throw new IllegalArgumentException("Bad property value passed");
+        }
+
+        // set the property for the current thread as well to avoid mass confusion
+        localSetThread(property, propertyValue);
+
+        globalProperties.put(property.name, propertyValue);
+    }
+
+    /**
+     * Clear the global value for the passed in property.
+     *
+     * @param property the property to be cleared.
+     * @param <T> the base type of the property value
+     * @return an array of T if a value was previously set, null otherwise.
+     */
+    public static <T> T[] clearGlobalProperty(Property property)
+    {
+        checkPermission(CanSetDefaultProperty);
+
+        // clear the property for the current thread as well to avoid confusion
+        localClearThreadProperty(property);
+
+        return (T[])globalProperties.remove(property.name);
+    }
+
+    /**
+     * Clear the thread local value for the passed in property.
+     *
+     * @param property the property to be cleared.
+     * @param <T> the base type of the property value
+     * @return an array of T if a value was previously set, null otherwise.
+     */
+    public static <T> T[] clearThreadProperty(Property property)
+    {
+        checkPermission(CanSetThreadProperty);
+
+        return (T[])localClearThreadProperty(property);
+    }
+
+    private static Object[] localClearThreadProperty(Property property)
+    {
+        Map<String, Object[]> properties = threadProperties.get();
+
+        if (properties == null)
+        {
+            properties = new HashMap<String, Object[]>();
+            threadProperties.set(properties);
+        }
+
+        return properties.remove(property.name);
+    }
+
+    private static void checkPermission(final Permission permission)
+    {
+        final SecurityManager securityManager = System.getSecurityManager();
+
+        if (securityManager != null)
+        {
+            AccessController.doPrivileged(new PrivilegedAction<Object>()
+            {
+                public Object run()
+                {
+                    securityManager.checkPermission(permission);
+
+                    return null;
+                }
+            });
+        }
+    }
+
+    private static DHParameters toDH(DSAParameters dsaParams)
+    {
+        int pSize = dsaParams.getP().bitLength();
+        int m = chooseLowerBound(pSize);
+        return new DHParameters(dsaParams.getP(), dsaParams.getG(), dsaParams.getQ(), m, 0, null,
+            new DHValidationParameters(dsaParams.getValidationParameters().getSeed(), dsaParams.getValidationParameters().getCounter()));
+    }
+
+    // based on lower limit of at least 2^{2 * bits_of_security}
+    private static int chooseLowerBound(int pSize)
+    {
+        int m = 160;
+        if (pSize > 1024)
+        {
+            if (pSize <= 2048)
+            {
+                m = 224;
+            }
+            else if (pSize <= 3072)
+            {
+                m = 256;
+            }
+            else if (pSize <= 7680)
+            {
+                m = 384;
+            }
+            else
+            {
+                m = 512;
+            }
+        }
+        return m;
+    }
+
+    /**
+     * Available properties that can be set.
+     */
+    public static final class Property
+    {
+        /**
+         * The parameters to be used for processing implicitlyCA X9.62 parameters
+         */
+        public static final Property EC_IMPLICITLY_CA = new Property("ecImplicitlyCA", X9ECParameters.class);
+        /**
+         * The default parameters for a particular size of Diffie-Hellman key.This is a sized property.
+         */
+        public static final Property DH_DEFAULT_PARAMS= new Property("dhDefaultParams", DHParameters.class);
+        /**
+         * The default parameters for a particular size of DSA key. This is a sized property.
+         */
+        public static final Property DSA_DEFAULT_PARAMS= new Property("dsaDefaultParams", DSAParameters.class);
+        private final String name;
+        private final Class type;
+
+        private Property(String name, Class type)
+        {
+            this.name = name;
+            this.type = type;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DSAExt.java b/bcprov/src/main/java/org/bouncycastle/crypto/DSAExt.java
new file mode 100644
index 0000000..b609961
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DSAExt.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto;
+
+import java.math.BigInteger;
+
+/**
+ * An "extended" interface for classes implementing DSA-style algorithms, that provides access to
+ * the group order.
+ */
+public interface DSAExt
+    extends DSA
+{
+    /**
+     * Get the order of the group that the r, s values in signatures belong to.
+     */
+    public BigInteger getOrder();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
index 16198ba..dc6d028 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
@@ -8,6 +8,8 @@
 {
     /**
      * return the MAC used as the basis for the function
+     *
+     * @return the Mac.
      */
     public Mac getMac();
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/RawAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/RawAgreement.java
new file mode 100644
index 0000000..4bac34d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/RawAgreement.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto;
+
+public interface RawAgreement
+{
+    void init(CipherParameters parameters);
+
+    int getAgreementSize();
+
+    void calculateAgreement(CipherParameters publicKey, byte[] buf, int off);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
index 09aadfb..77d8ac9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -32,15 +32,14 @@
     public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
         throws DataLengthException
     {
-        if (outOff + len > out.length)
-        {
-            throw new DataLengthException("output buffer too short");
-        }
-
         if (inOff + len > in.length)
         {
             throw new DataLengthException("input buffer too small");
         }
+        if (outOff + len > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
 
         int inStart = inOff;
         int inEnd = inOff + len;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
index 84c5839..c606eae 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
@@ -47,7 +48,7 @@
         }
         else
         {
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
             kParam = (AsymmetricKeyParameter)param;
         }
 
@@ -91,8 +92,14 @@
 
         BigInteger p = dhParams.getP();
 
-        BigInteger result = pub.getY().modPow(privateValue, p);
-        if (result.compareTo(ONE) == 0)
+        BigInteger peerY = pub.getY();
+        if (peerY == null || peerY.compareTo(ONE) <= 0 || peerY.compareTo(p.subtract(ONE)) >= 0)
+        {
+            throw new IllegalArgumentException("Diffie-Hellman public key is weak");
+        }
+
+        BigInteger result = peerY.modPow(privateValue, p);
+        if (result.equals(ONE))
         {
             throw new IllegalStateException("Shared key can't be 1");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
index 4dd80d0..3490819 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -68,8 +68,16 @@
             throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters.");
         }
 
-        BigInteger result = pub.getY().modPow(key.getX(), dhParams.getP());
-        if (result.compareTo(ONE) == 0)
+        BigInteger p = dhParams.getP();
+
+        BigInteger peerY = pub.getY();
+        if (peerY == null || peerY.compareTo(ONE) <= 0 || peerY.compareTo(p.subtract(ONE)) >= 0)
+        {
+            throw new IllegalArgumentException("Diffie-Hellman public key is weak");
+        }
+
+        BigInteger result = peerY.modPow(key.getX(), p);
+        if (result.equals(ONE))
         {
             throw new IllegalStateException("Shared key can't be 1");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
index c34912a..ceae404 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
@@ -10,6 +10,8 @@
  */
 public class DHStandardGroups
 {
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
     private static BigInteger fromHex(String hex)
     {
         return new BigInteger(1, Hex.decode(hex));
@@ -25,6 +27,13 @@
         return new DHParameters(fromHex(hexP), fromHex(hexG), fromHex(hexQ));
     }
 
+    private static DHParameters rfc7919Parameters(String hexP, int l)
+    {
+        // NOTE: All the groups in RFC 7919 use safe primes, i.e. q = (p-1)/2, and generator g = 2
+        BigInteger p = fromHex(hexP);
+        return new DHParameters(p, TWO, p.shiftRight(1), l);
+    }
+
     /*
      * RFC 2409
      */
@@ -215,4 +224,83 @@
      */
     public static final DHParameters rfc5996_768 = rfc4306_768;
     public static final DHParameters rfc5996_1024 = rfc4306_1024;
+
+    /*
+     * RFC 7919
+     */
+    private static final String rfc7919_ffdhe2048_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+        + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+        + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+        + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+        + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+        + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B423861285C97FFFFFFFFFFFFFFFF";
+    public static final DHParameters rfc7919_ffdhe2048 = rfc7919Parameters(rfc7919_ffdhe2048_p, 225);
+
+    private static final String rfc7919_ffdhe3072_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+        + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+        + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+        + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+        + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+        + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+        + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+        + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+        + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF";
+    public static final DHParameters rfc7919_ffdhe3072 = rfc7919Parameters(rfc7919_ffdhe3072_p, 275);
+
+    private static final String rfc7919_ffdhe4096_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+        + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+        + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+        + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+        + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+        + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+        + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+        + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+        + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+        + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+        + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A"
+        + "FFFFFFFFFFFFFFFF";
+    public static final DHParameters rfc7919_ffdhe4096 = rfc7919Parameters(rfc7919_ffdhe4096_p, 325);
+
+    private static final String rfc7919_ffdhe6144_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+        + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+        + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+        + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+        + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+        + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+        + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+        + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+        + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+        + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+        + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+        + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+        + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+        + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+        + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+        + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+        + "A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF";
+    public static final DHParameters rfc7919_ffdhe6144 = rfc7919Parameters(rfc7919_ffdhe6144_p, 375);
+
+    private static final String rfc7919_ffdhe8192_p = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+        + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+        + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+        + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+        + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+        + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+        + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+        + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+        + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+        + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+        + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+        + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+        + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+        + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+        + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+        + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+        + "A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838" + "1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E"
+        + "0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665" + "CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282"
+        + "2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022" + "BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C"
+        + "51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9" + "D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457"
+        + "1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30" + "FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D"
+        + "97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C" + "D68C8BB7C5C6424CFFFFFFFFFFFFFFFF";
+    public static final DHParameters rfc7919_ffdhe8192 = rfc7919Parameters(rfc7919_ffdhe8192_p, 400);
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHUnifiedAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHUnifiedAgreement.java
new file mode 100644
index 0000000..97172f6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHUnifiedAgreement.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.DHUPrivateParameters;
+import org.bouncycastle.crypto.params.DHUPublicParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * FFC Unified static/ephemeral agreement as described in NIST SP 800-56A.
+ */
+public class DHUnifiedAgreement
+{
+    private DHUPrivateParameters privParams;
+
+    public void init(
+        CipherParameters key)
+    {
+        this.privParams = (DHUPrivateParameters)key;
+    }
+
+    public int getFieldSize()
+    {
+        return (privParams.getStaticPrivateKey().getParameters().getP().bitLength() + 7) / 8;
+    }
+
+    public byte[] calculateAgreement(CipherParameters pubKey)
+    {
+        DHUPublicParameters pubParams = (DHUPublicParameters)pubKey;
+
+        DHBasicAgreement sAgree = new DHBasicAgreement();
+        DHBasicAgreement eAgree = new DHBasicAgreement();
+
+        sAgree.init(privParams.getStaticPrivateKey());
+
+        BigInteger sComp = sAgree.calculateAgreement(pubParams.getStaticPublicKey());
+
+        eAgree.init(privParams.getEphemeralPrivateKey());
+
+        BigInteger eComp = eAgree.calculateAgreement(pubParams.getEphemeralPublicKey());
+
+        return Arrays.concatenate(
+            BigIntegers.asUnsignedByteArray(this.getFieldSize(), eComp),
+            BigIntegers.asUnsignedByteArray(this.getFieldSize(), sComp));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 8e2d120..49a79c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -4,8 +4,11 @@
 
 import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
@@ -42,13 +45,29 @@
         CipherParameters pubKey)
     {
         ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
-        if (!pub.getParameters().equals(key.getParameters()))
+        ECDomainParameters params = key.getParameters();
+        if (!params.equals(pub.getParameters()))
         {
             throw new IllegalStateException("ECDH public key has wrong domain parameters");
         }
 
-        ECPoint P = pub.getQ().multiply(key.getD()).normalize();
+        BigInteger d = key.getD();
 
+        // Always perform calculations on the exact curve specified by our private key's parameters
+        ECPoint Q = ECAlgorithms.cleanPoint(params.getCurve(), pub.getQ());
+        if (Q.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid public key for ECDH");
+        }
+
+        BigInteger h = params.getH();
+        if (!h.equals(ECConstants.ONE))
+        {
+            d = params.getHInv().multiply(d).mod(params.getN());
+            Q = ECAlgorithms.referenceMultiply(Q, h);
+        }
+
+        ECPoint P = Q.multiply(d).normalize();
         if (P.isInfinity())
         {
             throw new IllegalStateException("Infinity is not a valid agreement value for ECDH");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
index 3329cfb..a0c8554 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
@@ -48,15 +49,22 @@
         CipherParameters pubKey)
     {
         ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
-        ECDomainParameters params = pub.getParameters();
-        if (!params.equals(key.getParameters()))
+        ECDomainParameters params = key.getParameters();
+        if (!params.equals(pub.getParameters()))
         {
             throw new IllegalStateException("ECDHC public key has wrong domain parameters");
         }
 
         BigInteger hd = params.getH().multiply(key.getD()).mod(params.getN());
 
-        ECPoint P = pub.getQ().multiply(hd).normalize();
+        // Always perform calculations on the exact curve specified by our private key's parameters
+        ECPoint pubPoint = ECAlgorithms.cleanPoint(params.getCurve(), pub.getQ());
+        if (pubPoint.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid public key for ECDHC");
+        }
+
+        ECPoint P = pubPoint.multiply(hd).normalize();
 
         if (P.isInfinity())
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCUnifiedAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCUnifiedAgreement.java
new file mode 100644
index 0000000..463c9a4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCUnifiedAgreement.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDHUPrivateParameters;
+import org.bouncycastle.crypto.params.ECDHUPublicParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * EC Unified static/ephemeral agreement as described in NIST SP 800-56A using EC co-factor Diffie-Hellman.
+ */
+public class ECDHCUnifiedAgreement
+{
+    private ECDHUPrivateParameters privParams;
+
+    public void init(
+        CipherParameters key)
+    {
+        this.privParams = (ECDHUPrivateParameters)key;
+    }
+
+    public int getFieldSize()
+    {
+        return (privParams.getStaticPrivateKey().getParameters().getCurve().getFieldSize() + 7) / 8;
+    }
+
+    public byte[] calculateAgreement(CipherParameters pubKey)
+    {
+        ECDHUPublicParameters pubParams = (ECDHUPublicParameters)pubKey;
+
+        ECDHCBasicAgreement sAgree = new ECDHCBasicAgreement();
+        ECDHCBasicAgreement eAgree = new ECDHCBasicAgreement();
+
+        sAgree.init(privParams.getStaticPrivateKey());
+
+        BigInteger sComp = sAgree.calculateAgreement(pubParams.getStaticPublicKey());
+
+        eAgree.init(privParams.getEphemeralPrivateKey());
+
+        BigInteger eComp = eAgree.calculateAgreement(pubParams.getEphemeralPublicKey());
+
+        return Arrays.concatenate(
+            BigIntegers.asUnsignedByteArray(this.getFieldSize(), eComp),
+            BigIntegers.asUnsignedByteArray(this.getFieldSize(), sComp));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
index a45ccd9..10aa3a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
@@ -75,16 +75,10 @@
 
         ECCurve curve = parameters.getCurve();
 
-        ECPoint[] points = new ECPoint[]{
-            // The Q2U public key is optional - but will be calculated for us if it wasn't present
-            ECAlgorithms.importPoint(curve, Q2U.getQ()),
-            ECAlgorithms.importPoint(curve, Q1V.getQ()),
-            ECAlgorithms.importPoint(curve, Q2V.getQ())
-        };
-
-        curve.normalizeAll(points);
-
-        ECPoint q2u = points[0], q1v = points[1], q2v = points[2];
+        // The Q2U public key is optional - but will be calculated for us if it wasn't present
+        ECPoint q2u = ECAlgorithms.cleanPoint(curve, Q2U.getQ());
+        ECPoint q1v = ECAlgorithms.cleanPoint(curve, Q1V.getQ());
+        ECPoint q2v = ECAlgorithms.cleanPoint(curve, Q2V.getQ());
 
         BigInteger x = q2u.getAffineXCoord().toBigInteger();
         BigInteger xBar = x.mod(powE);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECVKOAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECVKOAgreement.java
new file mode 100644
index 0000000..5a8de6a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECVKOAgreement.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.crypto.agreement;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * GOST VKO key agreement class - RFC 7836 Section 4.3
+ */
+public class ECVKOAgreement
+{
+    private final Digest digest;
+
+    private ECPrivateKeyParameters key;
+    private BigInteger ukm;
+
+    public ECVKOAgreement(Digest digest)
+    {
+        this.digest = digest;
+    }
+
+    public void init(
+        CipherParameters key)
+    {
+        ParametersWithUKM p = (ParametersWithUKM)key;
+
+        this.key = (ECPrivateKeyParameters)p.getParameters();
+        this.ukm = toInteger(p.getUKM());
+    }
+
+    public int getFieldSize()
+    {
+        return (key.getParameters().getCurve().getFieldSize() + 7) / 8;
+    }
+
+    public byte[] calculateAgreement(
+        CipherParameters pubKey)
+    {
+        ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
+        ECDomainParameters params = key.getParameters();
+        if (!params.equals(pub.getParameters()))
+        {
+            throw new IllegalStateException("ECVKO public key has wrong domain parameters");
+        }
+
+        BigInteger hd = params.getH().multiply(ukm).multiply(key.getD()).mod(params.getN());
+
+        // Always perform calculations on the exact curve specified by our private key's parameters
+        ECPoint pubPoint = ECAlgorithms.cleanPoint(params.getCurve(), pub.getQ());
+        if (pubPoint.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid public key for ECDHC");
+        }
+
+        ECPoint P = pubPoint.multiply(hd).normalize();
+
+        if (P.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid agreement value for ECVKO");
+        }
+
+        return fromPoint(P);
+    }
+
+    private static BigInteger toInteger(byte[] ukm)
+    {
+        byte[] v = new byte[ukm.length];
+
+        for (int i = 0; i != v.length; i++)
+        {
+            v[i] = ukm[ukm.length - i - 1];
+        }
+
+        return new BigInteger(1, v);
+    }
+
+    private byte[] fromPoint(ECPoint v)
+    {
+        BigInteger bX = v.getAffineXCoord().toBigInteger();
+        BigInteger bY = v.getAffineYCoord().toBigInteger();
+
+        int size;
+        if (bX.toByteArray().length > 33)
+        {
+            size = 64;
+        }
+        else
+        {
+            size = 32;
+        }
+
+        byte[] bytes = new byte[2 * size];
+        byte[] x = BigIntegers.asUnsignedByteArray(size, bX);
+        byte[] y = BigIntegers.asUnsignedByteArray(size, bY);
+
+        for (int i = 0; i != size; i++)
+        {
+            bytes[i] = x[size - i - 1];
+        }
+        for (int i = 0; i != size; i++)
+        {
+            bytes[size + i] = y[size - i - 1];
+        }
+
+        digest.update(bytes, 0, bytes.length);
+
+        byte[] rv = new byte[digest.getDigestSize()];
+
+        digest.doFinal(rv, 0);
+
+        return rv;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/MQVBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/MQVBasicAgreement.java
new file mode 100644
index 0000000..c16e4f8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/MQVBasicAgreement.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.crypto.agreement;
+
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.DHMQVPrivateParameters;
+import org.bouncycastle.crypto.params.DHMQVPublicParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+public class MQVBasicAgreement
+    implements BasicAgreement
+{
+    private static final BigInteger ONE = BigInteger.valueOf(1);
+
+    DHMQVPrivateParameters privParams;
+
+    public void init(
+        CipherParameters key)
+    {
+        this.privParams = (DHMQVPrivateParameters)key;
+    }
+
+    public int getFieldSize()
+    {
+        return (privParams.getStaticPrivateKey().getParameters().getP().bitLength() + 7) / 8;
+    }
+
+    public BigInteger calculateAgreement(CipherParameters pubKey)
+    {
+        DHMQVPublicParameters pubParams = (DHMQVPublicParameters)pubKey;
+
+        DHPrivateKeyParameters staticPrivateKey = privParams.getStaticPrivateKey();
+
+        if (!privParams.getStaticPrivateKey().getParameters().equals(pubParams.getStaticPublicKey().getParameters()))
+        {
+            throw new IllegalStateException("MQV public key components have wrong domain parameters");
+        }
+
+        if (privParams.getStaticPrivateKey().getParameters().getQ() == null)
+        {
+            throw new IllegalStateException("MQV key domain parameters do not have Q set");
+        }
+
+        BigInteger agreement = calculateDHMQVAgreement(staticPrivateKey.getParameters(), staticPrivateKey,
+            pubParams.getStaticPublicKey(), privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(),
+            pubParams.getEphemeralPublicKey());
+
+        if (agreement.equals(ONE))
+        {
+            throw new IllegalStateException("1 is not a valid agreement value for MQV");
+        }
+
+        return agreement;
+    }
+
+    private BigInteger calculateDHMQVAgreement(
+        DHParameters parameters,
+        DHPrivateKeyParameters xA,
+        DHPublicKeyParameters yB,
+        DHPrivateKeyParameters rA,
+        DHPublicKeyParameters tA,
+        DHPublicKeyParameters tB)
+    {
+        BigInteger q = parameters.getQ();
+
+        int w = (q.bitLength() + 1) / 2;
+        BigInteger twoW = BigInteger.valueOf(2).pow(w);
+
+        BigInteger TA =  tA.getY().mod(twoW).add(twoW);
+        BigInteger SA =  rA.getX().add(TA.multiply(xA.getX())).mod(q);
+        BigInteger TB =  tB.getY().mod(twoW).add(twoW);
+        BigInteger Z =   tB.getY().multiply(yB.getY().modPow(TB, parameters.getP())).modPow(SA, parameters.getP());
+
+        return Z;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/SM2KeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/SM2KeyExchange.java
index 9d2ee46..df792ca 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/SM2KeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/SM2KeyExchange.java
@@ -10,10 +10,12 @@
 import org.bouncycastle.crypto.params.ParametersWithID;
 import org.bouncycastle.crypto.params.SM2KeyExchangePrivateParameters;
 import org.bouncycastle.crypto.params.SM2KeyExchangePublicParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECFieldElement;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
 
 /**
  * SM2 Key Exchange protocol - based on https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02
@@ -27,7 +29,6 @@
     private ECPoint staticPubPoint;
     private ECPoint ephemeralPubPoint;
     private ECDomainParameters ecParams;
-    private int curveLength;
     private int w;
     private ECPrivateKeyParameters ephemeralKey;
     private boolean initiator;
@@ -65,15 +66,9 @@
         staticPubPoint = baseParam.getStaticPublicPoint();
         ephemeralPubPoint = baseParam.getEphemeralPublicPoint();
 
-        curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;
         w = ecParams.getCurve().getFieldSize() / 2 - 1;
     }
 
-    public int getFieldSize()
-    {
-        return (staticKey.getParameters().getCurve().getFieldSize() + 7) / 8;
-    }
-
     public byte[] calculateKey(int kLen, CipherParameters pubParam)
     {
         SM2KeyExchangePublicParameters otherPub;
@@ -162,52 +157,63 @@
 
     private ECPoint calculateU(SM2KeyExchangePublicParameters otherPub)
     {
+        ECDomainParameters params = staticKey.getParameters();
+
+        ECPoint p1 = ECAlgorithms.cleanPoint(params.getCurve(), otherPub.getStaticPublicKey().getQ());
+        ECPoint p2 = ECAlgorithms.cleanPoint(params.getCurve(), otherPub.getEphemeralPublicKey().getQ());
+
         BigInteger x1 = reduce(ephemeralPubPoint.getAffineXCoord().toBigInteger());
+        BigInteger x2 = reduce(p2.getAffineXCoord().toBigInteger());
+        BigInteger tA = staticKey.getD().add(x1.multiply(ephemeralKey.getD()));
+        BigInteger k1 = ecParams.getH().multiply(tA).mod(ecParams.getN());
+        BigInteger k2 = k1.multiply(x2).mod(ecParams.getN());
 
-        BigInteger tA = staticKey.getD().add(x1.multiply(ephemeralKey.getD())).mod(ecParams.getN());
-
-        BigInteger x2 = reduce(otherPub.getEphemeralPublicKey().getQ().getAffineXCoord().toBigInteger());
-
-        ECPoint B0 = otherPub.getEphemeralPublicKey().getQ().multiply(x2).normalize();
-
-        ECPoint B1 = otherPub.getStaticPublicKey().getQ().add(B0).normalize();
-
-        return B1.multiply(ecParams.getH().multiply(tA)).normalize();
+        return ECAlgorithms.sumOfTwoMultiplies(p1, k1, p2, k2).normalize();
     }
 
     private byte[] kdf(ECPoint u, byte[] za, byte[] zb, int klen)
     {
-         int ct = 1;
-         int v = digest.getDigestSize() * 8;
-
-         byte[] buf = new byte[digest.getDigestSize()];
+         int digestSize = digest.getDigestSize();
+         byte[] buf = new byte[Math.max(4, digestSize)];
          byte[] rv = new byte[(klen + 7) / 8];
          int off = 0;
 
-         for (int i = 1; i <= ((klen + v - 1) / v); i++)
+         Memoable memo = null;
+         Memoable copy = null;
+
+         if (digest instanceof Memoable)
          {
              addFieldElement(digest, u.getAffineXCoord());
              addFieldElement(digest, u.getAffineYCoord());
              digest.update(za, 0, za.length);
              digest.update(zb, 0, zb.length);
-             digest.update((byte)(ct >> 24));
-             digest.update((byte)(ct >> 16));
-             digest.update((byte)(ct >> 8));
-             digest.update((byte)ct);
+             memo = (Memoable)digest;
+             copy = memo.copy();
+         }
 
-             digest.doFinal(buf, 0);
+         int ct = 0;
 
-             if (off + buf.length < rv.length)
+         while (off < rv.length)
+         {
+             if (memo != null)
              {
-                 System.arraycopy(buf, 0, rv, off, buf.length);
+                 memo.reset(copy);
              }
              else
              {
-                 System.arraycopy(buf, 0, rv, off, rv.length - off);
+                 addFieldElement(digest, u.getAffineXCoord());
+                 addFieldElement(digest, u.getAffineYCoord());
+                 digest.update(za, 0, za.length);
+                 digest.update(zb, 0, zb.length);
              }
 
-             off += buf.length;
-             ct++;
+             Pack.intToBigEndian(++ct, buf, 0);
+             digest.update(buf, 0, 4);
+             digest.doFinal(buf, 0);
+
+             int copyLen = Math.min(digestSize, rv.length - off);
+             System.arraycopy(buf, 0, rv, off, copyLen);
+             off += copyLen;
          }
 
          return rv;
@@ -221,15 +227,11 @@
 
     private byte[] S1(Digest digest, ECPoint u, byte[] inner)
     {
-        byte[] rv = new byte[digest.getDigestSize()];
-
         digest.update((byte)0x02);
         addFieldElement(digest, u.getAffineYCoord());
         digest.update(inner, 0, inner.length);
 
-        digest.doFinal(rv, 0);
-
-        return rv;
+        return digestDoFinal();
     }
 
     private byte[] calculateInnerHash(Digest digest, ECPoint u, byte[] za, byte[] zb, ECPoint p1, ECPoint p2)
@@ -242,23 +244,16 @@
         addFieldElement(digest, p2.getAffineXCoord());
         addFieldElement(digest, p2.getAffineYCoord());
 
-        byte[] rv = new byte[digest.getDigestSize()];
-
-        digest.doFinal(rv, 0);
-        return rv;
+        return digestDoFinal();
     }
 
     private byte[] S2(Digest digest, ECPoint u, byte[] inner)
     {
-        byte[] rv = new byte[digest.getDigestSize()];
-
         digest.update((byte)0x03);
         addFieldElement(digest, u.getAffineYCoord());
         digest.update(inner, 0, inner.length);
 
-        digest.doFinal(rv, 0);
-
-        return rv;
+        return digestDoFinal();
     }
 
     private byte[] getZ(Digest digest, byte[] userID, ECPoint pubPoint)
@@ -272,26 +267,28 @@
         addFieldElement(digest, pubPoint.getAffineXCoord());
         addFieldElement(digest, pubPoint.getAffineYCoord());
 
-        byte[] rv = new byte[digest.getDigestSize()];
-
-        digest.doFinal(rv, 0);
-
-        return rv;
+        return digestDoFinal();
     }
 
     private void addUserID(Digest digest, byte[] userID)
     {
         int len = userID.length * 8;
 
-        digest.update((byte)(len >> 8 & 0xFF));
-        digest.update((byte)(len & 0xFF));
+        digest.update((byte)(len >>> 8));
+        digest.update((byte)len);
         digest.update(userID, 0, userID.length);
     }
 
     private void addFieldElement(Digest digest, ECFieldElement v)
     {
-        byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());
-
+        byte[] p = v.getEncoded();
         digest.update(p, 0, p.length);
     }
+
+    private byte[] digestDoFinal()
+    {
+        byte[] result = new byte[digest.getDigestSize()];
+        digest.doFinal(result, 0);
+        return result;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X25519Agreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X25519Agreement.java
new file mode 100644
index 0000000..f5acd70
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X25519Agreement.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.agreement;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.RawAgreement;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+
+public final class X25519Agreement
+    implements RawAgreement
+{
+    private X25519PrivateKeyParameters privateKey;
+
+    public void init(CipherParameters parameters)
+    {
+        this.privateKey = (X25519PrivateKeyParameters)parameters;
+    }
+
+    public int getAgreementSize()
+    {
+        return X25519PrivateKeyParameters.SECRET_SIZE;
+    }
+
+    public void calculateAgreement(CipherParameters publicKey, byte[] buf, int off)
+    {
+        privateKey.generateSecret((X25519PublicKeyParameters)publicKey, buf, off);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X448Agreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X448Agreement.java
new file mode 100644
index 0000000..561afca
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/X448Agreement.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.agreement;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.RawAgreement;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+
+public final class X448Agreement
+    implements RawAgreement
+{
+    private X448PrivateKeyParameters privateKey;
+
+    public void init(CipherParameters parameters)
+    {
+        this.privateKey = (X448PrivateKeyParameters)parameters;
+    }
+
+    public int getAgreementSize()
+    {
+        return X448PrivateKeyParameters.SECRET_SIZE;
+    }
+
+    public void calculateAgreement(CipherParameters publicKey, byte[] buf, int off)
+    {
+        privateKey.generateSecret((X448PublicKeyParameters)publicKey, buf, off);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/XDHUnifiedAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/XDHUnifiedAgreement.java
new file mode 100644
index 0000000..e922f18
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/XDHUnifiedAgreement.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.agreement;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.RawAgreement;
+import org.bouncycastle.crypto.params.XDHUPrivateParameters;
+import org.bouncycastle.crypto.params.XDHUPublicParameters;
+
+public class XDHUnifiedAgreement
+    implements RawAgreement
+{
+    private final RawAgreement xAgreement;
+
+    private XDHUPrivateParameters privParams;
+
+    public XDHUnifiedAgreement(RawAgreement xAgreement)
+    {
+        this.xAgreement = xAgreement;
+    }
+
+    public void init(
+        CipherParameters key)
+    {
+        this.privParams = (XDHUPrivateParameters)key;
+    }
+
+    public int getAgreementSize()
+    {
+        return xAgreement.getAgreementSize() * 2;
+    }
+
+    public void calculateAgreement(CipherParameters publicKey, byte[] buf, int off)
+    {
+        XDHUPublicParameters pubParams = (XDHUPublicParameters)publicKey;
+
+        xAgreement.init(privParams.getEphemeralPrivateKey());
+
+        xAgreement.calculateAgreement(pubParams.getEphemeralPublicKey(), buf, off);
+
+        xAgreement.init(privParams.getStaticPrivateKey());
+
+        xAgreement.calculateAgreement(pubParams.getStaticPublicKey(), buf, off + xAgreement.getAgreementSize());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
index ba5521c..aca5029 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
@@ -4,6 +4,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.util.Arrays;
@@ -193,7 +194,7 @@
             password,
             group,
             new SHA256Digest(),
-            new SecureRandom());
+            CryptoServicesRegistrar.getSecureRandom());
     }
 
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
index 3ec3cb6..ef0e450 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.KDFParameters;
 
 /**
@@ -78,7 +79,7 @@
     {
         if ((out.length - len) < outOff)
         {
-            throw new DataLengthException("output buffer too small");
+            throw new OutputLengthException("output buffer too small");
         }
 
         byte[]  hashBuf = new byte[hLen];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
index 6feb507..7b7d870 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.util.Pack;
 
 /**
@@ -53,7 +54,7 @@
     {
         if ((out.length - len) < outOff)
         {
-            throw new DataLengthException("output buffer too small");
+            throw new OutputLengthException("output buffer too small");
         }
 
         long    oBytes = len;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
index 5d15b99..1e50fc1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
@@ -53,6 +53,11 @@
     public int generateBytes(byte[] out, int outOff, int len)
         throws DataLengthException, IllegalArgumentException
     {
+        if (outOff + len > out.length)
+        {
+            throw new DataLengthException("output buffer too small");
+        }
+
         // TODO Create an ASN.1 class for this (RFC3278)
         // ECC-CMS-SharedInfo
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKDFParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKDFParameters.java
new file mode 100644
index 0000000..12c3798
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKDFParameters.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.agreement.kdf;
+
+import org.bouncycastle.crypto.DerivationParameters;
+
+/**
+ * BSI Key Derivation Function Parameters for Session Keys (see BSI-TR-03111 Section 4.3.3)
+ */
+public class GSKKDFParameters
+    implements DerivationParameters
+{
+    private final byte[] z;
+    private final int startCounter;
+    private final byte[] nonce;
+
+    public GSKKDFParameters(byte[] z, int startCounter)
+    {
+        this(z, startCounter, null);
+    }
+
+    public GSKKDFParameters(byte[] z, int startCounter, byte[] nonce)
+    {
+        this.z = z;
+        this.startCounter = startCounter;
+        this.nonce = nonce;
+    }
+
+    public byte[] getZ()
+    {
+        return z;
+    }
+
+    public int getStartCounter()
+    {
+        return startCounter;
+    }
+
+    public byte[] getNonce()
+    {
+        return nonce;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKFDGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKFDGenerator.java
new file mode 100644
index 0000000..36cb639
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/GSKKFDGenerator.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.crypto.agreement.kdf;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.DigestDerivationFunction;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * BSI Key Derivation Function for Session Keys (see BSI-TR-03111 Section 4.3.3)
+ */
+public class GSKKFDGenerator
+    implements DigestDerivationFunction
+{
+    private final Digest digest;
+
+    private byte[] z;
+    private int counter;
+    private byte[] r;
+
+    private byte[] buf;
+
+    public GSKKFDGenerator(Digest digest)
+    {
+        this.digest = digest;
+        this.buf = new byte[digest.getDigestSize()];
+    }
+
+    public Digest getDigest()
+    {
+        return digest;
+    }
+
+    public void init(DerivationParameters param)
+    {
+        if (param instanceof GSKKDFParameters)
+        {
+            this.z = ((GSKKDFParameters)param).getZ();
+            this.counter = ((GSKKDFParameters)param).getStartCounter();
+            this.r = ((GSKKDFParameters)param).getNonce();
+        }
+        else
+        {
+            throw new IllegalArgumentException("unkown parameters type");
+        }
+    }
+
+    public int generateBytes(byte[] out, int outOff, int len)
+        throws DataLengthException, IllegalArgumentException
+    {
+        if (outOff + len > out.length)
+        {
+            throw new DataLengthException("output buffer too small");
+        }
+
+        digest.update(z, 0, z.length);
+
+        byte[] c = Pack.intToBigEndian(counter++);
+
+        digest.update(c, 0, c.length);
+
+        if (r != null)
+        {
+            digest.update(r, 0, r.length);
+        }
+
+        digest.doFinal(buf, 0);
+
+        System.arraycopy(buf, 0, out, outOff, len);
+
+        Arrays.clear(buf);
+
+        return len;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
index fe7aae0..5e8af99 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
@@ -25,6 +25,7 @@
 
 import org.bouncycastle.crypto.ExtendedDigest;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
 
 
 /**
@@ -43,7 +44,7 @@
     implements ExtendedDigest
 {
     // Blake2b Initialization Vector:
-    private final static long blake2b_IV[] =
+    private final static long[] blake2b_IV =
         // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
         // The same as SHA-512 IV.
         {
@@ -69,7 +70,7 @@
             {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}
         };
 
-    private static int rOUNDS = 12; // to use for Catenas H'
+    private static int ROUNDS = 12; // to use for Catenas H'
     private final static int BLOCK_LENGTH_BYTES = 128;// bytes
 
     // General parameters:
@@ -136,9 +137,10 @@
      */
     public Blake2bDigest(int digestSize)
     {
-        if (digestSize != 160 && digestSize != 256 && digestSize != 384 && digestSize != 512)
+        if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0)
         {
-            throw new IllegalArgumentException("Blake2b digest restricted to one of [160, 256, 384, 512]");
+            throw new IllegalArgumentException(
+                "BLAKE2b digest bit length must be a multiple of 8 and not greater than 512");
         }
 
         buffer = new byte[BLOCK_LENGTH_BYTES];
@@ -260,16 +262,16 @@
             chainValue[5] = blake2b_IV[5];
             if (salt != null)
             {
-                chainValue[4] ^= (bytes2long(salt, 0));
-                chainValue[5] ^= (bytes2long(salt, 8));
+                chainValue[4] ^= Pack.littleEndianToLong(salt, 0);
+                chainValue[5] ^= Pack.littleEndianToLong(salt, 8);
             }
 
             chainValue[6] = blake2b_IV[6];
             chainValue[7] = blake2b_IV[7];
             if (personalization != null)
             {
-                chainValue[6] ^= (bytes2long(personalization, 0));
-                chainValue[7] ^= (bytes2long(personalization, 8));
+                chainValue[6] ^= Pack.littleEndianToLong(personalization, 0);
+                chainValue[7] ^= Pack.littleEndianToLong(personalization, 8);
             }
         }
     }
@@ -392,9 +394,7 @@
 
         f0 = 0xFFFFFFFFFFFFFFFFL;
         t0 += bufferPos;
-        // bufferPos may be < 128, so (t0 == 0) does not work
-        // for 2^64 < message length > 2^64 - 127
-        if ((t0 < 0) && (bufferPos > -t0))
+        if (bufferPos > 0 && t0 == 0)
         {
             t1++;
         }
@@ -404,7 +404,7 @@
 
         for (int i = 0; i < chainValue.length && (i * 8 < digestLength); i++)
         {
-            byte[] bytes = long2bytes(chainValue[i]);
+            byte[] bytes = Pack.longToLittleEndian(chainValue[i]);
 
             if (i * 8 < digestLength - 8)
             {
@@ -452,38 +452,29 @@
         long[] m = new long[16];
         for (int j = 0; j < 16; j++)
         {
-            m[j] = bytes2long(message, messagePos + j * 8);
+            m[j] = Pack.littleEndianToLong(message, messagePos + j * 8);
         }
 
-        for (int round = 0; round < rOUNDS; round++)
+        for (int round = 0; round < ROUNDS; round++)
         {
 
             // G apply to columns of internalState:m[blake2b_sigma[round][2 *
             // blockPos]] /+1
-            G(m[blake2b_sigma[round][0]], m[blake2b_sigma[round][1]], 0, 4, 8,
-                12);
-            G(m[blake2b_sigma[round][2]], m[blake2b_sigma[round][3]], 1, 5, 9,
-                13);
-            G(m[blake2b_sigma[round][4]], m[blake2b_sigma[round][5]], 2, 6, 10,
-                14);
-            G(m[blake2b_sigma[round][6]], m[blake2b_sigma[round][7]], 3, 7, 11,
-                15);
+            G(m[blake2b_sigma[round][0]], m[blake2b_sigma[round][1]], 0, 4, 8, 12);
+            G(m[blake2b_sigma[round][2]], m[blake2b_sigma[round][3]], 1, 5, 9, 13);
+            G(m[blake2b_sigma[round][4]], m[blake2b_sigma[round][5]], 2, 6, 10, 14);
+            G(m[blake2b_sigma[round][6]], m[blake2b_sigma[round][7]], 3, 7, 11, 15);
             // G apply to diagonals of internalState:
-            G(m[blake2b_sigma[round][8]], m[blake2b_sigma[round][9]], 0, 5, 10,
-                15);
-            G(m[blake2b_sigma[round][10]], m[blake2b_sigma[round][11]], 1, 6,
-                11, 12);
-            G(m[blake2b_sigma[round][12]], m[blake2b_sigma[round][13]], 2, 7,
-                8, 13);
-            G(m[blake2b_sigma[round][14]], m[blake2b_sigma[round][15]], 3, 4,
-                9, 14);
+            G(m[blake2b_sigma[round][8]], m[blake2b_sigma[round][9]], 0, 5, 10, 15);
+            G(m[blake2b_sigma[round][10]], m[blake2b_sigma[round][11]], 1, 6, 11, 12);
+            G(m[blake2b_sigma[round][12]], m[blake2b_sigma[round][13]], 2, 7, 8, 13);
+            G(m[blake2b_sigma[round][14]], m[blake2b_sigma[round][15]], 3, 4, 9, 14);
         }
 
         // update chain values:
         for (int offset = 0; offset < chainValue.length; offset++)
         {
-            chainValue[offset] = chainValue[offset] ^ internalState[offset]
-                ^ internalState[offset + 8];
+            chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
         }
     }
 
@@ -491,49 +482,20 @@
     {
 
         internalState[posA] = internalState[posA] + internalState[posB] + m1;
-        internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
-            32);
+        internalState[posD] = rotr64(internalState[posD] ^ internalState[posA], 32);
         internalState[posC] = internalState[posC] + internalState[posD];
-        internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
-            24); // replaces 25 of BLAKE
+        internalState[posB] = rotr64(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE
         internalState[posA] = internalState[posA] + internalState[posB] + m2;
-        internalState[posD] = rotr64(internalState[posD] ^ internalState[posA],
-            16);
+        internalState[posD] = rotr64(internalState[posD] ^ internalState[posA], 16);
         internalState[posC] = internalState[posC] + internalState[posD];
-        internalState[posB] = rotr64(internalState[posB] ^ internalState[posC],
-            63); // replaces 11 of BLAKE
+        internalState[posB] = rotr64(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE
     }
 
-    private long rotr64(long x, int rot)
+    private static long rotr64(long x, int rot)
     {
         return x >>> rot | (x << (64 - rot));
     }
 
-    // convert one long value in byte array
-    // little-endian byte order!
-    private final byte[] long2bytes(long longValue)
-    {
-        return new byte[]
-            {(byte)longValue, (byte)(longValue >> 8),
-                (byte)(longValue >> 16), (byte)(longValue >> 24),
-                (byte)(longValue >> 32), (byte)(longValue >> 40),
-                (byte)(longValue >> 48), (byte)(longValue >> 56)
-            };
-    }
-
-    // little-endian byte order!
-    private final long bytes2long(byte[] byteArray, int offset)
-    {
-        return (((long)byteArray[offset] & 0xFF)
-            | (((long)byteArray[offset + 1] & 0xFF) << 8)
-            | (((long)byteArray[offset + 2] & 0xFF) << 16)
-            | (((long)byteArray[offset + 3] & 0xFF) << 24)
-            | (((long)byteArray[offset + 4] & 0xFF) << 32)
-            | (((long)byteArray[offset + 5] & 0xFF) << 40)
-            | (((long)byteArray[offset + 6] & 0xFF) << 48)
-            | (((long)byteArray[offset + 7] & 0xFF) << 56));
-    }
-
     /**
      * return the algorithm name
      *
@@ -541,7 +503,7 @@
      */
     public String getAlgorithmName()
     {
-        return "Blake2b";
+        return "BLAKE2b";
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2sDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2sDigest.java
new file mode 100644
index 0000000..9f892eb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2sDigest.java
@@ -0,0 +1,567 @@
+package org.bouncycastle.crypto.digests;
+
+/*
+  The BLAKE2 cryptographic hash function was designed by Jean-
+  Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
+  Winnerlein.
+
+  Reference Implementation and Description can be found at: https://blake2.net/
+  RFC: https://tools.ietf.org/html/rfc7693
+
+  This implementation does not support the Tree Hashing Mode.
+
+  For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based
+  message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2.
+
+         Algorithm     | Target | Collision | Hash | Hash ASN.1 |
+            Identifier |  Arch  |  Security |  nn  | OID Suffix |
+        ---------------+--------+-----------+------+------------+
+         id-blake2s128 | 32-bit |   2**64   |  16  |   x.2.4    |
+         id-blake2s160 | 32-bit |   2**80   |  20  |   x.2.5    |
+         id-blake2s224 | 32-bit |   2**112  |  28  |   x.2.7    |
+         id-blake2s256 | 32-bit |   2**128  |  32  |   x.2.8    |
+        ---------------+--------+-----------+------+------------+
+ */
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Implementation of the cryptographic hash function BLAKE2s.
+ * <p/>
+ * BLAKE2s offers a built-in keying mechanism to be used directly
+ * for authentication ("Prefix-MAC") rather than a HMAC construction.
+ * <p/>
+ * BLAKE2s offers a built-in support for a salt for randomized hashing
+ * and a personal string for defining a unique hash function for each application.
+ * <p/>
+ * BLAKE2s is optimized for 32-bit platforms and produces digests of any size
+ * between 1 and 32 bytes.
+ */
+public class Blake2sDigest
+    implements ExtendedDigest
+{
+    /**
+     * BLAKE2s Initialization Vector
+     **/
+    private static final int[] blake2s_IV =
+        // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
+        // The same as SHA-256 IV.
+        {
+            0x6a09e667, 0xbb67ae85, 0x3c6ef372,
+            0xa54ff53a, 0x510e527f, 0x9b05688c,
+            0x1f83d9ab, 0x5be0cd19
+        };
+
+    /**
+     * Message word permutations
+     **/
+    private static final byte[][] blake2s_sigma =
+        {
+            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+            {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+            {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
+            {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
+            {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
+            {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
+            {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
+            {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
+            {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
+            {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}
+        };
+
+    private static final int ROUNDS = 10; // to use for Catenas H'
+    private static final int BLOCK_LENGTH_BYTES = 64;// bytes
+
+    // General parameters:
+    private int digestLength = 32; // 1- 32 bytes
+    private int keyLength = 0; // 0 - 32 bytes for keyed hashing for MAC
+    private byte[] salt = null;
+    private byte[] personalization = null;
+    private byte[] key = null;
+
+    // Tree hashing parameters:
+    // Because this class does not implement the Tree Hashing Mode,
+    // these parameters can be treated as constants (see init() function)
+	/*
+	 * private int fanout = 1; // 0-255
+	 * private int depth = 1; // 1 - 255
+	 * private int leafLength= 0;
+	 * private long nodeOffset = 0L;
+	 * private int nodeDepth = 0;
+	 * private int innerHashLength = 0;
+	 */
+
+    /**
+     * Whenever this buffer overflows, it will be processed in the compress()
+     * function. For performance issues, long messages will not use this buffer.
+     */
+    private byte[] buffer = null;
+    /**
+     * Position of last inserted byte
+     **/
+    private int bufferPos = 0;// a value from 0 up to BLOCK_LENGTH_BYTES
+
+    /**
+     * Internal state, in the BLAKE2 paper it is called v
+     **/
+    private int[] internalState = new int[16];
+    /**
+     * State vector, in the BLAKE2 paper it is called h
+     **/
+    private int[] chainValue = null;
+
+    // counter (counts bytes): Length up to 2^64 are supported
+    /**
+     * holds least significant bits of counter
+     **/
+    private int t0 = 0;
+    /**
+     * holds most significant bits of counter
+     **/
+    private int t1 = 0;
+    /**
+     * finalization flag, for last block: ~0
+     **/
+    private int f0 = 0;
+
+    // For Tree Hashing Mode, not used here:
+    // private long f1 = 0L; // finalization flag, for last node: ~0L
+
+    /**
+     * BLAKE2s-256 for hashing.
+     */
+    public Blake2sDigest()
+    {
+        this(256);
+    }
+
+    public Blake2sDigest(Blake2sDigest digest)
+    {
+        this.bufferPos = digest.bufferPos;
+        this.buffer = Arrays.clone(digest.buffer);
+        this.keyLength = digest.keyLength;
+        this.key = Arrays.clone(digest.key);
+        this.digestLength = digest.digestLength;
+        this.chainValue = Arrays.clone(digest.chainValue);
+        this.personalization = Arrays.clone(digest.personalization);
+    }
+
+    /**
+     * BLAKE2s for hashing.
+     *
+     * @param digestBits the desired digest length in bits. Must be a multiple of 8 and less than 256.
+     */
+    public Blake2sDigest(int digestBits)
+    {
+        if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0)
+        {
+            throw new IllegalArgumentException(
+                "BLAKE2s digest bit length must be a multiple of 8 and not greater than 256");
+        }
+        buffer = new byte[BLOCK_LENGTH_BYTES];
+        keyLength = 0;
+        digestLength = digestBits / 8;
+        init();
+    }
+
+    /**
+     * BLAKE2s for authentication ("Prefix-MAC mode").
+     * <p/>
+     * After calling the doFinal() method, the key will remain to be used for
+     * further computations of this instance. The key can be overwritten using
+     * the clearKey() method.
+     *
+     * @param key a key up to 32 bytes or null
+     */
+    public Blake2sDigest(byte[] key)
+    {
+        buffer = new byte[BLOCK_LENGTH_BYTES];
+        if (key != null)
+        {
+            if (key.length > 32)
+            {
+                throw new IllegalArgumentException(
+                    "Keys > 32 are not supported");
+            }
+            this.key = new byte[key.length];
+            System.arraycopy(key, 0, this.key, 0, key.length);
+
+            keyLength = key.length;
+            System.arraycopy(key, 0, buffer, 0, key.length);
+            bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+        }
+        digestLength = 32;
+        init();
+    }
+
+    /**
+     * BLAKE2s with key, required digest length, salt and personalization.
+     * <p/>
+     * After calling the doFinal() method, the key, the salt and the personal
+     * string will remain and might be used for further computations with this
+     * instance. The key can be overwritten using the clearKey() method, the
+     * salt (pepper) can be overwritten using the clearSalt() method.
+     *
+     * @param key             a key up to 32 bytes or null
+     * @param digestBytes     from 1 up to 32 bytes
+     * @param salt            8 bytes or null
+     * @param personalization 8 bytes or null
+     */
+    public Blake2sDigest(byte[] key, int digestBytes, byte[] salt,
+                         byte[] personalization)
+    {
+        buffer = new byte[BLOCK_LENGTH_BYTES];
+        if (digestBytes < 1 || digestBytes > 32)
+        {
+            throw new IllegalArgumentException(
+                "Invalid digest length (required: 1 - 32)");
+        }
+        digestLength = digestBytes;
+        if (salt != null)
+        {
+            if (salt.length != 8)
+            {
+                throw new IllegalArgumentException(
+                    "Salt length must be exactly 8 bytes");
+            }
+            this.salt = new byte[8];
+            System.arraycopy(salt, 0, this.salt, 0, salt.length);
+        }
+        if (personalization != null)
+        {
+            if (personalization.length != 8)
+            {
+                throw new IllegalArgumentException(
+                    "Personalization length must be exactly 8 bytes");
+            }
+            this.personalization = new byte[8];
+            System.arraycopy(personalization, 0, this.personalization, 0,
+                personalization.length);
+        }
+        if (key != null)
+        {
+            if (key.length > 32)
+            {
+                throw new IllegalArgumentException(
+                    "Keys > 32 bytes are not supported");
+            }
+            this.key = new byte[key.length];
+            System.arraycopy(key, 0, this.key, 0, key.length);
+
+            keyLength = key.length;
+            System.arraycopy(key, 0, buffer, 0, key.length);
+            bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+        }
+        init();
+    }
+
+    // initialize chainValue
+    private void init()
+    {
+        if (chainValue == null)
+        {
+            chainValue = new int[8];
+
+            chainValue[0] = blake2s_IV[0]
+                ^ (digestLength | (keyLength << 8) | 0x1010000);
+            // 0x1010000 = ((fanout << 16) | (depth << 24));
+            // with fanout = 1; depth = 0;
+            chainValue[1] = blake2s_IV[1];// ^ leafLength; with leafLength = 0;
+            chainValue[2] = blake2s_IV[2];// ^ nodeOffset; with nodeOffset = 0;
+            chainValue[3] = blake2s_IV[3];// ^ ( (nodeOffset << 32) |
+            // (nodeDepth << 16) | (innerHashLength << 24) );
+            // with nodeDepth = 0; innerHashLength = 0;
+
+            chainValue[4] = blake2s_IV[4];
+            chainValue[5] = blake2s_IV[5];
+            if (salt != null)
+            {
+                chainValue[4] ^= Pack.littleEndianToInt(salt, 0);
+                chainValue[5] ^= Pack.littleEndianToInt(salt, 4);
+            }
+
+            chainValue[6] = blake2s_IV[6];
+            chainValue[7] = blake2s_IV[7];
+            if (personalization != null)
+            {
+                chainValue[6] ^= Pack.littleEndianToInt(personalization, 0);
+                chainValue[7] ^= Pack.littleEndianToInt(personalization, 4);
+            }
+        }
+    }
+
+    private void initializeInternalState()
+    {
+        // initialize v:
+        System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
+        System.arraycopy(blake2s_IV, 0, internalState, chainValue.length, 4);
+        internalState[12] = t0 ^ blake2s_IV[4];
+        internalState[13] = t1 ^ blake2s_IV[5];
+        internalState[14] = f0 ^ blake2s_IV[6];
+        internalState[15] = blake2s_IV[7];// ^ f1 with f1 = 0
+    }
+
+    /**
+     * Update the message digest with a single byte.
+     *
+     * @param b the input byte to be entered.
+     */
+    public void update(byte b)
+    {
+        int remainingLength; // left bytes of buffer
+
+        // process the buffer if full else add to buffer:
+        remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+        if (remainingLength == 0)
+        { // full buffer
+            t0 += BLOCK_LENGTH_BYTES;
+            if (t0 == 0)
+            { // if message > 2^32
+                t1++;
+            }
+            compress(buffer, 0);
+            Arrays.fill(buffer, (byte)0);// clear buffer
+            buffer[0] = b;
+            bufferPos = 1;
+        }
+        else
+        {
+            buffer[bufferPos] = b;
+            bufferPos++;
+        }
+    }
+
+    /**
+     * Update the message digest with a block of bytes.
+     *
+     * @param message the byte array containing the data.
+     * @param offset  the offset into the byte array where the data starts.
+     * @param len     the length of the data.
+     */
+    public void update(byte[] message, int offset, int len)
+    {
+        if (message == null || len == 0)
+        {
+            return;
+        }
+
+        int remainingLength = 0; // left bytes of buffer
+
+        if (bufferPos != 0)
+        { // commenced, incomplete buffer
+
+            // complete the buffer:
+            remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+            if (remainingLength < len)
+            { // full buffer + at least 1 byte
+                System.arraycopy(message, offset, buffer, bufferPos,
+                    remainingLength);
+                t0 += BLOCK_LENGTH_BYTES;
+                if (t0 == 0)
+                { // if message > 2^32
+                    t1++;
+                }
+                compress(buffer, 0);
+                bufferPos = 0;
+                Arrays.fill(buffer, (byte)0);// clear buffer
+            }
+            else
+            {
+                System.arraycopy(message, offset, buffer, bufferPos, len);
+                bufferPos += len;
+                return;
+            }
+        }
+
+        // process blocks except last block (also if last block is full)
+        int messagePos;
+        int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
+        for (messagePos = offset + remainingLength;
+             messagePos < blockWiseLastPos;
+             messagePos += BLOCK_LENGTH_BYTES)
+        { // block wise 64 bytes
+            // without buffer:
+            t0 += BLOCK_LENGTH_BYTES;
+            if (t0 == 0)
+            {
+                t1++;
+            }
+            compress(message, messagePos);
+        }
+
+        // fill the buffer with left bytes, this might be a full block
+        System.arraycopy(message, messagePos, buffer, 0, offset + len
+            - messagePos);
+        bufferPos += offset + len - messagePos;
+    }
+
+    /**
+     * Close the digest, producing the final digest value. The doFinal() call
+     * leaves the digest reset. Key, salt and personal string remain.
+     *
+     * @param out       the array the digest is to be copied into.
+     * @param outOffset the offset into the out array the digest is to start at.
+     */
+    public int doFinal(byte[] out, int outOffset)
+    {
+        f0 = 0xFFFFFFFF;
+        t0 += bufferPos;
+        // bufferPos may be < 64, so (t0 == 0) does not work
+        // for 2^32 < message length > 2^32 - 63
+        if ((t0 < 0) && (bufferPos > -t0))
+        {
+            t1++;
+        }
+        compress(buffer, 0);
+        Arrays.fill(buffer, (byte)0);// Holds eventually the key if input is null
+        Arrays.fill(internalState, 0);
+
+        for (int i = 0; i < chainValue.length && (i * 4 < digestLength); i++)
+        {
+            byte[] bytes = Pack.intToLittleEndian(chainValue[i]);
+
+            if (i * 4 < digestLength - 4)
+            {
+                System.arraycopy(bytes, 0, out, outOffset + i * 4, 4);
+            }
+            else
+            {
+                System.arraycopy(bytes, 0, out, outOffset + i * 4,
+                    digestLength - (i * 4));
+            }
+        }
+
+        Arrays.fill(chainValue, 0);
+
+        reset();
+
+        return digestLength;
+    }
+
+    /**
+     * Reset the digest back to its initial state. The key, the salt and the
+     * personal string will remain for further computations.
+     */
+    public void reset()
+    {
+        bufferPos = 0;
+        f0 = 0;
+        t0 = 0;
+        t1 = 0;
+        chainValue = null;
+        Arrays.fill(buffer, (byte)0);
+        if (key != null)
+        {
+            System.arraycopy(key, 0, buffer, 0, key.length);
+            bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+        }
+        init();
+    }
+
+    private void compress(byte[] message, int messagePos)
+    {
+        initializeInternalState();
+
+        int[] m = new int[16];
+        for (int j = 0; j < 16; j++)
+        {
+            m[j] = Pack.littleEndianToInt(message, messagePos + j * 4);
+        }
+
+        for (int round = 0; round < ROUNDS; round++)
+        {
+
+            // G apply to columns of internalState:m[blake2s_sigma[round][2 *
+            // blockPos]] /+1
+            G(m[blake2s_sigma[round][0]], m[blake2s_sigma[round][1]], 0, 4, 8, 12);
+            G(m[blake2s_sigma[round][2]], m[blake2s_sigma[round][3]], 1, 5, 9, 13);
+            G(m[blake2s_sigma[round][4]], m[blake2s_sigma[round][5]], 2, 6, 10, 14);
+            G(m[blake2s_sigma[round][6]], m[blake2s_sigma[round][7]], 3, 7, 11, 15);
+            // G apply to diagonals of internalState:
+            G(m[blake2s_sigma[round][8]], m[blake2s_sigma[round][9]], 0, 5, 10, 15);
+            G(m[blake2s_sigma[round][10]], m[blake2s_sigma[round][11]], 1, 6, 11, 12);
+            G(m[blake2s_sigma[round][12]], m[blake2s_sigma[round][13]], 2, 7, 8, 13);
+            G(m[blake2s_sigma[round][14]], m[blake2s_sigma[round][15]], 3, 4, 9, 14);
+        }
+
+        // update chain values:
+        for (int offset = 0; offset < chainValue.length; offset++)
+        {
+            chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
+        }
+    }
+
+    private void G(int m1, int m2, int posA, int posB, int posC, int posD)
+    {
+        internalState[posA] = internalState[posA] + internalState[posB] + m1;
+        internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 16);
+        internalState[posC] = internalState[posC] + internalState[posD];
+        internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 12);
+        internalState[posA] = internalState[posA] + internalState[posB] + m2;
+        internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 8);
+        internalState[posC] = internalState[posC] + internalState[posD];
+        internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 7);
+    }
+
+    private int rotr32(int x, int rot)
+    {
+        return x >>> rot | (x << (32 - rot));
+    }
+
+    /**
+     * Return the algorithm name.
+     *
+     * @return the algorithm name
+     */
+    public String getAlgorithmName()
+    {
+        return "BLAKE2s";
+    }
+
+    /**
+     * Return the size in bytes of the digest produced by this message digest.
+     *
+     * @return the size in bytes of the digest produced by this message digest.
+     */
+    public int getDigestSize()
+    {
+        return digestLength;
+    }
+
+    /**
+     * Return the size in bytes of the internal buffer the digest applies its
+     * compression function to.
+     *
+     * @return byte length of the digest's internal buffer.
+     */
+    public int getByteLength()
+    {
+        return BLOCK_LENGTH_BYTES;
+    }
+
+    /**
+     * Overwrite the key if it is no longer used (zeroization).
+     */
+    public void clearKey()
+    {
+        if (key != null)
+        {
+            Arrays.fill(key, (byte)0);
+            Arrays.fill(buffer, (byte)0);
+        }
+    }
+
+    /**
+     * Overwrite the salt (pepper) if it is secret and no longer used
+     * (zeroization).
+     */
+    public void clearSalt()
+    {
+        if (salt != null)
+        {
+            Arrays.fill(salt, (byte)0);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java
new file mode 100644
index 0000000..7629497
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Customizable SHAKE function.
+ */
+public class CSHAKEDigest
+    extends SHAKEDigest
+{
+    private static final byte[] padding = new byte[100];
+    private final byte[] diff;
+
+    /**
+     * Base constructor.
+     *
+     * @param bitLength bit length of the underlying SHAKE function, 128 or 256.
+     * @param N the function name string, note this is reserved for use by NIST. Avoid using it if not required.
+     * @param S the customization string - available for local use.
+     */
+    public CSHAKEDigest(int bitLength, byte[] N, byte[] S)
+    {
+        super(bitLength);
+
+        if ((N == null || N.length == 0) && (S == null || S.length == 0))
+        {
+            diff = null;
+        }
+        else
+        {
+            diff = Arrays.concatenate(leftEncode(rate / 8), encodeString(N), encodeString(S));
+            diffPadAndAbsorb();
+        }
+    }
+
+    private void diffPadAndAbsorb()
+    {
+        int blockSize = rate / 8;
+        absorb(diff, 0, diff.length);
+
+        int required = blockSize - (diff.length % blockSize);
+
+        while (required > padding.length)
+        {
+            absorb(padding, 0, padding.length);
+            required -= padding.length;
+        }
+        
+        absorb(padding, 0, required);
+    }
+
+    private byte[] encodeString(byte[] str)
+    {
+        if (str == null || str.length == 0)
+        {
+            return leftEncode(0);
+        }
+
+        return Arrays.concatenate(leftEncode(str.length * 8L), str);
+    }
+    
+    private static byte[] leftEncode(long strLen)
+    {
+    	byte n = 1;
+
+        long v = strLen;
+    	while ((v >>= 8) != 0)
+        {
+    		n++;
+    	}
+
+        byte[] b = new byte[n + 1];
+
+    	b[0] = n;
+  
+    	for (int i = 1; i <= n; i++)
+    	{
+    		b[i] = (byte)(strLen >> (8 * (n - i)));
+    	}
+ 
+    	return b;
+    }
+    
+    public int doOutput(byte[] out, int outOff, int outLen)
+    {
+        if (diff != null)
+        {
+            if (!squeezing)
+            {
+                absorbBits(0x00, 2);
+            }
+
+            squeeze(out, outOff, ((long)outLen) * 8);
+
+            return outLen;
+        }
+        else
+        {
+            return super.doOutput(out, outOff, outLen);
+        }
+    }
+
+    public void reset()
+    {
+        super.reset();
+        
+        if (diff != null)
+        {
+            diffPadAndAbsorb();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/DSTU7564Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/DSTU7564Digest.java
new file mode 100644
index 0000000..0ca74a4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/DSTU7564Digest.java
@@ -0,0 +1,577 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Reference implementation of national ukrainian standard of hashing transformation DSTU7564.
+ * Thanks to Roman Oliynykov' native C implementation:
+ * https://github.com/Roman-Oliynykov/Kupyna-reference
+ */
+public class DSTU7564Digest
+    implements ExtendedDigest, Memoable
+{
+    /* Number of 8-byte words in operating state for <= 256-bit hash codes */
+    private static final int NB_512 = 8;
+
+    /* Number of 8-byte words in operating state for <= 512-bit hash codes */
+    private static final int NB_1024 = 16;
+
+    /* Number of rounds for 512-bit state */
+    private static final int NR_512 = 10;
+
+    /* Number of rounds for 1024-bit state */
+    private static final int NR_1024 = 14;
+
+    private int hashSize;
+    private int blockSize;
+
+    private int columns;
+    private int rounds;
+
+    private long[] state;
+    private long[] tempState1;
+    private long[] tempState2;
+
+    // TODO Guard against 'inputBlocks' overflow (2^64 blocks)
+    private long inputBlocks;
+    private int bufOff;
+    private byte[] buf;
+
+    public DSTU7564Digest(DSTU7564Digest digest)
+    {
+        copyIn(digest);
+    }
+
+    private void copyIn(DSTU7564Digest digest)
+    {
+        this.hashSize = digest.hashSize;
+        this.blockSize = digest.blockSize;
+
+        this.rounds = digest.rounds;
+        if (columns > 0 && columns == digest.columns)
+        {
+            System.arraycopy(digest.state, 0, state, 0, columns);
+            System.arraycopy(digest.buf, 0, buf, 0, blockSize);
+        }
+        else
+        {
+            this.columns = digest.columns;
+            this.state = Arrays.clone(digest.state);
+            this.tempState1 = new long[columns];
+            this.tempState2 = new long[columns];
+            this.buf = Arrays.clone(digest.buf);
+        }
+
+        this.inputBlocks = digest.inputBlocks;
+        this.bufOff = digest.bufOff;
+    }
+
+    public DSTU7564Digest(int hashSizeBits)
+    {
+        if (hashSizeBits == 256 || hashSizeBits == 384 || hashSizeBits == 512)
+        {
+            this.hashSize = hashSizeBits >>> 3;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Hash size is not recommended. Use 256/384/512 instead");
+        }
+
+        if (hashSizeBits > 256)
+        {
+            this.columns = NB_1024;
+            this.rounds = NR_1024;
+        }
+        else
+        {
+            this.columns = NB_512;
+            this.rounds = NR_512;
+        }
+
+        this.blockSize = columns << 3;
+
+        this.state = new long[columns];
+        this.state[0] = blockSize;
+
+        this.tempState1 = new long[columns];
+        this.tempState2 = new long[columns];
+
+        this.buf = new byte[blockSize];
+    }
+
+    public String getAlgorithmName()
+    {
+        return "DSTU7564";
+    }
+
+    public int getDigestSize()
+    {
+        return hashSize;
+    }
+
+    public int getByteLength()
+    {
+        return blockSize;
+    }
+
+    public void update(byte in)
+    {
+        buf[bufOff++] = in;
+        if (bufOff == blockSize)
+        {
+            processBlock(buf, 0);
+            bufOff = 0;
+            ++inputBlocks;
+        }
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        while (bufOff != 0 && len > 0)
+        {
+            update(in[inOff++]);
+            --len;
+        }
+
+        if (len > 0)
+        {
+            while (len >= blockSize)
+            {
+                processBlock(in, inOff);
+                inOff += blockSize;
+                len -= blockSize;
+                ++inputBlocks;
+            }
+
+            while (len > 0)
+            {
+                update(in[inOff++]);
+                --len;
+            }
+        }
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        // Apply padding: terminator byte and 96-bit length field
+        {
+            int inputBytes = bufOff;
+            buf[bufOff++] = (byte)0x80;
+
+            int lenPos = blockSize - 12;
+            if (bufOff > lenPos)
+            {
+                while (bufOff < blockSize)
+                {
+                    buf[bufOff++] = 0;
+                }
+                bufOff = 0;
+                processBlock(buf, 0);
+            }
+
+            while (bufOff < lenPos)
+            {
+                buf[bufOff++] = 0;
+            }
+
+            long c = ((inputBlocks & 0xFFFFFFFFL) * blockSize + inputBytes) << 3;
+            Pack.intToLittleEndian((int)c, buf, bufOff);
+            bufOff += 4;
+            c >>>= 32;
+            c += ((inputBlocks >>> 32) * blockSize) << 3;
+            Pack.longToLittleEndian(c, buf, bufOff);
+//            bufOff += 8;
+            processBlock(buf, 0);
+        }
+
+        {
+            System.arraycopy(state, 0, tempState1, 0, columns);
+
+            P(tempState1);
+
+            for (int col = 0; col < columns; ++col)
+            {
+                state[col] ^= tempState1[col];
+            }
+        }
+
+        int neededColumns = hashSize >>> 3;
+        for (int col = columns - neededColumns; col < columns; ++col)
+        {
+            Pack.longToLittleEndian(state[col], out, outOff);
+            outOff += 8;
+        }
+
+        reset();
+
+        return hashSize;
+    }
+
+    public void reset()
+    {
+        Arrays.fill(state, 0L);
+        state[0] = blockSize;
+
+        inputBlocks = 0;
+        bufOff = 0;
+    }
+
+    private void processBlock(byte[] input, int inOff)
+    {
+        int pos = inOff;
+        for (int col = 0; col < columns; ++col)
+        {
+            long word = Pack.littleEndianToLong(input, pos);
+            pos += 8;
+
+            tempState1[col] = state[col] ^ word;
+            tempState2[col] = word;
+        }
+
+        P(tempState1);
+        Q(tempState2);
+
+        for (int col = 0; col < columns; ++col)
+        {
+            state[col] ^= tempState1[col] ^ tempState2[col];
+        }
+    }
+
+    private void P(long[] s)
+    {
+        for (int round = 0; round < rounds; ++round)
+        {
+            long rc = round;
+
+            /* AddRoundConstants */
+            for (int col = 0; col < columns; ++col)
+            {
+                s[col] ^= rc;
+                rc += 0x10L;
+            }
+
+            shiftRows(s);
+            subBytes(s);
+            mixColumns(s);
+        }
+    }
+
+    private void Q(long[] s)
+    {
+        for (int round = 0; round < rounds; ++round)
+        {
+            /* AddRoundConstantsQ */
+            long rc = ((long)(((columns - 1) << 4) ^ round) << 56) | 0x00F0F0F0F0F0F0F3L;
+
+            for (int col = 0; col < columns; ++col)
+            {
+                s[col] += rc;
+                rc -= 0x1000000000000000L;
+            }
+
+            shiftRows(s);
+            subBytes(s);
+            mixColumns(s);
+        }
+    }
+
+    private static long mixColumn(long c)
+    {
+//        // Calculate column multiplied by powers of 'x'
+//        long x0 = c;
+//        long x1 = ((x0 & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((x0 & 0x8080808080808080L) >>> 7) * 0x1DL);
+//        long x2 = ((x1 & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((x1 & 0x8080808080808080L) >>> 7) * 0x1DL);
+//        long x3 = ((x2 & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((x2 & 0x8080808080808080L) >>> 7) * 0x1DL);
+//
+//        // Calculate products with circulant matrix from (0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04)
+//        long m0 = x0;
+//        long m1 = x0;
+//        long m2 = x0 ^ x2;
+//        long m3 = x0;
+//        long m4 = x3;
+//        long m5 = x1 ^ x2;
+//        long m6 = x0 ^ x1 ^ x2;
+//        long m7 = x2;
+//
+//        // Assemble the rotated products
+//        return m0
+//            ^ rotate(8, m1)
+//            ^ rotate(16, m2)
+//            ^ rotate(24, m3)
+//            ^ rotate(32, m4)
+//            ^ rotate(40, m5)
+//            ^ rotate(48, m6)
+//            ^ rotate(56, m7);
+
+        // Multiply elements by 'x'
+        long x1 = ((c & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((c & 0x8080808080808080L) >>> 7) * 0x1DL);
+        long u, v;
+
+        u  = rotate(8, c) ^ c;
+        u ^= rotate(16, u);
+        u ^= rotate(48, c);
+
+        v  = u ^ c ^ x1;
+
+        // Multiply elements by 'x^2'
+        v  = ((v & 0x3F3F3F3F3F3F3F3FL) << 2) ^ (((v & 0x8080808080808080L) >>> 6) * 0x1DL) ^ (((v & 0x4040404040404040L) >>> 6) * 0x1DL);
+
+        return u ^ rotate(32, v) ^ rotate(40, x1) ^ rotate(48, x1);
+    }
+
+    private void mixColumns(long[] s)
+    {
+        for (int col = 0; col < columns; ++col)
+        {
+            s[col] = mixColumn(s[col]);
+        }
+    }
+
+    private static long rotate(int n, long x)
+    {
+        return (x >>> n) | (x << -n);
+    }
+
+    private void shiftRows(long[] s)
+    {
+        switch (columns)
+        {
+        case NB_512:
+        {
+            long c0 = s[0], c1 = s[1], c2 = s[2], c3 = s[3];
+            long c4 = s[4], c5 = s[5], c6 = s[6], c7 = s[7];
+            long d;
+
+            d = (c0 ^ c4) & 0xFFFFFFFF00000000L; c0 ^= d; c4 ^= d;
+            d = (c1 ^ c5) & 0x00FFFFFFFF000000L; c1 ^= d; c5 ^= d;
+            d = (c2 ^ c6) & 0x0000FFFFFFFF0000L; c2 ^= d; c6 ^= d;
+            d = (c3 ^ c7) & 0x000000FFFFFFFF00L; c3 ^= d; c7 ^= d;
+
+            d = (c0 ^ c2) & 0xFFFF0000FFFF0000L; c0 ^= d; c2 ^= d;
+            d = (c1 ^ c3) & 0x00FFFF0000FFFF00L; c1 ^= d; c3 ^= d;
+            d = (c4 ^ c6) & 0xFFFF0000FFFF0000L; c4 ^= d; c6 ^= d;
+            d = (c5 ^ c7) & 0x00FFFF0000FFFF00L; c5 ^= d; c7 ^= d;
+
+            d = (c0 ^ c1) & 0xFF00FF00FF00FF00L; c0 ^= d; c1 ^= d;
+            d = (c2 ^ c3) & 0xFF00FF00FF00FF00L; c2 ^= d; c3 ^= d;
+            d = (c4 ^ c5) & 0xFF00FF00FF00FF00L; c4 ^= d; c5 ^= d;
+            d = (c6 ^ c7) & 0xFF00FF00FF00FF00L; c6 ^= d; c7 ^= d;
+
+            s[0] = c0; s[1] = c1; s[2] = c2; s[3] = c3;
+            s[4] = c4; s[5] = c5; s[6] = c6; s[7] = c7;
+            break;
+        }
+        case NB_1024:
+        {
+            long c00 = s[ 0], c01 = s[ 1], c02 = s[ 2], c03 = s[ 3];
+            long c04 = s[ 4], c05 = s[ 5], c06 = s[ 6], c07 = s[ 7];
+            long c08 = s[ 8], c09 = s[ 9], c10 = s[10], c11 = s[11];
+            long c12 = s[12], c13 = s[13], c14 = s[14], c15 = s[15];
+            long d;
+
+            // NOTE: Row 7 is shifted by 11
+
+            d = (c00 ^ c08) & 0xFF00000000000000L; c00 ^= d; c08 ^= d;
+            d = (c01 ^ c09) & 0xFF00000000000000L; c01 ^= d; c09 ^= d;
+            d = (c02 ^ c10) & 0xFFFF000000000000L; c02 ^= d; c10 ^= d;
+            d = (c03 ^ c11) & 0xFFFFFF0000000000L; c03 ^= d; c11 ^= d;
+            d = (c04 ^ c12) & 0xFFFFFFFF00000000L; c04 ^= d; c12 ^= d;
+            d = (c05 ^ c13) & 0x00FFFFFFFF000000L; c05 ^= d; c13 ^= d;
+            d = (c06 ^ c14) & 0x00FFFFFFFFFF0000L; c06 ^= d; c14 ^= d;
+            d = (c07 ^ c15) & 0x00FFFFFFFFFFFF00L; c07 ^= d; c15 ^= d;
+
+            d = (c00 ^ c04) & 0x00FFFFFF00000000L; c00 ^= d; c04 ^= d;
+            d = (c01 ^ c05) & 0xFFFFFFFFFF000000L; c01 ^= d; c05 ^= d;
+            d = (c02 ^ c06) & 0xFF00FFFFFFFF0000L; c02 ^= d; c06 ^= d;
+            d = (c03 ^ c07) & 0xFF0000FFFFFFFF00L; c03 ^= d; c07 ^= d;
+            d = (c08 ^ c12) & 0x00FFFFFF00000000L; c08 ^= d; c12 ^= d;
+            d = (c09 ^ c13) & 0xFFFFFFFFFF000000L; c09 ^= d; c13 ^= d;
+            d = (c10 ^ c14) & 0xFF00FFFFFFFF0000L; c10 ^= d; c14 ^= d;
+            d = (c11 ^ c15) & 0xFF0000FFFFFFFF00L; c11 ^= d; c15 ^= d;
+
+            d = (c00 ^ c02) & 0xFFFF0000FFFF0000L; c00 ^= d; c02 ^= d;
+            d = (c01 ^ c03) & 0x00FFFF0000FFFF00L; c01 ^= d; c03 ^= d;
+            d = (c04 ^ c06) & 0xFFFF0000FFFF0000L; c04 ^= d; c06 ^= d;
+            d = (c05 ^ c07) & 0x00FFFF0000FFFF00L; c05 ^= d; c07 ^= d;
+            d = (c08 ^ c10) & 0xFFFF0000FFFF0000L; c08 ^= d; c10 ^= d;
+            d = (c09 ^ c11) & 0x00FFFF0000FFFF00L; c09 ^= d; c11 ^= d;
+            d = (c12 ^ c14) & 0xFFFF0000FFFF0000L; c12 ^= d; c14 ^= d;
+            d = (c13 ^ c15) & 0x00FFFF0000FFFF00L; c13 ^= d; c15 ^= d;
+
+            d = (c00 ^ c01) & 0xFF00FF00FF00FF00L; c00 ^= d; c01 ^= d;
+            d = (c02 ^ c03) & 0xFF00FF00FF00FF00L; c02 ^= d; c03 ^= d;
+            d = (c04 ^ c05) & 0xFF00FF00FF00FF00L; c04 ^= d; c05 ^= d;
+            d = (c06 ^ c07) & 0xFF00FF00FF00FF00L; c06 ^= d; c07 ^= d;
+            d = (c08 ^ c09) & 0xFF00FF00FF00FF00L; c08 ^= d; c09 ^= d;
+            d = (c10 ^ c11) & 0xFF00FF00FF00FF00L; c10 ^= d; c11 ^= d;
+            d = (c12 ^ c13) & 0xFF00FF00FF00FF00L; c12 ^= d; c13 ^= d;
+            d = (c14 ^ c15) & 0xFF00FF00FF00FF00L; c14 ^= d; c15 ^= d;
+
+            s[ 0] = c00; s[ 1] = c01; s[ 2] = c02; s[ 3] = c03;
+            s[ 4] = c04; s[ 5] = c05; s[ 6] = c06; s[ 7] = c07;
+            s[ 8] = c08; s[ 9] = c09; s[10] = c10; s[11] = c11;
+            s[12] = c12; s[13] = c13; s[14] = c14; s[15] = c15;
+            break;
+        }
+        default:
+        {
+            throw new IllegalStateException("unsupported state size: only 512/1024 are allowed");
+        }
+        }
+    }
+
+    private void subBytes(long[] s)
+    {
+        for (int i = 0; i < columns; ++i)
+        {
+            long u = s[i];
+            int lo = (int)u, hi = (int)(u >>> 32);
+            byte t0 = S0[lo & 0xFF];
+            byte t1 = S1[(lo >>> 8) & 0xFF];
+            byte t2 = S2[(lo >>> 16) & 0xFF];
+            byte t3 = S3[lo >>> 24];
+            lo = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+            byte t4 = S0[hi & 0xFF];
+            byte t5 = S1[(hi >>> 8) & 0xFF];
+            byte t6 = S2[(hi >>> 16) & 0xFF];
+            byte t7 = S3[hi >>> 24];
+            hi = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+            s[i] = (lo & 0xFFFFFFFFL) | ((long)hi << 32);
+        }
+    }
+
+    private static final byte[] S0 = new byte[]{ (byte)0xa8, (byte)0x43, (byte)0x5f, (byte)0x06, (byte)0x6b, (byte)0x75,
+        (byte)0x6c, (byte)0x59, (byte)0x71, (byte)0xdf, (byte)0x87, (byte)0x95, (byte)0x17, (byte)0xf0, (byte)0xd8,
+        (byte)0x09, (byte)0x6d, (byte)0xf3, (byte)0x1d, (byte)0xcb, (byte)0xc9, (byte)0x4d, (byte)0x2c, (byte)0xaf,
+        (byte)0x79, (byte)0xe0, (byte)0x97, (byte)0xfd, (byte)0x6f, (byte)0x4b, (byte)0x45, (byte)0x39, (byte)0x3e,
+        (byte)0xdd, (byte)0xa3, (byte)0x4f, (byte)0xb4, (byte)0xb6, (byte)0x9a, (byte)0x0e, (byte)0x1f, (byte)0xbf,
+        (byte)0x15, (byte)0xe1, (byte)0x49, (byte)0xd2, (byte)0x93, (byte)0xc6, (byte)0x92, (byte)0x72, (byte)0x9e,
+        (byte)0x61, (byte)0xd1, (byte)0x63, (byte)0xfa, (byte)0xee, (byte)0xf4, (byte)0x19, (byte)0xd5, (byte)0xad,
+        (byte)0x58, (byte)0xa4, (byte)0xbb, (byte)0xa1, (byte)0xdc, (byte)0xf2, (byte)0x83, (byte)0x37, (byte)0x42,
+        (byte)0xe4, (byte)0x7a, (byte)0x32, (byte)0x9c, (byte)0xcc, (byte)0xab, (byte)0x4a, (byte)0x8f, (byte)0x6e,
+        (byte)0x04, (byte)0x27, (byte)0x2e, (byte)0xe7, (byte)0xe2, (byte)0x5a, (byte)0x96, (byte)0x16, (byte)0x23,
+        (byte)0x2b, (byte)0xc2, (byte)0x65, (byte)0x66, (byte)0x0f, (byte)0xbc, (byte)0xa9, (byte)0x47, (byte)0x41,
+        (byte)0x34, (byte)0x48, (byte)0xfc, (byte)0xb7, (byte)0x6a, (byte)0x88, (byte)0xa5, (byte)0x53, (byte)0x86,
+        (byte)0xf9, (byte)0x5b, (byte)0xdb, (byte)0x38, (byte)0x7b, (byte)0xc3, (byte)0x1e, (byte)0x22, (byte)0x33,
+        (byte)0x24, (byte)0x28, (byte)0x36, (byte)0xc7, (byte)0xb2, (byte)0x3b, (byte)0x8e, (byte)0x77, (byte)0xba,
+        (byte)0xf5, (byte)0x14, (byte)0x9f, (byte)0x08, (byte)0x55, (byte)0x9b, (byte)0x4c, (byte)0xfe, (byte)0x60,
+        (byte)0x5c, (byte)0xda, (byte)0x18, (byte)0x46, (byte)0xcd, (byte)0x7d, (byte)0x21, (byte)0xb0, (byte)0x3f,
+        (byte)0x1b, (byte)0x89, (byte)0xff, (byte)0xeb, (byte)0x84, (byte)0x69, (byte)0x3a, (byte)0x9d, (byte)0xd7,
+        (byte)0xd3, (byte)0x70, (byte)0x67, (byte)0x40, (byte)0xb5, (byte)0xde, (byte)0x5d, (byte)0x30, (byte)0x91,
+        (byte)0xb1, (byte)0x78, (byte)0x11, (byte)0x01, (byte)0xe5, (byte)0x00, (byte)0x68, (byte)0x98, (byte)0xa0,
+        (byte)0xc5, (byte)0x02, (byte)0xa6, (byte)0x74, (byte)0x2d, (byte)0x0b, (byte)0xa2, (byte)0x76, (byte)0xb3,
+        (byte)0xbe, (byte)0xce, (byte)0xbd, (byte)0xae, (byte)0xe9, (byte)0x8a, (byte)0x31, (byte)0x1c, (byte)0xec,
+        (byte)0xf1, (byte)0x99, (byte)0x94, (byte)0xaa, (byte)0xf6, (byte)0x26, (byte)0x2f, (byte)0xef, (byte)0xe8,
+        (byte)0x8c, (byte)0x35, (byte)0x03, (byte)0xd4, (byte)0x7f, (byte)0xfb, (byte)0x05, (byte)0xc1, (byte)0x5e,
+        (byte)0x90, (byte)0x20, (byte)0x3d, (byte)0x82, (byte)0xf7, (byte)0xea, (byte)0x0a, (byte)0x0d, (byte)0x7e,
+        (byte)0xf8, (byte)0x50, (byte)0x1a, (byte)0xc4, (byte)0x07, (byte)0x57, (byte)0xb8, (byte)0x3c, (byte)0x62,
+        (byte)0xe3, (byte)0xc8, (byte)0xac, (byte)0x52, (byte)0x64, (byte)0x10, (byte)0xd0, (byte)0xd9, (byte)0x13,
+        (byte)0x0c, (byte)0x12, (byte)0x29, (byte)0x51, (byte)0xb9, (byte)0xcf, (byte)0xd6, (byte)0x73, (byte)0x8d,
+        (byte)0x81, (byte)0x54, (byte)0xc0, (byte)0xed, (byte)0x4e, (byte)0x44, (byte)0xa7, (byte)0x2a, (byte)0x85,
+        (byte)0x25, (byte)0xe6, (byte)0xca, (byte)0x7c, (byte)0x8b, (byte)0x56, (byte)0x80 };
+
+    private static final byte[] S1 = new byte[]{ (byte)0xce, (byte)0xbb, (byte)0xeb, (byte)0x92, (byte)0xea, (byte)0xcb,
+        (byte)0x13, (byte)0xc1, (byte)0xe9, (byte)0x3a, (byte)0xd6, (byte)0xb2, (byte)0xd2, (byte)0x90, (byte)0x17,
+        (byte)0xf8, (byte)0x42, (byte)0x15, (byte)0x56, (byte)0xb4, (byte)0x65, (byte)0x1c, (byte)0x88, (byte)0x43,
+        (byte)0xc5, (byte)0x5c, (byte)0x36, (byte)0xba, (byte)0xf5, (byte)0x57, (byte)0x67, (byte)0x8d, (byte)0x31,
+        (byte)0xf6, (byte)0x64, (byte)0x58, (byte)0x9e, (byte)0xf4, (byte)0x22, (byte)0xaa, (byte)0x75, (byte)0x0f,
+        (byte)0x02, (byte)0xb1, (byte)0xdf, (byte)0x6d, (byte)0x73, (byte)0x4d, (byte)0x7c, (byte)0x26, (byte)0x2e,
+        (byte)0xf7, (byte)0x08, (byte)0x5d, (byte)0x44, (byte)0x3e, (byte)0x9f, (byte)0x14, (byte)0xc8, (byte)0xae,
+        (byte)0x54, (byte)0x10, (byte)0xd8, (byte)0xbc, (byte)0x1a, (byte)0x6b, (byte)0x69, (byte)0xf3, (byte)0xbd,
+        (byte)0x33, (byte)0xab, (byte)0xfa, (byte)0xd1, (byte)0x9b, (byte)0x68, (byte)0x4e, (byte)0x16, (byte)0x95,
+        (byte)0x91, (byte)0xee, (byte)0x4c, (byte)0x63, (byte)0x8e, (byte)0x5b, (byte)0xcc, (byte)0x3c, (byte)0x19,
+        (byte)0xa1, (byte)0x81, (byte)0x49, (byte)0x7b, (byte)0xd9, (byte)0x6f, (byte)0x37, (byte)0x60, (byte)0xca,
+        (byte)0xe7, (byte)0x2b, (byte)0x48, (byte)0xfd, (byte)0x96, (byte)0x45, (byte)0xfc, (byte)0x41, (byte)0x12,
+        (byte)0x0d, (byte)0x79, (byte)0xe5, (byte)0x89, (byte)0x8c, (byte)0xe3, (byte)0x20, (byte)0x30, (byte)0xdc,
+        (byte)0xb7, (byte)0x6c, (byte)0x4a, (byte)0xb5, (byte)0x3f, (byte)0x97, (byte)0xd4, (byte)0x62, (byte)0x2d,
+        (byte)0x06, (byte)0xa4, (byte)0xa5, (byte)0x83, (byte)0x5f, (byte)0x2a, (byte)0xda, (byte)0xc9, (byte)0x00,
+        (byte)0x7e, (byte)0xa2, (byte)0x55, (byte)0xbf, (byte)0x11, (byte)0xd5, (byte)0x9c, (byte)0xcf, (byte)0x0e,
+        (byte)0x0a, (byte)0x3d, (byte)0x51, (byte)0x7d, (byte)0x93, (byte)0x1b, (byte)0xfe, (byte)0xc4, (byte)0x47,
+        (byte)0x09, (byte)0x86, (byte)0x0b, (byte)0x8f, (byte)0x9d, (byte)0x6a, (byte)0x07, (byte)0xb9, (byte)0xb0,
+        (byte)0x98, (byte)0x18, (byte)0x32, (byte)0x71, (byte)0x4b, (byte)0xef, (byte)0x3b, (byte)0x70, (byte)0xa0,
+        (byte)0xe4, (byte)0x40, (byte)0xff, (byte)0xc3, (byte)0xa9, (byte)0xe6, (byte)0x78, (byte)0xf9, (byte)0x8b,
+        (byte)0x46, (byte)0x80, (byte)0x1e, (byte)0x38, (byte)0xe1, (byte)0xb8, (byte)0xa8, (byte)0xe0, (byte)0x0c,
+        (byte)0x23, (byte)0x76, (byte)0x1d, (byte)0x25, (byte)0x24, (byte)0x05, (byte)0xf1, (byte)0x6e, (byte)0x94,
+        (byte)0x28, (byte)0x9a, (byte)0x84, (byte)0xe8, (byte)0xa3, (byte)0x4f, (byte)0x77, (byte)0xd3, (byte)0x85,
+        (byte)0xe2, (byte)0x52, (byte)0xf2, (byte)0x82, (byte)0x50, (byte)0x7a, (byte)0x2f, (byte)0x74, (byte)0x53,
+        (byte)0xb3, (byte)0x61, (byte)0xaf, (byte)0x39, (byte)0x35, (byte)0xde, (byte)0xcd, (byte)0x1f, (byte)0x99,
+        (byte)0xac, (byte)0xad, (byte)0x72, (byte)0x2c, (byte)0xdd, (byte)0xd0, (byte)0x87, (byte)0xbe, (byte)0x5e,
+        (byte)0xa6, (byte)0xec, (byte)0x04, (byte)0xc6, (byte)0x03, (byte)0x34, (byte)0xfb, (byte)0xdb, (byte)0x59,
+        (byte)0xb6, (byte)0xc2, (byte)0x01, (byte)0xf0, (byte)0x5a, (byte)0xed, (byte)0xa7, (byte)0x66, (byte)0x21,
+        (byte)0x7f, (byte)0x8a, (byte)0x27, (byte)0xc7, (byte)0xc0, (byte)0x29, (byte)0xd7 };
+
+    private static final byte[] S2 = new byte[]{ (byte)0x93, (byte)0xd9, (byte)0x9a, (byte)0xb5, (byte)0x98, (byte)0x22,
+        (byte)0x45, (byte)0xfc, (byte)0xba, (byte)0x6a, (byte)0xdf, (byte)0x02, (byte)0x9f, (byte)0xdc, (byte)0x51,
+        (byte)0x59, (byte)0x4a, (byte)0x17, (byte)0x2b, (byte)0xc2, (byte)0x94, (byte)0xf4, (byte)0xbb, (byte)0xa3,
+        (byte)0x62, (byte)0xe4, (byte)0x71, (byte)0xd4, (byte)0xcd, (byte)0x70, (byte)0x16, (byte)0xe1, (byte)0x49,
+        (byte)0x3c, (byte)0xc0, (byte)0xd8, (byte)0x5c, (byte)0x9b, (byte)0xad, (byte)0x85, (byte)0x53, (byte)0xa1,
+        (byte)0x7a, (byte)0xc8, (byte)0x2d, (byte)0xe0, (byte)0xd1, (byte)0x72, (byte)0xa6, (byte)0x2c, (byte)0xc4,
+        (byte)0xe3, (byte)0x76, (byte)0x78, (byte)0xb7, (byte)0xb4, (byte)0x09, (byte)0x3b, (byte)0x0e, (byte)0x41,
+        (byte)0x4c, (byte)0xde, (byte)0xb2, (byte)0x90, (byte)0x25, (byte)0xa5, (byte)0xd7, (byte)0x03, (byte)0x11,
+        (byte)0x00, (byte)0xc3, (byte)0x2e, (byte)0x92, (byte)0xef, (byte)0x4e, (byte)0x12, (byte)0x9d, (byte)0x7d,
+        (byte)0xcb, (byte)0x35, (byte)0x10, (byte)0xd5, (byte)0x4f, (byte)0x9e, (byte)0x4d, (byte)0xa9, (byte)0x55,
+        (byte)0xc6, (byte)0xd0, (byte)0x7b, (byte)0x18, (byte)0x97, (byte)0xd3, (byte)0x36, (byte)0xe6, (byte)0x48,
+        (byte)0x56, (byte)0x81, (byte)0x8f, (byte)0x77, (byte)0xcc, (byte)0x9c, (byte)0xb9, (byte)0xe2, (byte)0xac,
+        (byte)0xb8, (byte)0x2f, (byte)0x15, (byte)0xa4, (byte)0x7c, (byte)0xda, (byte)0x38, (byte)0x1e, (byte)0x0b,
+        (byte)0x05, (byte)0xd6, (byte)0x14, (byte)0x6e, (byte)0x6c, (byte)0x7e, (byte)0x66, (byte)0xfd, (byte)0xb1,
+        (byte)0xe5, (byte)0x60, (byte)0xaf, (byte)0x5e, (byte)0x33, (byte)0x87, (byte)0xc9, (byte)0xf0, (byte)0x5d,
+        (byte)0x6d, (byte)0x3f, (byte)0x88, (byte)0x8d, (byte)0xc7, (byte)0xf7, (byte)0x1d, (byte)0xe9, (byte)0xec,
+        (byte)0xed, (byte)0x80, (byte)0x29, (byte)0x27, (byte)0xcf, (byte)0x99, (byte)0xa8, (byte)0x50, (byte)0x0f,
+        (byte)0x37, (byte)0x24, (byte)0x28, (byte)0x30, (byte)0x95, (byte)0xd2, (byte)0x3e, (byte)0x5b, (byte)0x40,
+        (byte)0x83, (byte)0xb3, (byte)0x69, (byte)0x57, (byte)0x1f, (byte)0x07, (byte)0x1c, (byte)0x8a, (byte)0xbc,
+        (byte)0x20, (byte)0xeb, (byte)0xce, (byte)0x8e, (byte)0xab, (byte)0xee, (byte)0x31, (byte)0xa2, (byte)0x73,
+        (byte)0xf9, (byte)0xca, (byte)0x3a, (byte)0x1a, (byte)0xfb, (byte)0x0d, (byte)0xc1, (byte)0xfe, (byte)0xfa,
+        (byte)0xf2, (byte)0x6f, (byte)0xbd, (byte)0x96, (byte)0xdd, (byte)0x43, (byte)0x52, (byte)0xb6, (byte)0x08,
+        (byte)0xf3, (byte)0xae, (byte)0xbe, (byte)0x19, (byte)0x89, (byte)0x32, (byte)0x26, (byte)0xb0, (byte)0xea,
+        (byte)0x4b, (byte)0x64, (byte)0x84, (byte)0x82, (byte)0x6b, (byte)0xf5, (byte)0x79, (byte)0xbf, (byte)0x01,
+        (byte)0x5f, (byte)0x75, (byte)0x63, (byte)0x1b, (byte)0x23, (byte)0x3d, (byte)0x68, (byte)0x2a, (byte)0x65,
+        (byte)0xe8, (byte)0x91, (byte)0xf6, (byte)0xff, (byte)0x13, (byte)0x58, (byte)0xf1, (byte)0x47, (byte)0x0a,
+        (byte)0x7f, (byte)0xc5, (byte)0xa7, (byte)0xe7, (byte)0x61, (byte)0x5a, (byte)0x06, (byte)0x46, (byte)0x44,
+        (byte)0x42, (byte)0x04, (byte)0xa0, (byte)0xdb, (byte)0x39, (byte)0x86, (byte)0x54, (byte)0xaa, (byte)0x8c,
+        (byte)0x34, (byte)0x21, (byte)0x8b, (byte)0xf8, (byte)0x0c, (byte)0x74, (byte)0x67 };
+
+    private static final byte[] S3 = new byte[]{ (byte)0x68, (byte)0x8d, (byte)0xca, (byte)0x4d, (byte)0x73, (byte)0x4b,
+        (byte)0x4e, (byte)0x2a, (byte)0xd4, (byte)0x52, (byte)0x26, (byte)0xb3, (byte)0x54, (byte)0x1e, (byte)0x19,
+        (byte)0x1f, (byte)0x22, (byte)0x03, (byte)0x46, (byte)0x3d, (byte)0x2d, (byte)0x4a, (byte)0x53, (byte)0x83,
+        (byte)0x13, (byte)0x8a, (byte)0xb7, (byte)0xd5, (byte)0x25, (byte)0x79, (byte)0xf5, (byte)0xbd, (byte)0x58,
+        (byte)0x2f, (byte)0x0d, (byte)0x02, (byte)0xed, (byte)0x51, (byte)0x9e, (byte)0x11, (byte)0xf2, (byte)0x3e,
+        (byte)0x55, (byte)0x5e, (byte)0xd1, (byte)0x16, (byte)0x3c, (byte)0x66, (byte)0x70, (byte)0x5d, (byte)0xf3,
+        (byte)0x45, (byte)0x40, (byte)0xcc, (byte)0xe8, (byte)0x94, (byte)0x56, (byte)0x08, (byte)0xce, (byte)0x1a,
+        (byte)0x3a, (byte)0xd2, (byte)0xe1, (byte)0xdf, (byte)0xb5, (byte)0x38, (byte)0x6e, (byte)0x0e, (byte)0xe5,
+        (byte)0xf4, (byte)0xf9, (byte)0x86, (byte)0xe9, (byte)0x4f, (byte)0xd6, (byte)0x85, (byte)0x23, (byte)0xcf,
+        (byte)0x32, (byte)0x99, (byte)0x31, (byte)0x14, (byte)0xae, (byte)0xee, (byte)0xc8, (byte)0x48, (byte)0xd3,
+        (byte)0x30, (byte)0xa1, (byte)0x92, (byte)0x41, (byte)0xb1, (byte)0x18, (byte)0xc4, (byte)0x2c, (byte)0x71,
+        (byte)0x72, (byte)0x44, (byte)0x15, (byte)0xfd, (byte)0x37, (byte)0xbe, (byte)0x5f, (byte)0xaa, (byte)0x9b,
+        (byte)0x88, (byte)0xd8, (byte)0xab, (byte)0x89, (byte)0x9c, (byte)0xfa, (byte)0x60, (byte)0xea, (byte)0xbc,
+        (byte)0x62, (byte)0x0c, (byte)0x24, (byte)0xa6, (byte)0xa8, (byte)0xec, (byte)0x67, (byte)0x20, (byte)0xdb,
+        (byte)0x7c, (byte)0x28, (byte)0xdd, (byte)0xac, (byte)0x5b, (byte)0x34, (byte)0x7e, (byte)0x10, (byte)0xf1,
+        (byte)0x7b, (byte)0x8f, (byte)0x63, (byte)0xa0, (byte)0x05, (byte)0x9a, (byte)0x43, (byte)0x77, (byte)0x21,
+        (byte)0xbf, (byte)0x27, (byte)0x09, (byte)0xc3, (byte)0x9f, (byte)0xb6, (byte)0xd7, (byte)0x29, (byte)0xc2,
+        (byte)0xeb, (byte)0xc0, (byte)0xa4, (byte)0x8b, (byte)0x8c, (byte)0x1d, (byte)0xfb, (byte)0xff, (byte)0xc1,
+        (byte)0xb2, (byte)0x97, (byte)0x2e, (byte)0xf8, (byte)0x65, (byte)0xf6, (byte)0x75, (byte)0x07, (byte)0x04,
+        (byte)0x49, (byte)0x33, (byte)0xe4, (byte)0xd9, (byte)0xb9, (byte)0xd0, (byte)0x42, (byte)0xc7, (byte)0x6c,
+        (byte)0x90, (byte)0x00, (byte)0x8e, (byte)0x6f, (byte)0x50, (byte)0x01, (byte)0xc5, (byte)0xda, (byte)0x47,
+        (byte)0x3f, (byte)0xcd, (byte)0x69, (byte)0xa2, (byte)0xe2, (byte)0x7a, (byte)0xa7, (byte)0xc6, (byte)0x93,
+        (byte)0x0f, (byte)0x0a, (byte)0x06, (byte)0xe6, (byte)0x2b, (byte)0x96, (byte)0xa3, (byte)0x1c, (byte)0xaf,
+        (byte)0x6a, (byte)0x12, (byte)0x84, (byte)0x39, (byte)0xe7, (byte)0xb0, (byte)0x82, (byte)0xf7, (byte)0xfe,
+        (byte)0x9d, (byte)0x87, (byte)0x5c, (byte)0x81, (byte)0x35, (byte)0xde, (byte)0xb4, (byte)0xa5, (byte)0xfc,
+        (byte)0x80, (byte)0xef, (byte)0xcb, (byte)0xbb, (byte)0x6b, (byte)0x76, (byte)0xba, (byte)0x5a, (byte)0x7d,
+        (byte)0x78, (byte)0x0b, (byte)0x95, (byte)0xe3, (byte)0xad, (byte)0x74, (byte)0x98, (byte)0x3b, (byte)0x36,
+        (byte)0x64, (byte)0x6d, (byte)0xdc, (byte)0xf0, (byte)0x59, (byte)0xa9, (byte)0x4c, (byte)0x17, (byte)0x7f,
+        (byte)0x91, (byte)0xb8, (byte)0xc9, (byte)0x57, (byte)0x1b, (byte)0xe0, (byte)0x61 };
+
+    public Memoable copy()
+    {
+        return new DSTU7564Digest(this);
+    }
+
+    public void reset(Memoable other)
+    {
+        DSTU7564Digest d = (DSTU7564Digest)other;
+
+        copyIn(d);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
index 4a6be46..e14c5c4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java
@@ -2,6 +2,7 @@
 
 import org.bouncycastle.crypto.ExtendedDigest;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
 
 /**
  * implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
@@ -11,86 +12,19 @@
 public class KeccakDigest
     implements ExtendedDigest
 {
-    private static long[] KeccakRoundConstants = keccakInitializeRoundConstants();
+    private static long[] KeccakRoundConstants = new long[]{ 0x0000000000000001L, 0x0000000000008082L,
+        0x800000000000808aL, 0x8000000080008000L, 0x000000000000808bL, 0x0000000080000001L, 0x8000000080008081L,
+        0x8000000000008009L, 0x000000000000008aL, 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000aL,
+        0x000000008000808bL, 0x800000000000008bL, 0x8000000000008089L, 0x8000000000008003L, 0x8000000000008002L,
+        0x8000000000000080L, 0x000000000000800aL, 0x800000008000000aL, 0x8000000080008081L, 0x8000000000008080L,
+        0x0000000080000001L, 0x8000000080008008L };
 
-    private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets();
-
-    private static long[] keccakInitializeRoundConstants()
-    {
-        long[] keccakRoundConstants = new long[24];
-        byte[] LFSRstate = new byte[1];
-
-        LFSRstate[0] = 0x01;
-        int i, j, bitPosition;
-
-        for (i = 0; i < 24; i++)
-        {
-            keccakRoundConstants[i] = 0;
-            for (j = 0; j < 7; j++)
-            {
-                bitPosition = (1 << j) - 1;
-                if (LFSR86540(LFSRstate))
-                {
-                    keccakRoundConstants[i] ^= 1L << bitPosition;
-                }
-            }
-        }
-
-        return keccakRoundConstants;
-    }
-
-    private static boolean LFSR86540(byte[] LFSR)
-    {
-        boolean result = (((LFSR[0]) & 0x01) != 0);
-        if (((LFSR[0]) & 0x80) != 0)
-        {
-            LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71);
-        }
-        else
-        {
-            LFSR[0] <<= 1;
-        }
-
-        return result;
-    }
-
-    private static int[] keccakInitializeRhoOffsets()
-    {
-        int[] keccakRhoOffsets = new int[25];
-        int x, y, t, newX, newY;
-
-        keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0;
-        x = 1;
-        y = 0;
-        for (t = 0; t < 24; t++)
-        {
-            keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64;
-            newX = (0 * x + 1 * y) % 5;
-            newY = (2 * x + 3 * y) % 5;
-            x = newX;
-            y = newY;
-        }
-
-        return keccakRhoOffsets;
-    }
-
-    protected byte[] state = new byte[(1600 / 8)];
-    protected byte[] dataQueue = new byte[(1536 / 8)];
+    protected long[] state = new long[25];
+    protected byte[] dataQueue = new byte[192];
     protected int rate;
     protected int bitsInQueue;
     protected int fixedOutputLength;
     protected boolean squeezing;
-    protected int bitsAvailableForSqueezing;
-    protected byte[] chunk;
-    protected byte[] oneByte;
-
-    private void clearDataQueueSection(int off, int len)
-    {
-        for (int i = off; i != off + len; i++)
-        {
-            dataQueue[i] = 0;
-        }
-    }
 
     public KeccakDigest()
     {
@@ -102,16 +36,14 @@
         init(bitLength);
     }
 
-    public KeccakDigest(KeccakDigest source) {
+    public KeccakDigest(KeccakDigest source)
+    {
         System.arraycopy(source.state, 0, this.state, 0, source.state.length);
         System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
         this.rate = source.rate;
         this.bitsInQueue = source.bitsInQueue;
         this.fixedOutputLength = source.fixedOutputLength;
         this.squeezing = source.squeezing;
-        this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
-        this.chunk = Arrays.clone(source.chunk);
-        this.oneByte = Arrays.clone(source.oneByte);
     }
 
     public String getAlgorithmName()
@@ -126,14 +58,12 @@
 
     public void update(byte in)
     {
-        oneByte[0] = in;
-
-        absorb(oneByte, 0, 8L);
+        absorb(new byte[]{ in }, 0, 1);
     }
 
     public void update(byte[] in, int inOff, int len)
     {
-        absorb(in, inOff, len * 8L);
+        absorb(in, inOff, len);
     }
 
     public int doFinal(byte[] out, int outOff)
@@ -152,8 +82,7 @@
     {
         if (partialBits > 0)
         {
-            oneByte[0] = partialByte;
-            absorb(oneByte, 0, partialBits);
+            absorbBits(partialByte, partialBits);
         }
 
         squeeze(out, outOff, fixedOutputLength);
@@ -182,64 +111,39 @@
     {
         switch (bitLength)
         {
-        case 288:
-            initSponge(1024, 576);
-            break;
         case 128:
-            initSponge(1344, 256);
-            break;
         case 224:
-            initSponge(1152, 448);
-            break;
         case 256:
-            initSponge(1088, 512);
-            break;
+        case 288:
         case 384:
-            initSponge(832, 768);
-            break;
         case 512:
-            initSponge(576, 1024);
+            initSponge(1600 - (bitLength << 1));
             break;
         default:
             throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512.");
         }
     }
 
-    private void initSponge(int rate, int capacity)
+    private void initSponge(int rate)
     {
-        if (rate + capacity != 1600)
-        {
-            throw new IllegalStateException("rate + capacity != 1600");
-        }
         if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
         {
             throw new IllegalStateException("invalid rate value");
         }
 
         this.rate = rate;
-        // this is never read, need to check to see why we want to save it
-        //  this.capacity = capacity;
-        Arrays.fill(this.state, (byte)0);
+        for (int i = 0; i < state.length; ++i)
+        {
+            state[i] = 0L;
+        }
         Arrays.fill(this.dataQueue, (byte)0);
         this.bitsInQueue = 0;
         this.squeezing = false;
-        this.bitsAvailableForSqueezing = 0;
-        this.fixedOutputLength = capacity / 2;
-        this.chunk = new byte[rate / 8];
-        this.oneByte = new byte[1];
+        this.fixedOutputLength = (1600 - rate) / 2;
     }
 
-    private void absorbQueue()
+    protected void absorb(byte[] data, int off, int len)
     {
-        KeccakAbsorb(state, dataQueue, rate / 8);
-
-        bitsInQueue = 0;
-    }
-
-    protected void absorb(byte[] data, int off, long databitlen)
-    {
-        long i, j, wholeBlocks;
-
         if ((bitsInQueue % 8) != 0)
         {
             throw new IllegalStateException("attempt to absorb with odd length queue");
@@ -249,94 +153,98 @@
             throw new IllegalStateException("attempt to absorb while squeezing");
         }
 
-        i = 0;
-        while (i < databitlen)
+        int bytesInQueue = bitsInQueue >> 3;
+        int rateBytes = rate >> 3;
+
+        int count = 0;
+        while (count < len)
         {
-            if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate)))
+            if (bytesInQueue == 0 && count <= (len - rateBytes))
             {
-                wholeBlocks = (databitlen - i) / rate;
-
-                for (j = 0; j < wholeBlocks; j++)
+                do
                 {
-                    System.arraycopy(data, (int)(off + (i / 8) + (j * chunk.length)), chunk, 0, chunk.length);
-
-//                            displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8);
-
-                    KeccakAbsorb(state, chunk, chunk.length);
+                    KeccakAbsorb(data, off + count);
+                    count += rateBytes;
                 }
-
-                i += wholeBlocks * rate;
+                while (count <= (len - rateBytes));
             }
             else
             {
-                int partialBlock = (int)(databitlen - i);
-                if (partialBlock + bitsInQueue > rate)
-                {
-                    partialBlock = rate - bitsInQueue;
-                }
-                int partialByte = partialBlock % 8;
-                partialBlock -= partialByte;
-                System.arraycopy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8);
+                int partialBlock = Math.min(rateBytes - bytesInQueue, len - count);
+                System.arraycopy(data, off + count, dataQueue, bytesInQueue, partialBlock);
 
-                bitsInQueue += partialBlock;
-                i += partialBlock;
-                if (bitsInQueue == rate)
+                bytesInQueue += partialBlock;
+                count += partialBlock;
+
+                if (bytesInQueue == rateBytes)
                 {
-                    absorbQueue();
-                }
-                if (partialByte > 0)
-                {
-                    int mask = (1 << partialByte) - 1;
-                    dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask);
-                    bitsInQueue += partialByte;
-                    i += partialByte;
+                    KeccakAbsorb(dataQueue, 0);
+                    bytesInQueue = 0;
                 }
             }
         }
+
+        bitsInQueue = bytesInQueue << 3;
+    }
+
+    protected void absorbBits(int data, int bits)
+    {
+        if (bits < 1 || bits > 7)
+        {
+            throw new IllegalArgumentException("'bits' must be in the range 1 to 7");
+        }
+        if ((bitsInQueue % 8) != 0)
+        {
+            throw new IllegalStateException("attempt to absorb with odd length queue");
+        }
+        if (squeezing)
+        {
+            throw new IllegalStateException("attempt to absorb while squeezing");
+        }
+
+        int mask = (1 << bits) - 1;
+        dataQueue[bitsInQueue >> 3] = (byte)(data & mask);
+
+        // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work
+        bitsInQueue += bits;
     }
 
     private void padAndSwitchToSqueezingPhase()
     {
-        if (bitsInQueue + 1 == rate)
+        dataQueue[bitsInQueue >> 3] |= (byte)(1L << (bitsInQueue & 7));
+
+        if (++bitsInQueue == rate)
         {
-            dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
-            absorbQueue();
-            clearDataQueueSection(0, rate / 8);
-        }
-        else
-        {
-            clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8);
-            dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8);
-        }
-        dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8);
-        absorbQueue();
-
-
-//            displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---");
-
-
-        if (rate == 1024)
-        {
-            KeccakExtract1024bits(state, dataQueue);
-            bitsAvailableForSqueezing = 1024;
-        }
-        else
-
-        {
-            KeccakExtract(state, dataQueue, rate / 64);
-            bitsAvailableForSqueezing = rate;
+            KeccakAbsorb(dataQueue, 0);
+            bitsInQueue = 0;
         }
 
-//            displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
+        {
+            int full = bitsInQueue >> 6, partial = bitsInQueue & 63;
+            int off = 0;
+            for (int i = 0; i < full; ++i)
+            {
+                state[i] ^= Pack.littleEndianToLong(dataQueue, off);
+                off += 8;
+            }
+            if (partial > 0)
+            {
+                long mask = (1L << partial) - 1L;
+                state[full] ^= Pack.littleEndianToLong(dataQueue, off) & mask;
+            }
+            state[(rate - 1) >> 6] ^= (1L << 63);
+        }
+
+        KeccakPermutation();
+
+        KeccakExtract();
+        bitsInQueue = rate;
 
         squeezing = true;
     }
 
     protected void squeeze(byte[] output, int offset, long outputLength)
     {
-        long i;
-        int partialBlock;
-
         if (!squeezing)
         {
             padAndSwitchToSqueezingPhase();
@@ -346,204 +254,146 @@
             throw new IllegalStateException("outputLength not a multiple of 8");
         }
 
-        i = 0;
+        long i = 0;
         while (i < outputLength)
         {
-            if (bitsAvailableForSqueezing == 0)
+            if (bitsInQueue == 0)
             {
-                keccakPermutation(state);
-
-                if (rate == 1024)
-                {
-                    KeccakExtract1024bits(state, dataQueue);
-                    bitsAvailableForSqueezing = 1024;
-                }
-                else
-
-                {
-                    KeccakExtract(state, dataQueue, rate / 64);
-                    bitsAvailableForSqueezing = rate;
-                }
-
-//                    displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8);
-
+                KeccakPermutation();
+                KeccakExtract();
+                bitsInQueue = rate;
             }
-            partialBlock = bitsAvailableForSqueezing;
-            if ((long)partialBlock > outputLength - i)
-            {
-                partialBlock = (int)(outputLength - i);
-            }
-
-            System.arraycopy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8);
-            bitsAvailableForSqueezing -= partialBlock;
+            int partialBlock = (int)Math.min((long)bitsInQueue, outputLength - i);
+            System.arraycopy(dataQueue, (rate - bitsInQueue) / 8, output, offset + (int)(i / 8), partialBlock / 8);
+            bitsInQueue -= partialBlock;
             i += partialBlock;
         }
     }
 
-    private void fromBytesToWords(long[] stateAsWords, byte[] state)
+    private void KeccakAbsorb(byte[] data, int off)
     {
-        for (int i = 0; i < (1600 / 64); i++)
+        int count = rate >> 6;
+        for (int i = 0; i < count; ++i)
         {
-            stateAsWords[i] = 0;
-            int index = i * (64 / 8);
-            for (int j = 0; j < (64 / 8); j++)
-            {
-                stateAsWords[i] |= ((long)state[index + j] & 0xff) << ((8 * j));
-            }
-        }
-    }
-
-    private void fromWordsToBytes(byte[] state, long[] stateAsWords)
-    {
-        for (int i = 0; i < (1600 / 64); i++)
-        {
-            int index = i * (64 / 8);
-            for (int j = 0; j < (64 / 8); j++)
-            {
-                state[index + j] = (byte)((stateAsWords[i] >>> ((8 * j))) & 0xFF);
-            }
-        }
-    }
-
-    private void keccakPermutation(byte[] state)
-    {
-        long[] longState = new long[state.length / 8];
-
-        fromBytesToWords(longState, state);
-
-//        displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState);
-
-        keccakPermutationOnWords(longState);
-
-//        displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState);
-
-        fromWordsToBytes(state, longState);
-    }
-
-    private void keccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes)
-    {
-        int i;
-
-        for (i = 0; i < dataLengthInBytes; i++)
-        {
-            state[i] ^= data[i];
+            state[i] ^= Pack.littleEndianToLong(data, off);
+            off += 8;
         }
 
-        keccakPermutation(state);
+        KeccakPermutation();
     }
 
-    private void keccakPermutationOnWords(long[] state)
+    private void KeccakExtract()
     {
-        int i;
+        Pack.longToLittleEndian(state, 0, rate >> 6, dataQueue, 0);
+    }
 
-//        displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state);
+    private void KeccakPermutation()
+    {
+        long[] A = state;
 
-        for (i = 0; i < 24; i++)
+        long a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
+        long a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
+        long a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
+        long a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
+        long a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
+
+        for (int i = 0; i < 24; i++)
         {
-//            displayIntermediateValues.displayRoundNumber(3, i);
+            // theta
+            long c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
+            long c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
+            long c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
+            long c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
+            long c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
 
-            theta(state);
-//            displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state);
+            long d1 = (c1 << 1 | c1 >>> -1) ^ c4;
+            long d2 = (c2 << 1 | c2 >>> -1) ^ c0;
+            long d3 = (c3 << 1 | c3 >>> -1) ^ c1;
+            long d4 = (c4 << 1 | c4 >>> -1) ^ c2;
+            long d0 = (c0 << 1 | c0 >>> -1) ^ c3;
 
-            rho(state);
-//            displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state);
+            a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
+            a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
+            a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
+            a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
+            a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
 
-            pi(state);
-//            displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state);
+            // rho/pi
+            c1  = a01 <<  1 | a01 >>> 63;
+            a01 = a06 << 44 | a06 >>> 20;
+            a06 = a09 << 20 | a09 >>> 44;
+            a09 = a22 << 61 | a22 >>>  3;
+            a22 = a14 << 39 | a14 >>> 25;
+            a14 = a20 << 18 | a20 >>> 46;
+            a20 = a02 << 62 | a02 >>>  2;
+            a02 = a12 << 43 | a12 >>> 21;
+            a12 = a13 << 25 | a13 >>> 39;
+            a13 = a19 <<  8 | a19 >>> 56;
+            a19 = a23 << 56 | a23 >>>  8;
+            a23 = a15 << 41 | a15 >>> 23;
+            a15 = a04 << 27 | a04 >>> 37;
+            a04 = a24 << 14 | a24 >>> 50;
+            a24 = a21 <<  2 | a21 >>> 62;
+            a21 = a08 << 55 | a08 >>>  9;
+            a08 = a16 << 45 | a16 >>> 19;
+            a16 = a05 << 36 | a05 >>> 28;
+            a05 = a03 << 28 | a03 >>> 36;
+            a03 = a18 << 21 | a18 >>> 43;
+            a18 = a17 << 15 | a17 >>> 49;
+            a17 = a11 << 10 | a11 >>> 54;
+            a11 = a07 <<  6 | a07 >>> 58;
+            a07 = a10 <<  3 | a10 >>> 61;
+            a10 = c1;
 
-            chi(state);
-//            displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state);
+            // chi
+            c0 = a00 ^ (~a01 & a02);
+            c1 = a01 ^ (~a02 & a03);
+            a02 ^= ~a03 & a04;
+            a03 ^= ~a04 & a00;
+            a04 ^= ~a00 & a01;
+            a00 = c0;
+            a01 = c1;
 
-            iota(state, i);
-//            displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state);
+            c0 = a05 ^ (~a06 & a07);
+            c1 = a06 ^ (~a07 & a08);
+            a07 ^= ~a08 & a09;
+            a08 ^= ~a09 & a05;
+            a09 ^= ~a05 & a06;
+            a05 = c0;
+            a06 = c1;
+
+            c0 = a10 ^ (~a11 & a12);
+            c1 = a11 ^ (~a12 & a13);
+            a12 ^= ~a13 & a14;
+            a13 ^= ~a14 & a10;
+            a14 ^= ~a10 & a11;
+            a10 = c0;
+            a11 = c1;
+
+            c0 = a15 ^ (~a16 & a17);
+            c1 = a16 ^ (~a17 & a18);
+            a17 ^= ~a18 & a19;
+            a18 ^= ~a19 & a15;
+            a19 ^= ~a15 & a16;
+            a15 = c0;
+            a16 = c1;
+
+            c0 = a20 ^ (~a21 & a22);
+            c1 = a21 ^ (~a22 & a23);
+            a22 ^= ~a23 & a24;
+            a23 ^= ~a24 & a20;
+            a24 ^= ~a20 & a21;
+            a20 = c0;
+            a21 = c1;
+
+            // iota
+            a00 ^= KeccakRoundConstants[i];
         }
-    }
-
-    long[] C = new long[5];
-
-    private void theta(long[] A)
-    {
-        for (int x = 0; x < 5; x++)
-        {
-            C[x] = 0;
-            for (int y = 0; y < 5; y++)
-            {
-                C[x] ^= A[x + 5 * y];
-            }
-        }
-        for (int x = 0; x < 5; x++)
-        {
-            long dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >>> (64 - 1)))) ^ C[(x + 4) % 5];
-            for (int y = 0; y < 5; y++)
-            {
-                A[x + 5 * y] ^= dX;
-            }
-        }
-    }
-
-    private void rho(long[] A)
-    {
-        for (int x = 0; x < 5; x++)
-        {
-            for (int y = 0; y < 5; y++)
-            {
-                int index = x + 5 * y;
-                A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >>> (64 - KeccakRhoOffsets[index]))) : A[index]);
-            }
-        }
-    }
-
-    long[] tempA = new long[25];
-
-    private void pi(long[] A)
-    {
-        System.arraycopy(A, 0, tempA, 0, tempA.length);
-
-        for (int x = 0; x < 5; x++)
-        {
-            for (int y = 0; y < 5; y++)
-            {
-                A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y];
-            }
-        }
-    }
-
-    long[] chiC = new long[5];
-
-    private void chi(long[] A)
-    {
-        for (int y = 0; y < 5; y++)
-        {
-            for (int x = 0; x < 5; x++)
-            {
-                chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]);
-            }
-            for (int x = 0; x < 5; x++)
-            {
-                A[x + 5 * y] = chiC[x];
-            }
-        }
-    }
-
-    private void iota(long[] A, int indexRound)
-    {
-        A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound];
-    }
-
-    private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes)
-    {
-        keccakPermutationAfterXor(byteState, data, dataInBytes);
-    }
-
-
-    private void KeccakExtract1024bits(byte[] byteState, byte[] data)
-    {
-        System.arraycopy(byteState, 0, data, 0, 128);
-    }
-
-
-    private void KeccakExtract(byte[] byteState, byte[] data, int laneCount)
-    {
-        System.arraycopy(byteState, 0, data, 0, laneCount * 8);
+        
+        A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
+        A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
+        A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
+        A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
+        A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
index 6cb0d4a..9219d9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
@@ -3,12 +3,13 @@
 import java.io.ByteArrayOutputStream;
 
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
 
 
 public class NullDigest
     implements Digest
 {
-    private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+    private OpenByteArrayOutputStream bOut = new OpenByteArrayOutputStream();
 
     public String getAlgorithmName()
     {
@@ -32,17 +33,33 @@
 
     public int doFinal(byte[] out, int outOff)
     {
-        byte[] res = bOut.toByteArray();
+        int size = bOut.size();
 
-        System.arraycopy(res, 0, out, outOff, res.length);
+        bOut.copy(out, outOff);
 
         reset();
         
-        return res.length;
+        return size;
     }
 
     public void reset()
     {
         bOut.reset();
     }
+
+    private static class OpenByteArrayOutputStream
+        extends ByteArrayOutputStream
+    {
+        public void reset()
+        {
+            super.reset();
+
+            Arrays.clear(buf);
+        }
+
+        void copy(byte[] out, int outOff)
+        {
+            System.arraycopy(buf, 0, out, outOff, this.size());
+        }
+    }
 }
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
index b81e7c0..4290d20 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -110,7 +110,7 @@
         }
 
         X[14] = (int)(bitLength >>> 32);
-        X[15] = (int)(bitLength & 0xffffffff);
+        X[15] = (int)bitLength;
     }
 
     public int doFinal(
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
index 676d5ed..85537d0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
@@ -44,7 +44,7 @@
 
     public int doFinal(byte[] out, int outOff)
     {
-        absorb(new byte[]{ 0x02 }, 0, 2);
+        absorbBits(0x02, 2);
         
         return super.doFinal(out,  outOff);
     }
@@ -64,8 +64,7 @@
 
         if (finalBits >= 8)
         {
-            oneByte[0] = (byte)finalInput;
-            absorb(oneByte, 0, 8);
+            absorb(new byte[]{ (byte)finalInput }, 0, 1);
             finalBits -= 8;
             finalInput >>>= 8;
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
index 4266575..67375c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java
@@ -61,7 +61,7 @@
     {
         if (!squeezing)
         {
-            absorb(new byte[]{0x0F}, 0, 4);
+            absorbBits(0x0F, 4);
         }
 
         squeeze(out, outOff, ((long)outLen) * 8);
@@ -92,16 +92,14 @@
 
         if (finalBits >= 8)
         {
-            oneByte[0] = (byte)finalInput;
-            absorb(oneByte, 0, 8);
+            absorb(new byte[]{ (byte)finalInput }, 0, 1);
             finalBits -= 8;
             finalInput >>>= 8;
         }
 
         if (finalBits > 0)
         {
-            oneByte[0] = (byte)finalInput;
-            absorb(oneByte, 0, finalBits);
+            absorbBits(finalInput, finalBits);
         }
 
         squeeze(out, outOff, ((long)outLen) * 8);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
index 590c2a2..4a527c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
@@ -27,7 +27,6 @@
 
     // Work-bufs used within processBlock()
     private int[] W = new int[68];
-    private int[] W1 = new int[64];
 
     // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions.
     private static final int[] T = new int[64];
@@ -124,14 +123,7 @@
     {
         finish();
 
-        Pack.intToBigEndian(this.V[0], out, outOff + 0);
-        Pack.intToBigEndian(this.V[1], out, outOff + 4);
-        Pack.intToBigEndian(this.V[2], out, outOff + 8);
-        Pack.intToBigEndian(this.V[3], out, outOff + 12);
-        Pack.intToBigEndian(this.V[4], out, outOff + 16);
-        Pack.intToBigEndian(this.V[5], out, outOff + 20);
-        Pack.intToBigEndian(this.V[6], out, outOff + 24);
-        Pack.intToBigEndian(this.V[7], out, outOff + 28);
+        Pack.intToBigEndian(V, out, outOff);
 
         reset();
 
@@ -267,10 +259,6 @@
             int r7 = ((wj13 << 7) | (wj13 >>> (32 - 7)));
             this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6];
         }
-        for (int j = 0; j < 64; ++j)
-        {
-            this.W1[j] = this.W[j] ^ this.W[j + 4];
-        }
 
         int A = this.V[0];
         int B = this.V[1];
@@ -288,8 +276,10 @@
             int s1_ = a12 + E + T[j];
             int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
             int SS2 = SS1 ^ a12;
-            int TT1 = FF0(A, B, C) + D + SS2 + this.W1[j];
-            int TT2 = GG0(E, F, G) + H + SS1 + this.W[j];
+            int Wj = W[j];
+            int W1j = Wj ^ W[j + 4];
+            int TT1 = FF0(A, B, C) + D + SS2 + W1j;
+            int TT2 = GG0(E, F, G) + H + SS1 + Wj;
             D = C;
             C = ((B << 9) | (B >>> (32 - 9)));
             B = A;
@@ -307,8 +297,10 @@
             int s1_ = a12 + E + T[j];
             int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
             int SS2 = SS1 ^ a12;
-            int TT1 = FF1(A, B, C) + D + SS2 + this.W1[j];
-            int TT2 = GG1(E, F, G) + H + SS1 + this.W[j];
+            int Wj = W[j];
+            int W1j = Wj ^ W[j + 4];
+            int TT1 = FF1(A, B, C) + D + SS2 + W1j;
+            int TT2 = GG1(E, F, G) + H + SS1 + Wj;
             D = C;
             C = ((B << 9) | (B >>> (32 - 9)));
             B = A;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
index bafea53..1d4e840 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
@@ -4,11 +4,12 @@
 import java.util.Hashtable;
 import java.util.Vector;
 
-import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.engines.ThreefishEngine;
 import org.bouncycastle.crypto.macs.SkeinMac;
 import org.bouncycastle.crypto.params.SkeinParameters;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Memoable;
 
 /**
@@ -221,7 +222,7 @@
 
     private static Integer variantIdentifier(int blockSizeBytes, int outputSizeBytes)
     {
-        return new Integer((outputSizeBytes << 16) | blockSizeBytes);
+        return Integers.valueOf((outputSizeBytes << 16) | blockSizeBytes);
     }
 
     private static class UbiTweak
@@ -756,7 +757,7 @@
         checkInitialised();
         if (out.length < (outOff + outputSizeBytes))
         {
-            throw new DataLengthException("Output buffer is too short to hold output");
+            throw new OutputLengthException("Output buffer is too short to hold output");
         }
 
         // Finalise message block
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
index 8109447..186f5a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
@@ -6,12 +6,15 @@
 import java.util.Vector;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import org.bouncycastle.asn1.x9.X9ECPoint;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.custom.djb.Curve25519;
+import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
 import org.bouncycastle.math.ec.custom.sec.SecP128R1Curve;
 import org.bouncycastle.math.ec.custom.sec.SecP160K1Curve;
 import org.bouncycastle.math.ec.custom.sec.SecP160R1Curve;
@@ -614,7 +617,22 @@
         }
     };
 
-    
+    /*
+     * sm2p256v1
+     */
+    static X9ECParametersHolder sm2p256v1 = new X9ECParametersHolder()
+    {
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = configureCurve(new SM2P256V1Curve());
+            X9ECPoint G = new X9ECPoint(curve, Hex.decode("04"
+                + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"
+                + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+        }
+    };
+
     static final Hashtable nameToCurve = new Hashtable();
     static final Hashtable nameToOID = new Hashtable();
     static final Hashtable oidToCurve = new Hashtable();
@@ -653,7 +671,7 @@
 
     static
     {
-        defineCurve("curve25519", curve25519);
+        defineCurveWithOID("curve25519", CryptlibObjectIdentifiers.curvey25519, curve25519);
 
 //        defineCurveWithOID("secp112r1", SECObjectIdentifiers.secp112r1, secp112r1);
 //        defineCurveWithOID("secp112r2", SECObjectIdentifiers.secp112r2, secp112r2);
@@ -690,6 +708,8 @@
         defineCurveWithOID("sect571k1", SECObjectIdentifiers.sect571k1, sect571k1);
         defineCurveWithOID("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1);
 
+        defineCurveWithOID("sm2p256v1", GMObjectIdentifiers.sm2p256v1, sm2p256v1);
+
         defineCurveAlias("B-163", SECObjectIdentifiers.sect163r2);
         defineCurveAlias("B-233", SECObjectIdentifiers.sect233r1);
         defineCurveAlias("B-283", SECObjectIdentifiers.sect283r1);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
index 2f1c937..bca958a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
@@ -2,6 +2,8 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
@@ -41,8 +43,9 @@
             throw new IllegalStateException("ECElGamalDecryptor not initialised");
         }
 
-        ECPoint tmp = pair.getX().multiply(key.getD());
+        ECCurve curve = key.getParameters().getCurve();
+        ECPoint tmp = ECAlgorithms.cleanPoint(curve, pair.getX()).multiply(key.getD());
 
-        return pair.getY().subtract(tmp).normalize();
+        return ECAlgorithms.cleanPoint(curve, pair.getY()).subtract(tmp).normalize();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
index 48fc046..b43bfb2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
@@ -4,9 +4,11 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
@@ -47,7 +49,7 @@
             }
 
             this.key = (ECPublicKeyParameters)param;
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
@@ -71,7 +73,7 @@
 
         ECPoint[] gamma_phi = new ECPoint[]{
             basePointMultiplier.multiply(ec.getG(), k),
-            key.getQ().multiply(k).add(point)
+            key.getQ().multiply(k).add(ECAlgorithms.cleanPoint(ec.getCurve(), point))
         };
 
         ec.getCurve().normalizeAll(gamma_phi);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
index 2c6a920..5a36135 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
@@ -62,8 +63,8 @@
         BigInteger k = this.k.mod(n);
 
         ECPoint[] gamma_phi = new ECPoint[]{
-            basePointMultiplier.multiply(ec.getG(), k).add(cipherText.getX()),
-            key.getQ().multiply(k).add(cipherText.getY())
+            basePointMultiplier.multiply(ec.getG(), k).add(ECAlgorithms.cleanPoint(ec.getCurve(), cipherText.getX())),
+            key.getQ().multiply(k).add(ECAlgorithms.cleanPoint(ec.getCurve(), cipherText.getY()))
         };
 
         ec.getCurve().normalizeAll(gamma_phi);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
index 112d20c..dd9d112 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -4,9 +4,11 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
@@ -47,7 +49,7 @@
             }
 
             this.key = (ECPublicKeyParameters)param;
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
@@ -73,7 +75,7 @@
 
         ECPoint[] gamma_phi = new ECPoint[]{
             basePointMultiplier.multiply(ec.getG(), k),
-            key.getQ().multiply(k).add(cipherText.getY())
+            key.getQ().multiply(k).add(ECAlgorithms.cleanPoint(ec.getCurve(), cipherText.getY()))
         };
 
         ec.getCurve().normalizeAll(gamma_phi);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
index 7bfc0b3..ac6fde3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -4,9 +4,11 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
@@ -50,7 +52,7 @@
             }
 
             this.key = (ECPublicKeyParameters)param;
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
@@ -77,8 +79,8 @@
         BigInteger k = ECUtil.generateK(n, random);
 
         ECPoint[] gamma_phi = new ECPoint[]{
-            basePointMultiplier.multiply(ec.getG(), k).add(cipherText.getX()),
-            key.getQ().multiply(k).add(cipherText.getY())
+            basePointMultiplier.multiply(ec.getG(), k).add(ECAlgorithms.cleanPoint(ec.getCurve(), cipherText.getX())),
+            key.getQ().multiply(k).add(ECAlgorithms.cleanPoint(ec.getCurve(), cipherText.getY()))
         };
 
         ec.getCurve().normalizeAll(gamma_phi);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
index 74921f0..ee75f31 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
@@ -4,6 +4,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.util.BigIntegers;
 
 class ECUtil
 {
@@ -13,7 +14,7 @@
         BigInteger k;
         do
         {
-            k = new BigInteger(nBitLength, random);
+            k = BigIntegers.createRandomBigInteger(nBitLength, random);
         }
         while (k.equals(ECConstants.ZERO) || (k.compareTo(n) >= 0));
         return k;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index 471f5c2..a0d9361 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
@@ -82,7 +83,7 @@
         }
         else
         {   
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
 
         engine.init(forEncryption, param);
@@ -220,10 +221,17 @@
         // on encryption, we need to make sure our decrypted block comes back
         // the same size.
         //
+        boolean wrongData = (block.length < (2 * defHash.length) + 1);
 
-        System.arraycopy(data, 0, block, block.length - data.length, data.length);
-
-        boolean shortData = (block.length < (2 * defHash.length) + 1);
+        if (data.length <= block.length)
+        {
+            System.arraycopy(data, 0, block, block.length - data.length, data.length);
+        }
+        else
+        {
+            System.arraycopy(data, 0, block, 0, block.length);
+            wrongData = true;
+        }
 
         //
         // unmask the seed.
@@ -277,7 +285,7 @@
 
         start++;
 
-        if (defHashWrong | shortData | dataStartWrong)
+        if (defHashWrong | wrongData | dataStartWrong)
         {
             Arrays.fill(block, (byte)0);
             throw new InvalidCipherTextException("data wrong");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index e79557f..cfd6dcf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -6,6 +6,7 @@
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
@@ -141,7 +142,7 @@
             kParam = (AsymmetricKeyParameter)param;
             if (!kParam.isPrivate() && forEncryption)
             {
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
             }
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java
index bdc202b..4307676 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CAST5Engine.java
@@ -20,9 +20,7 @@
 public class CAST5Engine
     implements BlockCipher
 {
-    protected final static int M32 = 0xffffffff;
-
-    protected final static int[] 
+    private final static int[]
         S1 = {
 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java
index 5102f15..9cd3f79 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java
@@ -4,6 +4,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.params.CramerShoupKeyParameters;
@@ -11,6 +12,7 @@
 import org.bouncycastle.crypto.params.CramerShoupPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Strings;
 
 /**
  * Essentially the Cramer-Shoup encryption / decryption algorithms according to
@@ -18,13 +20,12 @@
  */
 public class CramerShoupCoreEngine
 {
-
     private static final BigInteger ONE = BigInteger.valueOf(1);
 
     private CramerShoupKeyParameters key;
     private SecureRandom random;
     private boolean forEncryption;
-    private String label = null;
+    private byte[] label = null;
 
     /**
      * initialise the CramerShoup engine.
@@ -37,7 +38,7 @@
     {
         init(forEncryption, param);
 
-        this.label = label;
+        this.label = Strings.toUTF8ByteArray(label);
     }
 
     /**
@@ -216,7 +217,7 @@
             digest.update(eBytes, 0, eBytes.length);
             if (this.label != null)
             {
-                byte[] lBytes = this.label.getBytes();
+                byte[] lBytes = this.label;
                 digest.update(lBytes, 0, lBytes.length);
             }
             byte[] out = new byte[digest.getDigestSize()];
@@ -251,7 +252,7 @@
             digest.update(eBytes, 0, eBytes.length);
             if (this.label != null)
             {
-                byte[] lBytes = this.label.getBytes();
+                byte[] lBytes = this.label;
                 digest.update(lBytes, 0, lBytes.length);
             }
             byte[] out = new byte[digest.getDigestSize()];
@@ -289,7 +290,7 @@
 
     protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
     {
-        return !needed ? null : (provided != null) ? provided : new SecureRandom();
+        return !needed ? null : (provided != null) ? provided : CryptoServicesRegistrar.getSecureRandom();
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CryptoProWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CryptoProWrapEngine.java
new file mode 100644
index 0000000..d6cebac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CryptoProWrapEngine.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.modes.GCFBBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.util.Pack;
+
+public class CryptoProWrapEngine
+    extends GOST28147WrapEngine
+{
+    public void init(boolean forWrapping, CipherParameters param)
+    {
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom pr = (ParametersWithRandom)param;
+            param = pr.getParameters();
+        }
+        
+        ParametersWithUKM pU = (ParametersWithUKM)param;
+        byte[] sBox = null;
+
+
+        KeyParameter kParam;
+
+        if (pU.getParameters() instanceof ParametersWithSBox)
+        {
+            kParam = (KeyParameter)((ParametersWithSBox)pU.getParameters()).getParameters();
+            sBox = ((ParametersWithSBox)pU.getParameters()).getSBox();
+        }
+        else
+        {
+            kParam = (KeyParameter)pU.getParameters();
+        }
+
+        kParam = new KeyParameter(cryptoProDiversify(kParam.getKey(), pU.getUKM(), sBox));
+
+        if (sBox != null)
+        {
+            super.init(forWrapping, new ParametersWithUKM(new ParametersWithSBox(kParam, sBox), pU.getUKM()));
+        }
+        else
+        {
+            super.init(forWrapping, new ParametersWithUKM(kParam, pU.getUKM()));
+        }
+    }
+
+    /*
+         RFC 4357 6.5.  CryptoPro KEK Diversification Algorithm
+
+         Given a random 64-bit UKM and a GOST 28147-89 key K, this algorithm
+         creates a new GOST 28147-89 key K(UKM).
+
+          1) Let K[0] = K;
+          2) UKM is split into components a[i,j]:
+             UKM = a[0]|..|a[7] (a[i] - byte, a[i,0]..a[i,7] - it's bits)
+          3) Let i be 0.
+          4) K[1]..K[8] are calculated by repeating the following algorithm
+             eight times:
+           A) K[i] is split into components k[i,j]:
+              K[i] = k[i,0]|k[i,1]|..|k[i,7] (k[i,j] - 32-bit integer)
+           B) Vector S[i] is calculated:
+              S[i] = ((a[i,0]*k[i,0] + ... + a[i,7]*k[i,7]) mod 2^32) |
+              (((~a[i,0])*k[i,0] + ... + (~a[i,7])*k[i,7]) mod 2^32);
+           C) K[i+1] = encryptCFB (S[i], K[i], K[i])
+           D) i = i + 1
+          5) Let K(UKM) be K[8].
+     */
+    private static byte[] cryptoProDiversify(byte[] K, byte[] ukm, byte[] sBox)
+    {
+        for (int i = 0; i != 8; i++)
+        {
+            int sOn = 0;
+            int sOff = 0;
+            for (int j = 0; j != 8; j++)
+            {
+                int kj = Pack.littleEndianToInt(K, j * 4);
+                if (bitSet(ukm[i], j))
+                {
+                    sOn += kj;
+                }
+                else
+                {
+                    sOff += kj;
+                }
+            }
+
+            byte[] s = new byte[8];
+            Pack.intToLittleEndian(sOn, s, 0);
+            Pack.intToLittleEndian(sOff, s, 4);
+
+            GCFBBlockCipher c = new GCFBBlockCipher(new GOST28147Engine());
+
+            c.init(true, new ParametersWithIV(new ParametersWithSBox(new KeyParameter(K), sBox), s));
+
+            c.processBlock(K, 0, K, 0);
+            c.processBlock(K, 8, K, 8);
+            c.processBlock(K, 16, K, 16);
+            c.processBlock(K, 24, K, 24);
+        }
+
+        return K;
+    }
+
+    private static boolean bitSet(byte v, int bitNo)
+    {
+        return (v & (1 << bitNo)) != 0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
index 6980fd0..58ea618 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
 
 /**
  * a class that provides a basic DES engine.
@@ -407,15 +408,8 @@
     {
         int     work, right, left;
 
-        left     = (in[inOff + 0] & 0xff) << 24;
-        left    |= (in[inOff + 1] & 0xff) << 16;
-        left    |= (in[inOff + 2] & 0xff) << 8;
-        left    |= (in[inOff + 3] & 0xff);
-
-        right     = (in[inOff + 4] & 0xff) << 24;
-        right    |= (in[inOff + 5] & 0xff) << 16;
-        right    |= (in[inOff + 6] & 0xff) << 8;
-        right    |= (in[inOff + 7] & 0xff);
+        left = Pack.bigEndianToInt(in, inOff);
+        right = Pack.bigEndianToInt(in, inOff + 4);
 
         work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
         right ^= work;
@@ -429,11 +423,11 @@
         work = ((right >>> 8) ^ left) & 0x00ff00ff;
         left ^= work;
         right ^= (work << 8);
-        right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+        right = (right << 1) | (right >>> 31);
         work = (left ^ right) & 0xaaaaaaaa;
         left ^= work;
         right ^= work;
-        left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+        left = (left << 1) | (left >>> 31);
 
         for (int round = 0; round < 8; round++)
         {
@@ -483,13 +477,7 @@
         left ^= work;
         right ^= (work << 4);
 
-        out[outOff + 0] = (byte)((right >>> 24) & 0xff);
-        out[outOff + 1] = (byte)((right >>> 16) & 0xff);
-        out[outOff + 2] = (byte)((right >>>  8) & 0xff);
-        out[outOff + 3] = (byte)(right         & 0xff);
-        out[outOff + 4] = (byte)((left >>> 24) & 0xff);
-        out[outOff + 5] = (byte)((left >>> 16) & 0xff);
-        out[outOff + 6] = (byte)((left >>>  8) & 0xff);
-        out[outOff + 7] = (byte)(left         & 0xff);
+        Pack.intToBigEndian(right, out, outOff);
+        Pack.intToBigEndian(left, out, outOff + 4);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index d160a01..9db5b42 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Wrapper;
@@ -75,7 +76,7 @@
         }
         else
         {
-            sr = new SecureRandom();
+            sr = CryptoServicesRegistrar.getSecureRandom();
         }
 
         if (param instanceof KeyParameter)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624Engine.java
new file mode 100644
index 0000000..b18cb9a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624Engine.java
@@ -0,0 +1,1183 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+
+/*
+* Reference implementation of DSTU7624 national Ukrainian standard of block encryption.
+* Thanks to Roman Oliynikov' native C implementation:
+* https://github.com/Roman-Oliynikov/Kalyna-reference
+*
+* DSTU7564 is very similar to AES but with some security improvements in key schedule algorithm
+* and supports different block and key lengths (128/256/512 bits).
+*/
+public class DSTU7624Engine
+    implements BlockCipher
+{
+    private long[] internalState;
+    private long[] workingKey;
+    private long[][] roundKeys;
+
+    /* Number of 64-bit words in block */
+    private int wordsInBlock;
+
+    /* Number of 64-bit words in key */
+    private int wordsInKey;
+
+    /* Number of encryption rounds depending on key length */
+    private static final int ROUNDS_128 = 10;
+    private static final int ROUNDS_256 = 14;
+    private static final int ROUNDS_512 = 18;
+
+    private int roundsAmount;
+
+    private boolean forEncryption;
+
+    public DSTU7624Engine(int blockBitLength)
+        throws IllegalArgumentException
+    {
+        /* DSTU7624 supports 128 | 256 | 512 key/block sizes */
+        if (blockBitLength != 128 && blockBitLength != 256 && blockBitLength != 512)
+        {
+            throw new IllegalArgumentException("unsupported block length: only 128/256/512 are allowed");
+        }
+
+        wordsInBlock = blockBitLength >>> 6;
+        internalState = new long[wordsInBlock];
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        if (!(params instanceof KeyParameter))
+        {
+            throw new IllegalArgumentException("Invalid parameter passed to DSTU7624Engine init");
+        }
+
+        this.forEncryption = forEncryption;
+
+        byte[] keyBytes = ((KeyParameter)params).getKey();
+        int keyBitLength = keyBytes.length << 3;
+        int blockBitLength = wordsInBlock << 6;
+
+        if (keyBitLength != 128 && keyBitLength != 256 && keyBitLength != 512)
+        {
+            throw new IllegalArgumentException("unsupported key length: only 128/256/512 are allowed");
+        }
+
+        /* Limitations on key lengths depending on block lengths. See table 6.1 in standard */
+        if (keyBitLength != blockBitLength && keyBitLength != (2 * blockBitLength))
+        {
+            throw new IllegalArgumentException("Unsupported key length");
+        }
+
+        switch (keyBitLength)
+        {
+        case 128:
+            roundsAmount = ROUNDS_128;
+            break;
+        case 256:
+            roundsAmount = ROUNDS_256;
+            break;
+        case 512:
+            roundsAmount = ROUNDS_512;
+            break;
+        }
+
+        wordsInKey = keyBitLength >>> 6;
+
+        /* +1 round key as defined in standard */
+        roundKeys = new long[roundsAmount + 1][];
+        for (int roundKeyIndex = 0; roundKeyIndex < roundKeys.length; roundKeyIndex++)
+        {
+            roundKeys[roundKeyIndex] = new long[wordsInBlock];
+        }
+
+        workingKey = new long[wordsInKey];
+
+        if (keyBytes.length != (keyBitLength >>> 3))
+        {
+            throw new IllegalArgumentException("Invalid key parameter passed to DSTU7624Engine init");
+        }
+
+        /* Unpack encryption key bytes to words */
+        Pack.littleEndianToLong(keyBytes, 0, workingKey);
+
+        long[] tempKeys = new long[wordsInBlock];
+
+        /* KSA in DSTU7624 is strengthened to mitigate known weaknesses in AES KSA (eprint.iacr.org/2012/260.pdf) */
+        workingKeyExpandKT(workingKey, tempKeys);
+        workingKeyExpandEven(workingKey, tempKeys);
+        workingKeyExpandOdd();
+    }
+
+    public String getAlgorithmName()
+    {
+        return "DSTU7624";
+    }
+
+    public int getBlockSize()
+    {
+        return wordsInBlock << 3;
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (workingKey == null)
+        {
+            throw new IllegalStateException("DSTU7624Engine not initialised");
+        }
+
+        if (inOff + getBlockSize() > in.length)
+        {
+            throw new DataLengthException("Input buffer too short");
+        }
+
+        if (outOff + getBlockSize() > out.length)
+        {
+            throw new OutputLengthException("Output buffer too short");
+        }
+
+        if (forEncryption)
+        {
+            /* Encrypt */
+            switch (wordsInBlock)
+            {
+            case 2:
+            {
+                encryptBlock_128(in, inOff, out, outOff);
+                break;
+            }
+            default:
+            {
+                Pack.littleEndianToLong(in, inOff, internalState);
+                addRoundKey(0);
+                for (int round = 0;;)
+                {
+                    subBytes();
+                    shiftRows();
+                    mixColumns();
+
+                    if (++round == roundsAmount)
+                    {
+                        break;
+                    }
+
+                    xorRoundKey(round);
+                }
+                addRoundKey(roundsAmount);
+                Pack.longToLittleEndian(internalState, out, outOff);
+                break;
+            }
+            }
+        }
+        else
+        {
+            /* Decrypt */
+            switch (wordsInBlock)
+            {
+            case 2:
+            {
+                decryptBlock_128(in, inOff, out, outOff);
+                break;
+            }
+            default:
+            {
+                Pack.littleEndianToLong(in, inOff, internalState);
+                subRoundKey(roundsAmount);
+                for (int round = roundsAmount;;)
+                {
+                    mixColumnsInv();
+                    invShiftRows();
+                    invSubBytes();
+    
+                    if (--round == 0)
+                    {
+                        break;
+                    }
+    
+                    xorRoundKey(round);
+                }
+                subRoundKey(0);
+                Pack.longToLittleEndian(internalState, out, outOff);
+                break;
+            }
+            }
+        }
+
+        return getBlockSize();
+    }
+
+    public void reset()
+    {
+        Arrays.fill(internalState, 0);
+    }
+
+    private void addRoundKey(int round)
+    {
+        long[] roundKey = roundKeys[round];
+        for (int i = 0; i < wordsInBlock; ++i)
+        {
+            internalState[i] += roundKey[i];
+        }
+    }
+
+    private void subRoundKey(int round)
+    {
+        long[] roundKey = roundKeys[round];
+        for (int i = 0; i < wordsInBlock; ++i)
+        {
+            internalState[i] -= roundKey[i];
+        }
+    }
+
+    private void xorRoundKey(int round)
+    {
+        long[] roundKey = roundKeys[round];
+        for (int i = 0; i < wordsInBlock; ++i)
+        {
+            internalState[i] ^= roundKey[i];
+        }
+    }
+
+    private void workingKeyExpandKT(long[] workingKey, long[] tempKeys)
+    {
+        long[] k0 = new long[wordsInBlock];
+        long[] k1 = new long[wordsInBlock];
+
+        internalState = new long[wordsInBlock];
+        internalState[0] += wordsInBlock + wordsInKey + 1;
+
+        if (wordsInBlock == wordsInKey)
+        {
+            System.arraycopy(workingKey, 0, k0, 0, k0.length);
+            System.arraycopy(workingKey, 0, k1, 0, k1.length);
+        }
+        else
+        {
+            System.arraycopy(workingKey, 0, k0, 0, wordsInBlock);
+            System.arraycopy(workingKey, wordsInBlock, k1, 0, wordsInBlock);
+        }
+
+
+        for (int wordIndex = 0; wordIndex < internalState.length; wordIndex++)
+        {
+            internalState[wordIndex] += k0[wordIndex];
+        }
+
+        subBytes();
+        shiftRows();
+        mixColumns();
+
+        for (int wordIndex = 0; wordIndex < internalState.length; wordIndex++)
+        {
+            internalState[wordIndex] ^= k1[wordIndex];
+        }
+
+        subBytes();
+        shiftRows();
+        mixColumns();
+
+        for (int wordIndex = 0; wordIndex < internalState.length; wordIndex++)
+        {
+            internalState[wordIndex] += k0[wordIndex];
+        }
+
+        subBytes();
+        shiftRows();
+        mixColumns();
+
+        System.arraycopy(internalState, 0, tempKeys, 0, wordsInBlock);
+    }
+
+    private void workingKeyExpandEven(long[] workingKey, long[] tempKey)
+    {
+        long[] initialData = new long[wordsInKey];
+        long[] tempRoundKey = new long[wordsInBlock];
+
+        int round = 0;
+
+        System.arraycopy(workingKey, 0, initialData, 0, wordsInKey);
+
+        long tmv = 0x0001000100010001L;
+
+        while (true)
+        {
+            for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+            {
+                tempRoundKey[wordIndex] = tempKey[wordIndex] + tmv;
+            }
+
+            for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+            {
+                internalState[wordIndex] = initialData[wordIndex] + tempRoundKey[wordIndex];
+            }
+
+            subBytes();
+            shiftRows();
+            mixColumns();
+
+            for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+            {
+                internalState[wordIndex] ^= tempRoundKey[wordIndex];
+            }
+
+            subBytes();
+            shiftRows();
+            mixColumns();
+
+            for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+            {
+                internalState[wordIndex] += tempRoundKey[wordIndex];
+            }
+
+            System.arraycopy(internalState, 0, roundKeys[round], 0, wordsInBlock);
+
+            if (roundsAmount == round)
+            {
+                break;
+            }
+
+            if (wordsInBlock != wordsInKey)
+            {
+                round += 2;
+                tmv <<= 1;
+
+                for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+                {
+                    tempRoundKey[wordIndex] = tempKey[wordIndex] + tmv;
+                }
+
+                for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+                {
+                    internalState[wordIndex] = initialData[wordsInBlock + wordIndex] + tempRoundKey[wordIndex];
+                }
+
+                subBytes();
+                shiftRows();
+                mixColumns();
+
+                for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+                {
+                    internalState[wordIndex] ^= tempRoundKey[wordIndex];
+                }
+
+                subBytes();
+                shiftRows();
+                mixColumns();
+
+                for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++)
+                {
+                    internalState[wordIndex] += tempRoundKey[wordIndex];
+                }
+
+                System.arraycopy(internalState, 0, roundKeys[round], 0, wordsInBlock);
+
+                if (roundsAmount == round)
+                {
+                    break;
+                }
+            }
+
+            round += 2;
+            tmv <<= 1;
+
+            long temp = initialData[0];
+            for (int i = 1; i < initialData.length; ++i)
+            {
+                initialData[i - 1] = initialData[i];
+            }
+            initialData[initialData.length - 1] = temp;
+        }
+    }
+
+    private void workingKeyExpandOdd()
+    {
+        for (int roundIndex = 1; roundIndex < roundsAmount; roundIndex += 2)
+        {
+            rotateLeft(roundKeys[roundIndex - 1], roundKeys[roundIndex]);
+        }
+    }
+
+    private void decryptBlock_128(byte[] in, int inOff, byte[] out, int outOff)
+    {
+        long c0 = Pack.littleEndianToLong(in, inOff);
+        long c1 = Pack.littleEndianToLong(in, inOff + 8);
+
+        long[] roundKey = roundKeys[roundsAmount];
+        c0 -= roundKey[0];
+        c1 -= roundKey[1];
+
+        for (int round = roundsAmount;;)
+        {
+            c0 = mixColumnInv(c0);
+            c1 = mixColumnInv(c1);
+
+            int lo0 = (int)c0, hi0 = (int)(c0 >>> 32);
+            int lo1 = (int)c1, hi1 = (int)(c1 >>> 32);
+
+            {
+                byte t0 = T0[lo0 & 0xFF];
+                byte t1 = T1[(lo0 >>> 8) & 0xFF];
+                byte t2 = T2[(lo0 >>> 16) & 0xFF];
+                byte t3 = T3[lo0 >>> 24];
+                lo0 = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+                byte t4 = T0[hi1 & 0xFF];
+                byte t5 = T1[(hi1 >>> 8) & 0xFF];
+                byte t6 = T2[(hi1 >>> 16) & 0xFF];
+                byte t7 = T3[hi1 >>> 24];
+                hi1 = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+                c0 = (lo0 & 0xFFFFFFFFL) | ((long)hi1 << 32);
+            }
+
+            {
+                byte t0 = T0[lo1 & 0xFF];
+                byte t1 = T1[(lo1 >>> 8) & 0xFF];
+                byte t2 = T2[(lo1 >>> 16) & 0xFF];
+                byte t3 = T3[lo1 >>> 24];
+                lo1 = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+                byte t4 = T0[hi0 & 0xFF];
+                byte t5 = T1[(hi0 >>> 8) & 0xFF];
+                byte t6 = T2[(hi0 >>> 16) & 0xFF];
+                byte t7 = T3[hi0 >>> 24];
+                hi0 = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+                c1 = (lo1 & 0xFFFFFFFFL) | ((long)hi0 << 32);
+            }
+
+            if (--round == 0)
+            {
+                break;
+            }
+
+            roundKey = roundKeys[round];
+            c0 ^= roundKey[0];
+            c1 ^= roundKey[1];
+        }
+
+        roundKey = roundKeys[0];
+        c0 -= roundKey[0];
+        c1 -= roundKey[1];
+
+        Pack.longToLittleEndian(c0, out, outOff);
+        Pack.longToLittleEndian(c1, out, outOff + 8);
+    }
+
+    private void encryptBlock_128(byte[] in, int inOff, byte[] out, int outOff)
+    {
+        long c0 = Pack.littleEndianToLong(in, inOff);
+        long c1 = Pack.littleEndianToLong(in, inOff + 8);
+
+        long[] roundKey = roundKeys[0];
+        c0 += roundKey[0];
+        c1 += roundKey[1];
+
+        for (int round = 0;;)
+        {
+            int lo0 = (int)c0, hi0 = (int)(c0 >>> 32);
+            int lo1 = (int)c1, hi1 = (int)(c1 >>> 32);
+
+            {
+                byte t0 = S0[lo0 & 0xFF];
+                byte t1 = S1[(lo0 >>> 8) & 0xFF];
+                byte t2 = S2[(lo0 >>> 16) & 0xFF];
+                byte t3 = S3[lo0 >>> 24];
+                lo0 = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+                byte t4 = S0[hi1 & 0xFF];
+                byte t5 = S1[(hi1 >>> 8) & 0xFF];
+                byte t6 = S2[(hi1 >>> 16) & 0xFF];
+                byte t7 = S3[hi1 >>> 24];
+                hi1 = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+                c0 = (lo0 & 0xFFFFFFFFL) | ((long)hi1 << 32);
+            }
+
+            {
+                byte t0 = S0[lo1 & 0xFF];
+                byte t1 = S1[(lo1 >>> 8) & 0xFF];
+                byte t2 = S2[(lo1 >>> 16) & 0xFF];
+                byte t3 = S3[lo1 >>> 24];
+                lo1 = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+                byte t4 = S0[hi0 & 0xFF];
+                byte t5 = S1[(hi0 >>> 8) & 0xFF];
+                byte t6 = S2[(hi0 >>> 16) & 0xFF];
+                byte t7 = S3[hi0 >>> 24];
+                hi0 = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+                c1 = (lo1 & 0xFFFFFFFFL) | ((long)hi0 << 32);
+            }
+
+            c0 = mixColumn(c0);
+            c1 = mixColumn(c1);
+
+            if (++round == roundsAmount)
+            {
+                break;
+            }
+
+            roundKey = roundKeys[round];
+            c0 ^= roundKey[0];
+            c1 ^= roundKey[1];
+        }
+
+        roundKey = roundKeys[roundsAmount];
+        c0 += roundKey[0];
+        c1 += roundKey[1];
+
+        Pack.longToLittleEndian(c0, out, outOff);
+        Pack.longToLittleEndian(c1, out, outOff + 8);
+    }
+
+    private void subBytes()
+    {
+        for (int i = 0; i < wordsInBlock; i++)
+        {
+            long u = internalState[i];
+            int lo = (int)u, hi = (int)(u >>> 32);
+            byte t0 = S0[lo & 0xFF];
+            byte t1 = S1[(lo >>> 8) & 0xFF];
+            byte t2 = S2[(lo >>> 16) & 0xFF];
+            byte t3 = S3[lo >>> 24];
+            lo = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+            byte t4 = S0[hi & 0xFF];
+            byte t5 = S1[(hi >>> 8) & 0xFF];
+            byte t6 = S2[(hi >>> 16) & 0xFF];
+            byte t7 = S3[hi >>> 24];
+            hi = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+            internalState[i] = (lo & 0xFFFFFFFFL) | ((long)hi << 32);
+        }
+    }
+
+    private void invSubBytes()
+    {
+        for (int i = 0; i < wordsInBlock; i++)
+        {
+            long u = internalState[i];
+            int lo = (int)u, hi = (int)(u >>> 32);
+            byte t0 = T0[lo & 0xFF];
+            byte t1 = T1[(lo >>> 8) & 0xFF];
+            byte t2 = T2[(lo >>> 16) & 0xFF];
+            byte t3 = T3[lo >>> 24];
+            lo = (t0 & 0xFF) | ((t1 & 0xFF) << 8) | ((t2 & 0xFF) << 16) | ((int)t3 << 24);
+            byte t4 = T0[hi & 0xFF];
+            byte t5 = T1[(hi >>> 8) & 0xFF];
+            byte t6 = T2[(hi >>> 16) & 0xFF];
+            byte t7 = T3[hi >>> 24];
+            hi = (t4 & 0xFF) | ((t5 & 0xFF) << 8) | ((t6 & 0xFF) << 16) | ((int)t7 << 24);
+            internalState[i] = (lo & 0xFFFFFFFFL) | ((long)hi << 32);
+        }
+    }
+
+    private void shiftRows()
+    {
+        switch (wordsInBlock)
+        {
+        case 2:
+        {
+            long c0 = internalState[0], c1 = internalState[1];
+            long d;
+
+            d = (c0 ^ c1) & 0xFFFFFFFF00000000L; c0 ^= d; c1 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            break;
+        }
+        case 4:
+        {
+            long c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3];
+            long d;
+
+            d = (c0 ^ c2) & 0xFFFFFFFF00000000L; c0 ^= d; c2 ^= d;
+            d = (c1 ^ c3) & 0x0000FFFFFFFF0000L; c1 ^= d; c3 ^= d;
+
+            d = (c0 ^ c1) & 0xFFFF0000FFFF0000L; c0 ^= d; c1 ^= d;
+            d = (c2 ^ c3) & 0xFFFF0000FFFF0000L; c2 ^= d; c3 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            internalState[2] = c2;
+            internalState[3] = c3;
+            break;
+        }
+        case 8:
+        {
+            long c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3];
+            long c4 = internalState[4], c5 = internalState[5], c6 = internalState[6], c7 = internalState[7];
+            long d;
+
+            d = (c0 ^ c4) & 0xFFFFFFFF00000000L; c0 ^= d; c4 ^= d;
+            d = (c1 ^ c5) & 0x00FFFFFFFF000000L; c1 ^= d; c5 ^= d;
+            d = (c2 ^ c6) & 0x0000FFFFFFFF0000L; c2 ^= d; c6 ^= d;
+            d = (c3 ^ c7) & 0x000000FFFFFFFF00L; c3 ^= d; c7 ^= d;
+
+            d = (c0 ^ c2) & 0xFFFF0000FFFF0000L; c0 ^= d; c2 ^= d;
+            d = (c1 ^ c3) & 0x00FFFF0000FFFF00L; c1 ^= d; c3 ^= d;
+            d = (c4 ^ c6) & 0xFFFF0000FFFF0000L; c4 ^= d; c6 ^= d;
+            d = (c5 ^ c7) & 0x00FFFF0000FFFF00L; c5 ^= d; c7 ^= d;
+
+            d = (c0 ^ c1) & 0xFF00FF00FF00FF00L; c0 ^= d; c1 ^= d;
+            d = (c2 ^ c3) & 0xFF00FF00FF00FF00L; c2 ^= d; c3 ^= d;
+            d = (c4 ^ c5) & 0xFF00FF00FF00FF00L; c4 ^= d; c5 ^= d;
+            d = (c6 ^ c7) & 0xFF00FF00FF00FF00L; c6 ^= d; c7 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            internalState[2] = c2;
+            internalState[3] = c3;
+            internalState[4] = c4;
+            internalState[5] = c5;
+            internalState[6] = c6;
+            internalState[7] = c7;
+            break;
+        }
+        default:
+        {
+            throw new IllegalStateException("unsupported block length: only 128/256/512 are allowed");
+        }
+        }
+    }
+
+    private void invShiftRows()
+    {
+        switch (wordsInBlock)
+        {
+        case 2:
+        {
+            long c0 = internalState[0], c1 = internalState[1];
+            long d;
+
+            d = (c0 ^ c1) & 0xFFFFFFFF00000000L; c0 ^= d; c1 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            break;
+        }
+        case 4:
+        {
+            long c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3];
+            long d;
+
+            d = (c0 ^ c1) & 0xFFFF0000FFFF0000L; c0 ^= d; c1 ^= d;
+            d = (c2 ^ c3) & 0xFFFF0000FFFF0000L; c2 ^= d; c3 ^= d;
+
+            d = (c0 ^ c2) & 0xFFFFFFFF00000000L; c0 ^= d; c2 ^= d;
+            d = (c1 ^ c3) & 0x0000FFFFFFFF0000L; c1 ^= d; c3 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            internalState[2] = c2;
+            internalState[3] = c3;
+            break;
+        }
+        case 8:
+        {
+            long c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3];
+            long c4 = internalState[4], c5 = internalState[5], c6 = internalState[6], c7 = internalState[7];
+            long d;
+
+            d = (c0 ^ c1) & 0xFF00FF00FF00FF00L; c0 ^= d; c1 ^= d;
+            d = (c2 ^ c3) & 0xFF00FF00FF00FF00L; c2 ^= d; c3 ^= d;
+            d = (c4 ^ c5) & 0xFF00FF00FF00FF00L; c4 ^= d; c5 ^= d;
+            d = (c6 ^ c7) & 0xFF00FF00FF00FF00L; c6 ^= d; c7 ^= d;
+
+            d = (c0 ^ c2) & 0xFFFF0000FFFF0000L; c0 ^= d; c2 ^= d;
+            d = (c1 ^ c3) & 0x00FFFF0000FFFF00L; c1 ^= d; c3 ^= d;
+            d = (c4 ^ c6) & 0xFFFF0000FFFF0000L; c4 ^= d; c6 ^= d;
+            d = (c5 ^ c7) & 0x00FFFF0000FFFF00L; c5 ^= d; c7 ^= d;
+
+            d = (c0 ^ c4) & 0xFFFFFFFF00000000L; c0 ^= d; c4 ^= d;
+            d = (c1 ^ c5) & 0x00FFFFFFFF000000L; c1 ^= d; c5 ^= d;
+            d = (c2 ^ c6) & 0x0000FFFFFFFF0000L; c2 ^= d; c6 ^= d;
+            d = (c3 ^ c7) & 0x000000FFFFFFFF00L; c3 ^= d; c7 ^= d;
+
+            internalState[0] = c0;
+            internalState[1] = c1;
+            internalState[2] = c2;
+            internalState[3] = c3;
+            internalState[4] = c4;
+            internalState[5] = c5;
+            internalState[6] = c6;
+            internalState[7] = c7;
+            break;
+        }
+        default:
+        {
+            throw new IllegalStateException("unsupported block length: only 128/256/512 are allowed");
+        }
+        }
+    }
+
+    private static long mixColumn(long c)
+    {
+//        // Calculate column multiplied by powers of 'x'
+//        long x0 = c;
+//        long x1 = mulX(x0);
+//        long x2 = mulX(x1);
+//        long x3 = mulX(x2);
+//
+//        // Calculate products with circulant matrix from (0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04)
+//        long m0 = x0;
+//        long m1 = x0;
+//        long m2 = x0 ^ x2;
+//        long m3 = x0;
+//        long m4 = x3;
+//        long m5 = x1 ^ x2;
+//        long m6 = x0 ^ x1 ^ x2;
+//        long m7 = x2;
+//
+//        // Assemble the rotated products
+//        return m0
+//            ^ rotate(8, m1)
+//            ^ rotate(16, m2)
+//            ^ rotate(24, m3)
+//            ^ rotate(32, m4)
+//            ^ rotate(40, m5)
+//            ^ rotate(48, m6)
+//            ^ rotate(56, m7);
+
+        long x1 = mulX(c);
+        long u, v;
+
+        u  = rotate(8, c) ^ c;
+        u ^= rotate(16, u);
+        u ^= rotate(48, c);
+
+        v  = mulX2(u ^ c ^ x1);
+
+        return u ^ rotate(32, v) ^ rotate(40, x1) ^ rotate(48, x1);
+    }
+
+    private void mixColumns()
+    {
+        for (int col = 0; col < wordsInBlock; ++col)
+        {
+            internalState[col] = mixColumn(internalState[col]);
+        }
+    }
+
+    private static long mixColumnInv(long c)
+    {
+/*
+        // Calculate column multiplied by powers of 'x'
+        long x0 = c;
+        long x1 = mulX(x0);
+        long x2 = mulX(x1);
+        long x3 = mulX(x2);
+        long x4 = mulX(x3);
+        long x5 = mulX(x4);
+        long x6 = mulX(x5);
+        long x7 = mulX(x6);
+
+        // Calculate products with circulant matrix from (0xAD,0x95,0x76,0xA8,0x2F,0x49,0xD7,0xCA)
+//        long m0 = x0 ^ x2 ^ x3 ^ x5 ^ x7;
+//        long m1 = x0 ^ x2 ^ x4 ^ x7;
+//        long m2 = x1 ^ x2 ^ x4 ^ x5 ^ x6;
+//        long m3 = x3 ^ x5 ^ x7;
+//        long m4 = x0 ^ x1 ^ x2 ^ x3 ^ x5;
+//        long m5 = x0 ^ x3 ^ x6;
+//        long m6 = x0 ^ x1 ^ x2 ^ x4 ^ x6 ^ x7;
+//        long m7 = x1 ^ x3 ^ x6 ^ x7;
+
+        long m5 = x0 ^ x3 ^ x6;
+        x0 ^= x2;
+        long m3 = x3 ^ x5 ^ x7;
+        long m0 = m3 ^ x0;
+        long m6 = x0 ^ x4;
+        long m1 = m6 ^ x7;
+        x5 ^= x1;
+        x7 ^= x1 ^ x6;
+        long m2 = x2 ^ x4 ^ x5 ^ x6;
+        long m4 = x0 ^ x3 ^ x5;
+        m6 ^= x7;
+        long m7 = x3 ^ x7;
+
+        // Assemble the rotated products
+        return m0
+            ^ rotate(8, m1)
+            ^ rotate(16, m2)
+            ^ rotate(24, m3)
+            ^ rotate(32, m4)
+            ^ rotate(40, m5)
+            ^ rotate(48, m6)
+            ^ rotate(56, m7);
+*/
+        
+        long u0 = c;
+        u0 ^= rotate( 8, u0);
+        u0 ^= rotate(32, u0);
+        u0 ^= rotate(48, c);
+
+        long t = u0 ^ c;
+
+        long c48 = rotate(48, c);
+        long c56 = rotate(56, c);
+
+        long u7 = t ^ c56;
+        long u6 = rotate(56, t);
+        u6 ^= mulX(u7);
+        long u5 = rotate(16, t) ^ c;
+        u5 ^= rotate(40, mulX(u6) ^ c);
+        long u4 = t ^ c48;
+        u4 ^= mulX(u5);
+        long u3 = rotate(16, u0);
+        u3 ^= mulX(u4);
+        long u2 = t ^ rotate(24, c) ^ c48 ^ c56;
+        u2 ^= mulX(u3);
+        long u1 = rotate(32, t) ^ c ^ c56;
+        u1 ^= mulX(u2);
+        u0 ^= mulX(rotate(40, u1));
+
+        return u0;
+    }
+
+    private void mixColumnsInv()
+    {
+        for (int col = 0; col < wordsInBlock; ++col)
+        {
+            internalState[col] = mixColumnInv(internalState[col]);
+        }
+    }
+
+    private static long mulX(long n)
+    {
+        return ((n & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((n & 0x8080808080808080L) >>> 7) * 0x1DL);
+    }
+
+    private static long mulX2(long n)
+    {
+        return ((n & 0x3F3F3F3F3F3F3F3FL) << 2) ^ (((n & 0x8080808080808080L) >>> 6) * 0x1DL) ^ (((n & 0x4040404040404040L) >>> 6) * 0x1DL);
+    }
+
+//    private static long mulX4(long n)
+//    {
+//        long u = n & 0xF0F0F0F0F0F0F0F0L;
+//        return ((n & 0x0F0F0F0F0F0F0F0FL) << 4) ^ u ^ (u >>> 1) ^ (u >>> 2) ^ (u >>> 4);
+//    }
+
+    /*
+     * Pair-wise modular multiplication of 8 byte-pairs.
+     * 
+     * REDUCTION_POLYNOMIAL is x^8 + x^4 + x^3 + x^2 + 1
+     */  
+//    private static long multiplyGFx8(long u, long v, int vMaxDegree)
+//    {
+//        long r = u & ((v & 0x0101010101010101L) * 0xFFL);
+//        for (int i = 1; i <= vMaxDegree; ++i)
+//        {
+//            u = ((u & 0x7F7F7F7F7F7F7F7FL) << 1) ^ (((u >>> 7) & 0x0101010101010101L) * 0x1DL);
+//            v >>>= 1;
+//
+//            r ^= u & ((v & 0x0101010101010101L) * 0xFFL);
+//        }
+//
+//        return r;
+//    }
+
+//    private static long multiplyMDS(long u)
+//    {
+//        long r = 0, s = 0, t = (u >>> 8);
+//        r ^= u & 0x0000001F00000000L; r <<= 1;
+//        s ^= t & 0x00000000E0000000L; s <<= 1;
+//        r ^= u & 0x3F3F3F00003F0000L; r <<= 1;
+//        s ^= t & 0x00C0C0C00000C000L; s <<= 1;
+//        r ^= u & 0x007F7F0000000000L; r <<= 1;
+//        s ^= t & 0x0000808000000000L; s <<= 1;
+//        r ^= u & 0x00FF0000FFFFFFFFL;
+//        r ^= s ^ (s << 2) ^ (s << 3) ^ (s << 4);
+//        return r;
+//    }
+
+    private static long rotate(int n, long x)
+    {
+        return (x >>> n) | (x << -n);
+    }
+
+    private void rotateLeft(long[] x, long[] z)
+    {
+        switch (wordsInBlock)
+        {
+        case 2:
+        {
+            long x0 = x[0], x1 = x[1];
+            z[0] = (x0 >>> 56) | (x1 << 8);
+            z[1] = (x1 >>> 56) | (x0 << 8);
+            break;
+        }
+        case 4:
+        {
+            long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+            z[0] = (x1 >>> 24) | (x2 << 40);
+            z[1] = (x2 >>> 24) | (x3 << 40);
+            z[2] = (x3 >>> 24) | (x0 << 40);
+            z[3] = (x0 >>> 24) | (x1 << 40);
+            break;
+        }
+        case 8:
+        {
+            long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+            long x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+            z[0] = (x2 >>> 24) | (x3 << 40);
+            z[1] = (x3 >>> 24) | (x4 << 40);
+            z[2] = (x4 >>> 24) | (x5 << 40);
+            z[3] = (x5 >>> 24) | (x6 << 40);
+            z[4] = (x6 >>> 24) | (x7 << 40);
+            z[5] = (x7 >>> 24) | (x0 << 40);
+            z[6] = (x0 >>> 24) | (x1 << 40);
+            z[7] = (x1 >>> 24) | (x2 << 40);
+            break;
+        }
+        default:
+        {
+            throw new IllegalStateException("unsupported block length: only 128/256/512 are allowed");
+        }
+        }
+    }
+
+//    private static final long mdsMatrix = 0x0407060801050101L;
+//    private static final long mdsInvMatrix = 0xCAD7492FA87695ADL;
+
+    private static final byte[] S0 = new byte[]{ (byte)0xa8, (byte)0x43, (byte)0x5f, (byte)0x06, (byte)0x6b, (byte)0x75,
+        (byte)0x6c, (byte)0x59, (byte)0x71, (byte)0xdf, (byte)0x87, (byte)0x95, (byte)0x17, (byte)0xf0, (byte)0xd8,
+        (byte)0x09, (byte)0x6d, (byte)0xf3, (byte)0x1d, (byte)0xcb, (byte)0xc9, (byte)0x4d, (byte)0x2c, (byte)0xaf,
+        (byte)0x79, (byte)0xe0, (byte)0x97, (byte)0xfd, (byte)0x6f, (byte)0x4b, (byte)0x45, (byte)0x39, (byte)0x3e,
+        (byte)0xdd, (byte)0xa3, (byte)0x4f, (byte)0xb4, (byte)0xb6, (byte)0x9a, (byte)0x0e, (byte)0x1f, (byte)0xbf,
+        (byte)0x15, (byte)0xe1, (byte)0x49, (byte)0xd2, (byte)0x93, (byte)0xc6, (byte)0x92, (byte)0x72, (byte)0x9e,
+        (byte)0x61, (byte)0xd1, (byte)0x63, (byte)0xfa, (byte)0xee, (byte)0xf4, (byte)0x19, (byte)0xd5, (byte)0xad,
+        (byte)0x58, (byte)0xa4, (byte)0xbb, (byte)0xa1, (byte)0xdc, (byte)0xf2, (byte)0x83, (byte)0x37, (byte)0x42,
+        (byte)0xe4, (byte)0x7a, (byte)0x32, (byte)0x9c, (byte)0xcc, (byte)0xab, (byte)0x4a, (byte)0x8f, (byte)0x6e,
+        (byte)0x04, (byte)0x27, (byte)0x2e, (byte)0xe7, (byte)0xe2, (byte)0x5a, (byte)0x96, (byte)0x16, (byte)0x23,
+        (byte)0x2b, (byte)0xc2, (byte)0x65, (byte)0x66, (byte)0x0f, (byte)0xbc, (byte)0xa9, (byte)0x47, (byte)0x41,
+        (byte)0x34, (byte)0x48, (byte)0xfc, (byte)0xb7, (byte)0x6a, (byte)0x88, (byte)0xa5, (byte)0x53, (byte)0x86,
+        (byte)0xf9, (byte)0x5b, (byte)0xdb, (byte)0x38, (byte)0x7b, (byte)0xc3, (byte)0x1e, (byte)0x22, (byte)0x33,
+        (byte)0x24, (byte)0x28, (byte)0x36, (byte)0xc7, (byte)0xb2, (byte)0x3b, (byte)0x8e, (byte)0x77, (byte)0xba,
+        (byte)0xf5, (byte)0x14, (byte)0x9f, (byte)0x08, (byte)0x55, (byte)0x9b, (byte)0x4c, (byte)0xfe, (byte)0x60,
+        (byte)0x5c, (byte)0xda, (byte)0x18, (byte)0x46, (byte)0xcd, (byte)0x7d, (byte)0x21, (byte)0xb0, (byte)0x3f,
+        (byte)0x1b, (byte)0x89, (byte)0xff, (byte)0xeb, (byte)0x84, (byte)0x69, (byte)0x3a, (byte)0x9d, (byte)0xd7,
+        (byte)0xd3, (byte)0x70, (byte)0x67, (byte)0x40, (byte)0xb5, (byte)0xde, (byte)0x5d, (byte)0x30, (byte)0x91,
+        (byte)0xb1, (byte)0x78, (byte)0x11, (byte)0x01, (byte)0xe5, (byte)0x00, (byte)0x68, (byte)0x98, (byte)0xa0,
+        (byte)0xc5, (byte)0x02, (byte)0xa6, (byte)0x74, (byte)0x2d, (byte)0x0b, (byte)0xa2, (byte)0x76, (byte)0xb3,
+        (byte)0xbe, (byte)0xce, (byte)0xbd, (byte)0xae, (byte)0xe9, (byte)0x8a, (byte)0x31, (byte)0x1c, (byte)0xec,
+        (byte)0xf1, (byte)0x99, (byte)0x94, (byte)0xaa, (byte)0xf6, (byte)0x26, (byte)0x2f, (byte)0xef, (byte)0xe8,
+        (byte)0x8c, (byte)0x35, (byte)0x03, (byte)0xd4, (byte)0x7f, (byte)0xfb, (byte)0x05, (byte)0xc1, (byte)0x5e,
+        (byte)0x90, (byte)0x20, (byte)0x3d, (byte)0x82, (byte)0xf7, (byte)0xea, (byte)0x0a, (byte)0x0d, (byte)0x7e,
+        (byte)0xf8, (byte)0x50, (byte)0x1a, (byte)0xc4, (byte)0x07, (byte)0x57, (byte)0xb8, (byte)0x3c, (byte)0x62,
+        (byte)0xe3, (byte)0xc8, (byte)0xac, (byte)0x52, (byte)0x64, (byte)0x10, (byte)0xd0, (byte)0xd9, (byte)0x13,
+        (byte)0x0c, (byte)0x12, (byte)0x29, (byte)0x51, (byte)0xb9, (byte)0xcf, (byte)0xd6, (byte)0x73, (byte)0x8d,
+        (byte)0x81, (byte)0x54, (byte)0xc0, (byte)0xed, (byte)0x4e, (byte)0x44, (byte)0xa7, (byte)0x2a, (byte)0x85,
+        (byte)0x25, (byte)0xe6, (byte)0xca, (byte)0x7c, (byte)0x8b, (byte)0x56, (byte)0x80 };
+
+    private static final byte[] S1 = new byte[]{ (byte)0xce, (byte)0xbb, (byte)0xeb, (byte)0x92, (byte)0xea, (byte)0xcb,
+        (byte)0x13, (byte)0xc1, (byte)0xe9, (byte)0x3a, (byte)0xd6, (byte)0xb2, (byte)0xd2, (byte)0x90, (byte)0x17,
+        (byte)0xf8, (byte)0x42, (byte)0x15, (byte)0x56, (byte)0xb4, (byte)0x65, (byte)0x1c, (byte)0x88, (byte)0x43,
+        (byte)0xc5, (byte)0x5c, (byte)0x36, (byte)0xba, (byte)0xf5, (byte)0x57, (byte)0x67, (byte)0x8d, (byte)0x31,
+        (byte)0xf6, (byte)0x64, (byte)0x58, (byte)0x9e, (byte)0xf4, (byte)0x22, (byte)0xaa, (byte)0x75, (byte)0x0f,
+        (byte)0x02, (byte)0xb1, (byte)0xdf, (byte)0x6d, (byte)0x73, (byte)0x4d, (byte)0x7c, (byte)0x26, (byte)0x2e,
+        (byte)0xf7, (byte)0x08, (byte)0x5d, (byte)0x44, (byte)0x3e, (byte)0x9f, (byte)0x14, (byte)0xc8, (byte)0xae,
+        (byte)0x54, (byte)0x10, (byte)0xd8, (byte)0xbc, (byte)0x1a, (byte)0x6b, (byte)0x69, (byte)0xf3, (byte)0xbd,
+        (byte)0x33, (byte)0xab, (byte)0xfa, (byte)0xd1, (byte)0x9b, (byte)0x68, (byte)0x4e, (byte)0x16, (byte)0x95,
+        (byte)0x91, (byte)0xee, (byte)0x4c, (byte)0x63, (byte)0x8e, (byte)0x5b, (byte)0xcc, (byte)0x3c, (byte)0x19,
+        (byte)0xa1, (byte)0x81, (byte)0x49, (byte)0x7b, (byte)0xd9, (byte)0x6f, (byte)0x37, (byte)0x60, (byte)0xca,
+        (byte)0xe7, (byte)0x2b, (byte)0x48, (byte)0xfd, (byte)0x96, (byte)0x45, (byte)0xfc, (byte)0x41, (byte)0x12,
+        (byte)0x0d, (byte)0x79, (byte)0xe5, (byte)0x89, (byte)0x8c, (byte)0xe3, (byte)0x20, (byte)0x30, (byte)0xdc,
+        (byte)0xb7, (byte)0x6c, (byte)0x4a, (byte)0xb5, (byte)0x3f, (byte)0x97, (byte)0xd4, (byte)0x62, (byte)0x2d,
+        (byte)0x06, (byte)0xa4, (byte)0xa5, (byte)0x83, (byte)0x5f, (byte)0x2a, (byte)0xda, (byte)0xc9, (byte)0x00,
+        (byte)0x7e, (byte)0xa2, (byte)0x55, (byte)0xbf, (byte)0x11, (byte)0xd5, (byte)0x9c, (byte)0xcf, (byte)0x0e,
+        (byte)0x0a, (byte)0x3d, (byte)0x51, (byte)0x7d, (byte)0x93, (byte)0x1b, (byte)0xfe, (byte)0xc4, (byte)0x47,
+        (byte)0x09, (byte)0x86, (byte)0x0b, (byte)0x8f, (byte)0x9d, (byte)0x6a, (byte)0x07, (byte)0xb9, (byte)0xb0,
+        (byte)0x98, (byte)0x18, (byte)0x32, (byte)0x71, (byte)0x4b, (byte)0xef, (byte)0x3b, (byte)0x70, (byte)0xa0,
+        (byte)0xe4, (byte)0x40, (byte)0xff, (byte)0xc3, (byte)0xa9, (byte)0xe6, (byte)0x78, (byte)0xf9, (byte)0x8b,
+        (byte)0x46, (byte)0x80, (byte)0x1e, (byte)0x38, (byte)0xe1, (byte)0xb8, (byte)0xa8, (byte)0xe0, (byte)0x0c,
+        (byte)0x23, (byte)0x76, (byte)0x1d, (byte)0x25, (byte)0x24, (byte)0x05, (byte)0xf1, (byte)0x6e, (byte)0x94,
+        (byte)0x28, (byte)0x9a, (byte)0x84, (byte)0xe8, (byte)0xa3, (byte)0x4f, (byte)0x77, (byte)0xd3, (byte)0x85,
+        (byte)0xe2, (byte)0x52, (byte)0xf2, (byte)0x82, (byte)0x50, (byte)0x7a, (byte)0x2f, (byte)0x74, (byte)0x53,
+        (byte)0xb3, (byte)0x61, (byte)0xaf, (byte)0x39, (byte)0x35, (byte)0xde, (byte)0xcd, (byte)0x1f, (byte)0x99,
+        (byte)0xac, (byte)0xad, (byte)0x72, (byte)0x2c, (byte)0xdd, (byte)0xd0, (byte)0x87, (byte)0xbe, (byte)0x5e,
+        (byte)0xa6, (byte)0xec, (byte)0x04, (byte)0xc6, (byte)0x03, (byte)0x34, (byte)0xfb, (byte)0xdb, (byte)0x59,
+        (byte)0xb6, (byte)0xc2, (byte)0x01, (byte)0xf0, (byte)0x5a, (byte)0xed, (byte)0xa7, (byte)0x66, (byte)0x21,
+        (byte)0x7f, (byte)0x8a, (byte)0x27, (byte)0xc7, (byte)0xc0, (byte)0x29, (byte)0xd7 };
+
+    private static final byte[] S2 = new byte[]{ (byte)0x93, (byte)0xd9, (byte)0x9a, (byte)0xb5, (byte)0x98, (byte)0x22,
+        (byte)0x45, (byte)0xfc, (byte)0xba, (byte)0x6a, (byte)0xdf, (byte)0x02, (byte)0x9f, (byte)0xdc, (byte)0x51,
+        (byte)0x59, (byte)0x4a, (byte)0x17, (byte)0x2b, (byte)0xc2, (byte)0x94, (byte)0xf4, (byte)0xbb, (byte)0xa3,
+        (byte)0x62, (byte)0xe4, (byte)0x71, (byte)0xd4, (byte)0xcd, (byte)0x70, (byte)0x16, (byte)0xe1, (byte)0x49,
+        (byte)0x3c, (byte)0xc0, (byte)0xd8, (byte)0x5c, (byte)0x9b, (byte)0xad, (byte)0x85, (byte)0x53, (byte)0xa1,
+        (byte)0x7a, (byte)0xc8, (byte)0x2d, (byte)0xe0, (byte)0xd1, (byte)0x72, (byte)0xa6, (byte)0x2c, (byte)0xc4,
+        (byte)0xe3, (byte)0x76, (byte)0x78, (byte)0xb7, (byte)0xb4, (byte)0x09, (byte)0x3b, (byte)0x0e, (byte)0x41,
+        (byte)0x4c, (byte)0xde, (byte)0xb2, (byte)0x90, (byte)0x25, (byte)0xa5, (byte)0xd7, (byte)0x03, (byte)0x11,
+        (byte)0x00, (byte)0xc3, (byte)0x2e, (byte)0x92, (byte)0xef, (byte)0x4e, (byte)0x12, (byte)0x9d, (byte)0x7d,
+        (byte)0xcb, (byte)0x35, (byte)0x10, (byte)0xd5, (byte)0x4f, (byte)0x9e, (byte)0x4d, (byte)0xa9, (byte)0x55,
+        (byte)0xc6, (byte)0xd0, (byte)0x7b, (byte)0x18, (byte)0x97, (byte)0xd3, (byte)0x36, (byte)0xe6, (byte)0x48,
+        (byte)0x56, (byte)0x81, (byte)0x8f, (byte)0x77, (byte)0xcc, (byte)0x9c, (byte)0xb9, (byte)0xe2, (byte)0xac,
+        (byte)0xb8, (byte)0x2f, (byte)0x15, (byte)0xa4, (byte)0x7c, (byte)0xda, (byte)0x38, (byte)0x1e, (byte)0x0b,
+        (byte)0x05, (byte)0xd6, (byte)0x14, (byte)0x6e, (byte)0x6c, (byte)0x7e, (byte)0x66, (byte)0xfd, (byte)0xb1,
+        (byte)0xe5, (byte)0x60, (byte)0xaf, (byte)0x5e, (byte)0x33, (byte)0x87, (byte)0xc9, (byte)0xf0, (byte)0x5d,
+        (byte)0x6d, (byte)0x3f, (byte)0x88, (byte)0x8d, (byte)0xc7, (byte)0xf7, (byte)0x1d, (byte)0xe9, (byte)0xec,
+        (byte)0xed, (byte)0x80, (byte)0x29, (byte)0x27, (byte)0xcf, (byte)0x99, (byte)0xa8, (byte)0x50, (byte)0x0f,
+        (byte)0x37, (byte)0x24, (byte)0x28, (byte)0x30, (byte)0x95, (byte)0xd2, (byte)0x3e, (byte)0x5b, (byte)0x40,
+        (byte)0x83, (byte)0xb3, (byte)0x69, (byte)0x57, (byte)0x1f, (byte)0x07, (byte)0x1c, (byte)0x8a, (byte)0xbc,
+        (byte)0x20, (byte)0xeb, (byte)0xce, (byte)0x8e, (byte)0xab, (byte)0xee, (byte)0x31, (byte)0xa2, (byte)0x73,
+        (byte)0xf9, (byte)0xca, (byte)0x3a, (byte)0x1a, (byte)0xfb, (byte)0x0d, (byte)0xc1, (byte)0xfe, (byte)0xfa,
+        (byte)0xf2, (byte)0x6f, (byte)0xbd, (byte)0x96, (byte)0xdd, (byte)0x43, (byte)0x52, (byte)0xb6, (byte)0x08,
+        (byte)0xf3, (byte)0xae, (byte)0xbe, (byte)0x19, (byte)0x89, (byte)0x32, (byte)0x26, (byte)0xb0, (byte)0xea,
+        (byte)0x4b, (byte)0x64, (byte)0x84, (byte)0x82, (byte)0x6b, (byte)0xf5, (byte)0x79, (byte)0xbf, (byte)0x01,
+        (byte)0x5f, (byte)0x75, (byte)0x63, (byte)0x1b, (byte)0x23, (byte)0x3d, (byte)0x68, (byte)0x2a, (byte)0x65,
+        (byte)0xe8, (byte)0x91, (byte)0xf6, (byte)0xff, (byte)0x13, (byte)0x58, (byte)0xf1, (byte)0x47, (byte)0x0a,
+        (byte)0x7f, (byte)0xc5, (byte)0xa7, (byte)0xe7, (byte)0x61, (byte)0x5a, (byte)0x06, (byte)0x46, (byte)0x44,
+        (byte)0x42, (byte)0x04, (byte)0xa0, (byte)0xdb, (byte)0x39, (byte)0x86, (byte)0x54, (byte)0xaa, (byte)0x8c,
+        (byte)0x34, (byte)0x21, (byte)0x8b, (byte)0xf8, (byte)0x0c, (byte)0x74, (byte)0x67 };
+
+    private static final byte[] S3 = new byte[]{ (byte)0x68, (byte)0x8d, (byte)0xca, (byte)0x4d, (byte)0x73, (byte)0x4b,
+        (byte)0x4e, (byte)0x2a, (byte)0xd4, (byte)0x52, (byte)0x26, (byte)0xb3, (byte)0x54, (byte)0x1e, (byte)0x19,
+        (byte)0x1f, (byte)0x22, (byte)0x03, (byte)0x46, (byte)0x3d, (byte)0x2d, (byte)0x4a, (byte)0x53, (byte)0x83,
+        (byte)0x13, (byte)0x8a, (byte)0xb7, (byte)0xd5, (byte)0x25, (byte)0x79, (byte)0xf5, (byte)0xbd, (byte)0x58,
+        (byte)0x2f, (byte)0x0d, (byte)0x02, (byte)0xed, (byte)0x51, (byte)0x9e, (byte)0x11, (byte)0xf2, (byte)0x3e,
+        (byte)0x55, (byte)0x5e, (byte)0xd1, (byte)0x16, (byte)0x3c, (byte)0x66, (byte)0x70, (byte)0x5d, (byte)0xf3,
+        (byte)0x45, (byte)0x40, (byte)0xcc, (byte)0xe8, (byte)0x94, (byte)0x56, (byte)0x08, (byte)0xce, (byte)0x1a,
+        (byte)0x3a, (byte)0xd2, (byte)0xe1, (byte)0xdf, (byte)0xb5, (byte)0x38, (byte)0x6e, (byte)0x0e, (byte)0xe5,
+        (byte)0xf4, (byte)0xf9, (byte)0x86, (byte)0xe9, (byte)0x4f, (byte)0xd6, (byte)0x85, (byte)0x23, (byte)0xcf,
+        (byte)0x32, (byte)0x99, (byte)0x31, (byte)0x14, (byte)0xae, (byte)0xee, (byte)0xc8, (byte)0x48, (byte)0xd3,
+        (byte)0x30, (byte)0xa1, (byte)0x92, (byte)0x41, (byte)0xb1, (byte)0x18, (byte)0xc4, (byte)0x2c, (byte)0x71,
+        (byte)0x72, (byte)0x44, (byte)0x15, (byte)0xfd, (byte)0x37, (byte)0xbe, (byte)0x5f, (byte)0xaa, (byte)0x9b,
+        (byte)0x88, (byte)0xd8, (byte)0xab, (byte)0x89, (byte)0x9c, (byte)0xfa, (byte)0x60, (byte)0xea, (byte)0xbc,
+        (byte)0x62, (byte)0x0c, (byte)0x24, (byte)0xa6, (byte)0xa8, (byte)0xec, (byte)0x67, (byte)0x20, (byte)0xdb,
+        (byte)0x7c, (byte)0x28, (byte)0xdd, (byte)0xac, (byte)0x5b, (byte)0x34, (byte)0x7e, (byte)0x10, (byte)0xf1,
+        (byte)0x7b, (byte)0x8f, (byte)0x63, (byte)0xa0, (byte)0x05, (byte)0x9a, (byte)0x43, (byte)0x77, (byte)0x21,
+        (byte)0xbf, (byte)0x27, (byte)0x09, (byte)0xc3, (byte)0x9f, (byte)0xb6, (byte)0xd7, (byte)0x29, (byte)0xc2,
+        (byte)0xeb, (byte)0xc0, (byte)0xa4, (byte)0x8b, (byte)0x8c, (byte)0x1d, (byte)0xfb, (byte)0xff, (byte)0xc1,
+        (byte)0xb2, (byte)0x97, (byte)0x2e, (byte)0xf8, (byte)0x65, (byte)0xf6, (byte)0x75, (byte)0x07, (byte)0x04,
+        (byte)0x49, (byte)0x33, (byte)0xe4, (byte)0xd9, (byte)0xb9, (byte)0xd0, (byte)0x42, (byte)0xc7, (byte)0x6c,
+        (byte)0x90, (byte)0x00, (byte)0x8e, (byte)0x6f, (byte)0x50, (byte)0x01, (byte)0xc5, (byte)0xda, (byte)0x47,
+        (byte)0x3f, (byte)0xcd, (byte)0x69, (byte)0xa2, (byte)0xe2, (byte)0x7a, (byte)0xa7, (byte)0xc6, (byte)0x93,
+        (byte)0x0f, (byte)0x0a, (byte)0x06, (byte)0xe6, (byte)0x2b, (byte)0x96, (byte)0xa3, (byte)0x1c, (byte)0xaf,
+        (byte)0x6a, (byte)0x12, (byte)0x84, (byte)0x39, (byte)0xe7, (byte)0xb0, (byte)0x82, (byte)0xf7, (byte)0xfe,
+        (byte)0x9d, (byte)0x87, (byte)0x5c, (byte)0x81, (byte)0x35, (byte)0xde, (byte)0xb4, (byte)0xa5, (byte)0xfc,
+        (byte)0x80, (byte)0xef, (byte)0xcb, (byte)0xbb, (byte)0x6b, (byte)0x76, (byte)0xba, (byte)0x5a, (byte)0x7d,
+        (byte)0x78, (byte)0x0b, (byte)0x95, (byte)0xe3, (byte)0xad, (byte)0x74, (byte)0x98, (byte)0x3b, (byte)0x36,
+        (byte)0x64, (byte)0x6d, (byte)0xdc, (byte)0xf0, (byte)0x59, (byte)0xa9, (byte)0x4c, (byte)0x17, (byte)0x7f,
+        (byte)0x91, (byte)0xb8, (byte)0xc9, (byte)0x57, (byte)0x1b, (byte)0xe0, (byte)0x61 };
+
+    private static final byte[] T0 = new byte[]{ (byte)0xa4, (byte)0xa2, (byte)0xa9, (byte)0xc5, (byte)0x4e, (byte)0xc9,
+        (byte)0x03, (byte)0xd9, (byte)0x7e, (byte)0x0f, (byte)0xd2, (byte)0xad, (byte)0xe7, (byte)0xd3, (byte)0x27,
+        (byte)0x5b, (byte)0xe3, (byte)0xa1, (byte)0xe8, (byte)0xe6, (byte)0x7c, (byte)0x2a, (byte)0x55, (byte)0x0c,
+        (byte)0x86, (byte)0x39, (byte)0xd7, (byte)0x8d, (byte)0xb8, (byte)0x12, (byte)0x6f, (byte)0x28, (byte)0xcd,
+        (byte)0x8a, (byte)0x70, (byte)0x56, (byte)0x72, (byte)0xf9, (byte)0xbf, (byte)0x4f, (byte)0x73, (byte)0xe9,
+        (byte)0xf7, (byte)0x57, (byte)0x16, (byte)0xac, (byte)0x50, (byte)0xc0, (byte)0x9d, (byte)0xb7, (byte)0x47,
+        (byte)0x71, (byte)0x60, (byte)0xc4, (byte)0x74, (byte)0x43, (byte)0x6c, (byte)0x1f, (byte)0x93, (byte)0x77,
+        (byte)0xdc, (byte)0xce, (byte)0x20, (byte)0x8c, (byte)0x99, (byte)0x5f, (byte)0x44, (byte)0x01, (byte)0xf5,
+        (byte)0x1e, (byte)0x87, (byte)0x5e, (byte)0x61, (byte)0x2c, (byte)0x4b, (byte)0x1d, (byte)0x81, (byte)0x15,
+        (byte)0xf4, (byte)0x23, (byte)0xd6, (byte)0xea, (byte)0xe1, (byte)0x67, (byte)0xf1, (byte)0x7f, (byte)0xfe,
+        (byte)0xda, (byte)0x3c, (byte)0x07, (byte)0x53, (byte)0x6a, (byte)0x84, (byte)0x9c, (byte)0xcb, (byte)0x02,
+        (byte)0x83, (byte)0x33, (byte)0xdd, (byte)0x35, (byte)0xe2, (byte)0x59, (byte)0x5a, (byte)0x98, (byte)0xa5,
+        (byte)0x92, (byte)0x64, (byte)0x04, (byte)0x06, (byte)0x10, (byte)0x4d, (byte)0x1c, (byte)0x97, (byte)0x08,
+        (byte)0x31, (byte)0xee, (byte)0xab, (byte)0x05, (byte)0xaf, (byte)0x79, (byte)0xa0, (byte)0x18, (byte)0x46,
+        (byte)0x6d, (byte)0xfc, (byte)0x89, (byte)0xd4, (byte)0xc7, (byte)0xff, (byte)0xf0, (byte)0xcf, (byte)0x42,
+        (byte)0x91, (byte)0xf8, (byte)0x68, (byte)0x0a, (byte)0x65, (byte)0x8e, (byte)0xb6, (byte)0xfd, (byte)0xc3,
+        (byte)0xef, (byte)0x78, (byte)0x4c, (byte)0xcc, (byte)0x9e, (byte)0x30, (byte)0x2e, (byte)0xbc, (byte)0x0b,
+        (byte)0x54, (byte)0x1a, (byte)0xa6, (byte)0xbb, (byte)0x26, (byte)0x80, (byte)0x48, (byte)0x94, (byte)0x32,
+        (byte)0x7d, (byte)0xa7, (byte)0x3f, (byte)0xae, (byte)0x22, (byte)0x3d, (byte)0x66, (byte)0xaa, (byte)0xf6,
+        (byte)0x00, (byte)0x5d, (byte)0xbd, (byte)0x4a, (byte)0xe0, (byte)0x3b, (byte)0xb4, (byte)0x17, (byte)0x8b,
+        (byte)0x9f, (byte)0x76, (byte)0xb0, (byte)0x24, (byte)0x9a, (byte)0x25, (byte)0x63, (byte)0xdb, (byte)0xeb,
+        (byte)0x7a, (byte)0x3e, (byte)0x5c, (byte)0xb3, (byte)0xb1, (byte)0x29, (byte)0xf2, (byte)0xca, (byte)0x58,
+        (byte)0x6e, (byte)0xd8, (byte)0xa8, (byte)0x2f, (byte)0x75, (byte)0xdf, (byte)0x14, (byte)0xfb, (byte)0x13,
+        (byte)0x49, (byte)0x88, (byte)0xb2, (byte)0xec, (byte)0xe4, (byte)0x34, (byte)0x2d, (byte)0x96, (byte)0xc6,
+        (byte)0x3a, (byte)0xed, (byte)0x95, (byte)0x0e, (byte)0xe5, (byte)0x85, (byte)0x6b, (byte)0x40, (byte)0x21,
+        (byte)0x9b, (byte)0x09, (byte)0x19, (byte)0x2b, (byte)0x52, (byte)0xde, (byte)0x45, (byte)0xa3, (byte)0xfa,
+        (byte)0x51, (byte)0xc2, (byte)0xb5, (byte)0xd1, (byte)0x90, (byte)0xb9, (byte)0xf3, (byte)0x37, (byte)0xc1,
+        (byte)0x0d, (byte)0xba, (byte)0x41, (byte)0x11, (byte)0x38, (byte)0x7b, (byte)0xbe, (byte)0xd0, (byte)0xd5,
+        (byte)0x69, (byte)0x36, (byte)0xc8, (byte)0x62, (byte)0x1b, (byte)0x82, (byte)0x8f };
+
+    private static final byte[] T1 = new byte[]{ (byte)0x83, (byte)0xf2, (byte)0x2a, (byte)0xeb, (byte)0xe9, (byte)0xbf,
+        (byte)0x7b, (byte)0x9c, (byte)0x34, (byte)0x96, (byte)0x8d, (byte)0x98, (byte)0xb9, (byte)0x69, (byte)0x8c,
+        (byte)0x29, (byte)0x3d, (byte)0x88, (byte)0x68, (byte)0x06, (byte)0x39, (byte)0x11, (byte)0x4c, (byte)0x0e,
+        (byte)0xa0, (byte)0x56, (byte)0x40, (byte)0x92, (byte)0x15, (byte)0xbc, (byte)0xb3, (byte)0xdc, (byte)0x6f,
+        (byte)0xf8, (byte)0x26, (byte)0xba, (byte)0xbe, (byte)0xbd, (byte)0x31, (byte)0xfb, (byte)0xc3, (byte)0xfe,
+        (byte)0x80, (byte)0x61, (byte)0xe1, (byte)0x7a, (byte)0x32, (byte)0xd2, (byte)0x70, (byte)0x20, (byte)0xa1,
+        (byte)0x45, (byte)0xec, (byte)0xd9, (byte)0x1a, (byte)0x5d, (byte)0xb4, (byte)0xd8, (byte)0x09, (byte)0xa5,
+        (byte)0x55, (byte)0x8e, (byte)0x37, (byte)0x76, (byte)0xa9, (byte)0x67, (byte)0x10, (byte)0x17, (byte)0x36,
+        (byte)0x65, (byte)0xb1, (byte)0x95, (byte)0x62, (byte)0x59, (byte)0x74, (byte)0xa3, (byte)0x50, (byte)0x2f,
+        (byte)0x4b, (byte)0xc8, (byte)0xd0, (byte)0x8f, (byte)0xcd, (byte)0xd4, (byte)0x3c, (byte)0x86, (byte)0x12,
+        (byte)0x1d, (byte)0x23, (byte)0xef, (byte)0xf4, (byte)0x53, (byte)0x19, (byte)0x35, (byte)0xe6, (byte)0x7f,
+        (byte)0x5e, (byte)0xd6, (byte)0x79, (byte)0x51, (byte)0x22, (byte)0x14, (byte)0xf7, (byte)0x1e, (byte)0x4a,
+        (byte)0x42, (byte)0x9b, (byte)0x41, (byte)0x73, (byte)0x2d, (byte)0xc1, (byte)0x5c, (byte)0xa6, (byte)0xa2,
+        (byte)0xe0, (byte)0x2e, (byte)0xd3, (byte)0x28, (byte)0xbb, (byte)0xc9, (byte)0xae, (byte)0x6a, (byte)0xd1,
+        (byte)0x5a, (byte)0x30, (byte)0x90, (byte)0x84, (byte)0xf9, (byte)0xb2, (byte)0x58, (byte)0xcf, (byte)0x7e,
+        (byte)0xc5, (byte)0xcb, (byte)0x97, (byte)0xe4, (byte)0x16, (byte)0x6c, (byte)0xfa, (byte)0xb0, (byte)0x6d,
+        (byte)0x1f, (byte)0x52, (byte)0x99, (byte)0x0d, (byte)0x4e, (byte)0x03, (byte)0x91, (byte)0xc2, (byte)0x4d,
+        (byte)0x64, (byte)0x77, (byte)0x9f, (byte)0xdd, (byte)0xc4, (byte)0x49, (byte)0x8a, (byte)0x9a, (byte)0x24,
+        (byte)0x38, (byte)0xa7, (byte)0x57, (byte)0x85, (byte)0xc7, (byte)0x7c, (byte)0x7d, (byte)0xe7, (byte)0xf6,
+        (byte)0xb7, (byte)0xac, (byte)0x27, (byte)0x46, (byte)0xde, (byte)0xdf, (byte)0x3b, (byte)0xd7, (byte)0x9e,
+        (byte)0x2b, (byte)0x0b, (byte)0xd5, (byte)0x13, (byte)0x75, (byte)0xf0, (byte)0x72, (byte)0xb6, (byte)0x9d,
+        (byte)0x1b, (byte)0x01, (byte)0x3f, (byte)0x44, (byte)0xe5, (byte)0x87, (byte)0xfd, (byte)0x07, (byte)0xf1,
+        (byte)0xab, (byte)0x94, (byte)0x18, (byte)0xea, (byte)0xfc, (byte)0x3a, (byte)0x82, (byte)0x5f, (byte)0x05,
+        (byte)0x54, (byte)0xdb, (byte)0x00, (byte)0x8b, (byte)0xe3, (byte)0x48, (byte)0x0c, (byte)0xca, (byte)0x78,
+        (byte)0x89, (byte)0x0a, (byte)0xff, (byte)0x3e, (byte)0x5b, (byte)0x81, (byte)0xee, (byte)0x71, (byte)0xe2,
+        (byte)0xda, (byte)0x2c, (byte)0xb8, (byte)0xb5, (byte)0xcc, (byte)0x6e, (byte)0xa8, (byte)0x6b, (byte)0xad,
+        (byte)0x60, (byte)0xc6, (byte)0x08, (byte)0x04, (byte)0x02, (byte)0xe8, (byte)0xf5, (byte)0x4f, (byte)0xa4,
+        (byte)0xf3, (byte)0xc0, (byte)0xce, (byte)0x43, (byte)0x25, (byte)0x1c, (byte)0x21, (byte)0x33, (byte)0x0f,
+        (byte)0xaf, (byte)0x47, (byte)0xed, (byte)0x66, (byte)0x63, (byte)0x93, (byte)0xaa };
+
+    private static final byte[] T2 = new byte[]{ (byte)0x45, (byte)0xd4, (byte)0x0b, (byte)0x43, (byte)0xf1, (byte)0x72,
+        (byte)0xed, (byte)0xa4, (byte)0xc2, (byte)0x38, (byte)0xe6, (byte)0x71, (byte)0xfd, (byte)0xb6, (byte)0x3a,
+        (byte)0x95, (byte)0x50, (byte)0x44, (byte)0x4b, (byte)0xe2, (byte)0x74, (byte)0x6b, (byte)0x1e, (byte)0x11,
+        (byte)0x5a, (byte)0xc6, (byte)0xb4, (byte)0xd8, (byte)0xa5, (byte)0x8a, (byte)0x70, (byte)0xa3, (byte)0xa8,
+        (byte)0xfa, (byte)0x05, (byte)0xd9, (byte)0x97, (byte)0x40, (byte)0xc9, (byte)0x90, (byte)0x98, (byte)0x8f,
+        (byte)0xdc, (byte)0x12, (byte)0x31, (byte)0x2c, (byte)0x47, (byte)0x6a, (byte)0x99, (byte)0xae, (byte)0xc8,
+        (byte)0x7f, (byte)0xf9, (byte)0x4f, (byte)0x5d, (byte)0x96, (byte)0x6f, (byte)0xf4, (byte)0xb3, (byte)0x39,
+        (byte)0x21, (byte)0xda, (byte)0x9c, (byte)0x85, (byte)0x9e, (byte)0x3b, (byte)0xf0, (byte)0xbf, (byte)0xef,
+        (byte)0x06, (byte)0xee, (byte)0xe5, (byte)0x5f, (byte)0x20, (byte)0x10, (byte)0xcc, (byte)0x3c, (byte)0x54,
+        (byte)0x4a, (byte)0x52, (byte)0x94, (byte)0x0e, (byte)0xc0, (byte)0x28, (byte)0xf6, (byte)0x56, (byte)0x60,
+        (byte)0xa2, (byte)0xe3, (byte)0x0f, (byte)0xec, (byte)0x9d, (byte)0x24, (byte)0x83, (byte)0x7e, (byte)0xd5,
+        (byte)0x7c, (byte)0xeb, (byte)0x18, (byte)0xd7, (byte)0xcd, (byte)0xdd, (byte)0x78, (byte)0xff, (byte)0xdb,
+        (byte)0xa1, (byte)0x09, (byte)0xd0, (byte)0x76, (byte)0x84, (byte)0x75, (byte)0xbb, (byte)0x1d, (byte)0x1a,
+        (byte)0x2f, (byte)0xb0, (byte)0xfe, (byte)0xd6, (byte)0x34, (byte)0x63, (byte)0x35, (byte)0xd2, (byte)0x2a,
+        (byte)0x59, (byte)0x6d, (byte)0x4d, (byte)0x77, (byte)0xe7, (byte)0x8e, (byte)0x61, (byte)0xcf, (byte)0x9f,
+        (byte)0xce, (byte)0x27, (byte)0xf5, (byte)0x80, (byte)0x86, (byte)0xc7, (byte)0xa6, (byte)0xfb, (byte)0xf8,
+        (byte)0x87, (byte)0xab, (byte)0x62, (byte)0x3f, (byte)0xdf, (byte)0x48, (byte)0x00, (byte)0x14, (byte)0x9a,
+        (byte)0xbd, (byte)0x5b, (byte)0x04, (byte)0x92, (byte)0x02, (byte)0x25, (byte)0x65, (byte)0x4c, (byte)0x53,
+        (byte)0x0c, (byte)0xf2, (byte)0x29, (byte)0xaf, (byte)0x17, (byte)0x6c, (byte)0x41, (byte)0x30, (byte)0xe9,
+        (byte)0x93, (byte)0x55, (byte)0xf7, (byte)0xac, (byte)0x68, (byte)0x26, (byte)0xc4, (byte)0x7d, (byte)0xca,
+        (byte)0x7a, (byte)0x3e, (byte)0xa0, (byte)0x37, (byte)0x03, (byte)0xc1, (byte)0x36, (byte)0x69, (byte)0x66,
+        (byte)0x08, (byte)0x16, (byte)0xa7, (byte)0xbc, (byte)0xc5, (byte)0xd3, (byte)0x22, (byte)0xb7, (byte)0x13,
+        (byte)0x46, (byte)0x32, (byte)0xe8, (byte)0x57, (byte)0x88, (byte)0x2b, (byte)0x81, (byte)0xb2, (byte)0x4e,
+        (byte)0x64, (byte)0x1c, (byte)0xaa, (byte)0x91, (byte)0x58, (byte)0x2e, (byte)0x9b, (byte)0x5c, (byte)0x1b,
+        (byte)0x51, (byte)0x73, (byte)0x42, (byte)0x23, (byte)0x01, (byte)0x6e, (byte)0xf3, (byte)0x0d, (byte)0xbe,
+        (byte)0x3d, (byte)0x0a, (byte)0x2d, (byte)0x1f, (byte)0x67, (byte)0x33, (byte)0x19, (byte)0x7b, (byte)0x5e,
+        (byte)0xea, (byte)0xde, (byte)0x8b, (byte)0xcb, (byte)0xa9, (byte)0x8c, (byte)0x8d, (byte)0xad, (byte)0x49,
+        (byte)0x82, (byte)0xe4, (byte)0xba, (byte)0xc3, (byte)0x15, (byte)0xd1, (byte)0xe0, (byte)0x89, (byte)0xfc,
+        (byte)0xb1, (byte)0xb9, (byte)0xb5, (byte)0x07, (byte)0x79, (byte)0xb8, (byte)0xe1 };
+
+    private static final byte[] T3 = new byte[]{ (byte)0xb2, (byte)0xb6, (byte)0x23, (byte)0x11, (byte)0xa7, (byte)0x88,
+        (byte)0xc5, (byte)0xa6, (byte)0x39, (byte)0x8f, (byte)0xc4, (byte)0xe8, (byte)0x73, (byte)0x22, (byte)0x43,
+        (byte)0xc3, (byte)0x82, (byte)0x27, (byte)0xcd, (byte)0x18, (byte)0x51, (byte)0x62, (byte)0x2d, (byte)0xf7,
+        (byte)0x5c, (byte)0x0e, (byte)0x3b, (byte)0xfd, (byte)0xca, (byte)0x9b, (byte)0x0d, (byte)0x0f, (byte)0x79,
+        (byte)0x8c, (byte)0x10, (byte)0x4c, (byte)0x74, (byte)0x1c, (byte)0x0a, (byte)0x8e, (byte)0x7c, (byte)0x94,
+        (byte)0x07, (byte)0xc7, (byte)0x5e, (byte)0x14, (byte)0xa1, (byte)0x21, (byte)0x57, (byte)0x50, (byte)0x4e,
+        (byte)0xa9, (byte)0x80, (byte)0xd9, (byte)0xef, (byte)0x64, (byte)0x41, (byte)0xcf, (byte)0x3c, (byte)0xee,
+        (byte)0x2e, (byte)0x13, (byte)0x29, (byte)0xba, (byte)0x34, (byte)0x5a, (byte)0xae, (byte)0x8a, (byte)0x61,
+        (byte)0x33, (byte)0x12, (byte)0xb9, (byte)0x55, (byte)0xa8, (byte)0x15, (byte)0x05, (byte)0xf6, (byte)0x03,
+        (byte)0x06, (byte)0x49, (byte)0xb5, (byte)0x25, (byte)0x09, (byte)0x16, (byte)0x0c, (byte)0x2a, (byte)0x38,
+        (byte)0xfc, (byte)0x20, (byte)0xf4, (byte)0xe5, (byte)0x7f, (byte)0xd7, (byte)0x31, (byte)0x2b, (byte)0x66,
+        (byte)0x6f, (byte)0xff, (byte)0x72, (byte)0x86, (byte)0xf0, (byte)0xa3, (byte)0x2f, (byte)0x78, (byte)0x00,
+        (byte)0xbc, (byte)0xcc, (byte)0xe2, (byte)0xb0, (byte)0xf1, (byte)0x42, (byte)0xb4, (byte)0x30, (byte)0x5f,
+        (byte)0x60, (byte)0x04, (byte)0xec, (byte)0xa5, (byte)0xe3, (byte)0x8b, (byte)0xe7, (byte)0x1d, (byte)0xbf,
+        (byte)0x84, (byte)0x7b, (byte)0xe6, (byte)0x81, (byte)0xf8, (byte)0xde, (byte)0xd8, (byte)0xd2, (byte)0x17,
+        (byte)0xce, (byte)0x4b, (byte)0x47, (byte)0xd6, (byte)0x69, (byte)0x6c, (byte)0x19, (byte)0x99, (byte)0x9a,
+        (byte)0x01, (byte)0xb3, (byte)0x85, (byte)0xb1, (byte)0xf9, (byte)0x59, (byte)0xc2, (byte)0x37, (byte)0xe9,
+        (byte)0xc8, (byte)0xa0, (byte)0xed, (byte)0x4f, (byte)0x89, (byte)0x68, (byte)0x6d, (byte)0xd5, (byte)0x26,
+        (byte)0x91, (byte)0x87, (byte)0x58, (byte)0xbd, (byte)0xc9, (byte)0x98, (byte)0xdc, (byte)0x75, (byte)0xc0,
+        (byte)0x76, (byte)0xf5, (byte)0x67, (byte)0x6b, (byte)0x7e, (byte)0xeb, (byte)0x52, (byte)0xcb, (byte)0xd1,
+        (byte)0x5b, (byte)0x9f, (byte)0x0b, (byte)0xdb, (byte)0x40, (byte)0x92, (byte)0x1a, (byte)0xfa, (byte)0xac,
+        (byte)0xe4, (byte)0xe1, (byte)0x71, (byte)0x1f, (byte)0x65, (byte)0x8d, (byte)0x97, (byte)0x9e, (byte)0x95,
+        (byte)0x90, (byte)0x5d, (byte)0xb7, (byte)0xc1, (byte)0xaf, (byte)0x54, (byte)0xfb, (byte)0x02, (byte)0xe0,
+        (byte)0x35, (byte)0xbb, (byte)0x3a, (byte)0x4d, (byte)0xad, (byte)0x2c, (byte)0x3d, (byte)0x56, (byte)0x08,
+        (byte)0x1b, (byte)0x4a, (byte)0x93, (byte)0x6a, (byte)0xab, (byte)0xb8, (byte)0x7a, (byte)0xf2, (byte)0x7d,
+        (byte)0xda, (byte)0x3f, (byte)0xfe, (byte)0x3e, (byte)0xbe, (byte)0xea, (byte)0xaa, (byte)0x44, (byte)0xc6,
+        (byte)0xd0, (byte)0x36, (byte)0x48, (byte)0x70, (byte)0x96, (byte)0x77, (byte)0x24, (byte)0x53, (byte)0xdf,
+        (byte)0xf3, (byte)0x83, (byte)0x28, (byte)0x32, (byte)0x45, (byte)0x1e, (byte)0xa4, (byte)0xd3, (byte)0xa2,
+        (byte)0x46, (byte)0x6e, (byte)0x9c, (byte)0xdd, (byte)0x63, (byte)0xd4, (byte)0x9d };
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java
new file mode 100644
index 0000000..c262d2a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java
@@ -0,0 +1,239 @@
+package org.bouncycastle.crypto.engines;
+
+import java.util.ArrayList;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of DSTU7624 KEY WRAP mode
+ */
+public class DSTU7624WrapEngine
+    implements Wrapper
+{
+
+    private static final int BYTES_IN_INTEGER = 4;
+
+    private boolean forWrapping;
+    private DSTU7624Engine engine;
+
+    private byte[] B, intArray;
+    private byte[] checkSumArray, zeroArray;
+    private ArrayList<byte[]> Btemp;
+
+
+    public DSTU7624WrapEngine(int blockBitLength)
+    {
+
+        this.engine = new DSTU7624Engine(blockBitLength);
+        this.B = new byte[engine.getBlockSize() / 2];
+        this.checkSumArray = new byte[engine.getBlockSize()];
+        this.zeroArray = new byte[engine.getBlockSize()];
+        this.Btemp = new ArrayList<byte[]>();
+        this.intArray = new byte[BYTES_IN_INTEGER];
+
+    }
+
+    public void init(boolean forWrapping, CipherParameters param)
+    {
+        if (param instanceof ParametersWithRandom)
+        {
+            param = ((ParametersWithRandom)param).getParameters();
+        }
+
+        this.forWrapping = forWrapping;
+        if (param instanceof KeyParameter)
+        {
+            engine.init(forWrapping, param);
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to DSTU7624WrapEngine");
+        }
+
+    }
+
+    public String getAlgorithmName()
+    {
+        return "DSTU7624WrapEngine";
+    }
+
+    public byte[] wrap(byte[] in, int inOff, int inLen)
+    {
+        if (!forWrapping)
+        {
+            throw new IllegalStateException("not set for wrapping");
+        }
+
+        if ((inLen % engine.getBlockSize()) != 0)
+        {
+            //Partial blocks not supported
+            throw new DataLengthException("wrap data must be a multiple of " + engine.getBlockSize() + " bytes");
+        }
+
+        if (inOff + inLen > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+
+        int n = 2 * (1 + inLen / engine.getBlockSize()); /* Defined in DSTU7624 standard */
+        int V = (n - 1) * 6; /* Defined in DSTU7624 standard */
+
+
+        byte[] wrappedBuffer = new byte[inLen + engine.getBlockSize()];
+        System.arraycopy(in, inOff, wrappedBuffer, 0, inLen);
+
+        System.arraycopy(wrappedBuffer, 0, B, 0, engine.getBlockSize() / 2);
+
+        Btemp.clear();
+
+        int bHalfBlocksLen = wrappedBuffer.length - engine.getBlockSize() / 2;
+        int bufOff = engine.getBlockSize() / 2;
+        while (bHalfBlocksLen != 0)
+        {
+            byte[] temp = new byte[engine.getBlockSize() / 2];
+            System.arraycopy(wrappedBuffer, bufOff, temp, 0, engine.getBlockSize() / 2);
+
+            Btemp.add(temp);
+
+            bHalfBlocksLen -= engine.getBlockSize() / 2;
+            bufOff += engine.getBlockSize() / 2;
+        }
+
+        for (int j = 0; j < V; j++)
+        {
+            System.arraycopy(B, 0, wrappedBuffer, 0, engine.getBlockSize() / 2);
+            System.arraycopy(Btemp.get(0), 0, wrappedBuffer, engine.getBlockSize() / 2, engine.getBlockSize() / 2);
+
+            engine.processBlock(wrappedBuffer, 0, wrappedBuffer, 0);
+
+            intToBytes(j + 1, intArray, 0);
+            for (int byteNum = 0; byteNum < BYTES_IN_INTEGER; byteNum++)
+            {
+                wrappedBuffer[byteNum + engine.getBlockSize() / 2] ^= intArray[byteNum];
+            }
+
+            System.arraycopy(wrappedBuffer, engine.getBlockSize() / 2, B, 0, engine.getBlockSize() / 2);
+
+            for (int i = 2; i < n; i++)
+            {
+                System.arraycopy(Btemp.get(i - 1), 0, Btemp.get(i - 2), 0, engine.getBlockSize() / 2);
+            }
+
+            System.arraycopy(wrappedBuffer, 0, Btemp.get(n - 2), 0, engine.getBlockSize() / 2);
+        }
+
+
+        System.arraycopy(B, 0, wrappedBuffer, 0, engine.getBlockSize() / 2);
+        bufOff = engine.getBlockSize() / 2;
+
+        for (int i = 0; i < n - 1; i++)
+        {
+            System.arraycopy(Btemp.get(i), 0, wrappedBuffer, bufOff, engine.getBlockSize() / 2);
+            bufOff += engine.getBlockSize() / 2;
+        }
+
+        return wrappedBuffer;
+
+    }
+
+    public byte[] unwrap(byte[] in, int inOff, int inLen)
+        throws InvalidCipherTextException
+    {
+        if (forWrapping)
+        {
+            throw new IllegalStateException("not set for unwrapping");
+        }
+
+        if ((inLen % engine.getBlockSize()) != 0)
+        {
+            //Partial blocks not supported
+            throw new DataLengthException("unwrap data must be a multiple of " + engine.getBlockSize() + " bytes");
+        }
+
+        int n = 2 * inLen / engine.getBlockSize();
+
+        int V = (n - 1) * 6;
+
+        byte[] buffer = new byte[inLen];
+        System.arraycopy(in, inOff, buffer, 0, inLen);
+
+        byte[] B = new byte[engine.getBlockSize() / 2];
+        System.arraycopy(buffer, 0, B, 0, engine.getBlockSize() / 2);
+
+        Btemp.clear();
+
+        int bHalfBlocksLen = buffer.length - engine.getBlockSize() / 2;
+        int bufOff = engine.getBlockSize() / 2;
+        while (bHalfBlocksLen != 0)
+        {
+            byte[] temp = new byte[engine.getBlockSize() / 2];
+            System.arraycopy(buffer, bufOff, temp, 0, engine.getBlockSize() / 2);
+
+            Btemp.add(temp);
+
+            bHalfBlocksLen -= engine.getBlockSize() / 2;
+            bufOff += engine.getBlockSize() / 2;
+        }
+
+        for (int j = 0; j < V; j++)
+        {
+            System.arraycopy(Btemp.get(n - 2), 0, buffer, 0, engine.getBlockSize() / 2);
+            System.arraycopy(B, 0, buffer, engine.getBlockSize() / 2, engine.getBlockSize() / 2);
+            intToBytes(V - j, intArray, 0);
+            for (int byteNum = 0; byteNum < BYTES_IN_INTEGER; byteNum++)
+            {
+                buffer[byteNum + engine.getBlockSize() / 2] ^= intArray[byteNum];
+            }
+
+            engine.processBlock(buffer, 0, buffer, 0);
+
+            System.arraycopy(buffer, 0, B, 0, engine.getBlockSize() / 2);
+
+            for (int i = 2; i < n; i++)
+            {
+                System.arraycopy(Btemp.get(n - i - 1), 0, Btemp.get(n - i), 0, engine.getBlockSize() / 2);
+            }
+
+            System.arraycopy(buffer, engine.getBlockSize() / 2, Btemp.get(0), 0, engine.getBlockSize() / 2);
+        }
+
+        System.arraycopy(B, 0, buffer, 0, engine.getBlockSize() / 2);
+        bufOff = engine.getBlockSize() / 2;
+
+        for (int i = 0; i < n - 1; i++)
+        {
+            System.arraycopy(Btemp.get(i), 0, buffer, bufOff, engine.getBlockSize() / 2);
+            bufOff += engine.getBlockSize() / 2;
+        }
+
+        System.arraycopy(buffer, buffer.length - engine.getBlockSize(), checkSumArray, 0, engine.getBlockSize());
+
+        byte[] wrappedBuffer = new byte[buffer.length - engine.getBlockSize()];
+        if (!Arrays.areEqual(checkSumArray, zeroArray))
+        {
+            throw new InvalidCipherTextException("checksum failed");
+        }
+        else
+        {
+            System.arraycopy(buffer, 0, wrappedBuffer, 0, buffer.length - engine.getBlockSize());
+        }
+
+
+        return wrappedBuffer;
+    }
+
+
+    private void intToBytes(int number, byte[] outBytes, int outOff)
+    {
+        outBytes[outOff + 3] = (byte)(number >> 24);
+        outBytes[outOff + 2] = (byte)(number >> 16);
+        outBytes[outOff + 1] = (byte)(number >> 8);
+        outBytes[outOff] = (byte)number;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
index ef8e799..5b78c10 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.params.ElGamalKeyParameters;
 import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
@@ -47,7 +48,7 @@
         else
         {
             this.key = (ElGamalKeyParameters)param;
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
 
         this.forEncryption = forEncryption;
@@ -178,11 +179,11 @@
             ElGamalPublicKeyParameters  pub = (ElGamalPublicKeyParameters)key;
 
             int                         pBitLength = p.bitLength();
-            BigInteger                  k = new BigInteger(pBitLength, random);
+            BigInteger                  k = BigIntegers.createRandomBigInteger(pBitLength, random);
 
             while (k.equals(ZERO) || (k.compareTo(p.subtract(TWO)) > 0))
             {
-                k = new BigInteger(pBitLength, random);
+                k = BigIntegers.createRandomBigInteger(pBitLength, random);
             }
 
             BigInteger  g = key.getParameters().getG();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java
index 5a88b7f..e04673d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147Engine.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.crypto.engines;
 
+import java.util.Enumeration;
 import java.util.Hashtable;
 
 import org.bouncycastle.crypto.BlockCipher;
@@ -95,7 +96,19 @@
          0x3,0x0,0x6,0xF,0x1,0xE,0x9,0x2,0xD,0x8,0xC,0x4,0xB,0xA,0x5,0x7,
          0x1,0xA,0x6,0x8,0xF,0xB,0x0,0x4,0xC,0x3,0x5,0x9,0x7,0xD,0x2,0xE
     };
-    
+
+    // Rosstandart param-Z
+    private static byte Param_Z[] = {
+        0xc, 0x4, 0x6, 0x2, 0xa, 0x5, 0xb, 0x9, 0xe, 0x8, 0xd, 0x7, 0x0, 0x3, 0xf, 0x1,
+        0x6, 0x8, 0x2, 0x3, 0x9, 0xa, 0x5, 0xc, 0x1, 0xe, 0x4, 0x7, 0xb, 0xd, 0x0, 0xf,
+        0xb, 0x3, 0x5, 0x8, 0x2, 0xf, 0xa, 0xd, 0xe, 0x1, 0x7, 0x4, 0xc, 0x9, 0x6, 0x0,
+        0xc, 0x8, 0x2, 0x1, 0xd, 0x4, 0xf, 0x6, 0x7, 0x0, 0xa, 0x5, 0x3, 0xe, 0x9, 0xb,
+        0x7, 0xf, 0x5, 0xa, 0x8, 0x1, 0x6, 0xd, 0x0, 0x9, 0x3, 0xe, 0xb, 0x4, 0x2, 0xc,
+        0x5, 0xd, 0xf, 0x6, 0x9, 0x2, 0xc, 0xa, 0xb, 0x7, 0x8, 0x1, 0x4, 0x3, 0xe, 0x0,
+        0x8, 0xe, 0x2, 0x5, 0x6, 0x9, 0x1, 0xc, 0xf, 0x4, 0xb, 0x0, 0xd, 0xa, 0x3, 0x7,
+        0x1, 0x7, 0xe, 0xd, 0x0, 0x5, 0x8, 0x3, 0x4, 0xf, 0xa, 0x6, 0x9, 0xc, 0xb, 0x2
+    };
+
     //S-box for digest
     private static byte DSbox_Test[] = {
          0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
@@ -132,6 +145,7 @@
         addSBox("E-B", ESbox_B);
         addSBox("E-C", ESbox_C);
         addSBox("E-D", ESbox_D);
+        addSBox("Param-Z", Param_Z);
         addSBox("D-TEST", DSbox_Test);
         addSBox("D-A", DSbox_A);
     }
@@ -364,9 +378,24 @@
         if (sBox == null)
         {
             throw new IllegalArgumentException("Unknown S-Box - possible types: "
-                + "\"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\".");
+                + "\"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"Param-Z\", \"D-Test\", \"D-A\".");
         }
 
         return Arrays.clone(sBox);
     }
+
+    public static String getSBoxName(byte[] sBox)
+    {
+        for (Enumeration en = sBoxes.keys(); en.hasMoreElements();)
+        {
+            String name = (String)en.nextElement();
+            byte[] sb = (byte[])sBoxes.get(name);
+            if (Arrays.areEqual(sb, sBox))
+            {
+                return name;
+            }
+        }
+
+        throw new IllegalArgumentException("SBOX provided did not map to a known one");
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java
new file mode 100644
index 0000000..9cde5fe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java
@@ -0,0 +1,95 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.macs.GOST28147Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.util.Arrays;
+
+public class GOST28147WrapEngine
+    implements Wrapper
+{
+    private GOST28147Engine cipher = new GOST28147Engine();
+    private GOST28147Mac mac = new GOST28147Mac();
+
+    public void init(boolean forWrapping, CipherParameters param)
+    {
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom pr = (ParametersWithRandom)param;
+            param = pr.getParameters();
+        }
+        
+        ParametersWithUKM pU = (ParametersWithUKM)param;
+
+        cipher.init(forWrapping, pU.getParameters());
+
+        KeyParameter kParam;
+
+        if (pU.getParameters() instanceof ParametersWithSBox)
+        {
+            kParam = (KeyParameter)((ParametersWithSBox)pU.getParameters()).getParameters();
+        }
+        else
+        {
+            kParam = (KeyParameter)pU.getParameters();
+        }
+
+
+        mac.init(new ParametersWithIV(kParam, pU.getUKM()));
+    }
+
+    public String getAlgorithmName()
+    {
+        return "GOST28147Wrap";
+    }
+
+    public byte[] wrap(byte[] input, int inOff, int inLen)
+    {
+        mac.update(input, inOff, inLen);
+
+        byte[] wrappedKey = new byte[inLen + mac.getMacSize()];
+
+        cipher.processBlock(input, inOff, wrappedKey, 0);
+        cipher.processBlock(input, inOff + 8, wrappedKey, 8);
+        cipher.processBlock(input, inOff + 16, wrappedKey, 16);
+        cipher.processBlock(input, inOff + 24, wrappedKey, 24);
+
+        mac.doFinal(wrappedKey, inLen);
+
+        return wrappedKey;
+    }
+
+    public byte[] unwrap(byte[] input, int inOff, int inLen)
+        throws InvalidCipherTextException
+    {
+        byte[] decKey = new byte[inLen - mac.getMacSize()];
+
+        cipher.processBlock(input, inOff, decKey, 0);
+        cipher.processBlock(input, inOff + 8, decKey, 8);
+        cipher.processBlock(input, inOff + 16, decKey, 16);
+        cipher.processBlock(input, inOff + 24, decKey, 24);
+
+        byte[] macResult = new byte[mac.getMacSize()];
+
+        mac.update(decKey, 0, decKey.length);
+
+        mac.doFinal(macResult, 0);
+
+        byte[] macExpected = new byte[mac.getMacSize()];
+
+        System.arraycopy(input, inOff + inLen - 4, macExpected, 0, mac.getMacSize());
+
+        if (!Arrays.constantTimeAreEqual(macResult, macExpected))
+        {
+            throw new IllegalStateException("mac mismatch");
+        }
+
+        return decKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST3412_2015Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST3412_2015Engine.java
new file mode 100644
index 0000000..3df685b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/GOST3412_2015Engine.java
@@ -0,0 +1,344 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of GOST 3412 2015 (aka "Kuznyechik") RFC 7801, GOST 3412
+ */
+public class GOST3412_2015Engine
+    implements BlockCipher
+{
+
+    private static final byte[] PI = new byte[]
+        {
+            -4, -18, -35, 17, -49, 110, 49, 22, -5, -60, -6, -38, 35, -59, 4, 77, -23, 119, -16, -37, -109, 46, -103, -70,
+            23, 54, -15, -69, 20, -51, 95, -63, -7, 24, 101, 90, -30, 92, -17, 33, -127, 28, 60, 66, -117, 1, -114, 79, 5,
+            -124, 2, -82, -29, 106, -113, -96, 6, 11, -19, -104, 127, -44, -45, 31, -21, 52, 44, 81, -22, -56, 72, -85, -14,
+            42, 104, -94, -3, 58, -50, -52, -75, 112, 14, 86, 8, 12, 118, 18, -65, 114, 19, 71, -100, -73, 93, -121, 21,
+            -95, -106, 41, 16, 123, -102, -57, -13, -111, 120, 111, -99, -98, -78, -79, 50, 117, 25, 61, -1, 53, -118, 126,
+            109, 84, -58, -128, -61, -67, 13, 87, -33, -11, 36, -87, 62, -88, 67, -55, -41, 121, -42, -10, 124, 34, -71,
+            3, -32, 15, -20, -34, 122, -108, -80, -68, -36, -24, 40, 80, 78, 51, 10, 74, -89, -105, 96, 115, 30, 0, 98, 68,
+            26, -72, 56, -126, 100, -97, 38, 65, -83, 69, 70, -110, 39, 94, 85, 47, -116, -93, -91, 125, 105, -43, -107,
+            59, 7, 88, -77, 64, -122, -84, 29, -9, 48, 55, 107, -28, -120, -39, -25, -119, -31, 27, -125, 73, 76, 63, -8,
+            -2, -115, 83, -86, -112, -54, -40, -123, 97, 32, 113, 103, -92, 45, 43, 9, 91, -53, -101, 37, -48, -66, -27,
+            108, 82, 89, -90, 116, -46, -26, -12, -76, -64, -47, 102, -81, -62, 57, 75, 99, -74
+        };
+
+
+    private static final byte[] inversePI = new byte[]{
+        -91, 45, 50, -113, 14, 48, 56, -64, 84, -26, -98, 57, 85, 126, 82, -111, 100, 3, 87, 90, 28, 96, 7, 24, 33, 114,
+        -88, -47, 41, -58, -92, 63, -32, 39, -115, 12, -126, -22, -82, -76, -102, 99, 73, -27, 66, -28, 21, -73, -56, 6,
+        112, -99, 65, 117, 25, -55, -86, -4, 77, -65, 42, 115, -124, -43, -61, -81, 43, -122, -89, -79, -78, 91, 70, -45,
+        -97, -3, -44, 15, -100, 47, -101, 67, -17, -39, 121, -74, 83, 127, -63, -16, 35, -25, 37, 94, -75, 30, -94, -33,
+        -90, -2, -84, 34, -7, -30, 74, -68, 53, -54, -18, 120, 5, 107, 81, -31, 89, -93, -14, 113, 86, 17, 106, -119,
+        -108, 101, -116, -69, 119, 60, 123, 40, -85, -46, 49, -34, -60, 95, -52, -49, 118, 44, -72, -40, 46, 54, -37,
+        105, -77, 20, -107, -66, 98, -95, 59, 22, 102, -23, 92, 108, 109, -83, 55, 97, 75, -71, -29, -70, -15, -96, -123,
+        -125, -38, 71, -59, -80, 51, -6, -106, 111, 110, -62, -10, 80, -1, 93, -87, -114, 23, 27, -105, 125, -20, 88, -9,
+        31, -5, 124, 9, 13, 122, 103, 69, -121, -36, -24, 79, 29, 78, 4, -21, -8, -13, 62, 61, -67, -118, -120, -35, -51,
+        11, 19, -104, 2, -109, -128, -112, -48, 36, 52, -53, -19, -12, -50, -103, 16, 68, 64, -110, 58, 1, 38, 18, 26,
+        72, 104, -11, -127, -117, -57, -42, 32, 10, 8, 0, 76, -41, 116
+    };
+
+
+    private final byte[] lFactors = {
+        -108, 32, -123, 16, -62, -64, 1, -5, 1, -64, -62, 16, -123, 32, -108, 1
+    };
+
+
+    protected static final int BLOCK_SIZE = 16;
+    private int KEY_LENGTH = 32;
+    private int SUB_LENGTH = KEY_LENGTH / 2;
+    private byte[][] subKeys = null;
+    private boolean forEncryption;
+    private byte[][] _gf_mul = init_gf256_mul_table();
+
+
+    private static byte[][] init_gf256_mul_table()
+    {
+        byte[][] mul_table = new byte[256][];
+        for (int x = 0; x < 256; x++)
+        {
+            mul_table[x] = new byte[256];
+            for (int y = 0; y < 256; y++)
+            {
+                mul_table[x][y] = kuz_mul_gf256_slow((byte)x, (byte)y);
+            }
+        }
+        return mul_table;
+    }
+
+    private static byte kuz_mul_gf256_slow(byte a, byte b)
+    {
+        byte p = 0;
+        byte counter;
+        byte hi_bit_set;
+        for (counter = 0; counter < 8 && a != 0 && b != 0; counter++)
+        {
+            if ((b & 1) != 0)
+            {
+                p ^= a;
+            }
+            hi_bit_set = (byte)(a & 0x80);
+            a <<= 1;
+            if (hi_bit_set != 0)
+            {
+                a ^= 0xc3; /* x^8 + x^7 + x^6 + x + 1 */
+            }
+            b >>= 1;
+        }
+        return p;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "GOST3412_2015";
+    }
+
+    public int getBlockSize()
+    {
+        return BLOCK_SIZE;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+
+        if (params instanceof KeyParameter)
+        {
+            this.forEncryption = forEncryption;
+            generateSubKeys(((KeyParameter)params).getKey());
+        }
+        else if (params != null)
+        {
+            throw new IllegalArgumentException("invalid parameter passed to GOST3412_2015 init - " + params.getClass().getName());
+        }
+    }
+
+    private void generateSubKeys(
+        byte[] userKey)
+    {
+
+        if (userKey.length != KEY_LENGTH)
+        {
+            throw new IllegalArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+        }
+
+        subKeys = new byte[10][];
+        for (int i = 0; i < 10; i++)
+        {
+            subKeys[i] = new byte[SUB_LENGTH];
+        }
+
+        byte[] x = new byte[SUB_LENGTH];
+        byte[] y = new byte[SUB_LENGTH];
+
+
+        for (int i = 0; i < SUB_LENGTH; i++)
+        {
+            subKeys[0][i] = x[i] = userKey[i];
+            subKeys[1][i] = y[i] = userKey[i + SUB_LENGTH];
+        }
+
+        byte[] c = new byte[SUB_LENGTH];
+
+        for (int k = 1; k < 5; k++)
+        {
+
+            for (int j = 1; j <= 8; j++)
+            {
+                C(c, 8 * (k - 1) + j);
+                F(c, x, y);
+            }
+
+            System.arraycopy(x, 0, subKeys[2 * k], 0, SUB_LENGTH);
+            System.arraycopy(y, 0, subKeys[2 * k + 1], 0, SUB_LENGTH);
+        }
+    }
+
+
+    private void C(byte[] c, int i)
+    {
+
+        Arrays.clear(c);
+        c[15] = (byte)i;
+        L(c);
+    }
+
+
+    private void F(byte[] k, byte[] a1, byte[] a0)
+    {
+
+        byte[] temp = LSX(k, a1);
+        X(temp, a0);
+
+        System.arraycopy(a1, 0, a0, 0, SUB_LENGTH);
+        System.arraycopy(temp, 0, a1, 0, SUB_LENGTH);
+
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        if (subKeys == null)
+        {
+            throw new IllegalStateException("GOST3412_2015 engine not initialised");
+        }
+
+        if ((inOff + BLOCK_SIZE) > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+
+        if ((outOff + BLOCK_SIZE) > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        GOST3412_2015Func(in, inOff, out, outOff);
+
+        return BLOCK_SIZE;
+    }
+
+
+    private void GOST3412_2015Func(
+        byte[] in,
+        int inOff,
+        byte[] out,
+        int outOff)
+    {
+
+        byte[] block = new byte[BLOCK_SIZE];
+        System.arraycopy(in, inOff, block, 0, BLOCK_SIZE);
+
+        if (forEncryption)
+        {
+
+            for (int i = 0; i < 9; i++)
+            {
+
+                byte[] temp = LSX(subKeys[i], block);
+                block = Arrays.copyOf(temp, BLOCK_SIZE);
+            }
+
+            X(block, subKeys[9]);
+        }
+        else
+        {
+
+            for (int i = 9; i > 0; i--)
+            {
+
+                byte[] temp = XSL(subKeys[i], block);
+                block = Arrays.copyOf(temp, BLOCK_SIZE);
+            }
+            X(block, subKeys[0]);
+        }
+
+
+        System.arraycopy(block, 0, out, outOff, BLOCK_SIZE);
+    }
+
+    private byte[] LSX(byte[] k, byte[] a)
+    {
+
+        byte[] result = Arrays.copyOf(k, k.length);
+        X(result, a);
+        S(result);
+        L(result);
+        return result;
+    }
+
+    private byte[] XSL(byte[] k, byte[] a)
+    {
+        byte[] result = Arrays.copyOf(k, k.length);
+        X(result, a);
+        inverseL(result);
+        inverseS(result);
+        return result;
+    }
+
+    private void X(byte[] result, byte[] data)
+    {
+        for (int i = 0; i < result.length; i++)
+        {
+            result[i] ^= data[i];
+        }
+    }
+
+    private void S(byte[] data)
+    {
+        for (int i = 0; i < data.length; i++)
+        {
+            data[i] = PI[unsignedByte(data[i])];
+        }
+    }
+
+    private void inverseS(byte[] data)
+    {
+        for (int i = 0; i < data.length; i++)
+        {
+            data[i] = inversePI[unsignedByte(data[i])];
+        }
+    }
+
+    private int unsignedByte(byte b)
+    {
+        return b & 0xFF;
+    }
+
+    private void L(byte[] data)
+    {
+        for (int i = 0; i < 16; i++)
+        {
+            R(data);
+        }
+    }
+
+    private void inverseL(byte[] data)
+    {
+        for (int i = 0; i < 16; i++)
+        {
+            inverseR(data);
+        }
+    }
+
+
+    private void R(byte[] data)
+    {
+        byte z = l(data);
+        System.arraycopy(data, 0, data, 1, 15);
+        data[0] = z;
+    }
+
+    private void inverseR(byte[] data)
+    {
+        byte[] temp = new byte[16];
+        System.arraycopy(data, 1, temp, 0, 15);
+        temp[15] = data[0];
+        byte z = l(temp);
+        System.arraycopy(data, 1, data, 0, 15);
+        data[15] = z;
+    }
+
+
+    private byte l(byte[] data)
+    {
+        byte x = data[15];
+        for (int i = 14; i >= 0; i--)
+        {
+            x ^= _gf_mul[unsignedByte(data[i])][unsignedByte(lFactors[i])];
+        }
+        return x;
+    }
+
+    public void reset()
+    {
+
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
index 740379b..b1bfddf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
@@ -46,7 +46,7 @@
     private byte[] IV;
 
     /**
-     * set up for use with stream mode, where the key derivation function
+     * Set up for use with stream mode, where the key derivation function
      * is used to provide a stream of bytes to xor with the message.
      *
      * @param agree the key agreement used as the basis for the encryption
@@ -68,7 +68,7 @@
 
     /**
      * Set up for use in conjunction with a block cipher to handle the
-     * message.It is <b>strongly</b> recommended that the cipher is not in ECB mode.
+     * message. It is <b>strongly</b> recommended that the cipher is not in ECB mode.
      *
      * @param agree  the key agreement used as the basis for the encryption
      * @param kdf    the key derivation function used for byte generation
@@ -318,15 +318,15 @@
             System.arraycopy(K, 0, K1, 0, K1.length);
             System.arraycopy(K, K1.length, K2, 0, K2.length);
 
+            CipherParameters cp = new KeyParameter(K1);
+
             // If IV provide use it to initialize the cipher
             if (IV != null)
             {
-                cipher.init(false, new ParametersWithIV(new KeyParameter(K1), IV));
+                cp = new ParametersWithIV(cp, IV);
             }
-            else
-            {
-                cipher.init(false, new KeyParameter(K1));
-            }
+
+            cipher.init(false, cp);
 
             M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())];
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
index 27b1c3c..52bc031 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Wrapper;
@@ -65,7 +66,7 @@
         }
         else
         {
-            sr = new SecureRandom();
+            sr = CryptoServicesRegistrar.getSecureRandom();
         }
         
         if (param instanceof ParametersWithIV)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java
index 600ae8b..397e75e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3211WrapEngine.java
@@ -4,11 +4,13 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Wrapper;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
 
 /**
  * an implementation of the RFC 3211 Key Wrap
@@ -38,13 +40,24 @@
             ParametersWithRandom p = (ParametersWithRandom)param;
 
             rand = p.getRandom();
+
+            if (!(p.getParameters() instanceof ParametersWithIV))
+            {
+                throw new IllegalArgumentException("RFC3211Wrap requires an IV");
+            }
+
             this.param = (ParametersWithIV)p.getParameters();
         }
         else
         {
             if (forWrapping)
             {
-                rand = new SecureRandom();
+                rand = CryptoServicesRegistrar.getSecureRandom();
+            }
+
+            if (!(param instanceof ParametersWithIV))
+            {
+                throw new IllegalArgumentException("RFC3211Wrap requires an IV");
             }
 
             this.param = (ParametersWithIV)param;
@@ -66,6 +79,11 @@
             throw new IllegalStateException("not set for wrapping");
         }
 
+        if (inLen > 255 || inLen < 0)
+        {
+            throw new IllegalArgumentException("input must be from 0 to 255 bytes");
+        }
+        
         engine.init(true, param);
 
         int blockSize = engine.getBlockSize();
@@ -81,9 +99,6 @@
         }
 
         cekBlock[0] = (byte)inLen;
-        cekBlock[1] = (byte)~in[inOff];
-        cekBlock[2] = (byte)~in[inOff + 1];
-        cekBlock[3] = (byte)~in[inOff + 2];
 
         System.arraycopy(in, inOff, cekBlock, 4, inLen);
 
@@ -92,6 +107,10 @@
         rand.nextBytes(pad);
         System.arraycopy(pad, 0, cekBlock, inLen + 4, pad.length);
 
+        cekBlock[1] = (byte)~cekBlock[4];
+        cekBlock[2] = (byte)~cekBlock[4 + 1];
+        cekBlock[3] = (byte)~cekBlock[4 + 2];
+        
         for (int i = 0; i < cekBlock.length; i += blockSize)
         {
             engine.processBlock(cekBlock, i, cekBlock, i);
@@ -149,25 +168,33 @@
             engine.processBlock(cekBlock, i, cekBlock, i);
         }
 
-        if ((cekBlock[0] & 0xff) > cekBlock.length - 4)
+        boolean invalidLength = ((cekBlock[0] & 0xff) > cekBlock.length - 4);
+
+        byte[] key;
+        if (invalidLength)
         {
-            throw new InvalidCipherTextException("wrapped key corrupted");
+            key = new byte[cekBlock.length - 4];
+        }
+        else
+        {
+            key = new byte[cekBlock[0] & 0xff];
         }
 
-        byte[] key = new byte[cekBlock[0] & 0xff];
-
-        System.arraycopy(cekBlock, 4, key, 0, cekBlock[0]);
-
+        System.arraycopy(cekBlock, 4, key, 0, key.length);
+        
         // Note: Using constant time comparison
         int nonEqual = 0;
         for (int i = 0; i != 3; i++)
         {
             byte check = (byte)~cekBlock[1 + i];
-            nonEqual |= (check ^ key[i]);
+            nonEqual |= (check ^ cekBlock[4 + i]);
         }
-        if (nonEqual != 0)
+
+        Arrays.clear(cekBlock);
+
+        if (nonEqual != 0 | invalidLength)
         {
-            throw new InvalidCipherTextException("wrapped key fails checksum");
+            throw new InvalidCipherTextException("wrapped key corrupted");
         }
 
         return key;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index d74e2b3..65ac67b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
@@ -45,7 +46,7 @@
         else
         {
             key = (RSAKeyParameters)param;
-            random = new SecureRandom();
+            random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
index 510cd5a..ca482d9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -1,12 +1,13 @@
 package org.bouncycastle.crypto.engines;
 
+import java.math.BigInteger;
+
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
-
-import java.math.BigInteger;
+import org.bouncycastle.util.Arrays;
 
 /**
  * this does your basic RSA algorithm.
@@ -142,20 +143,29 @@
 
                 return tmp;
             }
+
+            return output;
         }
         else
         {
+            byte[]  rv;
             if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
             {
-                byte[]  tmp = new byte[output.length - 1];
+                rv = new byte[output.length - 1];
 
-                System.arraycopy(output, 1, tmp, 0, tmp.length);
-
-                return tmp;
+                System.arraycopy(output, 1, rv, 0, rv.length);
             }
-        }
+            else        // maintain decryption time
+            {
+                rv = new byte[output.length];
 
-        return output;
+                System.arraycopy(output, 0, rv, 0, rv.length);
+            }
+
+            Arrays.fill(output, (byte)0);
+
+            return rv;
+        }
     }
 
     public BigInteger processBlock(BigInteger input)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java
index 64a2a2d..f3eb4ee 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java
@@ -14,9 +14,13 @@
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
 
 /**
  * SM2 public key encryption engine - based on https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02.
@@ -85,6 +89,16 @@
         }
     }
 
+    public int getOutputSize(int inputLen)
+    {
+        return (1 + 2 * curveLength) + inputLen + digest.getDigestSize();
+    }
+
+    protected ECMultiplier createBasePointMultiplier()
+    {
+        return new FixedPointCombMultiplier();
+    }
+
     private byte[] encrypt(byte[] in, int inOff, int inLen)
         throws InvalidCipherTextException
     {
@@ -92,13 +106,15 @@
 
         System.arraycopy(in, inOff, c2, 0, c2.length);
 
+        ECMultiplier multiplier = createBasePointMultiplier();
+
         byte[] c1;
         ECPoint kPB;
         do
         {
             BigInteger k = nextK();
 
-            ECPoint c1P = ecParams.getG().multiply(k).normalize();
+            ECPoint c1P = multiplier.multiply(ecParams.getG(), k).normalize();
 
             c1 = c1P.getEncoded(false);
 
@@ -153,16 +169,16 @@
         int check = 0;
         for (int i = 0; i != c3.length; i++)
         {
-            check |= c3[i] ^ in[c1.length + c2.length + i];
+            check |= c3[i] ^ in[inOff + c1.length + c2.length + i];
         }
 
-        clearBlock(c1);
-        clearBlock(c3);
+        Arrays.fill(c1, (byte)0);
+        Arrays.fill(c3, (byte)0);
 
         if (check != 0)
         {
-           clearBlock(c2);
-           throw new InvalidCipherTextException("invalid cipher text");
+            Arrays.fill(c2, (byte)0);
+            throw new InvalidCipherTextException("invalid cipher text");
         }
 
         return c2;
@@ -183,34 +199,42 @@
 
     private void kdf(Digest digest, ECPoint c1, byte[] encData)
     {
-        int ct = 1;
-        int v = digest.getDigestSize();
-
-        byte[] buf = new byte[digest.getDigestSize()];
+        int digestSize = digest.getDigestSize();
+        byte[] buf = new byte[Math.max(4, digestSize)];
         int off = 0;
 
-        for (int i = 1; i <= ((encData.length + v - 1) / v); i++)
+        Memoable memo = null;
+        Memoable copy = null;
+
+        if (digest instanceof Memoable)
         {
             addFieldElement(digest, c1.getAffineXCoord());
             addFieldElement(digest, c1.getAffineYCoord());
-            digest.update((byte)(ct >> 24));
-            digest.update((byte)(ct >> 16));
-            digest.update((byte)(ct >> 8));
-            digest.update((byte)ct);
+            memo = (Memoable)digest;
+            copy = memo.copy();
+        }
 
-            digest.doFinal(buf, 0);
+        int ct = 0;
 
-            if (off + buf.length < encData.length)
+        while (off < encData.length)
+        {
+            if (memo != null)
             {
-                xor(encData, buf, off, buf.length);
+                memo.reset(copy);
             }
             else
             {
-                xor(encData, buf, off, encData.length - off);
+                addFieldElement(digest, c1.getAffineXCoord());
+                addFieldElement(digest, c1.getAffineYCoord());
             }
 
-            off += buf.length;
-            ct++;
+            Pack.intToBigEndian(++ct, buf, 0);
+            digest.update(buf, 0, 4);
+            digest.doFinal(buf, 0);
+
+            int xorLen = Math.min(digestSize, encData.length - off);
+            xor(encData, buf, off, xorLen);
+            off += xorLen;
         }
     }
 
@@ -229,7 +253,7 @@
         BigInteger k;
         do
         {
-            k = new BigInteger(qBitLength, random);
+            k = BigIntegers.createRandomBigInteger(qBitLength, random);
         }
         while (k.equals(ECConstants.ZERO) || k.compareTo(ecParams.getN()) >= 0);
 
@@ -242,16 +266,4 @@
 
         digest.update(p, 0, p.length);
     }
-
-    /**
-     * clear possible sensitive data
-     */
-    private void clearBlock(
-        byte[]  block)
-    {
-        for (int i = 0; i != block.length; i++)
-        {
-            block[i] = 0;
-        }
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
index bf70ead..033595b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java
@@ -20,41 +20,41 @@
     private static final int BLOCK_SIZE = 16;
 
     private final static byte[] Sbox =
-        {
-            (byte)0xd6, (byte)0x90, (byte)0xe9, (byte)0xfe, (byte)0xcc, (byte)0xe1, (byte)0x3d, (byte)0xb7, (byte)0x16, (byte)0xb6, (byte)0x14, (byte)0xc2, (byte)0x28, (byte)0xfb, (byte)0x2c, (byte)0x05,
-            (byte)0x2b, (byte)0x67, (byte)0x9a, (byte)0x76, (byte)0x2a, (byte)0xbe, (byte)0x04, (byte)0xc3, (byte)0xaa, (byte)0x44, (byte)0x13, (byte)0x26, (byte)0x49, (byte)0x86, (byte)0x06, (byte)0x99,
-            (byte)0x9c, (byte)0x42, (byte)0x50, (byte)0xf4, (byte)0x91, (byte)0xef, (byte)0x98, (byte)0x7a, (byte)0x33, (byte)0x54, (byte)0x0b, (byte)0x43, (byte)0xed, (byte)0xcf, (byte)0xac, (byte)0x62,
-            (byte)0xe4, (byte)0xb3, (byte)0x1c, (byte)0xa9, (byte)0xc9, (byte)0x08, (byte)0xe8, (byte)0x95, (byte)0x80, (byte)0xdf, (byte)0x94, (byte)0xfa, (byte)0x75, (byte)0x8f, (byte)0x3f, (byte)0xa6,
-            (byte)0x47, (byte)0x07, (byte)0xa7, (byte)0xfc, (byte)0xf3, (byte)0x73, (byte)0x17, (byte)0xba, (byte)0x83, (byte)0x59, (byte)0x3c, (byte)0x19, (byte)0xe6, (byte)0x85, (byte)0x4f, (byte)0xa8,
-            (byte)0x68, (byte)0x6b, (byte)0x81, (byte)0xb2, (byte)0x71, (byte)0x64, (byte)0xda, (byte)0x8b, (byte)0xf8, (byte)0xeb, (byte)0x0f, (byte)0x4b, (byte)0x70, (byte)0x56, (byte)0x9d, (byte)0x35,
-            (byte)0x1e, (byte)0x24, (byte)0x0e, (byte)0x5e, (byte)0x63, (byte)0x58, (byte)0xd1, (byte)0xa2, (byte)0x25, (byte)0x22, (byte)0x7c, (byte)0x3b, (byte)0x01, (byte)0x21, (byte)0x78, (byte)0x87,
-            (byte)0xd4, (byte)0x00, (byte)0x46, (byte)0x57, (byte)0x9f, (byte)0xd3, (byte)0x27, (byte)0x52, (byte)0x4c, (byte)0x36, (byte)0x02, (byte)0xe7, (byte)0xa0, (byte)0xc4, (byte)0xc8, (byte)0x9e,
-            (byte)0xea, (byte)0xbf, (byte)0x8a, (byte)0xd2, (byte)0x40, (byte)0xc7, (byte)0x38, (byte)0xb5, (byte)0xa3, (byte)0xf7, (byte)0xf2, (byte)0xce, (byte)0xf9, (byte)0x61, (byte)0x15, (byte)0xa1,
-            (byte)0xe0, (byte)0xae, (byte)0x5d, (byte)0xa4, (byte)0x9b, (byte)0x34, (byte)0x1a, (byte)0x55, (byte)0xad, (byte)0x93, (byte)0x32, (byte)0x30, (byte)0xf5, (byte)0x8c, (byte)0xb1, (byte)0xe3,
-            (byte)0x1d, (byte)0xf6, (byte)0xe2, (byte)0x2e, (byte)0x82, (byte)0x66, (byte)0xca, (byte)0x60, (byte)0xc0, (byte)0x29, (byte)0x23, (byte)0xab, (byte)0x0d, (byte)0x53, (byte)0x4e, (byte)0x6f,
-            (byte)0xd5, (byte)0xdb, (byte)0x37, (byte)0x45, (byte)0xde, (byte)0xfd, (byte)0x8e, (byte)0x2f, (byte)0x03, (byte)0xff, (byte)0x6a, (byte)0x72, (byte)0x6d, (byte)0x6c, (byte)0x5b, (byte)0x51,
-            (byte)0x8d, (byte)0x1b, (byte)0xaf, (byte)0x92, (byte)0xbb, (byte)0xdd, (byte)0xbc, (byte)0x7f, (byte)0x11, (byte)0xd9, (byte)0x5c, (byte)0x41, (byte)0x1f, (byte)0x10, (byte)0x5a, (byte)0xd8,
-            (byte)0x0a, (byte)0xc1, (byte)0x31, (byte)0x88, (byte)0xa5, (byte)0xcd, (byte)0x7b, (byte)0xbd, (byte)0x2d, (byte)0x74, (byte)0xd0, (byte)0x12, (byte)0xb8, (byte)0xe5, (byte)0xb4, (byte)0xb0,
-            (byte)0x89, (byte)0x69, (byte)0x97, (byte)0x4a, (byte)0x0c, (byte)0x96, (byte)0x77, (byte)0x7e, (byte)0x65, (byte)0xb9, (byte)0xf1, (byte)0x09, (byte)0xc5, (byte)0x6e, (byte)0xc6, (byte)0x84,
-            (byte)0x18, (byte)0xf0, (byte)0x7d, (byte)0xec, (byte)0x3a, (byte)0xdc, (byte)0x4d, (byte)0x20, (byte)0x79, (byte)0xee, (byte)0x5f, (byte)0x3e, (byte)0xd7, (byte)0xcb, (byte)0x39, (byte)0x48
-        };
+    {
+        (byte)0xd6, (byte)0x90, (byte)0xe9, (byte)0xfe, (byte)0xcc, (byte)0xe1, (byte)0x3d, (byte)0xb7, (byte)0x16, (byte)0xb6, (byte)0x14, (byte)0xc2, (byte)0x28, (byte)0xfb, (byte)0x2c, (byte)0x05,
+        (byte)0x2b, (byte)0x67, (byte)0x9a, (byte)0x76, (byte)0x2a, (byte)0xbe, (byte)0x04, (byte)0xc3, (byte)0xaa, (byte)0x44, (byte)0x13, (byte)0x26, (byte)0x49, (byte)0x86, (byte)0x06, (byte)0x99,
+        (byte)0x9c, (byte)0x42, (byte)0x50, (byte)0xf4, (byte)0x91, (byte)0xef, (byte)0x98, (byte)0x7a, (byte)0x33, (byte)0x54, (byte)0x0b, (byte)0x43, (byte)0xed, (byte)0xcf, (byte)0xac, (byte)0x62,
+        (byte)0xe4, (byte)0xb3, (byte)0x1c, (byte)0xa9, (byte)0xc9, (byte)0x08, (byte)0xe8, (byte)0x95, (byte)0x80, (byte)0xdf, (byte)0x94, (byte)0xfa, (byte)0x75, (byte)0x8f, (byte)0x3f, (byte)0xa6,
+        (byte)0x47, (byte)0x07, (byte)0xa7, (byte)0xfc, (byte)0xf3, (byte)0x73, (byte)0x17, (byte)0xba, (byte)0x83, (byte)0x59, (byte)0x3c, (byte)0x19, (byte)0xe6, (byte)0x85, (byte)0x4f, (byte)0xa8,
+        (byte)0x68, (byte)0x6b, (byte)0x81, (byte)0xb2, (byte)0x71, (byte)0x64, (byte)0xda, (byte)0x8b, (byte)0xf8, (byte)0xeb, (byte)0x0f, (byte)0x4b, (byte)0x70, (byte)0x56, (byte)0x9d, (byte)0x35,
+        (byte)0x1e, (byte)0x24, (byte)0x0e, (byte)0x5e, (byte)0x63, (byte)0x58, (byte)0xd1, (byte)0xa2, (byte)0x25, (byte)0x22, (byte)0x7c, (byte)0x3b, (byte)0x01, (byte)0x21, (byte)0x78, (byte)0x87,
+        (byte)0xd4, (byte)0x00, (byte)0x46, (byte)0x57, (byte)0x9f, (byte)0xd3, (byte)0x27, (byte)0x52, (byte)0x4c, (byte)0x36, (byte)0x02, (byte)0xe7, (byte)0xa0, (byte)0xc4, (byte)0xc8, (byte)0x9e,
+        (byte)0xea, (byte)0xbf, (byte)0x8a, (byte)0xd2, (byte)0x40, (byte)0xc7, (byte)0x38, (byte)0xb5, (byte)0xa3, (byte)0xf7, (byte)0xf2, (byte)0xce, (byte)0xf9, (byte)0x61, (byte)0x15, (byte)0xa1,
+        (byte)0xe0, (byte)0xae, (byte)0x5d, (byte)0xa4, (byte)0x9b, (byte)0x34, (byte)0x1a, (byte)0x55, (byte)0xad, (byte)0x93, (byte)0x32, (byte)0x30, (byte)0xf5, (byte)0x8c, (byte)0xb1, (byte)0xe3,
+        (byte)0x1d, (byte)0xf6, (byte)0xe2, (byte)0x2e, (byte)0x82, (byte)0x66, (byte)0xca, (byte)0x60, (byte)0xc0, (byte)0x29, (byte)0x23, (byte)0xab, (byte)0x0d, (byte)0x53, (byte)0x4e, (byte)0x6f,
+        (byte)0xd5, (byte)0xdb, (byte)0x37, (byte)0x45, (byte)0xde, (byte)0xfd, (byte)0x8e, (byte)0x2f, (byte)0x03, (byte)0xff, (byte)0x6a, (byte)0x72, (byte)0x6d, (byte)0x6c, (byte)0x5b, (byte)0x51,
+        (byte)0x8d, (byte)0x1b, (byte)0xaf, (byte)0x92, (byte)0xbb, (byte)0xdd, (byte)0xbc, (byte)0x7f, (byte)0x11, (byte)0xd9, (byte)0x5c, (byte)0x41, (byte)0x1f, (byte)0x10, (byte)0x5a, (byte)0xd8,
+        (byte)0x0a, (byte)0xc1, (byte)0x31, (byte)0x88, (byte)0xa5, (byte)0xcd, (byte)0x7b, (byte)0xbd, (byte)0x2d, (byte)0x74, (byte)0xd0, (byte)0x12, (byte)0xb8, (byte)0xe5, (byte)0xb4, (byte)0xb0,
+        (byte)0x89, (byte)0x69, (byte)0x97, (byte)0x4a, (byte)0x0c, (byte)0x96, (byte)0x77, (byte)0x7e, (byte)0x65, (byte)0xb9, (byte)0xf1, (byte)0x09, (byte)0xc5, (byte)0x6e, (byte)0xc6, (byte)0x84,
+        (byte)0x18, (byte)0xf0, (byte)0x7d, (byte)0xec, (byte)0x3a, (byte)0xdc, (byte)0x4d, (byte)0x20, (byte)0x79, (byte)0xee, (byte)0x5f, (byte)0x3e, (byte)0xd7, (byte)0xcb, (byte)0x39, (byte)0x48
+    };
 
     private final static int[] CK =
-        {
-            0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
-            0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
-            0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
-            0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
-            0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
-            0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
-            0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
-            0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
-        };
+    {
+        0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+        0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+        0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+        0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+        0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+        0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+        0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+        0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
+    };
 
     private final static int[] FK =
-        {
-            0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
-        };
+    {
+        0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
+    };
 
     private final int[] X = new int[4];
 
@@ -151,22 +151,6 @@
         return L(tau(Z));
     }
 
-    // reverse substitution
-    private void R(int[] A, int off)
-    {
-        int off0 = off;
-        int off1 = off + 1;
-        int off2 = off + 2;
-        int off3 = off + 3;
-
-        A[off0] = A[off0] ^ A[off3];
-        A[off3] = A[off0] ^ A[off3];
-        A[off0] = A[off0] ^ A[off3];
-        A[off1] = A[off1] ^ A[off2];
-        A[off2] = A[off1] ^ A[off2];
-        A[off1] = A[off1] ^ A[off2];
-    }
-
     // The round functions
     private int F0(int[] X, int rk)
     {
@@ -250,18 +234,16 @@
             X[2] = F2(X, rk[i + 2]);
             X[3] = F3(X, rk[i + 3]);
         }
-        R(X, 0);
 
-        Pack.intToBigEndian(X[0], out, outOff);
-        Pack.intToBigEndian(X[1], out, outOff + 4);
-        Pack.intToBigEndian(X[2], out, outOff + 8);
-        Pack.intToBigEndian(X[3], out, outOff + 12);
+        Pack.intToBigEndian(X[3], out, outOff);
+        Pack.intToBigEndian(X[2], out, outOff + 4);
+        Pack.intToBigEndian(X[1], out, outOff + 8);
+        Pack.intToBigEndian(X[0], out, outOff + 12);
 
-        return 16;
+        return BLOCK_SIZE;
     }
 
     public void reset()
     {
-
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
index 3366022..1e4d61d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
 
@@ -286,15 +287,14 @@
         throws DataLengthException,
         IllegalStateException
     {
-        if ((outOff + blocksizeBytes) > out.length)
-        {
-            throw new DataLengthException("Output buffer too short");
-        }
-
         if ((inOff + blocksizeBytes) > in.length)
         {
             throw new DataLengthException("Input buffer too short");
         }
+        if ((outOff + blocksizeBytes) > out.length)
+        {
+            throw new OutputLengthException("Output buffer too short");
+        }
 
         for (int i = 0; i < blocksizeBytes; i += 8)
         {
@@ -332,7 +332,7 @@
         }
         if (out.length != blocksizeWords)
         {
-            throw new DataLengthException("Output buffer too short");
+            throw new OutputLengthException("Output buffer too short");
         }
 
         if (forEncryption)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Argon2BytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Argon2BytesGenerator.java
new file mode 100644
index 0000000..c5d04d9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Argon2BytesGenerator.java
@@ -0,0 +1,722 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+import org.bouncycastle.crypto.params.Argon2Parameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+import org.bouncycastle.util.encoders.Hex;
+
+
+/**
+ * Argon2 PBKDF - Based on the results of https://password-hashing.net/ and https://www.ietf.org/archive/id/draft-irtf-cfrg-argon2-03.txt
+ */
+public class Argon2BytesGenerator
+{
+
+    private static final int ARGON2_BLOCK_SIZE = 1024;
+    private static final int ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8;
+
+    private static final int ARGON2_ADDRESSES_IN_BLOCK = 128;
+
+    private static final int ARGON2_PREHASH_DIGEST_LENGTH = 64;
+    private static final int ARGON2_PREHASH_SEED_LENGTH = 72;
+
+    private static final int ARGON2_SYNC_POINTS = 4;
+
+    /* Minimum and maximum number of lanes (degree of parallelism) */
+    private static final int MIN_PARALLELISM = 1;
+    private static final int MAX_PARALLELISM = 16777216;
+
+    /* Minimum and maximum digest size in bytes */
+    private static final int MIN_OUTLEN = 4;
+    
+    /* Minimum and maximum number of passes */
+    private static final int MIN_ITERATIONS = 1;
+
+    private Block[] memory;
+
+
+    private int segmentLength;
+    private int laneLength;
+
+
+    private Argon2Parameters parameters;
+
+    private byte[] result;
+
+    public Argon2BytesGenerator()
+    {
+
+    }
+
+    /**
+     * Initialise the Argon2BytesGenerator from the parameters.
+     *
+     * @param parameters Argon2 configuration.
+     */
+    public void init(Argon2Parameters parameters)
+    {
+        this.parameters = parameters;
+
+
+        if (parameters.getLanes() < Argon2BytesGenerator.MIN_PARALLELISM)
+        {
+            throw new IllegalStateException("lanes must be greater than " + Argon2BytesGenerator.MIN_PARALLELISM);
+        }
+        else if (parameters.getLanes() > Argon2BytesGenerator.MAX_PARALLELISM)
+        {
+            throw new IllegalStateException("lanes must be less than " + Argon2BytesGenerator.MAX_PARALLELISM);
+        }
+        else if (parameters.getMemory() < 2 * parameters.getLanes())
+        {
+            throw new IllegalStateException("memory is less than: " + (2 * parameters.getLanes()) + " expected " + (2 * parameters.getLanes()));
+        }
+        else if (parameters.getIterations() < Argon2BytesGenerator.MIN_ITERATIONS)
+        {
+            throw new IllegalStateException("iterations is less than: " + Argon2BytesGenerator.MIN_ITERATIONS);
+        }
+
+        doInit(parameters);
+    }
+
+    public int generateBytes(char[] password, byte[] out)
+    {
+        return generateBytes(parameters.getCharToByteConverter().convert(password), out);
+    }
+
+    public int generateBytes(char[] password, byte[] out, int outOff, int outLen)
+    {
+        return generateBytes(parameters.getCharToByteConverter().convert(password), out, outOff, outLen);
+    }
+
+    public int generateBytes(byte[] password, byte[] out)
+    {
+        return generateBytes(password, out, 0, out.length);
+    }
+
+    public int generateBytes(byte[] password, byte[] out, int outOff, int outLen)
+    {
+        if (outLen < Argon2BytesGenerator.MIN_OUTLEN)
+        {
+            throw new IllegalStateException("output length less than " + Argon2BytesGenerator.MIN_OUTLEN);
+        }
+
+        initialize(password, outLen);
+        fillMemoryBlocks();
+        digest(outLen);
+
+        System.arraycopy(result, 0, out, outOff, outLen);
+
+        reset();
+
+        return outLen;
+    }
+
+    // Clear memory.
+    private void reset()
+    {
+        // Reset memory.
+        for (int i = 0; i < memory.length; i++)
+        {
+            Block b = memory[i];
+
+            b.clear();
+        }
+        memory = null;
+        Arrays.fill(result, (byte)0);
+        doInit(parameters);
+    }
+
+    private void doInit(Argon2Parameters parameters)
+    {
+        /* 2. Align memory size */
+        /* Minimum memoryBlocks = 8L blocks, where L is the number of lanes */
+        int memoryBlocks = parameters.getMemory();
+
+        if (memoryBlocks < 2 * Argon2BytesGenerator.ARGON2_SYNC_POINTS * parameters.getLanes())
+        {
+            memoryBlocks = 2 * Argon2BytesGenerator.ARGON2_SYNC_POINTS * parameters.getLanes();
+        }
+
+        this.segmentLength = memoryBlocks / (parameters.getLanes() * Argon2BytesGenerator.ARGON2_SYNC_POINTS);
+        this.laneLength = segmentLength * Argon2BytesGenerator.ARGON2_SYNC_POINTS;
+
+        /* Ensure that all segments have equal length */
+        memoryBlocks = segmentLength * (parameters.getLanes() * Argon2BytesGenerator.ARGON2_SYNC_POINTS);
+
+        initMemory(memoryBlocks);
+    }
+
+
+    private void initMemory(int memoryBlocks)
+    {
+        this.memory = new Block[memoryBlocks];
+
+        for (int i = 0; i < memory.length; i++)
+        {
+            memory[i] = new Block();
+        }
+    }
+
+    private void fillBlock(Block X, Block Y, Block currentBlock, boolean withXor)
+    {
+        Block R = new Block();
+        Block Z = new Block();
+
+        R.xor(X, Y);
+        Z.copyBlock(R);
+
+        /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
+        (16,17,..31)... finally (112,113,...127) */
+        for (int i = 0; i < 8; i++)
+        {
+
+            roundFunction(Z,
+                16 * i, 16 * i + 1, 16 * i + 2,
+                16 * i + 3, 16 * i + 4, 16 * i + 5,
+                16 * i + 6, 16 * i + 7, 16 * i + 8,
+                16 * i + 9, 16 * i + 10, 16 * i + 11,
+                16 * i + 12, 16 * i + 13, 16 * i + 14,
+                16 * i + 15
+            );
+        }
+
+        /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
+        (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
+        for (int i = 0; i < 8; i++)
+        {
+
+            roundFunction(Z,
+                2 * i, 2 * i + 1, 2 * i + 16,
+                2 * i + 17, 2 * i + 32, 2 * i + 33,
+                2 * i + 48, 2 * i + 49, 2 * i + 64,
+                2 * i + 65, 2 * i + 80, 2 * i + 81,
+                2 * i + 96, 2 * i + 97, 2 * i + 112,
+                2 * i + 113
+            );
+
+        }
+
+        if (withXor)
+        {
+            currentBlock.xor(R, Z, currentBlock);
+        }
+        else
+        {
+            currentBlock.xor(R, Z);
+        }
+    }
+
+    private void fillMemoryBlocks()
+    {
+        for (int i = 0; i < parameters.getIterations(); i++)
+        {
+            for (int j = 0; j < ARGON2_SYNC_POINTS; j++)
+            {
+                for (int k = 0; k < parameters.getLanes(); k++)
+                {
+                    Position position = new Position(i, k, j, 0);
+                    fillSegment(position);
+                }
+            }
+        }
+    }
+
+    private void fillSegment(Position position)
+    {
+
+        Block addressBlock = null, inputBlock = null, zeroBlock = null;
+
+        boolean dataIndependentAddressing = isDataIndependentAddressing(position);
+        int startingIndex = getStartingIndex(position);
+        int currentOffset = position.lane * laneLength + position.slice * segmentLength + startingIndex;
+        int prevOffset = getPrevOffset(currentOffset);
+
+        if (dataIndependentAddressing)
+        {
+            addressBlock = new Block();
+            zeroBlock = new Block();
+            inputBlock = new Block();
+
+            initAddressBlocks(position, zeroBlock, inputBlock, addressBlock);
+        }
+
+        for (position.index = startingIndex; position.index < segmentLength; position.index++, currentOffset++, prevOffset++)
+        {
+            prevOffset = rotatePrevOffset(currentOffset, prevOffset);
+
+            long pseudoRandom = getPseudoRandom(position, addressBlock, inputBlock, zeroBlock, prevOffset, dataIndependentAddressing);
+            int refLane = getRefLane(position, pseudoRandom);
+            int refColumn = getRefColumn(position, pseudoRandom, refLane == position.lane);
+
+            /* 2 Creating a new block */
+            Block prevBlock = memory[prevOffset];
+            Block refBlock = memory[((laneLength) * refLane + refColumn)];
+            Block currentBlock = memory[currentOffset];
+
+            boolean withXor = isWithXor(position);
+            fillBlock(prevBlock, refBlock, currentBlock, withXor);
+        }
+    }
+
+    private boolean isDataIndependentAddressing(Position position)
+    {
+        return (parameters.getType() == Argon2Parameters.ARGON2_i) ||
+            (parameters.getType() == Argon2Parameters.ARGON2_id
+                && (position.pass == 0)
+                && (position.slice < ARGON2_SYNC_POINTS / 2)
+            );
+    }
+
+    private void initAddressBlocks(Position position, Block zeroBlock, Block inputBlock, Block addressBlock)
+    {
+        inputBlock.v[0] = intToLong(position.pass);
+        inputBlock.v[1] = intToLong(position.lane);
+        inputBlock.v[2] = intToLong(position.slice);
+        inputBlock.v[3] = intToLong(memory.length);
+        inputBlock.v[4] = intToLong(parameters.getIterations());
+        inputBlock.v[5] = intToLong(parameters.getType());
+
+        if ((position.pass == 0) && (position.slice == 0))
+        {
+            /* Don't forget to generate the first block of addresses: */
+            nextAddresses(zeroBlock, inputBlock, addressBlock);
+        }
+    }
+
+    private boolean isWithXor(Position position)
+    {
+        return !(position.pass == 0 || parameters.getVersion() == Argon2Parameters.ARGON2_VERSION_10);
+    }
+
+    private int getPrevOffset(int currentOffset)
+    {
+        if (currentOffset % laneLength == 0)
+        {
+            /* Last block in this lane */
+            return currentOffset + laneLength - 1;
+        }
+        else
+        {
+            /* Previous block */
+            return currentOffset - 1;
+        }
+    }
+
+    private int rotatePrevOffset(int currentOffset, int prevOffset)
+    {
+        if (currentOffset % laneLength == 1)
+        {
+            prevOffset = currentOffset - 1;
+        }
+        return prevOffset;
+    }
+
+    private static int getStartingIndex(Position position)
+    {
+        if ((position.pass == 0) && (position.slice == 0))
+        {
+            return 2; /* we have already generated the first two blocks */
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    private void nextAddresses(Block zeroBlock, Block inputBlock, Block addressBlock)
+    {
+        inputBlock.v[6]++;
+        fillBlock(zeroBlock, inputBlock, addressBlock, false);
+        fillBlock(zeroBlock, addressBlock, addressBlock, false);
+    }
+
+    /* 1.2 Computing the index of the reference block */
+    /* 1.2.1 Taking pseudo-random value from the previous block */
+    private long getPseudoRandom(Position position, Block addressBlock, Block inputBlock, Block zeroBlock, int prevOffset, boolean dataIndependentAddressing)
+    {
+        if (dataIndependentAddressing)
+        {
+            if (position.index % ARGON2_ADDRESSES_IN_BLOCK == 0)
+            {
+                nextAddresses(zeroBlock, inputBlock, addressBlock);
+            }
+            return addressBlock.v[position.index % ARGON2_ADDRESSES_IN_BLOCK];
+        }
+        else
+        {
+            return memory[prevOffset].v[0];
+        }
+    }
+
+    private int getRefLane(Position position, long pseudoRandom)
+    {
+        int refLane = (int)(((pseudoRandom >>> 32)) % parameters.getLanes());
+
+        if ((position.pass == 0) && (position.slice == 0))
+        {
+            /* Can not reference other lanes yet */
+            refLane = position.lane;
+        }
+        return refLane;
+    }
+
+    private int getRefColumn(Position position, long pseudoRandom,
+                             boolean sameLane)
+    {
+
+        int referenceAreaSize;
+        int startPosition;
+
+        if (position.pass == 0)
+        {
+            startPosition = 0;
+
+            if (sameLane)
+            {
+                /* The same lane => add current segment */
+                referenceAreaSize = position.slice * segmentLength + position.index - 1;
+            }
+            else
+            {
+                /* pass == 0 && !sameLane => position.slice > 0*/
+                referenceAreaSize = position.slice * segmentLength + ((position.index == 0) ? (-1) : 0);
+            }
+
+        }
+        else
+        {
+            startPosition = ((position.slice + 1) * segmentLength) % laneLength;
+
+            if (sameLane)
+            {
+                referenceAreaSize = laneLength - segmentLength + position.index - 1;
+            }
+            else
+            {
+                referenceAreaSize = laneLength - segmentLength + ((position.index == 0) ? (-1) : 0);
+            }
+        }
+
+        long relativePosition = pseudoRandom & 0xFFFFFFFFL;
+//        long relativePosition = pseudoRandom << 32 >>> 32;
+        relativePosition = (relativePosition * relativePosition) >>> 32;
+        relativePosition = referenceAreaSize - 1 - ((referenceAreaSize * relativePosition) >>> 32);
+
+        return (int)(startPosition + relativePosition) % laneLength;
+    }
+
+    private void digest(int outputLength)
+    {
+        Block finalBlock = memory[laneLength - 1];
+
+        /* XOR the last blocks */
+        for (int i = 1; i < parameters.getLanes(); i++)
+        {
+            int lastBlockInLane = i * laneLength + (laneLength - 1);
+            finalBlock.xorWith(memory[lastBlockInLane]);
+        }
+
+        byte[] finalBlockBytes = finalBlock.toBytes();
+
+        result = hash(finalBlockBytes, outputLength);
+    }
+
+    /**
+     * H0 = H64(p, Ï„, m, t, v, y, |P|, P, |S|, S, |L|, K, |X|, X)
+     * -> 64 byte (ARGON2_PREHASH_DIGEST_LENGTH)
+     */
+    private byte[] initialHash(Argon2Parameters parameters, int outputLength, byte[] password)
+    {
+        Blake2bDigest blake = new Blake2bDigest(ARGON2_PREHASH_DIGEST_LENGTH * 8);
+
+        addIntToLittleEndian(blake, parameters.getLanes());
+        addIntToLittleEndian(blake, outputLength);
+        addIntToLittleEndian(blake, parameters.getMemory());
+        addIntToLittleEndian(blake, parameters.getIterations());
+        addIntToLittleEndian(blake, parameters.getVersion());
+        addIntToLittleEndian(blake, parameters.getType());
+
+        addByteString(blake, password);
+        addByteString(blake, parameters.getSalt());
+        addByteString(blake, parameters.getSecret());
+        addByteString(blake, parameters.getAdditional());
+
+        byte[] blake2hash = new byte[blake.getDigestSize()];
+        blake.doFinal(blake2hash, 0);
+
+        return blake2hash;
+    }
+
+    /**
+     * H' - hash - variable length hash function
+     */
+    private byte[] hash(byte[] input, int outputLength)
+    {
+        byte[] result = new byte[outputLength];
+        byte[] outlenBytes = Pack.intToLittleEndian(outputLength);
+
+        int blake2bLength = 64;
+
+        if (outputLength <= blake2bLength)
+        {
+            Blake2bDigest blake = new Blake2bDigest(outputLength * 8);
+
+            blake.update(outlenBytes, 0, outlenBytes.length);
+            blake.update(input, 0, input.length);
+            blake.doFinal(result, 0);
+        }
+        else
+        {
+            Blake2bDigest digest = new Blake2bDigest(blake2bLength * 8);
+            byte[] outBuffer = new byte[blake2bLength];
+
+            /* V1 */
+            digest.update(outlenBytes, 0, outlenBytes.length);
+            digest.update(input, 0, input.length);
+            digest.doFinal(outBuffer, 0);
+
+            System.arraycopy(outBuffer, 0, result, 0, blake2bLength / 2);
+
+            int r = ((outputLength + 31) / 32) - 2;
+
+            int position = blake2bLength / 2;
+
+            for (int i = 2; i <= r; i++, position += blake2bLength / 2)
+            {
+                /* V2 to Vr */
+                digest.update(outBuffer, 0, outBuffer.length);
+                digest.doFinal(outBuffer, 0);
+
+                System.arraycopy(outBuffer, 0, result, position, blake2bLength / 2);
+            }
+
+            int lastLength = outputLength - 32 * r;
+
+            /* Vr+1 */
+            digest = new Blake2bDigest(lastLength * 8);
+
+            digest.update(outBuffer, 0, outBuffer.length);
+            digest.doFinal(result, position);
+        }
+
+        return result;
+    }
+
+    private void roundFunction(Block block,
+                               int v0, int v1, int v2, int v3,
+                               int v4, int v5, int v6, int v7,
+                               int v8, int v9, int v10, int v11,
+                               int v12, int v13, int v14, int v15)
+    {
+
+        F(block, v0, v4, v8, v12);
+        F(block, v1, v5, v9, v13);
+        F(block, v2, v6, v10, v14);
+        F(block, v3, v7, v11, v15);
+
+        F(block, v0, v5, v10, v15);
+        F(block, v1, v6, v11, v12);
+        F(block, v2, v7, v8, v13);
+        F(block, v3, v4, v9, v14);
+    }
+
+    private void F(Block block, int a, int b, int c, int d)
+    {
+        fBlaMka(block, a, b);
+        rotr64(block, d, a, 32);
+
+        fBlaMka(block, c, d);
+        rotr64(block, b, c, 24);
+
+        fBlaMka(block, a, b);
+        rotr64(block, d, a, 16);
+
+        fBlaMka(block, c, d);
+        rotr64(block, b, c, 63);
+    }
+
+    /*designed by the Lyra PHC team */
+    /* a <- a + b + 2*aL*bL
+     * + == addition modulo 2^64
+     * aL = least 32 bit */
+    private void fBlaMka(Block block, int x, int y)
+    {
+        final long m = 0xFFFFFFFFL;
+        final long xy = (block.v[x] & m) * (block.v[y] & m);
+
+        block.v[x] = block.v[x] + block.v[y] + 2 * xy;
+    }
+
+    private void rotr64(Block block, int v, int w, long c)
+    {
+        final long temp = block.v[v] ^ block.v[w];
+        block.v[v] = (temp >>> c) | (temp << (64 - c));
+    }
+
+    private void initialize(byte[] password, int outputLength)
+    {
+        byte[] initialHash = initialHash(parameters, outputLength, password);
+        
+        fillFirstBlocks(initialHash);
+    }
+
+    private static void addIntToLittleEndian(Digest digest, int n)
+    {
+        digest.update((byte)(n       ));
+        digest.update((byte)(n >>>  8));
+        digest.update((byte)(n >>> 16));
+        digest.update((byte)(n >>> 24));
+    }
+
+    private static void addByteString(Digest digest, byte[] octets)
+    {
+        if (octets != null)
+        {
+            addIntToLittleEndian(digest, octets.length);
+            digest.update(octets, 0, octets.length);
+        }
+        else
+        {
+            addIntToLittleEndian(digest, 0);
+        }
+    }
+
+    /**
+     * (H0 || 0 || i) 72 byte -> 1024 byte
+     * (H0 || 1 || i) 72 byte -> 1024 byte
+     */
+    private void fillFirstBlocks(byte[] initialHash)
+    {
+        final byte[] zeroBytes = {0, 0, 0, 0};
+        final byte[] oneBytes = {1, 0, 0, 0};
+
+        byte[] initialHashWithZeros = getInitialHashLong(initialHash, zeroBytes);
+        byte[] initialHashWithOnes = getInitialHashLong(initialHash, oneBytes);
+
+        for (int i = 0; i < parameters.getLanes(); i++)
+        {
+            Pack.intToLittleEndian(i, initialHashWithZeros, ARGON2_PREHASH_DIGEST_LENGTH + 4);
+            Pack.intToLittleEndian(i, initialHashWithOnes, ARGON2_PREHASH_DIGEST_LENGTH + 4);
+
+            byte[] blockhashBytes = hash(initialHashWithZeros, ARGON2_BLOCK_SIZE);
+            memory[i * laneLength + 0].fromBytes(blockhashBytes);
+
+            blockhashBytes = hash(initialHashWithOnes, ARGON2_BLOCK_SIZE);
+            memory[i * laneLength + 1].fromBytes(blockhashBytes);
+        }
+    }
+
+    private byte[] getInitialHashLong(byte[] initialHash, byte[] appendix)
+    {
+        byte[] initialHashLong = new byte[ARGON2_PREHASH_SEED_LENGTH];
+
+        System.arraycopy(initialHash, 0, initialHashLong, 0, ARGON2_PREHASH_DIGEST_LENGTH);
+        System.arraycopy(appendix, 0, initialHashLong, ARGON2_PREHASH_DIGEST_LENGTH, 4);
+
+        return initialHashLong;
+    }
+
+    private long intToLong(int x)
+    {
+        return (long)(x & 0xffffffffL);
+    }
+    
+    private static class Block
+    {
+        /* 128 * 8 Byte QWords */
+        private long[] v;
+
+        private Block()
+        {
+            v = new long[ARGON2_QWORDS_IN_BLOCK];
+        }
+
+        void fromBytes(byte[] input)
+        {
+            if (input.length != ARGON2_BLOCK_SIZE)
+            {
+                throw new IllegalArgumentException("input shorter than blocksize");
+            }
+
+            for (int i = 0; i < v.length; i++)
+            {
+                v[i] = Pack.littleEndianToLong(input, i * 8);
+            }
+        }
+
+        byte[] toBytes()
+        {
+            byte[] result = new byte[ARGON2_BLOCK_SIZE];
+
+            for (int i = 0; i < v.length; i++)
+            {
+                Pack.longToLittleEndian(v[i], result, i * 8);
+            }
+
+            return result;
+        }
+
+        private void copyBlock(Block other)
+        {
+            System.arraycopy(other.v, 0, v, 0, v.length);
+        }
+
+        private void xor(Block b1, Block b2)
+        {
+            for (int i = 0; i < v.length; i++)
+            {
+                v[i] = b1.v[i] ^ b2.v[i];
+            }
+        }
+
+        public void xor(Block b1, Block b2, Block b3)
+        {
+            for (int i = 0; i < v.length; i++)
+            {
+                v[i] = b1.v[i] ^ b2.v[i] ^ b3.v[i];
+            }
+        }
+
+        private void xorWith(Block other)
+        {
+            for (int i = 0; i < v.length; i++)
+            {
+                v[i] = v[i] ^ other.v[i];
+            }
+        }
+        
+        public String toString()
+        {
+            StringBuffer result = new StringBuffer();
+            for (int i = 0; i < v.length; i++)
+            {
+                result.append(Hex.toHexString(Pack.longToLittleEndian(v[i])));
+            }
+
+            return result.toString();
+        }
+
+        public void clear()
+        {
+            Arrays.fill(v, 0);
+        }
+    }
+
+    private static class Position
+    {
+        int pass;
+        int lane;
+        int slice;
+        int index;
+
+        Position(int pass, int lane, int slice, int index)
+        {
+            this.pass = pass;
+            this.lane = lane;
+            this.slice = slice;
+            this.index = index;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
index 5dac6a1..80eab44 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
+import org.bouncycastle.util.Strings;
 
 /**
  * Core of password hashing scheme Bcrypt,
@@ -13,8 +14,8 @@
  * "A Future-Adaptable Password Scheme" of Niels Provos and David Mazières,
  * see: https://www.usenix.org/legacy/events/usenix99/provos/provos_html/node1.html.
  * In contrast to the paper, the order of key setup and salt setup is reversed:
- * state <- ExpandKey(state, 0, key)
- * state <- ExpandKey(state, 0, salt)
+ * state &lt;- ExpandKey(state, 0, key)
+ * state &lt;- ExpandKey(state, 0, salt)
  * This corresponds to the OpenBSD reference implementation of Bcrypt. 
  * </p><p>
  * Note: 
@@ -602,6 +603,17 @@
     static final int MAX_PASSWORD_BYTES = 72;
 
     /**
+     * Converts a character password to bytes incorporating the required trailing zero byte.
+     *
+     * @param password the password to be encoded.
+     * @return a byte representation of the password in UTF8 + trailing zero.
+     */
+    public static byte[] passwordToByteArray(char[] password)
+    {
+        return Arrays.append(Strings.toUTF8ByteArray(password), (byte)0);
+    }
+
+    /**
      * Calculates the <b>bcrypt</b> hash of a password.
      * <p>
      * This implements the raw <b>bcrypt</b> function as defined in the bcrypt specification, not
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
index b5a2080..bc09ab9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.DigestDerivationFunction;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.ISO18033KDFParameters;
 import org.bouncycastle.crypto.params.KDFParameters;
 import org.bouncycastle.util.Pack;
@@ -80,7 +81,7 @@
     {
         if ((out.length - len) < outOff)
         {
-            throw new DataLengthException("output buffer too small");
+            throw new OutputLengthException("output buffer too small");
         }
 
         long oBytes = len;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java
index 22dd34a..bfe8d93 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java
@@ -38,6 +38,7 @@
      * <p>
      * Note: can take a while...
      * </p>
+     * @return a generated CramerShoupParameters object.
      */
     public CramerShoupParameters generateParameters()
     {
@@ -90,7 +91,7 @@
 
             for (; ; )
             {
-                q = new BigInteger(qLength, 2, random);
+                q = BigIntegers.createRandomPrime(qLength, 2, random);
                 p = q.shiftLeft(1).add(ONE);
                 if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty)))
                 {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
index 6795ec9..17d9403 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
@@ -27,7 +27,7 @@
             int minWeight = limit >>> 2;
             for (;;)
             {
-                BigInteger x = new BigInteger(limit, random).setBit(limit - 1);
+                BigInteger x = BigIntegers.createRandomBigInteger(limit, random).setBit(limit - 1);
                 if (WNafUtil.getNafWeight(x) >= minWeight)
                 {
                     return x;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
index f5d4264..9b7bb86 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
@@ -1,10 +1,10 @@
 package org.bouncycastle.crypto.generators;
 
-import org.bouncycastle.crypto.params.DHParameters;
-
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.params.DHParameters;
+
 public class DHParametersGenerator
 {
     private int             size;
@@ -35,6 +35,7 @@
      * returning the DHParameters object.
      * <p>
      * Note: can take a while...
+     * @return a generated Diffie-Hellman parameters object.
      */
     public DHParameters generateParameters()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
index dc0a5ae..2b5f87b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
@@ -24,7 +24,7 @@
 
         for (;;)
         {
-            q = new BigInteger(qLength, 2, random);
+            q = BigIntegers.createRandomPrime(qLength, 2, random);
 
             // p <- 2q + 1
             p = q.shiftLeft(1).add(ONE);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
index 6411a1f..fde0fa6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -110,6 +110,7 @@
      * returning the DSAParameters object.
      * <p>
      * Note: can take a while...
+     * @return a generated DSA parameters object.
      */
     public DSAParameters generateParameters()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index c766071..502f8b3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
@@ -15,6 +16,7 @@
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
 import org.bouncycastle.math.ec.WNafUtil;
+import org.bouncycastle.util.BigIntegers;
 
 public class ECKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator, ECConstants
@@ -32,7 +34,7 @@
 
         if (this.random == null)
         {
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
@@ -49,7 +51,7 @@
         BigInteger d;
         for (;;)
         {
-            d = new BigInteger(nBitLength, random);
+            d = BigIntegers.createRandomBigInteger(nBitLength, random);
 
             if (d.compareTo(TWO) < 0  || (d.compareTo(n) >= 0))
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java
new file mode 100644
index 0000000..c8991b8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.generators;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+
+public class Ed25519KeyPairGenerator
+    implements AsymmetricCipherKeyPairGenerator
+{
+    private SecureRandom random;
+
+    public void init(KeyGenerationParameters parameters)
+    {
+        this.random = parameters.getRandom();
+    }
+
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(random);
+        Ed25519PublicKeyParameters publicKey = privateKey.generatePublicKey();
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java
new file mode 100644
index 0000000..f796095
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.generators;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+
+public class Ed448KeyPairGenerator
+    implements AsymmetricCipherKeyPairGenerator
+{
+    private SecureRandom random;
+
+    public void init(KeyGenerationParameters parameters)
+    {
+        this.random = parameters.getRandom();
+    }
+
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(random);
+        Ed448PublicKeyParameters publicKey = privateKey.generatePublicKey();
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java
index 21e8c2a..94875c4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java
@@ -1,10 +1,10 @@
 package org.bouncycastle.crypto.generators;
 
-import org.bouncycastle.crypto.params.ElGamalParameters;
-
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.params.ElGamalParameters;
+
 public class ElGamalParametersGenerator
 {
     private int             size;
@@ -26,6 +26,8 @@
      * returning the ElGamalParameters object.
      * <p>
      * Note: can take a while...
+     *
+     * @return a generated ElGamal parameters object.
      */
     public ElGamalParameters generateParameters()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
index eb7b957..124edbf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
@@ -8,9 +11,7 @@
 import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
 import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
 import org.bouncycastle.math.ec.WNafUtil;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * a GOST3410 key pair generator.
@@ -41,7 +42,7 @@
             int minWeight = 64;
             for (;;)
             {
-                x = new BigInteger(256, random);
+                x = BigIntegers.createRandomBigInteger(256, random);
 
                 if (x.signum() < 1 || x.compareTo(q) >= 0)
                 {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java
index 1c7cecf..8383175 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java
@@ -1,11 +1,12 @@
 package org.bouncycastle.crypto.generators;
 
-import org.bouncycastle.crypto.params.GOST3410Parameters;
-import org.bouncycastle.crypto.params.GOST3410ValidationParameters;
-
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.GOST3410ValidationParameters;
+import org.bouncycastle.util.BigIntegers;
+
 /**
  * generate suitable parameters for GOST3410.
  */
@@ -453,7 +454,7 @@
     }
 
 
-    /**
+    /*
      * Procedure C
      * procedure generates the a value from the given p,q,
      * returning the a value.
@@ -466,7 +467,7 @@
 
         for(;;)
         {
-            BigInteger d = new BigInteger(length, init_random);
+            BigInteger d = BigIntegers.createRandomBigInteger(length, init_random);
 
             // 1 < d < p-1
             if (d.compareTo(ONE) > 0 && d.compareTo(pSub1) < 0)
@@ -484,6 +485,8 @@
     /**
      * which generates the p , q and a values from the given parameters,
      * returning the GOST3410Parameters object.
+     *
+     * @return a generated GOST3410 parameters object.
      */
     public GOST3410Parameters generateParameters()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
index 7147add..6305db8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
@@ -20,14 +20,13 @@
  * <li>3b: K(i) := PRF( KI, Label || 0x00 || [i]_2 || [L]_2 || Context ) OR:</li>
  * <li>3c: K(i) := PRF( KI, Label || [i]_2 || 0x00 || Context || [L]_2 ) etc... with the counter somewhere in the 'middle' of the fixedInputData.</li>
  * </ul>
- * </p>
- * <p>
  * This function must be called with the following KDFCounterParameters():
- *  - KI      <br/>
- *  - The part of the fixedInputData that comes BEFORE the counter OR null  <br/>
- *  - the part of the fixedInputData that comes AFTER the counter OR null <br/>
- *  - the length of the counter in bits (not bytes)
- *  </p>
+ * <ul>
+ *  <li>KI</li>
+ *  <li>The part of the fixedInputData that comes BEFORE the counter OR null</li>
+ *  <li>the part of the fixedInputData that comes AFTER the counter OR null </li>
+ *  <li>the length of the counter in bits (not bytes)</li>
+ * </ul>
  * Resulting function calls assuming an 8 bit counter.
  * <ul>
  * <li>1.  KDFCounterParameters(ki, 	null, 									"Label || 0x00 || Context || [L]_2]",	8);</li>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
index e93c0d7..5eda85a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.MGFParameters;
 
 /**
@@ -74,7 +75,7 @@
     {
         if ((out.length - len) < outOff)
         {
-            throw new DataLengthException("output buffer too small");
+            throw new OutputLengthException("output buffer too small");
         }
         
         byte[]  hashBuf = new byte[hLen];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
index ceb3940..7d4b90b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java
@@ -1,15 +1,16 @@
 package org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Vector;
+
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.params.NaccacheSternKeyGenerationParameters;
 import org.bouncycastle.crypto.params.NaccacheSternKeyParameters;
 import org.bouncycastle.crypto.params.NaccacheSternPrivateKeyParameters;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
-import java.util.Vector;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * Key generation parameters for NaccacheStern cipher. For details on this cipher, please see
@@ -176,7 +177,7 @@
                 for (;;)
                 {
                     tries++;
-                    g = new BigInteger(strength, certainty, rand);
+                    g = BigIntegers.createRandomPrime(strength, certainty, rand);
                     if (g.modPow(e, n).equals(ONE))
                     {
                         continue;
@@ -284,10 +285,10 @@
             int certainty,
             SecureRandom rand)
     {
-        BigInteger p_ = new BigInteger(bitLength, certainty, rand);
+        BigInteger p_ = BigIntegers.createRandomPrime(bitLength, certainty, rand);
         while (p_.bitLength() != bitLength)
         {
-            p_ = new BigInteger(bitLength, certainty, rand);
+            p_ = BigIntegers.createRandomPrime(bitLength, certainty, rand);
         }
         return p_;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java
index a546c5d..1c30065 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java
@@ -1,6 +1,8 @@
 package org.bouncycastle.crypto.generators;
 
 import java.io.ByteArrayOutputStream;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.util.Arrays;
@@ -32,10 +34,16 @@
      * set up the decoding table.
      */
     private static final byte[] decodingTable = new byte[128];
-    private static final String version = "2a"; // previous version was not UTF-8
+    private static final String defaultVersion = "2y";
+    private static final Set<String> allowedVersions = new HashSet<String>();
 
     static
     {
+        // Presently just the Bcrypt versions.
+        allowedVersions.add("2a");
+        allowedVersions.add("2y");
+        allowedVersions.add("2b");
+
         for (int i = 0; i < decodingTable.length; i++)
         {
             decodingTable[i] = (byte)0xff;
@@ -56,16 +64,22 @@
      * Creates a 60 character Bcrypt String, including
      * version, cost factor, salt and hash, separated by '$'
      *
+     * @param version  the version, 2y,2b or 2a. (2a is not backwards compatible.)
      * @param cost     the cost factor, treated as an exponent of 2
      * @param salt     a 16 byte salt
      * @param password the password
      * @return a 60 character Bcrypt String
      */
-    private static String createBcryptString(
-        byte[] password,
-        byte[] salt,
-        int cost)
+    private static String createBcryptString(String version,
+                                             byte[] password,
+                                             byte[] salt,
+                                             int cost)
     {
+        if (!allowedVersions.contains(version))
+        {
+            throw new IllegalArgumentException("Version " + version + " is not accepted by this implementation.");
+        }
+
         StringBuffer sb = new StringBuffer(60);
         sb.append('$');
         sb.append(version);
@@ -83,7 +97,8 @@
 
     /**
      * Creates a 60 character Bcrypt String, including
-     * version, cost factor, salt and hash, separated by '$'
+     * version, cost factor, salt and hash, separated by '$' using version
+     * '2y'.
      *
      * @param cost     the cost factor, treated as an exponent of 2
      * @param salt     a 16 byte salt
@@ -95,6 +110,31 @@
         byte[] salt,
         int cost)
     {
+        return generate(defaultVersion, password, salt, cost);
+    }
+
+
+    /**
+     * Creates a 60 character Bcrypt String, including
+     * version, cost factor, salt and hash, separated by '$'
+     *
+     * @param version  the version, may be 2b, 2y or 2a. (2a is not backwards compatible.)
+     * @param cost     the cost factor, treated as an exponent of 2
+     * @param salt     a 16 byte salt
+     * @param password the password
+     * @return a 60 character Bcrypt String
+     */
+    public static String generate(
+        String version,
+        char[] password,
+        byte[] salt,
+        int cost)
+    {
+        if (!allowedVersions.contains(version))
+        {
+            throw new IllegalArgumentException("Version " + version + " is not accepted by this implementation.");
+        }
+
         if (password == null)
         {
             throw new IllegalArgumentException("Password required.");
@@ -129,7 +169,7 @@
 
         Arrays.fill(psw, (byte)0);
 
-        String rv = createBcryptString(tmp, salt, cost);
+        String rv = createBcryptString(version, tmp, salt, cost);
 
         Arrays.fill(tmp, (byte)0);
 
@@ -156,30 +196,34 @@
             throw new DataLengthException("Bcrypt String length: "
                 + bcryptString.length() + ", 60 required.");
         }
+
         if (bcryptString.charAt(0) != '$'
             || bcryptString.charAt(3) != '$'
             || bcryptString.charAt(6) != '$')
         {
             throw new IllegalArgumentException("Invalid Bcrypt String format.");
         }
-        if (!bcryptString.substring(1, 3).equals(version))
+
+        String version = bcryptString.substring(1, 3);
+
+        if (!allowedVersions.contains(version))
         {
-            throw new IllegalArgumentException("Wrong Bcrypt version, 2a expected.");
+            throw new IllegalArgumentException("Bcrypt version '" + version + "' is not supported by this implementation");
         }
+
         int cost = 0;
+        String costStr = bcryptString.substring(4, 6);
         try
         {
-            cost = Integer.parseInt(bcryptString.substring(4, 6));
+            cost = Integer.parseInt(costStr);
         }
         catch (NumberFormatException nfe)
         {
-            throw new IllegalArgumentException("Invalid cost factor: "
-                + bcryptString.substring(4, 6));
+            throw new IllegalArgumentException("Invalid cost factor: " + costStr);
         }
         if (cost < 4 || cost > 31)
         {
-            throw new IllegalArgumentException("Invalid cost factor: "
-                + cost + ", 4 < cost < 31 expected.");
+            throw new IllegalArgumentException("Invalid cost factor: " + cost + ", 4 < cost < 31 expected.");
         }
         // check password:
         if (password == null)
@@ -190,7 +234,7 @@
             bcryptString.substring(bcryptString.lastIndexOf('$') + 1,
                 bcryptString.length() - 31));
 
-        String newBcryptString = generate(password, salt, cost);
+        String newBcryptString = generate(version, password, salt, cost);
 
         return bcryptString.equals(newBcryptString);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java
index add6714..9e2df35 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java
@@ -1,12 +1,14 @@
 package org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * Generate a random factor suitable for use with RSA blind signatures
@@ -39,7 +41,7 @@
         else
         {
             key = (RSAKeyParameters)param;
-            random = new SecureRandom();
+            random = CryptoServicesRegistrar.getSecureRandom();
         }
 
         if (key instanceof RSAPrivateCrtKeyParameters)
@@ -67,7 +69,7 @@
 
         do
         {
-            factor = new BigInteger(length, random);
+            factor = BigIntegers.createRandomBigInteger(length, random);
             gcd = factor.gcd(m);
         }
         while (factor.equals(ZERO) || factor.equals(ONE) || !gcd.equals(ONE));
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index f23f654..eadbaa6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 import org.bouncycastle.math.Primes;
 import org.bouncycastle.math.ec.WNafUtil;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * an RSA key pair generator.
@@ -20,12 +21,10 @@
     private static final BigInteger ONE = BigInteger.valueOf(1);
 
     private RSAKeyGenerationParameters param;
-    private int iterations;
 
     public void init(KeyGenerationParameters param)
     {
         this.param = (RSAKeyGenerationParameters)param;
-        this.iterations = getNumberOfIterations(this.param.getStrength(), this.param.getCertainty());
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
@@ -161,7 +160,7 @@
     {
         for (int i = 0; i != 5 * bitlength; i++)
         {
-            BigInteger p = new BigInteger(bitlength, 1, param.getRandom());
+            BigInteger p = BigIntegers.createRandomPrime(bitlength, 1, param.getRandom());
 
             if (p.mod(e).equals(ONE))
             {
@@ -191,6 +190,8 @@
 
     protected boolean isProbablePrime(BigInteger x)
     {
+        int iterations = getNumberOfIterations(x.bitLength(), param.getCertainty());
+
         /*
          * Primes class for FIPS 186-4 C.3 primality checking
          */
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
index 0b3dc14..3fdc3d3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
@@ -11,28 +11,31 @@
  * Implementation of the scrypt a password-based key derivation function.
  * <p>
  * Scrypt was created by Colin Percival and is specified in <a
- * href="http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01">draft-josefsson-scrypt-kd</a>
- *
+ * href="https://tools.ietf.org/html/rfc7914">RFC 7914 - The scrypt Password-Based Key Derivation Function</a>
  */
 public class SCrypt
 {
+    private SCrypt()
+    {
+         // not used.
+    }
+
     /**
      * Generate a key using the scrypt key derivation function.
-     * 
-     * @param P the bytes of the pass phrase.
-     * @param S the salt to use for this invocation.
-     * @param N CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than
-     *            <code>2^(128 * r / 8)</code>.
-     * @param r the block size, must be >= 1.
-     * @param p Parallelization parameter. Must be a positive integer less than or equal to
-     *            <code>Integer.MAX_VALUE / (128 * r * 8)</code>.
-     * 
+     *
+     * @param P     the bytes of the pass phrase.
+     * @param S     the salt to use for this invocation.
+     * @param N     CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than
+     *              <code>2^(128 * r / 8)</code>.
+     * @param r     the block size, must be &gt;= 1.
+     * @param p     Parallelization parameter. Must be a positive integer less than or equal to
+     *              <code>Integer.MAX_VALUE / (128 * r * 8)</code>.
      * @param dkLen the length of the key to generate.
      * @return the generated key.
      */
     public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
     {
-        if (P== null)
+        if (P == null)
         {
             throw new IllegalArgumentException("Passphrase P must be provided.");
         }
@@ -40,12 +43,12 @@
         {
             throw new IllegalArgumentException("Salt S must be provided.");
         }
-        if (N <= 1)
+        if (N <= 1 || !isPowerOf2(N))
         {
-            throw new IllegalArgumentException("Cost parameter N must be > 1.");
+            throw new IllegalArgumentException("Cost parameter N must be > 1 and a power of 2");
         }
         // Only value of r that cost (as an int) could be exceeded for is 1
-        if (r == 1 && N > 65536)
+        if (r == 1 && N >= 65536)
         {
             throw new IllegalArgumentException("Cost parameter N must be > 1 and < 65536.");
         }
@@ -102,7 +105,7 @@
     {
         PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
         pGen.init(P, S, 1);
-        KeyParameter key = (KeyParameter) pGen.generateDerivedMacParameters(dkLen * 8);
+        KeyParameter key = (KeyParameter)pGen.generateDerivedMacParameters(dkLen * 8);
         return key.getKey();
     }
 
@@ -140,7 +143,7 @@
         finally
         {
             ClearAll(V);
-            ClearAll(new int[][]{ X, blockX1, blockX2, blockY });
+            ClearAll(new int[][]{X, blockX1, blockX2, blockY});
         }
     }
 
@@ -195,4 +198,10 @@
             Clear(arrays[i]);
         }
     }
+
+    // note: we know X is non-zero
+    private static boolean isPowerOf2(int x)
+    {
+        return ((x & (x - 1)) == 0);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/X25519KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/X25519KeyPairGenerator.java
new file mode 100644
index 0000000..2d622df
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/X25519KeyPairGenerator.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.generators;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+
+public class X25519KeyPairGenerator
+    implements AsymmetricCipherKeyPairGenerator
+{
+    private SecureRandom random;
+
+    public void init(KeyGenerationParameters parameters)
+    {
+        this.random = parameters.getRandom();
+    }
+
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        X25519PrivateKeyParameters privateKey = new X25519PrivateKeyParameters(random);
+        X25519PublicKeyParameters publicKey = privateKey.generatePublicKey();
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/X448KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/X448KeyPairGenerator.java
new file mode 100644
index 0000000..7b311ec
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/X448KeyPairGenerator.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.generators;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+
+public class X448KeyPairGenerator
+    implements AsymmetricCipherKeyPairGenerator
+{
+    private SecureRandom random;
+
+    public void init(KeyGenerationParameters parameters)
+    {
+        this.random = parameters.getRandom();
+    }
+
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        X448PrivateKeyParameters privateKey = new X448PrivateKeyParameters(random);
+        X448PublicKeyParameters publicKey = privateKey.generatePublicKey();
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
index c0ae2a9..71b29a3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -73,7 +73,15 @@
         // If both cofactorMode and oldCofactorMode are set to true
         // then the implementation will use the new cofactor ECDH 
         this.CofactorMode = cofactorMode;
-        this.OldCofactorMode = oldCofactorMode;
+        // https://www.shoup.net/iso/std4.pdf, Page 34.
+        if (cofactorMode)
+        {
+            this.OldCofactorMode = false;
+        }
+        else
+        {
+            this.OldCofactorMode = oldCofactorMode;
+        }
         this.SingleHashMode = singleHashMode;
     }
 
@@ -121,7 +129,7 @@
         BigInteger r = BigIntegers.createRandomInRange(ONE, n, rnd);
 
         // Compute the static-ephemeral key agreement
-        BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r;
+        BigInteger rPrime = OldCofactorMode ? r.multiply(h).mod(n) : r;
 
         ECMultiplier basePointMultiplier = createBasePointMultiplier();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
index d7ad612..d700fd8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.paddings.BlockCipherPadding;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 
@@ -125,7 +126,7 @@
 
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
 
         cipher.processBlock(cfbV, 0, cfbOutV, 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7564Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7564Mac.java
new file mode 100644
index 0000000..cdadc43
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7564Mac.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.digests.DSTU7564Digest;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Implementation of DSTU7564 MAC mode
+ */
+public class DSTU7564Mac
+    implements Mac
+{
+    private static final int BITS_IN_BYTE = 8;
+
+    private DSTU7564Digest engine;
+
+    private int macSize;
+
+    private byte[] paddedKey;
+    private byte[] invertedKey;
+
+    private long inputLength;
+
+    public DSTU7564Mac(int macBitSize)
+    {
+        /* Mac size can be only 256 / 384 / 512. Same as hash size for DSTU7654Digest */
+        this.engine = new DSTU7564Digest(macBitSize);
+        this.macSize = macBitSize / BITS_IN_BYTE;
+
+        this.paddedKey = null;
+        this.invertedKey = null;
+    }
+
+    public void init(CipherParameters params)
+        throws IllegalArgumentException
+    {
+        if (params instanceof KeyParameter)
+        {
+            byte[] key = ((KeyParameter)params).getKey();
+
+            invertedKey = new byte[key.length];
+
+            paddedKey = padKey(key);
+
+            for (int byteIndex = 0; byteIndex < invertedKey.length; byteIndex++)
+            {
+                invertedKey[byteIndex] = (byte)(key[byteIndex] ^ (byte)0xFF);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("Bad parameter passed");
+        }
+
+        engine.update(paddedKey, 0, paddedKey.length);
+    }
+
+    public String getAlgorithmName()
+    {
+        return "DSTU7564Mac";
+    }
+
+    public int getMacSize()
+    {
+        return macSize;
+    }
+
+    public void update(byte in)
+        throws IllegalStateException
+    {
+        engine.update(in);
+        inputLength++;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+        throws DataLengthException, IllegalStateException
+    {
+        if (in.length - inOff < len)
+        {
+            throw new DataLengthException("Input buffer too short");
+        }
+
+        if (paddedKey == null)
+        {
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
+        }
+
+        engine.update(in, inOff, len);
+        inputLength += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (paddedKey == null)
+        {
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
+        }
+        if (out.length - outOff < macSize)
+        {
+            throw new OutputLengthException("Output buffer too short");
+        }
+
+        pad();
+
+        engine.update(invertedKey, 0, invertedKey.length);
+
+        inputLength = 0;
+
+        return engine.doFinal(out, outOff);
+    }
+
+    public void reset()
+    {
+        inputLength = 0;
+        engine.reset();
+        if (paddedKey != null)
+        {
+            engine.update(paddedKey, 0, paddedKey.length);
+        }
+    }
+
+    private void pad()
+    {
+        int extra = engine.getByteLength() - (int)(inputLength % engine.getByteLength());
+        if (extra < 13)  // terminator byte + 96 bits of length
+        {
+            extra += engine.getByteLength();
+        }
+
+        byte[] padded = new byte[extra];
+           
+        padded[0] = (byte)0x80; // Defined in standard;
+
+        // Defined in standard;
+        Pack.longToLittleEndian(inputLength * BITS_IN_BYTE, padded, padded.length - 12);
+
+        engine.update(padded, 0, padded.length);
+    }
+
+    private byte[] padKey(byte[] in)
+    {
+        int paddedLen = ((in.length + engine.getByteLength() - 1) / engine.getByteLength()) * engine.getByteLength();
+
+        int extra = engine.getByteLength() - (int)(in.length % engine.getByteLength());
+        if (extra < 13)  // terminator byte + 96 bits of length
+        {
+            paddedLen += engine.getByteLength();
+        }
+
+        byte[] padded = new byte[paddedLen];
+
+        System.arraycopy(in, 0, padded, 0, in.length);
+  
+        padded[in.length] = (byte)0x80; // Defined in standard;
+        Pack.intToLittleEndian(in.length * BITS_IN_BYTE, padded, padded.length - 12); // Defined in standard;
+
+        return padded;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7624Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7624Mac.java
new file mode 100644
index 0000000..01c46f1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/DSTU7624Mac.java
@@ -0,0 +1,163 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.engines.DSTU7624Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of DSTU7624 MAC mode
+ */
+public class DSTU7624Mac
+    implements Mac
+{
+    private final static int BITS_IN_BYTE = 8;
+
+    private byte[]              buf;
+    private int                 bufOff;
+
+    private int macSize;
+    private int blockSize;
+    private DSTU7624Engine engine;
+
+    private byte[] c, cTemp, kDelta;
+
+    public DSTU7624Mac(int blockBitLength, int q)
+    {
+        this.engine = new DSTU7624Engine(blockBitLength);
+        this.blockSize = blockBitLength / BITS_IN_BYTE;
+        this.macSize = q / BITS_IN_BYTE;
+        this.c = new byte[blockSize];
+        this.kDelta = new byte[blockSize];
+        this.cTemp = new byte[blockSize];
+        this.buf = new byte[blockSize];
+    }
+
+    public void init(CipherParameters params)
+        throws IllegalArgumentException
+    {
+        if (params instanceof KeyParameter)
+        {
+            engine.init(true, params);
+            engine.processBlock(kDelta, 0, kDelta, 0);
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid parameter passed to DSTU7624Mac");
+        }
+    }
+
+    public String getAlgorithmName()
+    {
+        return "DSTU7624Mac";
+    }
+
+    public int getMacSize()
+    {
+        return macSize;
+    }
+
+    public void update(byte in)
+    {
+        if (bufOff == buf.length)
+        {
+            processBlock(buf, 0);
+            bufOff = 0;
+        }
+
+        buf[bufOff++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException(
+                "can't have a negative input length!");
+        }
+
+        int blockSize = engine.getBlockSize();
+        int gapLen = blockSize - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            processBlock(buf, 0);
+
+            bufOff = 0;
+            len -= gapLen;
+            inOff += gapLen;
+
+            while (len > blockSize)
+            {
+                processBlock(in, inOff);
+
+                len -= blockSize;
+                inOff += blockSize;
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+    }
+
+    private void processBlock(byte[] in, int inOff)
+    {
+        xor(c, 0, in, inOff, cTemp);
+
+        engine.processBlock(cTemp, 0, c, 0);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (bufOff % buf.length != 0)
+        {
+            throw new DataLengthException("input must be a multiple of blocksize");
+        }
+
+        //Last block
+        xor(c, 0, buf, 0, cTemp);
+        xor(cTemp, 0, kDelta, 0, c);
+        engine.processBlock(c, 0, c, 0);
+
+        if (macSize + outOff > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        System.arraycopy(c, 0, out, outOff, macSize);
+
+        return macSize;
+    }
+
+    public void reset()
+    {
+        Arrays.fill(c, (byte)0x00);
+        Arrays.fill(cTemp, (byte)0x00);
+        Arrays.fill(kDelta, (byte)0x00);
+        Arrays.fill(buf, (byte)0x00);
+        engine.reset();
+        engine.processBlock(kDelta, 0, kDelta, 0);
+        bufOff = 0;
+    }
+
+    private void xor(byte[] x, int xOff, byte[] y, int yOff, byte[] x_xor_y)
+    {
+
+        if (x.length - xOff < blockSize || y.length - yOff < blockSize || x_xor_y.length < blockSize)
+        {
+            throw new IllegalArgumentException("some of input buffers too short");
+        }
+        for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
+        {
+            x_xor_y[byteIndex] = (byte)(x[byteIndex + xOff] ^ y[byteIndex + yOff]);
+        }
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java
index b71975b..28e8d89 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/GOST28147Mac.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.crypto.params.ParametersWithSBox;
 
 /**
@@ -19,6 +20,7 @@
     private byte[]              mac;
     private boolean             firstStep = true;
     private int[]               workingKey = null;
+    private byte[]              macIV = null;
 
     //
     // This is default S-box - E_A.
@@ -64,6 +66,7 @@
     {
         reset();
         buf = new byte[blockSize];
+        macIV = null;
         if (params instanceof ParametersWithSBox)
         {
             ParametersWithSBox   param = (ParametersWithSBox)params;
@@ -85,6 +88,14 @@
         {
             workingKey = generateWorkingKey(((KeyParameter)params).getKey());
         }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV p = (ParametersWithIV)params;
+
+            workingKey = generateWorkingKey(((KeyParameter)p.getParameters()).getKey());
+            System.arraycopy(p.getIV(), 0, mac, 0, mac.length);
+            macIV = p.getIV(); // don't skip the initial CM5Func
+        }
         else
         {
            throw new IllegalArgumentException("invalid parameter passed to GOST28147 init - " + params.getClass().getName());
@@ -190,6 +201,10 @@
             if (firstStep)
             {
                 firstStep = false;
+                if (macIV != null)
+                {
+                    sumbuf = CM5func(buf, 0, macIV);
+                }
             }
             else
             {
@@ -223,6 +238,10 @@
                 if (firstStep)
                 {
                     firstStep = false;
+                    if (macIV != null)
+                    {
+                        sumbuf = CM5func(buf, 0, macIV);
+                    }
                 }
                 else
                 {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/KGMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/KGMac.java
new file mode 100644
index 0000000..7b6b308
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/KGMac.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.modes.KGCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+/**
+ * The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication
+ * 800-38D as adapted for the Kalyna version of GCM.
+ * <p>
+ * KGMac is an invocation of the KGCM mode where no data is encrypted (i.e. all input data to the Mac
+ * is processed as additional authenticated data with the underlying KGCM block cipher).
+ */
+public class KGMac
+    implements Mac
+{
+    private final KGCMBlockCipher cipher;
+    private final int macSizeBits;
+
+    /**
+     * Creates a KGMAC based on the operation of a block cipher in GCM mode.
+     * <p>
+     * This will produce an authentication code the length of the block size of the cipher.
+     *
+     * @param cipher
+     *            the cipher to be used in GCM mode to generate the MAC.
+     */
+    public KGMac(final KGCMBlockCipher cipher)
+    {
+        // use of this confused flow analyser in some earlier JDKs
+        this.cipher = cipher;
+        this.macSizeBits = cipher.getUnderlyingCipher().getBlockSize() * 8;
+    }
+
+    /**
+     * Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode.
+     *
+     * @param macSizeBits
+     *            the mac size to generate, in bits. Must be a multiple of 8 and &gt;= 32 and &lt;= 128.
+     *            Sizes less than 96 are not recommended, but are supported for specialized applications.
+     * @param cipher
+     *            the cipher to be used in GCM mode to generate the MAC.
+     */
+    public KGMac(final KGCMBlockCipher cipher, final int macSizeBits)
+    {
+        this.cipher = cipher;
+        this.macSizeBits = macSizeBits;
+    }
+
+    /**
+     * Initialises the GMAC - requires a {@link ParametersWithIV} providing a {@link KeyParameter}
+     * and a nonce.
+     */
+    public void init(final CipherParameters params) throws IllegalArgumentException
+    {
+        if (params instanceof ParametersWithIV)
+        {
+            final ParametersWithIV param = (ParametersWithIV)params;
+
+            final byte[] iv = param.getIV();
+            final KeyParameter keyParam = (KeyParameter)param.getParameters();
+
+            // GCM is always operated in encrypt mode to calculate MAC
+            cipher.init(true, new AEADParameters(keyParam, macSizeBits, iv));
+        }
+        else
+        {
+            throw new IllegalArgumentException("KGMAC requires ParametersWithIV");
+        }
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getUnderlyingCipher().getAlgorithmName() + "-KGMAC";
+    }
+
+    public int getMacSize()
+    {
+        return macSizeBits / 8;
+    }
+
+    public void update(byte in) throws IllegalStateException
+    {
+        cipher.processAADByte(in);
+    }
+
+    public void update(byte[] in, int inOff, int len)
+        throws DataLengthException, IllegalStateException
+    {
+        cipher.processAADBytes(in, inOff, len);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        try
+        {
+            return cipher.doFinal(out, outOff);
+        }
+        catch (InvalidCipherTextException e)
+        {
+            // Impossible in encrypt mode
+            throw new IllegalStateException(e.toString());
+        }
+    }
+
+    public void reset()
+    {
+        cipher.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
index 85a9581..382b6fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -249,7 +250,7 @@
     {
         if (outOff + BLOCK_SIZE > out.length)
         {
-            throw new DataLengthException("Output buffer is too short.");
+            throw new OutputLengthException("Output buffer is too short.");
         }
 
         if (currentBlockOffset > 0)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index 64b076d..ea80706 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.StreamBlockCipher;
 
 /**
@@ -135,7 +136,7 @@
         {
             if ((outOff + length) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
         }
 
@@ -192,7 +193,7 @@
     {
         if (bufOff + outOff > out.length)
         {
-            throw new DataLengthException("output buffer to small in doFinal");
+            throw new OutputLengthException("output buffer to small in doFinal");
         }
 
         int     blockSize = cipher.getBlockSize();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CBCBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CBCBlockCipher.java
new file mode 100644
index 0000000..97ba1f9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CBCBlockCipher.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An implementation of the CBC mode for GOST 3412 2015 cipher.
+ * See  <a href="http://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf">GOST R 3413 2015</a>
+ */
+public class G3413CBCBlockCipher
+    implements BlockCipher
+{
+
+    private int m;
+    private int blockSize;
+    private byte[] R;
+    private byte[] R_init;
+    private BlockCipher cipher;
+    private boolean initialized = false;
+    private boolean forEncryption;
+
+    /**
+     * @param cipher base cipher
+     */
+    public G3413CBCBlockCipher(BlockCipher cipher)
+    {
+        this.blockSize = cipher.getBlockSize();
+        this.cipher = cipher;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+        if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV ivParam = (ParametersWithIV)params;
+
+            byte[] iv = ivParam.getIV();
+
+            if (iv.length < blockSize)
+            {
+                throw new IllegalArgumentException("Parameter m must blockSize <= m");
+            }
+            this.m = iv.length;
+
+            initArrays();
+
+            R_init = Arrays.clone(iv);
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+            // if null it's an IV changed only.
+            if (ivParam.getParameters() != null)
+            {
+                cipher.init(forEncryption, ivParam.getParameters());
+            }
+        }
+        else
+        {
+            setupDefaultParams();
+
+            initArrays();
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+            // if it's null, key is to be reused.
+            if (params != null)
+            {
+                cipher.init(forEncryption, params);
+            }
+        }
+
+        initialized = true;
+    }
+    
+    /**
+     * allocate memory for R and R_init arrays
+     */
+    private void initArrays()
+    {
+        R = new byte[m];
+        R_init = new byte[m];
+    }
+
+    /**
+     * this method sets default values to <b>m</b> parameter:<br>
+     * m = <b>blockSize</b>
+     */
+    private void setupDefaultParams()
+    {
+        this.m = blockSize;
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName() + "/CBC";
+    }
+
+    public int getBlockSize()
+    {
+        return blockSize;
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        return (forEncryption) ? encrypt(in, inOff, out, outOff) : decrypt(in, inOff, out, outOff);
+    }
+
+
+    private int encrypt(byte[] in, int inOff, byte[] out, int outOff)
+    {
+
+        byte[] msb = GOST3413CipherUtil.MSB(R, blockSize);
+        byte[] input = GOST3413CipherUtil.copyFromInput(in, blockSize, inOff);
+        byte[] sum = GOST3413CipherUtil.sum(input, msb);
+        byte[] c = new byte[sum.length];
+        cipher.processBlock(sum, 0, c, 0);
+
+        System.arraycopy(c, 0, out, outOff, c.length);
+
+        if (out.length > (outOff + sum.length))
+        {
+            generateR(c);
+        }
+
+        return c.length;
+    }
+
+
+    private int decrypt(byte[] in, int inOff, byte[] out, int outOff)
+    {
+
+        byte[] msb = GOST3413CipherUtil.MSB(R, blockSize);
+        byte[] input = GOST3413CipherUtil.copyFromInput(in, blockSize, inOff);
+
+        byte[] c = new byte[input.length];
+        cipher.processBlock(input, 0, c, 0);
+
+        byte[] sum = GOST3413CipherUtil.sum(c, msb);
+
+        System.arraycopy(sum, 0, out, outOff, sum.length);
+
+
+        if (out.length > (outOff + sum.length))
+        {
+            generateR(input);
+        }
+
+        return sum.length;
+    }
+
+    /**
+     * generate new R value
+     *
+     * @param C processed block
+     */
+    private void generateR(byte[] C)
+    {
+        byte[] buf = GOST3413CipherUtil.LSB(R, m - blockSize);
+        System.arraycopy(buf, 0, R, 0, buf.length);
+        System.arraycopy(C, 0, R, buf.length, m - buf.length);
+    }
+
+
+    public void reset()
+    {
+        if (initialized)
+        {
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+            cipher.reset();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CFBBlockCipher.java
new file mode 100644
index 0000000..34afce2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CFBBlockCipher.java
@@ -0,0 +1,219 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An implementation of the CFB mode for GOST 3412 2015 cipher.
+ * See  <a href="http://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf">GOST R 3413 2015</a>
+ */
+public class G3413CFBBlockCipher
+    extends StreamBlockCipher
+{
+    private final int s;
+    private int m;
+    private int blockSize;
+    private byte[] R;
+    private byte[] R_init;
+    private BlockCipher cipher;
+    private boolean forEncryption;
+    private boolean initialized = false;
+
+    private byte[] gamma;
+    private byte[] inBuf;
+    private int byteCount;
+
+    /**
+     * Base constructor.
+     *
+     * @param cipher base cipher
+     */
+    public G3413CFBBlockCipher(BlockCipher cipher)
+    {
+        this(cipher, cipher.getBlockSize() * 8);
+    }
+
+    /**
+     * Base constructor with specific block size.
+     *
+     * @param cipher base cipher
+     * @param bitBlockSize basic unit (defined as s)
+     */
+    public G3413CFBBlockCipher(BlockCipher cipher, int bitBlockSize)
+    {
+        super(cipher);
+
+        if (bitBlockSize < 0 || bitBlockSize > cipher.getBlockSize() * 8)
+        {
+            throw new IllegalArgumentException("Parameter bitBlockSize must be in range 0 < bitBlockSize <= "
+                            + cipher.getBlockSize() * 8);
+        }
+
+        this.blockSize = cipher.getBlockSize();
+        this.cipher = cipher;
+        this.s = bitBlockSize / 8;
+        inBuf = new byte[getBlockSize()];
+    }
+
+    /**
+     * Initialise the cipher and initialisation vector R.
+     * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+     * An IV which is too short is handled in FIPS compliant fashion.
+     * R_init = IV, and R1 = R_init
+     *
+     * @param forEncryption ignored because encryption and decryption are same
+     * @param params        the key and other data required by the cipher.
+     * @throws IllegalArgumentException
+     */
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+        if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV ivParam = (ParametersWithIV)params;
+
+            byte[] iv = ivParam.getIV();
+
+            if (iv.length < blockSize)
+            {
+                throw new IllegalArgumentException("Parameter m must blockSize <= m");
+            }
+            m = iv.length;
+
+            initArrays();
+
+            R_init = Arrays.clone(iv);
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+
+            // if null it's an IV changed only.
+            if (ivParam.getParameters() != null)
+            {
+                cipher.init(true, ivParam.getParameters());
+            }
+        }
+        else
+        {
+            setupDefaultParams();
+
+            initArrays();
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+
+            // if it's null, key is to be reused.
+            if (params != null)
+            {
+                cipher.init(true, params);
+            }
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * allocate memory for R and R_init arrays
+     */
+    private void initArrays()
+    {
+        R = new byte[m];
+        R_init = new byte[m];
+    }
+
+    /**
+     * this method sets default values to <b>s</b> and <b>m</b> parameters:<br>
+     * s = <b>blockSize</b>; <br>
+     * m = <b>2 * blockSize</b>
+     */
+    private void setupDefaultParams()
+    {
+        this.m = 2 * blockSize;
+    }
+
+
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
+    }
+
+    public int getBlockSize()
+    {
+        return s;
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        this.processBytes(in, inOff, getBlockSize(), out, outOff);
+
+        return getBlockSize();
+    }
+
+    protected byte calculateByte(byte in)
+    {
+        if (byteCount == 0)
+        {
+            gamma = createGamma();
+        }
+
+        byte rv = (byte)(gamma[byteCount] ^ in);
+        inBuf[byteCount++] = (forEncryption) ? rv : in;
+
+        if (byteCount == getBlockSize())
+        {
+            byteCount = 0;
+            generateR(inBuf);
+        }
+
+        return rv;
+    }
+
+    /**
+     * creating gamma value
+     *
+     * @return gamma
+     */
+    byte[] createGamma()
+    {
+        byte[] msb = GOST3413CipherUtil.MSB(R, blockSize);
+        byte[] encryptedMsb = new byte[msb.length];
+        cipher.processBlock(msb, 0, encryptedMsb, 0);
+        return GOST3413CipherUtil.MSB(encryptedMsb, s);
+    }
+
+    /**
+     * generate new R value
+     *
+     * @param C processed block
+     */
+    void generateR(byte[] C)
+    {
+
+        byte[] buf = GOST3413CipherUtil.LSB(R, m - s);
+        System.arraycopy(buf, 0, R, 0, buf.length);
+        System.arraycopy(C, 0, R, buf.length, m - buf.length);
+    }
+
+    /**
+     * copy R_init into R and reset the underlying
+     * cipher.
+     */
+    public void reset()
+    {
+
+        byteCount = 0;
+        Arrays.clear(inBuf);
+        Arrays.clear(gamma);
+
+        if (initialized)
+        {
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+            cipher.reset();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CTRBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CTRBlockCipher.java
new file mode 100644
index 0000000..9e4a677
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413CTRBlockCipher.java
@@ -0,0 +1,228 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * implements the GOST 3412 2015 CTR counter mode (GCTR).
+ */
+public class G3413CTRBlockCipher
+    extends StreamBlockCipher
+{
+
+
+    private final int s;
+    private byte[] CTR;
+    private byte[] IV;
+    private byte[] buf;
+    private final int blockSize;
+    private final BlockCipher cipher;
+    private int byteCount = 0;
+    private boolean initialized;
+
+
+    /**
+     * Basic constructor.
+     *
+     * @param cipher the block cipher to be used as the basis of the
+     *               counter mode (must have a 64 bit block size).
+     */
+    public G3413CTRBlockCipher(
+        BlockCipher cipher)
+    {
+        this(cipher, cipher.getBlockSize() * 8);
+    }
+
+    /**
+     * Basic constructor.
+     *
+     * @param cipher       the block cipher to be used as the basis of the
+     *                     counter mode (must have a 64 bit block size).
+     * @param bitBlockSize basic unit (defined as s)
+     */
+    public G3413CTRBlockCipher(BlockCipher cipher, int bitBlockSize)
+    {
+        super(cipher);
+
+        if (bitBlockSize < 0 || bitBlockSize > cipher.getBlockSize() * 8)
+        {
+            throw new IllegalArgumentException("Parameter bitBlockSize must be in range 0 < bitBlockSize <= "
+                            + cipher.getBlockSize() * 8);
+        }
+
+        this.cipher = cipher;
+        this.blockSize = cipher.getBlockSize();
+        this.s = bitBlockSize / 8;
+        CTR = new byte[blockSize];
+    }
+
+    /**
+     * Initialise the cipher and, possibly, the initialisation vector (IV).
+     * If an IV isn't passed as part of the parameter, the IV will be all zeros.
+     * An IV which is too short is handled in FIPS compliant fashion.
+     *
+     * @param encrypting if true the cipher is initialised for
+     *                   encryption, if false for decryption.
+     * @param params     the key and other data required by the cipher.
+     * @throws IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean encrypting, //ignored by this CTR mode
+        CipherParameters params)
+        throws IllegalArgumentException
+    {
+
+        if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV ivParam = (ParametersWithIV)params;
+
+            initArrays();
+
+            IV = Arrays.clone(ivParam.getIV());
+
+            if (IV.length != blockSize / 2)
+            {
+                throw new IllegalArgumentException("Parameter IV length must be == blockSize/2");
+            }
+
+            System.arraycopy(IV, 0, CTR, 0, IV.length);
+            for (int i = IV.length; i < blockSize; i++)
+            {
+                CTR[i] = 0;
+            }
+
+            // if null it's an IV changed only.
+            if (ivParam.getParameters() != null)
+            {
+                cipher.init(true, ivParam.getParameters());
+            }
+        }
+        else
+        {
+            initArrays();
+
+            // if it's null, key is to be reused.
+            if (params != null)
+            {
+                cipher.init(true, params);
+            }
+        }
+
+        initialized = true;
+    }
+    
+    private void initArrays()
+    {
+        IV = new byte[blockSize / 2];
+        CTR = new byte[blockSize];
+        buf = new byte[s];
+    }
+
+    /**
+     * return the algorithm name and mode.
+     *
+     * @return the name of the underlying algorithm followed by "/GCTR"
+     * and the block size in bits
+     */
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName() + "/GCTR";
+    }
+
+    /**
+     * return the block size we are operating at (in bytes).
+     *
+     * @return the block size we are operating at (in bytes).
+     */
+    public int getBlockSize()
+    {
+        return s;
+    }
+
+    /**
+     * Process one block of input from the array in and write it to
+     * the out array.
+     *
+     * @param in     the array containing the input data.
+     * @param inOff  offset into the in array the data starts at.
+     * @param out    the array the output data will be copied into.
+     * @param outOff the offset into the out array the output will start at.
+     * @return the number of bytes processed and produced.
+     * @throws DataLengthException if there isn't enough data in in, or
+     * space in out.
+     * @throws IllegalStateException if the cipher isn't initialised.
+     */
+    public int processBlock(
+        byte[] in,
+        int inOff,
+        byte[] out,
+        int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        processBytes(in, inOff, s, out, outOff);
+
+        return s;
+    }
+
+    protected byte calculateByte(byte in)
+    {
+
+        if (byteCount == 0)
+        {
+            buf = generateBuf();
+        }
+
+        byte rv = (byte)(buf[byteCount] ^ in);
+        byteCount++;
+
+        if (byteCount == s)
+        {
+            byteCount = 0;
+            generateCRT();
+        }
+
+        return rv;
+
+    }
+
+    private void generateCRT()
+    {
+        CTR[CTR.length - 1]++;
+    }
+
+
+    private byte[] generateBuf()
+    {
+
+        byte[] encryptedCTR = new byte[CTR.length];
+        cipher.processBlock(CTR, 0, encryptedCTR, 0);
+
+        return GOST3413CipherUtil.MSB(encryptedCTR, s);
+
+    }
+
+
+    /**
+     * reset the feedback vector back to the IV and reset the underlying
+     * cipher.
+     */
+    public void reset()
+    {
+        if (initialized)
+        {
+            System.arraycopy(IV, 0, CTR, 0, IV.length);
+            for (int i = IV.length; i < blockSize; i++)
+            {
+                CTR[i] = 0;
+            }
+            byteCount = 0;
+            cipher.reset();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413OFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413OFBBlockCipher.java
new file mode 100644
index 0000000..148f382
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/G3413OFBBlockCipher.java
@@ -0,0 +1,173 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An implementation of the OFB mode for GOST 3412 2015 cipher.
+ * See  <a href="http://www.tc26.ru/standard/gost/GOST_R_3413-2015.pdf">GOST R 3413 2015</a>
+ */
+public class G3413OFBBlockCipher
+    extends StreamBlockCipher
+{
+    //    private int s;
+    private int m;
+    private int blockSize;
+    private byte[] R;
+    private byte[] R_init;
+    private byte[] Y;
+    private BlockCipher cipher;
+    private int byteCount;
+    private boolean initialized = false;
+
+    /**
+     * @param cipher base cipher
+     */
+    public G3413OFBBlockCipher(BlockCipher cipher)
+    {
+        super(cipher);
+        this.blockSize = cipher.getBlockSize();
+        this.cipher = cipher;
+        Y = new byte[blockSize];
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV ivParam = (ParametersWithIV)params;
+
+            byte[] iv = ivParam.getIV();
+
+            if (iv.length < blockSize)
+            {
+                throw new IllegalArgumentException("Parameter m must blockSize <= m");
+            }
+            this.m = iv.length;
+
+            initArrays();
+
+            R_init = Arrays.clone(iv);
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+
+            // if null it's an IV changed only.
+            if (ivParam.getParameters() != null)
+            {
+                cipher.init(true, ivParam.getParameters());
+            }
+
+
+        }
+        else
+        {
+
+            setupDefaultParams();
+
+            initArrays();
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+
+            // if it's null, key is to be reused.
+            if (params != null)
+            {
+                cipher.init(true, params);
+            }
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * allocate memory for R and R_init arrays
+     */
+    private void initArrays()
+    {
+        R = new byte[m];
+        R_init = new byte[m];
+    }
+
+    /**
+     * this method sets default values to <b>s</b> and <b>m</b> parameters:<br>
+     * s = <b>blockSize</b>; <br>
+     * m = <b>2 * blockSize</b>
+     */
+    private void setupDefaultParams()
+    {
+        this.m = 2 * blockSize;
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName() + "/OFB";
+    }
+
+    public int getBlockSize()
+    {
+        return blockSize;
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        processBytes(in, inOff, blockSize, out, outOff);
+        return blockSize;
+    }
+
+
+    protected byte calculateByte(byte in)
+    {
+        if (byteCount == 0)
+        {
+            generateY();
+        }
+
+        byte rv = (byte)(Y[byteCount] ^ in);
+        byteCount++;
+
+        if (byteCount == getBlockSize())
+        {
+            byteCount = 0;
+            generateR();
+        }
+
+        return rv;
+    }
+
+    /**
+     * generate new Y value
+     */
+    private void generateY()
+    {
+        byte[] msb = GOST3413CipherUtil.MSB(R, blockSize);
+        cipher.processBlock(msb, 0, Y, 0);
+    }
+
+
+    /**
+     * generate new R value
+     */
+    private void generateR()
+    {
+        byte[] buf = GOST3413CipherUtil.LSB(R, m - blockSize);
+        System.arraycopy(buf, 0, R, 0, buf.length);
+        System.arraycopy(Y, 0, R, buf.length, m - buf.length);
+    }
+
+
+    public void reset()
+    {
+        if (initialized)
+        {
+            System.arraycopy(R_init, 0, R, 0, R_init.length);
+            Arrays.clear(Y);
+            byteCount = 0;
+            cipher.reset();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index e9235c4..3a11e9b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -5,11 +5,11 @@
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.modes.gcm.BasicGCMExponentiator;
 import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
 import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
 import org.bouncycastle.crypto.modes.gcm.GCMUtil;
-import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
-import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
 import org.bouncycastle.crypto.params.AEADParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -68,8 +68,7 @@
 
         if (m == null)
         {
-            // TODO Consider a static property specifying default multiplier
-            m = new Tables8kGCMMultiplier();
+            m = new Tables4kGCMMultiplier();
         }
 
         this.cipher = c;
@@ -250,6 +249,7 @@
     public void processAADByte(byte in)
     {
         checkStatus();
+
         atBlock[atBlockPos] = in;
         if (++atBlockPos == BLOCK_SIZE)
         {
@@ -262,6 +262,8 @@
 
     public void processAADBytes(byte[] in, int inOff, int len)
     {
+        checkStatus();
+
         for (int i = 0; i < len; ++i)
         {
             atBlock[atBlockPos] = in[inOff + i];
@@ -300,11 +302,20 @@
         throws DataLengthException
     {
         checkStatus();
-        
+
         bufBlock[bufOff] = in;
         if (++bufOff == bufBlock.length)
         {
-            outputBlock(out, outOff);
+            processBlock(bufBlock, 0, out, outOff);
+            if (forEncryption)
+            {
+                bufOff = 0;
+            }
+            else
+            {
+                System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+                bufOff = macSize;
+            }
             return BLOCK_SIZE;
         }
         return 0;
@@ -315,47 +326,63 @@
     {
         checkStatus();
 
-        if (in.length < (inOff + len))
+        if ((in.length - inOff) < len)
         {
             throw new DataLengthException("Input buffer too short");
         }
+
         int resultLen = 0;
 
-        for (int i = 0; i < len; ++i)
+        if (forEncryption)
         {
-            bufBlock[bufOff] = in[inOff + i];
-            if (++bufOff == bufBlock.length)
+            if (bufOff != 0)
             {
-                outputBlock(out, outOff + resultLen);
+                while (len > 0)
+                {
+                    --len;
+                    bufBlock[bufOff] = in[inOff++];
+                    if (++bufOff == BLOCK_SIZE)
+                    {
+                        processBlock(bufBlock, 0, out, outOff);
+                        bufOff = 0;
+                        resultLen += BLOCK_SIZE;
+                        break;
+                    }
+                }
+            }
+
+            while (len >= BLOCK_SIZE)
+            {
+                processBlock(in, inOff, out, outOff + resultLen);
+                inOff += BLOCK_SIZE;
+                len -= BLOCK_SIZE;
                 resultLen += BLOCK_SIZE;
             }
+
+            if (len > 0)
+            {
+                System.arraycopy(in, inOff, bufBlock, 0, len);
+                bufOff = len;
+            }
+        }
+        else
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                bufBlock[bufOff] = in[inOff + i];
+                if (++bufOff == bufBlock.length)
+                {
+                    processBlock(bufBlock, 0, out, outOff + resultLen);
+                    System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+                    bufOff = macSize;
+                    resultLen += BLOCK_SIZE;
+                }
+            }
         }
 
         return resultLen;
     }
 
-    private void outputBlock(byte[] output, int offset)
-    {
-        if (output.length < (offset + BLOCK_SIZE))
-        {
-            throw new OutputLengthException("Output buffer too short");
-        }
-        if (totalLength == 0)
-        {
-            initCipher();
-        }
-        gCTRBlock(bufBlock, output, offset);
-        if (forEncryption)
-        {
-            bufOff = 0;
-        }
-        else
-        {
-            System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
-            bufOff = macSize;
-        }
-    }
-
     public int doFinal(byte[] out, int outOff)
         throws IllegalStateException, InvalidCipherTextException
     {
@@ -370,7 +397,7 @@
 
         if (forEncryption)
         {
-            if (out.length < (outOff + extra + macSize))
+            if ((out.length - outOff) < (extra + macSize))
             {
                 throw new OutputLengthException("Output buffer too short");
             }
@@ -383,7 +410,7 @@
             }
             extra -= macSize;
 
-            if (out.length < (outOff + extra))
+            if ((out.length - outOff) < extra)
             {
                 throw new OutputLengthException("Output buffer too short");
             }
@@ -391,7 +418,7 @@
 
         if (extra > 0)
         {
-            gCTRPartial(bufBlock, 0, extra, out, outOff);
+            processPartial(bufBlock, 0, extra, out, outOff);
         }
 
         atLength += atBlockPos;
@@ -424,7 +451,7 @@
             byte[] H_c = new byte[16];
             if (exp == null)
             {
-                exp = new Tables1kGCMExponentiator();
+                exp = new BasicGCMExponentiator();
                 exp.init(H);
             }
             exp.exponentiateX(c, H_c);
@@ -523,27 +550,52 @@
         }
     }
 
-    private void gCTRBlock(byte[] block, byte[] out, int outOff)
+    private void processBlock(byte[] buf, int bufOff, byte[] out, int outOff)
     {
-        byte[] tmp = getNextCounterBlock();
+        if ((out.length - outOff) < BLOCK_SIZE)
+        {
+            throw new OutputLengthException("Output buffer too short");
+        }
+        if (totalLength == 0)
+        {
+            initCipher();
+        }
 
-        GCMUtil.xor(tmp, block);
-        System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
+        byte[] ctrBlock = new byte[BLOCK_SIZE];
+        getNextCTRBlock(ctrBlock);
 
-        gHASHBlock(S, forEncryption ? tmp : block);
+        if (forEncryption)
+        {
+            GCMUtil.xor(ctrBlock, buf, bufOff);
+            gHASHBlock(S, ctrBlock);
+            System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+        }
+        else
+        {
+            gHASHBlock(S, buf, bufOff);
+            GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+        }
 
         totalLength += BLOCK_SIZE;
     }
 
-    private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff)
+    private void processPartial(byte[] buf, int off, int len, byte[] out, int outOff)
     {
-        byte[] tmp = getNextCounterBlock();
+        byte[] ctrBlock = new byte[BLOCK_SIZE];
+        getNextCTRBlock(ctrBlock);
 
-        GCMUtil.xor(tmp, buf, off, len);
-        System.arraycopy(tmp, 0, out, outOff, len);
+        if (forEncryption)
+        {
+            GCMUtil.xor(buf, off, ctrBlock, 0, len);
+            gHASHPartial(S, buf, off, len);
+        }
+        else
+        {
+            gHASHPartial(S, buf, off, len);
+            GCMUtil.xor(buf, off, ctrBlock, 0, len);
+        }
 
-        gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
-
+        System.arraycopy(buf, off, out, outOff, len);
         totalLength += len;
     }
 
@@ -562,13 +614,19 @@
         multiplier.multiplyH(Y);
     }
 
+    private void gHASHBlock(byte[] Y, byte[] b, int off)
+    {
+        GCMUtil.xor(Y, b, off);
+        multiplier.multiplyH(Y);
+    }
+
     private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
     {
         GCMUtil.xor(Y, b, off, len);
         multiplier.multiplyH(Y);
     }
 
-    private byte[] getNextCounterBlock()
+    private void getNextCTRBlock(byte[] block)
     {
         if (blocksRemaining == 0)
         {
@@ -582,10 +640,7 @@
         c += counter[13] & 0xFF; counter[13] = (byte)c; c >>>= 8;
         c += counter[12] & 0xFF; counter[12] = (byte)c;
 
-        byte[] tmp = new byte[BLOCK_SIZE];
-        // TODO Sure would be nice if ciphers could operate on int[]
-        cipher.processBlock(counter, 0, tmp, 0);
-        return tmp;
+        cipher.processBlock(counter, 0, block, 0);
     }
 
     private void checkStatus()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOST3413CipherUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOST3413CipherUtil.java
new file mode 100644
index 0000000..cfcb7d0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOST3413CipherUtil.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Some methods for GOST 3412 cipher algorithm
+ */
+class GOST3413CipherUtil
+{
+    /**
+     * copy first <b>size</b> elements from <b>from</b>
+     *
+     * @param from source array
+     * @param size size of new array
+     * @return
+     */
+    public static byte[] MSB(byte[] from, int size)
+    {
+        return Arrays.copyOf(from, size);
+    }
+
+
+    /**
+     * copy last <b>size</b> elements from <b>from</b>
+     *
+     * @param from source array
+     * @param size size of new array
+     * @return
+     */
+    public static byte[] LSB(byte[] from, int size)
+    {
+        byte[] result = new byte[size];
+        System.arraycopy(from, from.length - size, result, 0, size);
+        return result;
+    }
+
+
+    /**
+     * componentwise addition modulo 2 (XOR)
+     *
+     * @param in    clear text
+     * @param gamma gamma parameter
+     * @return
+     */
+    public static byte[] sum(byte[] in, byte[] gamma)
+    {
+
+        byte[] out = new byte[in.length];
+        for (int i = 0; i < in.length; i++)
+        {
+            out[i] = (byte)(in[i] ^ gamma[i]);
+        }
+        return out;
+    }
+
+
+    /**
+     * copy from <b>input</b> array <b>size</b> bytes with <b>offset</b>
+     *
+     * @param input  input byte array
+     * @param size   count bytes to copy
+     * @param offset <b>inputs</b> offset
+     * @return
+     */
+    public static byte[] copyFromInput(byte[] input, int size, int offset)
+    {
+
+        if (input.length < (size + offset))
+        {
+            size = input.length - offset;
+        }
+
+        byte[] newIn = new byte[size];
+        System.arraycopy(input, offset, newIn, 0, size);
+        return newIn;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCCMBlockCipher.java
new file mode 100644
index 0000000..430f30b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCCMBlockCipher.java
@@ -0,0 +1,514 @@
+package org.bouncycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of DSTU7624 CCM mode
+ */
+public class KCCMBlockCipher
+    implements AEADBlockCipher
+{
+
+    private static final int BYTES_IN_INT = 4;
+    private static final int BITS_IN_BYTE = 8;
+
+    private static final int MAX_MAC_BIT_LENGTH = 512;
+    private static final int MIN_MAC_BIT_LENGTH = 64;
+
+    private BlockCipher engine;
+
+    private int macSize;
+    private boolean forEncryption;
+
+    private byte[] initialAssociatedText;
+    private byte[] mac;
+    private byte[] macBlock;
+
+    private byte[] nonce;
+
+    private byte[] G1;
+    private byte[] buffer;
+
+    private byte[] s;
+    private byte[] counter;
+
+
+    private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
+    private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
+
+
+    private int Nb_ = 4;
+
+    private void setNb(int Nb)
+    {
+        if (Nb == 4 || Nb == 6 || Nb == 8)
+        {
+            Nb_ = Nb;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation");
+        }
+    }
+
+    /**
+     * Base constructor. Nb value is set to 4.
+     *
+     * @param engine base cipher to use under CCM.
+     */
+    public KCCMBlockCipher(BlockCipher engine)
+    {
+        this(engine, 4);
+    }
+
+    /**
+     * Constructor allowing Nb configuration.
+     * <p>
+     * Nb is a parameter specified in CCM mode of DSTU7624 standard.
+     * This parameter specifies maximum possible length of input. It should
+     * be calculated as follows: Nb = 1/8 * (-3 + log[2]Nmax) + 1,
+     * where Nmax - length of input message in bits. For practical reasons
+     * Nmax usually less than 4Gb, e.g. for Nmax = 2^32 - 1, Nb = 4.
+     * </p>
+     * @param engine base cipher to use under CCM.
+     * @param nB Nb value to use.
+     */
+    public KCCMBlockCipher(BlockCipher engine, int nB)
+    {
+        this.engine = engine;
+        this.macSize = engine.getBlockSize();
+        this.nonce = new byte[engine.getBlockSize()];
+        this.initialAssociatedText = new byte[engine.getBlockSize()];
+        this.mac = new byte[engine.getBlockSize()];
+        this.macBlock = new byte[engine.getBlockSize()];
+        this.G1 = new byte[engine.getBlockSize()];
+        this.buffer = new byte[engine.getBlockSize()];
+        this.s = new byte[engine.getBlockSize()];
+        this.counter = new byte[engine.getBlockSize()];
+        setNb(nB);
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+
+        CipherParameters cipherParameters;
+        if (params instanceof AEADParameters)
+        {
+
+            AEADParameters parameters = (AEADParameters)params;
+
+            if (parameters.getMacSize() > MAX_MAC_BIT_LENGTH || parameters.getMacSize() < MIN_MAC_BIT_LENGTH || parameters.getMacSize() % 8 != 0)
+            {
+                throw new IllegalArgumentException("Invalid mac size specified");
+            }
+
+            nonce = parameters.getNonce();
+            macSize = parameters.getMacSize() / BITS_IN_BYTE;
+            initialAssociatedText = parameters.getAssociatedText();
+            cipherParameters = parameters.getKey();
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            nonce = ((ParametersWithIV)params).getIV();
+            macSize = engine.getBlockSize(); // use default blockSize for MAC if it is not specified
+            initialAssociatedText = null;
+            cipherParameters = ((ParametersWithIV)params).getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid parameters specified");
+        }
+
+        this.mac = new byte[macSize];
+        this.forEncryption = forEncryption;
+        engine.init(true, cipherParameters);
+
+        counter[0] = 0x01; // defined in standard
+
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+    public String getAlgorithmName()
+    {
+        return engine.getAlgorithmName() + "/KCCM";
+    }
+
+    public BlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void processAADByte(byte in)
+    {
+        associatedText.write(in);
+    }
+
+    public void processAADBytes(byte[] in, int inOff, int len)
+    {
+        associatedText.write(in, inOff, len);
+    }
+
+    private void processAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
+    {
+        if (assocLen - assocOff < engine.getBlockSize())
+        {
+            throw new IllegalArgumentException("authText buffer too short");
+        }
+        if (assocLen % engine.getBlockSize() != 0)
+        {
+            throw new IllegalArgumentException("padding not supported");
+        }
+
+        System.arraycopy(nonce, 0, G1, 0, nonce.length - Nb_ - 1);
+
+        intToBytes(dataLen, buffer, 0); // for G1
+
+        System.arraycopy(buffer, 0, G1, nonce.length - Nb_ - 1, BYTES_IN_INT);
+
+        G1[G1.length - 1] = getFlag(true, macSize);
+
+        engine.processBlock(G1, 0, macBlock, 0);
+
+        intToBytes(assocLen, buffer, 0); // for G2
+
+        if (assocLen <= engine.getBlockSize() - Nb_)
+        {
+            for (int byteIndex = 0; byteIndex < assocLen; byteIndex++)
+            {
+                buffer[byteIndex + Nb_] ^= assocText[assocOff + byteIndex];
+            }
+
+            for (int byteIndex = 0; byteIndex < engine.getBlockSize(); byteIndex++)
+            {
+                macBlock[byteIndex] ^= buffer[byteIndex];
+            }
+
+            engine.processBlock(macBlock, 0, macBlock, 0);
+
+            return;
+        }
+
+        for (int byteIndex = 0; byteIndex < engine.getBlockSize(); byteIndex++)
+        {
+            macBlock[byteIndex] ^= buffer[byteIndex];
+        }
+
+        engine.processBlock(macBlock, 0, macBlock, 0);
+
+        int authLen = assocLen;
+        while (authLen != 0)
+        {
+            for (int byteIndex = 0; byteIndex < engine.getBlockSize(); byteIndex++)
+            {
+                macBlock[byteIndex] ^= assocText[byteIndex + assocOff];
+            }
+
+            engine.processBlock(macBlock, 0, macBlock, 0);
+
+            assocOff += engine.getBlockSize();
+            authLen -= engine.getBlockSize();
+        }
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        data.write(in);
+
+        return 0;
+    }
+
+    public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (in.length < (inOff + inLen))
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        data.write(in, inOff, inLen);
+
+        return 0;
+    }
+
+    public int processPacket(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        if (in.length - inOff < len)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        if (out.length - outOff < len)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        if (associatedText.size() > 0)
+        {
+            if (forEncryption)
+            {
+                processAAD(associatedText.getBuffer(), 0, associatedText.size(), data.size());
+            }
+            else
+            {
+                processAAD(associatedText.getBuffer(), 0, associatedText.size(), data.size() - macSize);
+            }
+        }
+
+        if (forEncryption)
+        {
+            if ((len % engine.getBlockSize()) != 0)
+            {
+                throw new DataLengthException("partial blocks not supported");
+            }
+
+            CalculateMac(in, inOff, len);
+            engine.processBlock(nonce, 0, s, 0);
+
+            int totalLength = len;
+            while (totalLength > 0)
+            {
+                ProcessBlock(in, inOff, len, out, outOff);
+                totalLength -= engine.getBlockSize();
+                inOff += engine.getBlockSize();
+                outOff += engine.getBlockSize();
+            }
+
+            for (int byteIndex = 0; byteIndex < counter.length; byteIndex++)
+            {
+                s[byteIndex] += counter[byteIndex];
+            }
+
+            engine.processBlock(s, 0, buffer, 0);
+
+            for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
+            {
+                out[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
+            }
+
+            System.arraycopy(macBlock, 0, mac, 0, macSize);
+            
+            reset();
+
+            return len + macSize;
+        }
+        else
+        {
+            if ((len - macSize) % engine.getBlockSize() != 0)
+            {
+                throw new DataLengthException("partial blocks not supported");
+            }
+
+            engine.processBlock(nonce, 0, s, 0);
+
+            int blocks = len / engine.getBlockSize();
+
+            for (int blockNum = 0; blockNum < blocks; blockNum++)
+            {
+                ProcessBlock(in, inOff, len, out, outOff);
+
+                inOff += engine.getBlockSize();
+                outOff += engine.getBlockSize();
+            }
+
+            if (len > inOff)
+            {
+                for (int byteIndex = 0; byteIndex < counter.length; byteIndex++)
+                {
+                    s[byteIndex] += counter[byteIndex];
+                }
+
+                engine.processBlock(s, 0, buffer, 0);
+
+                for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
+                {
+                    out[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ in[inOff + byteIndex]);
+                }
+                outOff += macSize;
+            }
+
+            for (int byteIndex = 0; byteIndex < counter.length; byteIndex++)
+            {
+                s[byteIndex] += counter[byteIndex];
+            }
+
+            engine.processBlock(s, 0, buffer, 0);
+
+            System.arraycopy(out, outOff - macSize, buffer, 0, macSize);
+
+            CalculateMac(out, 0, outOff - macSize);
+
+            System.arraycopy(macBlock, 0, mac, 0, macSize);
+
+            byte[] calculatedMac = new byte[macSize];
+
+            System.arraycopy(buffer, 0, calculatedMac, 0, macSize);
+
+            if (!Arrays.constantTimeAreEqual(mac, calculatedMac))
+            {
+                throw new InvalidCipherTextException("mac check failed");
+            }
+
+            reset();
+
+            return len - macSize;
+        }
+    }
+
+    private void ProcessBlock(byte[] input, int inOff, int len, byte[] output, int outOff)
+    {
+
+        for (int byteIndex = 0; byteIndex < counter.length; byteIndex++)
+        {
+            s[byteIndex] += counter[byteIndex];
+        }
+
+        engine.processBlock(s, 0, buffer, 0);
+
+        for (int byteIndex = 0; byteIndex < engine.getBlockSize(); byteIndex++)
+        {
+            output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
+        }
+    }
+
+    private void CalculateMac(byte[] authText, int authOff, int len)
+    {
+        int totalLen = len;
+        while (totalLen > 0)
+        {
+            for (int byteIndex = 0; byteIndex < engine.getBlockSize(); byteIndex++)
+            {
+                macBlock[byteIndex] ^= authText[authOff + byteIndex];
+            }
+
+            engine.processBlock(macBlock, 0, macBlock, 0);
+
+            totalLen -= engine.getBlockSize();
+            authOff += engine.getBlockSize();
+        }
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff);
+
+        reset();
+
+        return len;
+    }
+
+    public byte[] getMac()
+    {
+        return Arrays.clone(mac);
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        return len;
+    }
+
+    public int getOutputSize(int len)
+    {
+        return len + macSize;
+    }
+
+    public void reset()
+    {
+        Arrays.fill(G1, (byte)0);
+        Arrays.fill(buffer, (byte)0);
+        Arrays.fill(counter, (byte)0);
+        Arrays.fill(macBlock, (byte)0);
+        counter[0] = 0x01;
+        data.reset();
+        associatedText.reset();
+
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+
+    private void intToBytes(
+        int num,
+        byte[] outBytes,
+        int outOff)
+    {
+        outBytes[outOff + 3] = (byte)(num >> 24);
+        outBytes[outOff + 2] = (byte)(num >> 16);
+        outBytes[outOff + 1] = (byte)(num >> 8);
+        outBytes[outOff] = (byte)num;
+    }
+
+    private byte getFlag(boolean authTextPresents, int macSize)
+    {
+        StringBuffer flagByte = new StringBuffer();
+
+        if (authTextPresents)
+        {
+            flagByte.append("1");
+        }
+        else
+        {
+            flagByte.append("0");
+        }
+
+
+        switch (macSize)
+        {
+        case 8:
+            flagByte.append("010"); // binary 2
+            break;
+        case 16:
+            flagByte.append("011"); // binary 3
+            break;
+        case 32:
+            flagByte.append("100"); // binary 4
+            break;
+        case 48:
+            flagByte.append("101"); // binary 5
+            break;
+        case 64:
+            flagByte.append("110"); // binary 6
+            break;
+        }
+
+        String binaryNb = Integer.toBinaryString(Nb_ - 1);
+        while (binaryNb.length() < 4)
+        {
+            binaryNb = new StringBuffer(binaryNb).insert(0, "0").toString();
+        }
+
+        flagByte.append(binaryNb);
+
+        return (byte)Integer.parseInt(flagByte.toString(), 2);
+
+    }
+
+    private class ExposedByteArrayOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ExposedByteArrayOutputStream()
+        {
+        }
+
+        public byte[] getBuffer()
+        {
+            return this.buf;
+        }
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCTRBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCTRBlockCipher.java
new file mode 100644
index 0000000..2fbea7a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KCTRBlockCipher.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implementation of DSTU7624 CTR mode
+ */
+public class KCTRBlockCipher
+    extends StreamBlockCipher
+{
+    private byte[] iv;
+    private byte[] ofbV;
+    private byte[] ofbOutV;
+
+    private int             byteCount;
+
+    private boolean initialised;
+    private BlockCipher engine;
+
+    public KCTRBlockCipher(BlockCipher engine)
+    {
+        super(engine);
+
+        this.engine = engine;
+        this.iv = new byte[engine.getBlockSize()];
+        this.ofbV = new byte[engine.getBlockSize()];
+        this.ofbOutV = new byte[engine.getBlockSize()];
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.initialised = true;
+
+        if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV ivParam = (ParametersWithIV)params;
+            byte[] iv = ivParam.getIV();
+            int diff = this.iv.length - iv.length;
+
+            Arrays.fill(this.iv, (byte)0);
+            System.arraycopy(iv, 0, this.iv, diff, iv.length);
+            params = ivParam.getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameter passed");
+        }
+ 
+        if (params != null)
+        {
+            engine.init(true, params);
+        }
+
+        reset();
+    }
+
+    public String getAlgorithmName()
+    {
+        return engine.getAlgorithmName() + "/KCTR";
+    }
+
+    public int getBlockSize()
+    {
+        return engine.getBlockSize();
+    }
+
+    protected byte calculateByte(byte b)
+    {
+        if (byteCount == 0)
+        {
+            incrementCounterAt(0);
+
+            checkCounter();
+
+            engine.processBlock(ofbV, 0, ofbOutV, 0);
+
+            return (byte)(ofbOutV[byteCount++] ^ b);
+        }
+
+        byte rv = (byte)(ofbOutV[byteCount++] ^ b);
+
+        if (byteCount == ofbV.length)
+        {
+            byteCount = 0;
+        }
+
+        return rv;
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (in.length - inOff < getBlockSize())
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        if (out.length - outOff < getBlockSize())
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        
+        processBytes(in, inOff, getBlockSize(), out, outOff);
+
+        return getBlockSize();
+    }
+
+    public void reset()
+    {
+        if (initialised)
+        {
+            engine.processBlock(this.iv, 0, ofbV, 0);
+        }
+        engine.reset();
+        byteCount = 0;
+    }
+
+    private void incrementCounterAt(int pos)
+    {
+        int i = pos;
+        while (i < ofbV.length)
+        {
+            if (++ofbV[i++] != 0)
+            {
+                break;
+            }
+        }
+    }
+
+    private void checkCounter()
+    {
+        // TODO:
+        // if the IV is the same as the blocksize we assume the user knows what they are doing
+//        if (IV.length < ofbV.length)
+//        {
+//            for (int i = 0; i != IV.length; i++)
+//            {
+//                if (ofbV[i] != IV[i])
+//                {
+//                    throw new IllegalStateException("Counter in KCTR mode out of range.");
+//                }
+//            }
+//        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/KGCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KGCMBlockCipher.java
new file mode 100644
index 0000000..f39aba5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KGCMBlockCipher.java
@@ -0,0 +1,372 @@
+package org.bouncycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.modes.kgcm.KGCMMultiplier;
+import org.bouncycastle.crypto.modes.kgcm.Tables16kKGCMMultiplier_512;
+import org.bouncycastle.crypto.modes.kgcm.Tables4kKGCMMultiplier_128;
+import org.bouncycastle.crypto.modes.kgcm.Tables8kKGCMMultiplier_256;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Implementation of DSTU7624 GCM mode
+ */
+public class KGCMBlockCipher
+    implements AEADBlockCipher
+{
+    private static final int MIN_MAC_BITS = 64;
+
+    private static KGCMMultiplier createDefaultMultiplier(int blockSize)
+    {
+        switch (blockSize)
+        {
+        case 16:    return new Tables4kKGCMMultiplier_128();
+        case 32:    return new Tables8kKGCMMultiplier_256();
+        case 64:    return new Tables16kKGCMMultiplier_512();
+        default:    throw new IllegalArgumentException("Only 128, 256, and 512 -bit block sizes supported");
+        }
+    }
+
+    private BlockCipher engine;
+    private BufferedBlockCipher ctrEngine;
+
+    private int macSize;
+    private boolean forEncryption;
+
+    private byte[] initialAssociatedText;
+    private byte[] macBlock;
+    private byte[] iv;
+
+    private KGCMMultiplier multiplier;
+    private long[] b;
+
+    private final int blockSize;
+
+    private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
+    private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
+
+    public KGCMBlockCipher(BlockCipher dstu7624Engine)
+    {
+        this.engine = dstu7624Engine;
+        this.ctrEngine = new BufferedBlockCipher(new KCTRBlockCipher(this.engine));
+        this.macSize = -1;
+        this.blockSize = engine.getBlockSize();
+
+        this.initialAssociatedText = new byte[blockSize];
+        this.iv = new byte[blockSize];
+        this.multiplier = createDefaultMultiplier(blockSize);
+        this.b = new long[blockSize >>> 3];
+
+        this.macBlock = null;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+
+        KeyParameter engineParam;
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters param = (AEADParameters)params;
+
+            byte[] iv = param.getNonce();
+            int diff = this.iv.length - iv.length;
+            Arrays.fill(this.iv, (byte)0);
+            System.arraycopy(iv, 0, this.iv, diff, iv.length);
+
+            initialAssociatedText = param.getAssociatedText();
+
+            int macSizeBits = param.getMacSize();
+            if (macSizeBits < MIN_MAC_BITS || macSizeBits > (blockSize << 3) || (macSizeBits & 7) != 0)
+            {
+                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+            }
+
+            macSize = macSizeBits >>> 3;
+            engineParam = param.getKey();
+
+            if (initialAssociatedText != null)
+            {
+                processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+            }
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV param = (ParametersWithIV)params;
+
+            byte[] iv = param.getIV();
+            int diff = this.iv.length - iv.length;
+            Arrays.fill(this.iv, (byte)0);
+            System.arraycopy(iv, 0, this.iv, diff, iv.length);
+
+            initialAssociatedText = null;
+
+            macSize = blockSize; // Set default mac size
+
+            engineParam = (KeyParameter)param.getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid parameter passed");
+        }
+
+        // TODO Nonce re-use check (sample code from GCMBlockCipher)
+//        if (forEncryption)
+//        {
+//            if (nonce != null && Arrays.areEqual(nonce, newNonce))
+//            {
+//                if (keyParam == null)
+//                {
+//                    throw new IllegalArgumentException("cannot reuse nonce for GCM encryption");
+//                }
+//                if (lastKey != null && Arrays.areEqual(lastKey, keyParam.getKey()))
+//                {
+//                    throw new IllegalArgumentException("cannot reuse nonce for GCM encryption");
+//                }
+//            }
+//        }
+
+        this.macBlock = new byte[blockSize];
+        ctrEngine.init(true, new ParametersWithIV(engineParam, this.iv));
+        engine.init(true, engineParam);
+    }
+
+    public String getAlgorithmName()
+    {
+        return engine.getAlgorithmName() + "/KGCM";
+    }
+
+    public BlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void processAADByte(byte in)
+    {
+        associatedText.write(in);
+    }
+
+    public void processAADBytes(byte[] in, int inOff, int len)
+    {
+        associatedText.write(in, inOff, len);
+    }
+
+    private void processAAD(byte[] authText, int authOff, int len)
+    {
+        int pos = authOff, end = authOff + len;
+        while (pos < end)
+        {
+            xorWithInput(b, authText, pos);
+            multiplier.multiplyH(b);
+            pos += blockSize;
+        }
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        data.write(in);
+
+        return 0;
+    }
+
+    public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (in.length < (inOff + inLen))
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+
+        data.write(in, inOff, inLen);
+
+        return 0;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        int len = data.size();
+        if (!forEncryption && len < macSize)
+        {
+            throw new InvalidCipherTextException("data too short");
+        }
+
+        // TODO Total blocks restriction in GCM mode (extend limit naturally for larger block sizes?)
+
+        // Set up the multiplier
+        {
+            byte[] temp = new byte[blockSize];
+            engine.processBlock(temp, 0, temp, 0);
+            long[] H = new long[blockSize >>> 3];
+            Pack.littleEndianToLong(temp, 0, H);
+            multiplier.init(H);
+            Arrays.fill(temp, (byte)0);
+            Arrays.fill(H, 0L);
+        }
+
+        int lenAAD = associatedText.size();
+        if (lenAAD > 0)
+        {
+            processAAD(associatedText.getBuffer(), 0, lenAAD);
+        }
+        
+        //use alternative cipher to produce output
+        int resultLen;
+        if (forEncryption)
+        {
+            if (out.length - outOff - macSize < len)
+            {
+                throw new OutputLengthException("Output buffer too short");
+            }
+
+            resultLen = ctrEngine.processBytes(data.getBuffer(), 0, len, out, outOff);
+            resultLen += ctrEngine.doFinal(out, outOff + resultLen);
+
+            calculateMac(out, outOff, len, lenAAD);
+        }
+        else
+        {
+            int ctLen = len - macSize; 
+            if (out.length - outOff < ctLen)
+            {
+                throw new OutputLengthException("Output buffer too short");
+            }
+
+            calculateMac(data.getBuffer(), 0, ctLen, lenAAD);
+
+            resultLen = ctrEngine.processBytes(data.getBuffer(), 0, ctLen, out, outOff);
+            resultLen += ctrEngine.doFinal(out, outOff + resultLen);
+        }
+
+        if (macBlock == null)
+        {
+            throw new IllegalStateException("mac is not calculated");
+        }
+
+        if (forEncryption)
+        {
+            System.arraycopy(macBlock, 0, out, outOff + resultLen, macSize);
+
+            reset();
+
+            return resultLen + macSize;
+        }
+        else
+        {
+            byte[] mac = new byte[macSize];
+            System.arraycopy(data.getBuffer(), len - macSize, mac, 0, macSize);
+
+            byte[] calculatedMac = new byte[macSize];
+            System.arraycopy(macBlock, 0, calculatedMac, 0, macSize);
+
+            if (!Arrays.constantTimeAreEqual(mac, calculatedMac))
+            {
+                throw new InvalidCipherTextException("mac verification failed");
+            }
+
+            reset();
+
+            return resultLen;
+        }
+    }
+
+    public byte[] getMac()
+    {
+        byte[] mac = new byte[macSize];
+
+        System.arraycopy(macBlock, 0, mac, 0, macSize);
+
+        return mac;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        return 0;
+    }
+
+    public int getOutputSize(int len)
+    {
+        int totalData = len + data.size();
+
+        if (forEncryption)
+        {
+            return totalData + macSize;
+        }
+
+        return totalData < macSize ? 0 : totalData - macSize;
+    }
+
+    public void reset()
+    {
+        Arrays.fill(b, 0L);
+
+        engine.reset();
+
+        data.reset();
+        associatedText.reset();
+
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+    private void calculateMac(byte[] input, int inOff, int len, int lenAAD)
+    {
+        int pos = inOff, end = inOff + len;
+        while (pos < end)
+        {
+            xorWithInput(b, input, pos);
+            multiplier.multiplyH(b);
+            pos += blockSize;
+        }
+
+        long lambda_o = (lenAAD & 0xFFFFFFFFL) << 3;
+        long lambda_c = (len & 0xFFFFFFFFL) << 3;
+
+//        byte[] temp = new byte[blockSize];
+//        Pack.longToLittleEndian(lambda_o, temp, 0);
+//        Pack.longToLittleEndian(lambda_c, temp, blockSize / 2);
+//
+//        xorWithInput(b, temp, 0);
+        b[0] ^= lambda_o;
+        b[blockSize >>> 4] ^= lambda_c;
+
+        macBlock = Pack.longToLittleEndian(b);
+        engine.processBlock(macBlock, 0, macBlock, 0);
+    }
+
+    private static void xorWithInput(long[] z, byte[] buf, int off)
+    {
+        for (int i = 0; i < z.length; ++i)
+        {
+            z[i] ^= Pack.littleEndianToLong(buf, off);
+            off += 8;
+        }
+    }
+
+    private class ExposedByteArrayOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ExposedByteArrayOutputStream()
+        {
+        }
+
+        public byte[] getBuffer()
+        {
+            return this.buf;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java
new file mode 100644
index 0000000..81c9424
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java
@@ -0,0 +1,200 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Implementation of DSTU7624 XTS mode
+ */
+public class KXTSBlockCipher
+    extends BufferedBlockCipher
+{
+    /*
+     * Constants for GF(2^m) operations
+     *
+     * GF(2 ^ 128) -> x^128 + x^7 + x^2 + x + 1
+     * GF(2 ^ 256) -> x^256 + x^10 + x^5 + x^2 + 1
+     * GF(2 ^ 512) -> x^512 + x^8 + x^5 + x^2 + 1
+     */
+    private static final long RED_POLY_128 = 0x0087L;
+    private static final long RED_POLY_256 = 0x0425L;
+    private static final long RED_POLY_512 = 0x0125L;
+
+    protected static long getReductionPolynomial(int blockSize)
+    {
+        switch (blockSize)
+        {
+        case 16:
+            return RED_POLY_128;
+        case 32:
+            return RED_POLY_256;
+        case 64:
+            return RED_POLY_512;
+        default:
+            throw new IllegalArgumentException("Only 128, 256, and 512 -bit block sizes supported"); 
+        }
+    }
+
+    private final int blockSize;
+    private final long reductionPolynomial;
+    private final long[] tw_init, tw_current;
+    private int counter;
+
+    public KXTSBlockCipher(BlockCipher cipher)
+    {
+//        super(cipher);
+        this.cipher = cipher;
+
+        this.blockSize = cipher.getBlockSize();
+        this.reductionPolynomial = getReductionPolynomial(blockSize);
+        this.tw_init = new long[blockSize >>> 3];
+        this.tw_current = new long[blockSize >>> 3];
+        this.counter = -1;
+    }
+
+    public int getOutputSize(int length)
+    {
+        return length;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        return len;
+    }
+
+    public void init(boolean forEncryption, CipherParameters parameters)
+    {
+        if (!(parameters instanceof ParametersWithIV))
+        {
+            throw new IllegalArgumentException("Invalid parameters passed");
+        }
+
+        ParametersWithIV ivParam = (ParametersWithIV)parameters;
+        parameters = ivParam.getParameters();
+
+        byte[] iv = ivParam.getIV();
+
+        /*
+         * TODO We need to check what the rule is supposed to be for IVs that aren't exactly one block.
+         * 
+         * Given general little-endianness, presumably a short IV should be right-padded with zeroes.
+         */
+        if (iv.length != blockSize)
+        {
+            throw new IllegalArgumentException("Currently only support IVs of exactly one block");
+        }
+
+        byte[] tweak = new byte[blockSize];
+        System.arraycopy(iv, 0, tweak, 0, blockSize);
+
+        cipher.init(true, parameters);
+        cipher.processBlock(tweak, 0, tweak, 0);
+
+        cipher.init(forEncryption, parameters);
+        Pack.littleEndianToLong(tweak, 0, tw_init);
+        System.arraycopy(tw_init, 0, tw_current, 0, tw_init.length);
+        counter = 0;
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+    {
+        /*
+         * TODO This class isn't really behaving like a BufferedBlockCipher yet
+         */
+        throw new IllegalStateException("unsupported operation");
+    }
+
+    public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+    {
+        if (input.length - inOff < len)
+        {
+            throw new DataLengthException("Input buffer too short");
+        }
+        if (output.length - inOff < len)
+        {
+            throw new OutputLengthException("Output buffer too short");
+        }
+        if (len % blockSize != 0)
+        {
+            throw new IllegalArgumentException("Partial blocks not supported");
+        }
+
+        for (int pos = 0; pos < len; pos += blockSize)
+        {
+            processBlock(input, inOff + pos, output, outOff + pos);
+        }
+
+        return len;
+    }
+
+    private void processBlock(byte[] input, int inOff, byte[] output, int outOff)
+    {
+        /*
+         * A somewhat arbitrary limit of 2^32 - 1 blocks
+         */
+        if (counter == -1)
+        {
+            throw new IllegalStateException("Attempt to process too many blocks");
+        }
+
+        ++counter;
+
+        /*
+         * Multiply tweak by 'alpha', which is just 2
+         */
+        GF_double(reductionPolynomial, tw_current);
+
+        byte[] tweak = new byte[blockSize];
+        Pack.longToLittleEndian(tw_current, tweak, 0);
+
+        byte[] buffer = new byte[blockSize];
+        System.arraycopy(tweak, 0, buffer, 0, blockSize);
+
+        for (int i = 0; i < blockSize; ++i)
+        {
+            buffer[i] ^= input[inOff + i];
+        }
+
+        cipher.processBlock(buffer, 0, buffer, 0);
+
+        for (int i = 0; i < blockSize; ++i)
+        {
+            output[outOff + i] = (byte)(buffer[i] ^ tweak[i]);
+        }
+    }
+
+    public int doFinal(byte[] output, int outOff)
+    {
+        reset();
+
+        return 0;
+    }
+
+    public void reset()
+    {
+//        super.reset();
+        cipher.reset();
+
+        System.arraycopy(tw_init, 0, tw_current, 0, tw_init.length);
+        counter = 0;
+    }
+
+    private static void GF_double(long redPoly, long[] z)
+    {
+        long c = 0;
+        for (int i = 0; i < z.length; ++i)
+        {
+            long zVal = z[i];
+            long bit = zVal >>> 63;
+            z[i] = (zVal << 1) ^ c;
+            c = bit;
+        }
+
+        z[0] ^= redPoly & -c;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java
index fe7bf97..e15cbd4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java
@@ -8,9 +8,7 @@
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.StreamBlockCipher;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.crypto.OutputLengthException;
 
 /**
  * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
@@ -147,7 +145,7 @@
         {
             if ((outOff + length) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
         }
 
@@ -204,7 +202,7 @@
     {
         if (bufOff + outOff > out.length)
         {
-            throw new DataLengthException("output buffer to small in doFinal");
+            throw new OutputLengthException("output buffer to small in doFinal");
         }
 
         int     blockSize = cipher.getBlockSize();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
index b34432a..30aed5b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
 
 /**
  * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
@@ -138,7 +139,7 @@
         {
             if ((outOff + length) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
         }
 
@@ -195,7 +196,7 @@
     {
         if (bufOff + outOff > out.length)
         {
-            throw new DataLengthException("output buffer to small in doFinal");
+            throw new OutputLengthException("output buffer to small in doFinal");
         }
 
         int     blockSize = cipher.getBlockSize();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java
index fa82752..8ae1588 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
 
 /**
  * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
@@ -166,10 +167,9 @@
         {
             throw new DataLengthException("input buffer too short");
         }
-
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
         
         if (count > blockSize)
@@ -244,10 +244,9 @@
         {
             throw new DataLengthException("input buffer too short");
         }
-
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
         
         if (count > blockSize)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
index 4dee63a..72f68ca 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 
 /**
@@ -224,7 +225,7 @@
         {
             if ((outOff + 2 * blockSize + 2) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
 
             cipher.processBlock(FR, 0, FRE, 0);
@@ -260,7 +261,7 @@
         {
             if ((outOff + blockSize) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
 
             cipher.processBlock(FR, 0, FRE, 0);
@@ -299,10 +300,9 @@
         {
             throw new DataLengthException("input buffer too short");
         }
-
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
         
         if (count == 0)
@@ -388,10 +388,9 @@
         {
             throw new DataLengthException("input buffer too short");
         }
-
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
         
         cipher.processBlock(FR, 0, FRE, 0);
@@ -432,10 +431,9 @@
         {
             throw new DataLengthException("input buffer too short");
         }
-
         if ((outOff + blockSize) > out.length)
         {
-            throw new DataLengthException("output buffer too short");
+            throw new OutputLengthException("output buffer too short");
         }
         
         cipher.processBlock(FR, 0, FRE, 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
index f15ed67..9406808 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
 
 /**
  * A wrapper class that allows block ciphers to be used to process data in
@@ -139,7 +140,7 @@
         {
             if ((outOff + length) > out.length)
             {
-                throw new DataLengthException("output buffer too short");
+                throw new OutputLengthException("output buffer too short");
             }
         }
 
@@ -199,7 +200,7 @@
             {
                 if ((outOff + 2 * blockSize) > out.length)
                 {
-                    throw new DataLengthException("output buffer too short");
+                    throw new OutputLengthException("output buffer too short");
                 }
 
                 resultLen = cipher.processBlock(buf, 0, out, outOff);
@@ -236,7 +237,7 @@
             //
             int count = buf[blockSize - 1] & 0xff;
 
-            if ((count < 0) || (count > blockSize))
+            if (count > blockSize)
             {
                 throw new InvalidCipherTextException("pad block corrupted");
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index fc25810..7316a04 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -2,30 +2,31 @@
 
 import org.bouncycastle.util.Arrays;
 
-public class BasicGCMExponentiator implements GCMExponentiator
+public class BasicGCMExponentiator
+    implements GCMExponentiator
 {
-    private int[] x;
+    private long[] x;
 
     public void init(byte[] x)
     {
-        this.x = GCMUtil.asInts(x);
+        this.x = GCMUtil.asLongs(x);
     }
 
     public void exponentiateX(long pow, byte[] output)
     {
         // Initial value is little-endian 1
-        int[] y = GCMUtil.oneAsInts();
+        long[] y = GCMUtil.oneAsLongs();
 
         if (pow > 0)
         {
-            int[] powX = Arrays.clone(x);
+            long[] powX = Arrays.clone(x);
             do
             {
                 if ((pow & 1L) != 0)
                 {
                     GCMUtil.multiply(y, powX);
                 }
-                GCMUtil.multiply(powX, powX);
+                GCMUtil.square(powX, powX);
                 pow >>>= 1;
             }
             while (pow > 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
index 2afb18f..22b43a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
@@ -1,17 +1,18 @@
 package org.bouncycastle.crypto.modes.gcm;
 
-public class BasicGCMMultiplier implements GCMMultiplier
+public class BasicGCMMultiplier
+    implements GCMMultiplier
 {
-    private int[] H;
+    private long[] H;
 
     public void init(byte[] H)
     {
-        this.H = GCMUtil.asInts(H);
+        this.H = GCMUtil.asLongs(H);
     }
 
     public void multiplyH(byte[] x)
     {
-        int[] t = GCMUtil.asInts(x);
+        long[] t = GCMUtil.asLongs(x);
         GCMUtil.multiply(t, H);
         GCMUtil.asBytes(t, x);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index f08f71f..3e24a15 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.crypto.modes.gcm;
 
+import org.bouncycastle.math.raw.Interleave;
 import org.bouncycastle.util.Pack;
 
 public abstract class GCMUtil
@@ -7,28 +8,6 @@
     private static final int E1 = 0xe1000000;
     private static final long E1L = (E1 & 0xFFFFFFFFL) << 32;
 
-    private static int[] generateLookup()
-    {
-        int[] lookup = new int[256];
-
-        for (int c = 0; c < 256; ++c)
-        {
-            int v = 0;
-            for (int i = 7; i >= 0; --i)
-            {
-                if ((c & (1 << i)) != 0)
-                {
-                    v ^= (E1 >>> (7 - i));
-                }
-            }
-            lookup[c] = v;
-        }
-
-        return lookup;
-    }
-
-    private static final int[] LOOKUP = generateLookup();
-
     public static byte[] oneAsBytes()
     {
         byte[] tmp = new byte[16];
@@ -98,216 +77,214 @@
         Pack.bigEndianToLong(x, 0, z);
     }
 
+    public static void copy(int[] x, int[] z)
+    {
+        z[0] = x[0];
+        z[1] = x[1];
+        z[2] = x[2];
+        z[3] = x[3];
+    }
+
+    public static void copy(long[] x, long[] z)
+    {
+        z[0] = x[0];
+        z[1] = x[1];
+    }
+
+    public static void divideP(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long m = x0 >> 63;
+        x0 ^= (m & E1L);
+        z[0] = (x0 << 1) | (x1 >>> 63);
+        z[1] = (x1 << 1) | -m;
+    }
+
     public static void multiply(byte[] x, byte[] y)
     {
-        int[] t1 = GCMUtil.asInts(x);
-        int[] t2 = GCMUtil.asInts(y);
+        long[] t1 = GCMUtil.asLongs(x);
+        long[] t2 = GCMUtil.asLongs(y);
         GCMUtil.multiply(t1, t2);
         GCMUtil.asBytes(t1, x);
     }
 
     public static void multiply(int[] x, int[] y)
     {
-        int r00 = x[0], r01 = x[1], r02 = x[2], r03 = x[3];
-        int r10 = 0, r11 = 0, r12 = 0, r13 = 0;
-        
+        int y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
+        int z0 = 0, z1 = 0, z2 = 0, z3 = 0;
+
         for (int i = 0; i < 4; ++i)
         {
-            int bits = y[i];
+            int bits = x[i];
             for (int j = 0; j < 32; ++j)
             {
                 int m1 = bits >> 31; bits <<= 1;
-                r10 ^= (r00 & m1);
-                r11 ^= (r01 & m1);
-                r12 ^= (r02 & m1);
-                r13 ^= (r03 & m1);
+                z0 ^= (y0 & m1);
+                z1 ^= (y1 & m1);
+                z2 ^= (y2 & m1);
+                z3 ^= (y3 & m1);
 
-                int m2 = (r03 << 31) >> 8;
-                r03 = (r03 >>> 1) | (r02 << 31);
-                r02 = (r02 >>> 1) | (r01 << 31);
-                r01 = (r01 >>> 1) | (r00 << 31);
-                r00 = (r00 >>> 1) ^ (m2 & E1);
+                int m2 = (y3 << 31) >> 8;
+                y3 = (y3 >>> 1) | (y2 << 31);
+                y2 = (y2 >>> 1) | (y1 << 31);
+                y1 = (y1 >>> 1) | (y0 << 31);
+                y0 = (y0 >>> 1) ^ (m2 & E1);
             }
         }
 
-        x[0] = r10;
-        x[1] = r11;
-        x[2] = r12;
-        x[3] = r13;
+        x[0] = z0;
+        x[1] = z1;
+        x[2] = z2;
+        x[3] = z3;
     }
 
     public static void multiply(long[] x, long[] y)
     {
-        long r00 = x[0], r01 = x[1], r10 = 0, r11 = 0;
+        long x0 = x[0], x1 = x[1];
+        long y0 = y[0], y1 = y[1];
+        long z0 = 0, z1 = 0, z2 = 0;
 
-        for (int i = 0; i < 2; ++i)
+        for (int j = 0; j < 64; ++j)
         {
-            long bits = y[i];
-            for (int j = 0; j < 64; ++j)
-            {
-                long m1 = bits >> 63; bits <<= 1;
-                r10 ^= (r00 & m1);
-                r11 ^= (r01 & m1);
+            long m0 = x0 >> 63; x0 <<= 1;
+            z0 ^= (y0 & m0);
+            z1 ^= (y1 & m0);
 
-                long m2 = (r01 << 63) >> 8;
-                r01 = (r01 >>> 1) | (r00 << 63);
-                r00 = (r00 >>> 1) ^ (m2 & E1L);
-            }
+            long m1 = x1 >> 63; x1 <<= 1;
+            z1 ^= (y0 & m1);
+            z2 ^= (y1 & m1);
+
+            long c = (y1 << 63) >> 8;
+            y1 = (y1 >>> 1) | (y0 << 63);
+            y0 = (y0 >>> 1) ^ (c & E1L);
         }
 
-        x[0] = r10;
-        x[1] = r11;
+        z0 ^= z2 ^ (z2 >>>  1) ^ (z2 >>>  2) ^ (z2 >>>  7);
+        z1 ^=      (z2 <<  63) ^ (z2 <<  62) ^ (z2 <<  57);
+
+        x[0] = z0;
+        x[1] = z1;
     }
 
-    // P is the value with only bit i=1 set
     public static void multiplyP(int[] x)
     {
-        int m = shiftRight(x) >> 8;
-        x[0] ^= (m & E1);
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        int m = (x3 << 31) >> 31;
+        x[0] = (x0 >>> 1) ^ (m & E1);
+        x[1] = (x1 >>> 1) | (x0 << 31);
+        x[2] = (x2 >>> 1) | (x1 << 31);
+        x[3] = (x3 >>> 1) | (x2 << 31);
     }
 
     public static void multiplyP(int[] x, int[] z)
     {
-        int m = shiftRight(x, z) >> 8;
-        z[0] ^= (m & E1);
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        int m = (x3 << 31) >> 31;
+        z[0] = (x0 >>> 1) ^ (m & E1);
+        z[1] = (x1 >>> 1) | (x0 << 31);
+        z[2] = (x2 >>> 1) | (x1 << 31);
+        z[3] = (x3 >>> 1) | (x2 << 31);
     }
 
-    // P is the value with only bit i=1 set
+    public static void multiplyP(long[] x)
+    {
+        long x0 = x[0], x1 = x[1];
+        long m = (x1 << 63) >> 63;
+        x[0] = (x0 >>> 1) ^ (m & E1L);
+        x[1] = (x1 >>> 1) | (x0 << 63);
+    }
+
+    public static void multiplyP(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long m = (x1 << 63) >> 63;
+        z[0] = (x0 >>> 1) ^ (m & E1L);
+        z[1] = (x1 >>> 1) | (x0 << 63);
+    }
+
+    public static void multiplyP3(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 61;
+        z[0] = (x0 >>> 3) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        z[1] = (x1 >>> 3) | (x0 << 61);
+    }
+
+    public static void multiplyP4(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 60;
+        z[0] = (x0 >>> 4) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        z[1] = (x1 >>> 4) | (x0 << 60);
+    }
+
+    public static void multiplyP7(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 57;
+        z[0] = (x0 >>> 7) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        z[1] = (x1 >>> 7) | (x0 << 57);
+    }
+
     public static void multiplyP8(int[] x)
     {
-//        for (int i = 8; i != 0; --i)
-//        {
-//            multiplyP(x);
-//        }
-
-        int c = shiftRightN(x, 8);
-        x[0] ^= LOOKUP[c >>> 24];
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        int c = x3 << 24;
+        x[0] = (x0 >>> 8) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        x[1] = (x1 >>> 8) | (x0 << 24);
+        x[2] = (x2 >>> 8) | (x1 << 24);
+        x[3] = (x3 >>> 8) | (x2 << 24);
     }
 
     public static void multiplyP8(int[] x, int[] y)
     {
-        int c = shiftRightN(x, 8, y);
-        y[0] ^= LOOKUP[c >>> 24];
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        int c = x3 << 24;
+        y[0] = (x0 >>> 8) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        y[1] = (x1 >>> 8) | (x0 << 24);
+        y[2] = (x2 >>> 8) | (x1 << 24);
+        y[3] = (x3 >>> 8) | (x2 << 24);
     }
 
-    static int shiftRight(int[] x)
+    public static void multiplyP8(long[] x)
     {
-//        int c = 0;
-//        for (int i = 0; i < 4; ++i)
-//        {
-//            int b = x[i];
-//            x[i] = (b >>> 1) | c;
-//            c = b << 31;
-//        }
-//        return c;
-
-        int b = x[0];
-        x[0] = b >>> 1;
-        int c = b << 31;
-        b = x[1];
-        x[1] = (b >>> 1) | c;
-        c = b << 31;
-        b = x[2];
-        x[2] = (b >>> 1) | c;
-        c = b << 31;
-        b = x[3];
-        x[3] = (b >>> 1) | c;
-        return b << 31;
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 56;
+        x[0] = (x0 >>> 8) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        x[1] = (x1 >>> 8) | (x0 << 56);
     }
 
-    static int shiftRight(int[] x, int[] z)
+    public static void multiplyP8(long[] x, long[] y)
     {
-//      int c = 0;
-//      for (int i = 0; i < 4; ++i)
-//      {
-//          int b = x[i];
-//          z[i] = (b >>> 1) | c;
-//          c = b << 31;
-//      }
-//      return c;
-
-        int b = x[0];
-        z[0] = b >>> 1;
-        int c = b << 31;
-        b = x[1];
-        z[1] = (b >>> 1) | c;
-        c = b << 31;
-        b = x[2];
-        z[2] = (b >>> 1) | c;
-        c = b << 31;
-        b = x[3];
-        z[3] = (b >>> 1) | c;
-        return b << 31;
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 56;
+        y[0] = (x0 >>> 8) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        y[1] = (x1 >>> 8) | (x0 << 56);
     }
 
-    static long shiftRight(long[] x)
+    public static long[] pAsLongs()
     {
-        long b = x[0];
-        x[0] = b >>> 1;
-        long c = b << 63; 
-        b = x[1];
-        x[1] = (b >>> 1) | c;
-        return b << 63;
+        long[] tmp = new long[2];
+        tmp[0] = 1L << 62;
+        return tmp;
     }
 
-    static long shiftRight(long[] x, long[] z)
+    public static void square(long[] x, long[] z)
     {
-        long b = x[0];
-        z[0] = b >>> 1;
-        long c = b << 63; 
-        b = x[1];
-        z[1] = (b >>> 1) | c;
-        return b << 63;
-    }
+        long[] t  = new long[4];
+        Interleave.expand64To128Rev(x[0], t, 0);
+        Interleave.expand64To128Rev(x[1], t, 2);
 
-    static int shiftRightN(int[] x, int n)
-    {
-//        int c = 0, nInv = 32 - n;
-//        for (int i = 0; i < 4; ++i)
-//        {
-//            int b = x[i];
-//            x[i] = (b >>> n) | c;
-//            c = b << nInv;
-//        }
-//        return c;
+        long z0 = t[0], z1 = t[1], z2 = t[2], z3 = t[3];
 
-        int b = x[0], nInv = 32 - n;
-        x[0] = b >>> n;
-        int c = b << nInv;
-        b = x[1];
-        x[1] = (b >>> n) | c;
-        c = b << nInv;
-        b = x[2];
-        x[2] = (b >>> n) | c;
-        c = b << nInv;
-        b = x[3];
-        x[3] = (b >>> n) | c;
-        return b << nInv;
-    }
+        z1 ^= z3 ^ (z3 >>>  1) ^ (z3 >>>  2) ^ (z3 >>>  7);
+        z2 ^=      (z3 <<  63) ^ (z3 <<  62) ^ (z3 <<  57);
 
-    static int shiftRightN(int[] x, int n, int[] z)
-    {
-//        int c = 0, nInv = 32 - n;
-//        for (int i = 0; i < 4; ++i)
-//        {
-//            int b = x[i];
-//            z[i] = (b >>> n) | c;
-//            c = b << nInv;
-//        }
-//        return c;
+        z0 ^= z2 ^ (z2 >>>  1) ^ (z2 >>>  2) ^ (z2 >>>  7);
+        z1 ^=      (z2 <<  63) ^ (z2 <<  62) ^ (z2 <<  57);
 
-        int b = x[0], nInv = 32 - n;
-        z[0] = b >>> n;
-        int c = b << nInv;
-        b = x[1];
-        z[1] = (b >>> n) | c;
-        c = b << nInv;
-        b = x[2];
-        z[2] = (b >>> n) | c;
-        c = b << nInv;
-        b = x[3];
-        z[3] = (b >>> n) | c;
-        return b << nInv;
+        z[0] = z0;
+        z[1] = z1;
     }
 
     public static void xor(byte[] x, byte[] y)
@@ -323,6 +300,32 @@
         while (i < 16);
     }
 
+    public static void xor(byte[] x, byte[] y, int yOff)
+    {
+        int i = 0;
+        do
+        {
+            x[i] ^= y[yOff + i]; ++i;
+            x[i] ^= y[yOff + i]; ++i;
+            x[i] ^= y[yOff + i]; ++i;
+            x[i] ^= y[yOff + i]; ++i;
+        }
+        while (i < 16);
+    }
+
+    public static void xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
+    {
+        int i = 0;
+        do
+        {
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+        }
+        while (i < 16);
+    }
+
     public static void xor(byte[] x, byte[] y, int yOff, int yLen)
     {
         while (--yLen >= 0)
@@ -331,6 +334,14 @@
         }
     }
 
+    public static void xor(byte[] x, int xOff, byte[] y, int yOff, int len)
+    {
+        while (--len >= 0)
+        {
+            x[xOff + len] ^= y[yOff + len];
+        }
+    }
+
     public static void xor(byte[] x, byte[] y, byte[] z)
     {
         int i = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index 6eff4e3..b8766bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -4,7 +4,8 @@
 
 import org.bouncycastle.util.Arrays;
 
-public class Tables1kGCMExponentiator implements GCMExponentiator
+public class Tables1kGCMExponentiator
+    implements GCMExponentiator
 {
     // A lookup table of the power-of-two powers of 'x'
     // - lookupPowX2[i] = x^(2^i)
@@ -12,8 +13,8 @@
 
     public void init(byte[] x)
     {
-        int[] y = GCMUtil.asInts(x);
-        if (lookupPowX2 != null && Arrays.areEqual(y, (int[])lookupPowX2.elementAt(0)))
+        long[] y = GCMUtil.asLongs(x);
+        if (lookupPowX2 != null && Arrays.areEqual(y, (long[])lookupPowX2.elementAt(0)))
         {
             return;
         }
@@ -24,14 +25,14 @@
 
     public void exponentiateX(long pow, byte[] output)
     {
-        int[] y = GCMUtil.oneAsInts();
+        long[] y = GCMUtil.oneAsLongs();
         int bit = 0;
         while (pow > 0)
         {
             if ((pow & 1L) != 0)
             {
                 ensureAvailable(bit);
-                GCMUtil.multiply(y, (int[])lookupPowX2.elementAt(bit));
+                GCMUtil.multiply(y, (long[])lookupPowX2.elementAt(bit));
             }
             ++bit;
             pow >>>= 1;
@@ -45,11 +46,11 @@
         int count = lookupPowX2.size();
         if (count <= bit)
         {
-            int[] tmp = (int[])lookupPowX2.elementAt(count - 1);
+            long[] tmp = (long[])lookupPowX2.elementAt(count - 1);
             do
             {
                 tmp = Arrays.clone(tmp);
-                GCMUtil.multiply(tmp, tmp);
+                GCMUtil.square(tmp, tmp);
                 lookupPowX2.addElement(tmp);
             }
             while (++count <= bit);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
new file mode 100644
index 0000000..e5ea748
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+public class Tables4kGCMMultiplier
+    implements GCMMultiplier
+{
+    private byte[] H;
+    private long[][] T;
+
+    public void init(byte[] H)
+    {
+        if (T == null)
+        {
+            T = new long[256][2];
+        }
+        else if (Arrays.areEqual(this.H, H))
+        {
+            return;
+        }
+
+        this.H = Arrays.clone(H);
+
+        // T[0] = 0
+
+        // T[1] = H.p^7
+        GCMUtil.asLongs(this.H, T[1]);
+        GCMUtil.multiplyP7(T[1], T[1]);
+
+        for (int n = 2; n < 256; n += 2)
+        {
+            // T[2.n] = T[n].p^-1
+            GCMUtil.divideP(T[n >> 1], T[n]);
+
+            // T[2.n + 1] = T[2.n] + T[1]
+            GCMUtil.xor(T[n], T[1], T[n + 1]);
+        }
+    }
+
+    public void multiplyH(byte[] x)
+    {
+//        long[] z = new long[2];
+//        GCMUtil.copy(T[x[15] & 0xFF], z);
+//        for (int i = 14; i >= 0; --i)
+//        {
+//            GCMUtil.multiplyP8(z);
+//            GCMUtil.xor(z, T[x[i] & 0xFF]);
+//        }
+//        Pack.longToBigEndian(z, x, 0);
+
+        long[] t = T[x[15] & 0xFF];
+        long z0 = t[0], z1 = t[1];
+
+        for (int i = 14; i >= 0; --i)
+        {
+            t = T[x[i] & 0xFF];
+
+            long c = z1 << 56;
+            z1 = t[1] ^ ((z1 >>> 8) | (z0 << 56));
+            z0 = t[0] ^ (z0 >>> 8) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        }
+
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
index 4f32a0d..6a63fad 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
@@ -3,16 +3,17 @@
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
-public class Tables64kGCMMultiplier implements GCMMultiplier
+public class Tables64kGCMMultiplier
+    implements GCMMultiplier
 {
     private byte[] H;
-    private int[][][] M;
+    private long[][][] T;
 
     public void init(byte[] H)
     {
-        if (M == null)
+        if (T == null)
         {
-            M = new int[16][256][4];
+            T = new long[16][256][2];
         }
         else if (Arrays.areEqual(this.H, H))
         {
@@ -21,53 +22,55 @@
 
         this.H = Arrays.clone(H);
 
-        // M[0][0] is ZEROES;
-        GCMUtil.asInts(H, M[0][128]);
-
-        for (int j = 64; j >= 1; j >>= 1)
+        for (int i = 0; i < 16; ++i)
         {
-            GCMUtil.multiplyP(M[0][j + j], M[0][j]);
-        }
+            long[][] t = T[i];
 
-        int i = 0;
-        for (;;)
-        {
-            for (int j = 2; j < 256; j += j)
+            // t[0] = 0
+
+            if (i == 0)
             {
-                for (int k = 1; k < j; ++k)
-                {
-                    GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
-                }
+                // t[1] = H.p^7
+                GCMUtil.asLongs(this.H, t[1]);
+                GCMUtil.multiplyP7(t[1], t[1]);
+            }
+            else
+            {
+                // t[1] = T[i-1][1].p^8
+                GCMUtil.multiplyP8(T[i - 1][1], t[1]);
             }
 
-            if (++i == 16)
+            for (int n = 2; n < 256; n += 2)
             {
-                return;
-            }
+                // t[2.n] = t[n].p^-1
+                GCMUtil.divideP(t[n >> 1], t[n]);
 
-            // M[i][0] is ZEROES;
-            for (int j = 128; j > 0; j >>= 1)
-            {
-                GCMUtil.multiplyP8(M[i - 1][j], M[i][j]);
+                // t[2.n + 1] = t[2.n] + t[1]
+                GCMUtil.xor(t[n], t[1], t[n + 1]);
             }
         }
     }
 
     public void multiplyH(byte[] x)
     {
-//      assert x.Length == 16;
+//        long[] z = new long[2];
+//        for (int i = 15; i >= 0; --i)
+//        {
+//            GCMUtil.xor(z, T[i][x[i] & 0xFF]);
+//        }
+//        Pack.longToBigEndian(z, x, 0);
 
-        int[] z = new int[4];
-        for (int i = 15; i >= 0; --i)
+        long[] t = T[15][x[15] & 0xFF];
+        long z0 = t[0], z1 = t[1];
+
+        for (int i = 14; i >= 0; --i)
         {
-//            GCMUtil.xor(z, M[i][x[i] & 0xff]);
-            int[] m = M[i][x[i] & 0xff];
-            z[0] ^= m[0];
-            z[1] ^= m[1];
-            z[2] ^= m[2];
-            z[3] ^= m[3];
+            t = T[i][x[i] & 0xFF];
+            z0 ^= t[0];
+            z1 ^= t[1];
         }
 
-        Pack.intToBigEndian(z, x, 0);
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 69c1dce..6c3ae26 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -3,16 +3,17 @@
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
-public class Tables8kGCMMultiplier  implements GCMMultiplier
+public class Tables8kGCMMultiplier
+    implements GCMMultiplier
 {
     private byte[] H;
-    private int[][][] M;
+    private long[][][] T;
 
     public void init(byte[] H)
     {
-        if (M == null)
+        if (T == null)
         {
-            M = new int[32][16][4];
+            T = new long[32][16][2];
         }
         else if (Arrays.areEqual(this.H, H))
         {
@@ -21,70 +22,58 @@
 
         this.H = Arrays.clone(H);
 
-        // M[0][0] is ZEROES;
-        // M[1][0] is ZEROES;
-        GCMUtil.asInts(H, M[1][8]);
-
-        for (int j = 4; j >= 1; j >>= 1)
+        for (int i = 0; i < 32; ++i)
         {
-            GCMUtil.multiplyP(M[1][j + j], M[1][j]);
-        }
+            long[][] t = T[i];
 
-        GCMUtil.multiplyP(M[1][1], M[0][8]);
+            // t[0] = 0
 
-        for (int j = 4; j >= 1; j >>= 1)
-        {
-            GCMUtil.multiplyP(M[0][j + j], M[0][j]);
-        }
-
-        int i = 0;
-        for (;;)
-        {
-            for (int j = 2; j < 16; j += j)
+            if (i == 0)
             {
-                for (int k = 1; k < j; ++k)
-                {
-                    GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
-                }
+                // t[1] = H.p^3
+                GCMUtil.asLongs(this.H, t[1]);
+                GCMUtil.multiplyP3(t[1], t[1]);
+            }
+            else
+            {
+                // t[1] = T[i-1][1].p^4
+                GCMUtil.multiplyP4(T[i - 1][1], t[1]);
             }
 
-            if (++i == 32)
+            for (int n = 2; n < 16; n += 2)
             {
-                return;
-            }
+                // t[2.n] = t[n].p^-1
+                GCMUtil.divideP(t[n >> 1], t[n]);
 
-            if (i > 1)
-            {
-                // M[i][0] is ZEROES;
-                for(int j = 8; j > 0; j >>= 1)
-                {
-                    GCMUtil.multiplyP8(M[i - 2][j], M[i][j]);
-                }
+                // t[2.n + 1] = t[2.n] + t[1]
+                GCMUtil.xor(t[n], t[1], t[n + 1]);
             }
         }
+
     }
 
     public void multiplyH(byte[] x)
     {
-//      assert x.Length == 16;
+//        long[] z = new long[2];
+//        for (int i = 15; i >= 0; --i)
+//        {
+//            GCMUtil.xor(z, T[i + i + 1][(x[i] & 0x0F)]);
+//            GCMUtil.xor(z, T[i + i    ][(x[i] & 0xF0) >>> 4]);
+//        }
+//        Pack.longToBigEndian(z, x, 0);
 
-        int[] z = new int[4];
+        long z0 = 0, z1 = 0;
+
         for (int i = 15; i >= 0; --i)
         {
-//            GCMUtil.xor(z, M[i + i][x[i] & 0x0f]);
-            int[] m = M[i + i][x[i] & 0x0f];
-            z[0] ^= m[0];
-            z[1] ^= m[1];
-            z[2] ^= m[2];
-            z[3] ^= m[3];
-//            GCMUtil.xor(z, M[i + i + 1][(x[i] & 0xf0) >>> 4]);
-            m = M[i + i + 1][(x[i] & 0xf0) >>> 4];
-            z[0] ^= m[0];
-            z[1] ^= m[1];
-            z[2] ^= m[2];
-            z[3] ^= m[3];
+            long[] u = T[i + i + 1][(x[i] & 0x0F)];
+            long[] v = T[i + i    ][(x[i] & 0xF0) >>> 4];
+
+            z0 ^= u[0] ^ v[0];
+            z1 ^= u[1] ^ v[1];
         }
 
-        Pack.intToBigEndian(z, x, 0);
-    }
-}
\ No newline at end of file
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
+   }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_128.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_128.java
new file mode 100644
index 0000000..17d28bb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_128.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class BasicKGCMMultiplier_128
+    implements KGCMMultiplier
+{
+    private final long[] H = new long[KGCMUtil_128.SIZE];
+
+    public void init(long[] H)
+    {
+        KGCMUtil_128.copy(H,  this.H);
+    }
+
+    public void multiplyH(long[] z)
+    {
+        KGCMUtil_128.multiply(z, H, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_256.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_256.java
new file mode 100644
index 0000000..f14f920
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_256.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class BasicKGCMMultiplier_256
+    implements KGCMMultiplier
+{
+    private final long[] H = new long[KGCMUtil_256.SIZE];
+
+    public void init(long[] H)
+    {
+        KGCMUtil_256.copy(H,  this.H);
+    }
+
+    public void multiplyH(long[] z)
+    {
+        KGCMUtil_256.multiply(z, H, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_512.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_512.java
new file mode 100644
index 0000000..f2ad971
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/BasicKGCMMultiplier_512.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class BasicKGCMMultiplier_512
+    implements KGCMMultiplier
+{
+    private final long[] H = new long[KGCMUtil_512.SIZE];
+
+    public void init(long[] H)
+    {
+        KGCMUtil_512.copy(H,  this.H);
+    }
+
+    public void multiplyH(long[] z)
+    {
+        KGCMUtil_512.multiply(z, H, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMMultiplier.java
new file mode 100644
index 0000000..d3b6673
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMMultiplier.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public interface KGCMMultiplier
+{
+    void init(long[] H);
+    void multiplyH(long[] z);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_128.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_128.java
new file mode 100644
index 0000000..6a7befd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_128.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+import org.bouncycastle.math.raw.Interleave;
+
+/**
+ * Utilities for the GF(2^m) field with corresponding extension polynomial:
+ *
+ * GF (2^128) -> x^128 + x^7 + x^2 + x + 1
+ * 
+ * The representation is little-endian arrays of 64-bit words
+*/
+public class KGCMUtil_128
+{
+    public static final int SIZE = 2;
+
+    public static void add(long[] x, long[] y, long[] z)
+    {
+        z[0] = x[0] ^ y[0];
+        z[1] = x[1] ^ y[1];
+    }
+
+    public static void copy(long[] x, long[] z)
+    {
+        z[0] = x[0];
+        z[1] = x[1];
+    }
+
+    public static boolean equal(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        return d == 0L;
+    }
+
+    public static void multiply(long[] x, long[] y, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long y0 = y[0], y1 = y[1];
+        long z0 = 0, z1 = 0, z2 = 0;
+
+        for (int j = 0; j < 64; ++j)
+        {
+            long m0 = -(x0 & 1L); x0 >>>= 1;
+            z0 ^= (y0 & m0);
+            z1 ^= (y1 & m0);
+
+            long m1 = -(x1 & 1L); x1 >>>= 1;
+            z1 ^= (y0 & m1);
+            z2 ^= (y1 & m1);
+
+            long c = y1 >> 63;
+            y1 = (y1 << 1) | (y0 >>> 63);
+            y0 = (y0 << 1) ^ (c & 0x87L);
+        }
+
+        z0 ^= z2 ^ (z2 <<   1) ^ (z2 <<   2) ^ (z2 <<   7);
+        z1 ^=      (z2 >>> 63) ^ (z2 >>> 62) ^ (z2 >>> 57);      
+
+        z[0] = z0; z[1] = z1;
+    }
+
+    public static void multiplyX(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long m = x1 >> 63;
+        z[0] = (x0 << 1) ^ (m & 0x87L);
+        z[1] = (x1 << 1) | (x0 >>> 63);
+    }
+
+    public static void multiplyX8(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 >>> 56;
+        z[0] = (x0 << 8) ^ c ^ (c << 1) ^ (c << 2) ^ (c << 7);
+        z[1] = (x1 << 8) | (x0 >>> 56);
+    }
+
+    public static void one(long[] z)
+    {
+        z[0] = 1;
+        z[1] = 0;
+    }
+
+    public static void square(long[] x, long[] z)
+    {
+        long[] t  = new long[4];
+        Interleave.expand64To128(x[0], t, 0);
+        Interleave.expand64To128(x[1], t, 2);
+
+        long z0 = t[0], z1 = t[1], z2 = t[2], z3 = t[3];
+
+        z1 ^= z3 ^ (z3 <<   1) ^ (z3 <<   2) ^ (z3 <<   7);
+        z2 ^=      (z3 >>> 63) ^ (z3 >>> 62) ^ (z3 >>> 57);      
+
+        z0 ^= z2 ^ (z2 <<   1) ^ (z2 <<   2) ^ (z2 <<   7);
+        z1 ^=      (z2 >>> 63) ^ (z2 >>> 62) ^ (z2 >>> 57);      
+
+        z[0] = z0;
+        z[1] = z1;
+    }
+
+    public static void x(long[] z)
+    {
+        z[0] = 2;
+        z[1] = 0;
+    }
+
+    public static void zero(long[] z)
+    {
+        z[0] = 0;
+        z[1] = 0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_256.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_256.java
new file mode 100644
index 0000000..fffd204
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_256.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+import org.bouncycastle.math.raw.Interleave;
+
+/**
+ * Utilities for the GF(2^m) field with corresponding extension polynomial:
+ *
+ * GF (2^256) -> x^256 + x^10 + x^5 + x^2 + 1
+ * 
+ * The representation is little-endian arrays of 64-bit words
+*/
+public class KGCMUtil_256
+{
+    public static final int SIZE = 4;
+
+    public static void add(long[] x, long[] y, long[] z)
+    {
+        z[0] = x[0] ^ y[0];
+        z[1] = x[1] ^ y[1];
+        z[2] = x[2] ^ y[2];
+        z[3] = x[3] ^ y[3];
+    }
+
+    public static void copy(long[] x, long[] z)
+    {
+        z[0] = x[0];
+        z[1] = x[1];
+        z[2] = x[2];
+        z[3] = x[3];
+    }
+
+    public static boolean equal(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d |= x[2] ^ y[2];
+        d |= x[3] ^ y[3];
+        return d == 0L;
+    }
+
+    public static void multiply(long[] x, long[] y, long[] z)
+    {
+        long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        long y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
+        long z0 = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0;
+
+        for (int j = 0; j < 64; ++j)
+        {
+            long m0 = -(x0 & 1L); x0 >>>= 1;
+            z0 ^= (y0 & m0);
+            z1 ^= (y1 & m0);
+            z2 ^= (y2 & m0);
+            z3 ^= (y3 & m0);
+
+            long m1 = -(x1 & 1L); x1 >>>= 1;
+            z1 ^= (y0 & m1);
+            z2 ^= (y1 & m1);
+            z3 ^= (y2 & m1);
+            z4 ^= (y3 & m1);
+
+            long c = y3 >> 63;
+            y3 = (y3 << 1) | (y2 >>> 63);
+            y2 = (y2 << 1) | (y1 >>> 63);
+            y1 = (y1 << 1) | (y0 >>> 63);
+            y0 = (y0 << 1) ^ (c & 0x425L);
+        }
+
+        long y4 = y3;
+        y3 = y2;
+        y2 = y1;
+        y1 = y0 ^ (y4 >>> 62) ^ (y4 >>> 59) ^ (y4 >>> 54);
+        y0 = y4 ^ (y4 <<   2) ^ (y4 <<   5) ^ (y4 <<  10);
+
+        for (int j = 0; j < 64; ++j)
+        {
+            long m2 = -(x2 & 1L); x2 >>>= 1;
+            z0 ^= (y0 & m2);
+            z1 ^= (y1 & m2);
+            z2 ^= (y2 & m2);
+            z3 ^= (y3 & m2);
+
+            long m3 = -(x3 & 1L); x3 >>>= 1;
+            z1 ^= (y0 & m3);
+            z2 ^= (y1 & m3);
+            z3 ^= (y2 & m3);
+            z4 ^= (y3 & m3);
+
+            long c = y3 >> 63;
+            y3 = (y3 << 1) | (y2 >>> 63);
+            y2 = (y2 << 1) | (y1 >>> 63);
+            y1 = (y1 << 1) | (y0 >>> 63);
+            y0 = (y0 << 1) ^ (c & 0x425L);
+        }
+
+        z0 ^= z4 ^ (z4 <<   2) ^ (z4 <<   5) ^ (z4 <<  10);
+        z1 ^=      (z4 >>> 62) ^ (z4 >>> 59) ^ (z4 >>> 54);      
+
+        z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3;
+    }
+
+    public static void multiplyX(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        long m = x3 >> 63;
+        z[0] = (x0 << 1) ^ (m & 0x425L);
+        z[1] = (x1 << 1) | (x0 >>> 63);
+        z[2] = (x2 << 1) | (x1 >>> 63);
+        z[3] = (x3 << 1) | (x2 >>> 63);
+    }
+
+    public static void multiplyX8(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        long c = x3 >>> 56;
+        z[0] = (x0 << 8) ^ c ^ (c << 2) ^ (c << 5) ^ (c << 10);
+        z[1] = (x1 << 8) | (x0 >>> 56);
+        z[2] = (x2 << 8) | (x1 >>> 56);
+        z[3] = (x3 << 8) | (x2 >>> 56);
+    }
+
+    public static void one(long[] z)
+    {
+        z[0] = 1;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+    }
+
+    public static void square(long[] x, long[] z)
+    {
+        long[] t  = new long[SIZE << 1];
+        for (int i = 0; i < SIZE; ++i)
+        {
+            Interleave.expand64To128(x[i], t, i << 1);
+        }
+
+        int j = SIZE << 1;
+        while (--j >= SIZE)
+        {
+            long n = t[j];
+            t[j - SIZE    ] ^= n ^ (n <<   2) ^ (n <<   5) ^ (n <<  10);
+            t[j - SIZE + 1] ^=     (n >>> 62) ^ (n >>> 59) ^ (n >>> 54);      
+        }
+
+        copy(t, z);
+    }
+
+    public static void x(long[] z)
+    {
+        z[0] = 2;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+    }
+
+    public static void zero(long[] z)
+    {
+        z[0] = 0;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_512.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_512.java
new file mode 100644
index 0000000..1a25908
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/KGCMUtil_512.java
@@ -0,0 +1,201 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+import org.bouncycastle.math.raw.Interleave;
+
+/**
+ * Utilities for the GF(2^m) field with corresponding extension polynomial:
+ *
+ * GF (2^512) -> x^512 + x^8 + x^5 + x^2 + 1
+ * 
+ * The representation is little-endian arrays of 64-bit words
+ */
+public class KGCMUtil_512
+{
+    public static final int SIZE = 8;
+
+    public static void add(long[] x, long[] y, long[] z)
+    {
+        z[0] = x[0] ^ y[0];
+        z[1] = x[1] ^ y[1];
+        z[2] = x[2] ^ y[2];
+        z[3] = x[3] ^ y[3];
+        z[4] = x[4] ^ y[4];
+        z[5] = x[5] ^ y[5];
+        z[6] = x[6] ^ y[6];
+        z[7] = x[7] ^ y[7];
+    }
+
+    public static void copy(long[] x, long[] z)
+    {
+        z[0] = x[0];
+        z[1] = x[1];
+        z[2] = x[2];
+        z[3] = x[3];
+        z[4] = x[4];
+        z[5] = x[5];
+        z[6] = x[6];
+        z[7] = x[7];
+    }
+
+    public static boolean equal(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d |= x[2] ^ y[2];
+        d |= x[3] ^ y[3];
+        d |= x[4] ^ y[4];
+        d |= x[5] ^ y[5];
+        d |= x[6] ^ y[6];
+        d |= x[7] ^ y[7];
+        return d == 0L;
+    }
+
+    public static void multiply(long[] x, long[] y, long[] z)
+    {
+        long y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
+        long y4 = y[4], y5 = y[5], y6 = y[6], y7 = y[7];
+        long z0 = 0, z1 = 0, z2 = 0, z3 = 0;
+        long z4 = 0, z5 = 0, z6 = 0, z7 = 0;
+        long z8 = 0;
+
+        for (int i = 0; i < 8; i += 2)
+        {
+            long x0 = x[i], x1 = x[i + 1];
+
+            for (int j = 0; j < 64; ++j)
+            {
+                long m0 = -(x0 & 1L); x0 >>>= 1;
+                z0 ^= (y0 & m0);
+                z1 ^= (y1 & m0);
+                z2 ^= (y2 & m0);
+                z3 ^= (y3 & m0);
+                z4 ^= (y4 & m0);
+                z5 ^= (y5 & m0);
+                z6 ^= (y6 & m0);
+                z7 ^= (y7 & m0);
+
+                long m1 = -(x1 & 1L); x1 >>>= 1;
+                z1 ^= (y0 & m1);
+                z2 ^= (y1 & m1);
+                z3 ^= (y2 & m1);
+                z4 ^= (y3 & m1);
+                z5 ^= (y4 & m1);
+                z6 ^= (y5 & m1);
+                z7 ^= (y6 & m1);
+                z8 ^= (y7 & m1);
+
+                long c = y7 >> 63;
+                y7 = (y7 << 1) | (y6 >>> 63);
+                y6 = (y6 << 1) | (y5 >>> 63);
+                y5 = (y5 << 1) | (y4 >>> 63);
+                y4 = (y4 << 1) | (y3 >>> 63);
+                y3 = (y3 << 1) | (y2 >>> 63);
+                y2 = (y2 << 1) | (y1 >>> 63);
+                y1 = (y1 << 1) | (y0 >>> 63);
+                y0 = (y0 << 1) ^ (c & 0x125L);
+            }
+
+            long y8 = y7;
+            y7 = y6;
+            y6 = y5;
+            y5 = y4;
+            y4 = y3;
+            y3 = y2;
+            y2 = y1;
+            y1 = y0 ^ (y8 >>> 62) ^ (y8 >>> 59) ^ (y8 >>> 56);
+            y0 = y8 ^ (y8 <<   2) ^ (y8 <<   5) ^ (y8 <<   8);
+        }
+
+        z0 ^= z8 ^ (z8 <<   2) ^ (z8 <<   5) ^ (z8 <<   8);
+        z1 ^=      (z8 >>> 62) ^ (z8 >>> 59) ^ (z8 >>> 56);
+
+        z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3;
+        z[4] = z4; z[5] = z5; z[6] = z6; z[7] = z7;
+    }
+
+    public static void multiplyX(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        long x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+        long m = x7 >> 63;
+        z[0] = (x0 << 1) ^ (m & 0x125L);
+        z[1] = (x1 << 1) | (x0 >>> 63);
+        z[2] = (x2 << 1) | (x1 >>> 63);
+        z[3] = (x3 << 1) | (x2 >>> 63);
+        z[4] = (x4 << 1) | (x3 >>> 63);
+        z[5] = (x5 << 1) | (x4 >>> 63);
+        z[6] = (x6 << 1) | (x5 >>> 63);
+        z[7] = (x7 << 1) | (x6 >>> 63);
+    }
+
+    public static void multiplyX8(long[] x, long[] z)
+    {
+        long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3];
+        long x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+        long c = x7 >>> 56;
+        z[0] = (x0 << 8) ^ c ^ (c << 2) ^ (c << 5) ^ (c << 8);
+        z[1] = (x1 << 8) | (x0 >>> 56);
+        z[2] = (x2 << 8) | (x1 >>> 56);
+        z[3] = (x3 << 8) | (x2 >>> 56);
+        z[4] = (x4 << 8) | (x3 >>> 56);
+        z[5] = (x5 << 8) | (x4 >>> 56);
+        z[6] = (x6 << 8) | (x5 >>> 56);
+        z[7] = (x7 << 8) | (x6 >>> 56);
+    }
+
+    public static void one(long[] z)
+    {
+        z[0] = 1;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+        z[4] = 0;
+        z[5] = 0;
+        z[6] = 0;
+        z[7] = 0;
+    }
+
+    public static void square(long[] x, long[] z)
+    {
+        long[] t  = new long[SIZE << 1];
+        for (int i = 0; i < SIZE; ++i)
+        {
+            Interleave.expand64To128(x[i], t, i << 1);
+        }
+
+        int j = SIZE << 1;
+        while (--j >= SIZE)
+        {
+            long n = t[j];
+            t[j - SIZE    ] ^= n ^ (n <<   2) ^ (n <<   5) ^ (n <<   8);
+            t[j - SIZE + 1] ^=     (n >>> 62) ^ (n >>> 59) ^ (n >>> 56);      
+        }
+
+        copy(t, z);
+    }
+
+    public static void x(long[] z)
+    {
+        z[0] = 2;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+        z[4] = 0;
+        z[5] = 0;
+        z[6] = 0;
+        z[7] = 0;
+    }
+
+    public static void zero(long[] z)
+    {
+        z[0] = 0;
+        z[1] = 0;
+        z[2] = 0;
+        z[3] = 0;
+        z[4] = 0;
+        z[5] = 0;
+        z[6] = 0;
+        z[7] = 0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables16kKGCMMultiplier_512.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables16kKGCMMultiplier_512.java
new file mode 100644
index 0000000..7c9aff7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables16kKGCMMultiplier_512.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class Tables16kKGCMMultiplier_512
+    implements KGCMMultiplier
+{
+    private long[][] T;
+
+    public void init(long[] H)
+    {
+        if (T == null)
+        {
+            T = new long[256][KGCMUtil_512.SIZE];
+        }
+        else if (KGCMUtil_512.equal(H, T[1]))
+        {
+            return;
+        }
+
+        // T[0] = 0
+
+        // T[1] = H
+        KGCMUtil_512.copy(H, T[1]);
+
+        for (int n = 2; n < 256; n += 2)
+        {
+            // T[2.n] = x.T[n]
+            KGCMUtil_512.multiplyX(T[n >> 1], T[n]);
+
+            // T[2.n + 1] = T[2.n] + T[1]
+            KGCMUtil_512.add(T[n], T[1], T[n + 1]);
+        }
+    }
+
+    public void multiplyH(long[] z)
+    {
+        long[] r = new long[KGCMUtil_512.SIZE];
+        KGCMUtil_512.copy(T[(int)(z[KGCMUtil_512.SIZE - 1] >>> 56) & 0xFF], r);
+        for (int i = (KGCMUtil_512.SIZE << 3) - 2; i >= 0; --i)
+        {
+            KGCMUtil_512.multiplyX8(r, r);
+            KGCMUtil_512.add(T[(int)(z[i >>> 3] >>> ((i & 7) << 3)) & 0xFF], r, r);
+        }
+        KGCMUtil_512.copy(r, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables4kKGCMMultiplier_128.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables4kKGCMMultiplier_128.java
new file mode 100644
index 0000000..3502b4a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables4kKGCMMultiplier_128.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class Tables4kKGCMMultiplier_128
+    implements KGCMMultiplier
+{
+    private long[][] T;
+
+    public void init(long[] H)
+    {
+        if (T == null)
+        {
+            T = new long[256][KGCMUtil_128.SIZE];
+        }
+        else if (KGCMUtil_128.equal(H, T[1]))
+        {
+            return;
+        }
+
+        // T[0] = 0
+
+        // T[1] = H
+        KGCMUtil_128.copy(H, T[1]);
+
+        for (int n = 2; n < 256; n += 2)
+        {
+            // T[2.n] = x.T[n]
+            KGCMUtil_128.multiplyX(T[n >> 1], T[n]);
+
+            // T[2.n + 1] = T[2.n] + T[1]
+            KGCMUtil_128.add(T[n], T[1], T[n + 1]);
+        }
+    }
+
+    public void multiplyH(long[] z)
+    {
+        long[] r = new long[KGCMUtil_128.SIZE];
+        KGCMUtil_128.copy(T[(int)(z[KGCMUtil_128.SIZE - 1] >>> 56) & 0xFF], r);
+        for (int i = (KGCMUtil_128.SIZE << 3) - 2; i >= 0; --i)
+        {
+            KGCMUtil_128.multiplyX8(r, r);
+            KGCMUtil_128.add(T[(int)(z[i >>> 3] >>> ((i & 7) << 3)) & 0xFF], r, r);
+        }
+        KGCMUtil_128.copy(r, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables8kKGCMMultiplier_256.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables8kKGCMMultiplier_256.java
new file mode 100644
index 0000000..42ec9c0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/kgcm/Tables8kKGCMMultiplier_256.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.modes.kgcm;
+
+public class Tables8kKGCMMultiplier_256
+    implements KGCMMultiplier
+{
+    private long[][] T;
+
+    public void init(long[] H)
+    {
+        if (T == null)
+        {
+            T = new long[256][KGCMUtil_256.SIZE];
+        }
+        else if (KGCMUtil_256.equal(H, T[1]))
+        {
+            return;
+        }
+
+        // T[0] = 0
+
+        // T[1] = H
+        KGCMUtil_256.copy(H, T[1]);
+
+        for (int n = 2; n < 256; n += 2)
+        {
+            // T[2.n] = x.T[n]
+            KGCMUtil_256.multiplyX(T[n >> 1], T[n]);
+
+            // T[2.n + 1] = T[2.n] + T[1]
+            KGCMUtil_256.add(T[n], T[1], T[n + 1]);
+        }
+    }
+
+    public void multiplyH(long[] z)
+    {
+        long[] r = new long[KGCMUtil_256.SIZE];
+        KGCMUtil_256.copy(T[(int)(z[KGCMUtil_256.SIZE - 1] >>> 56) & 0xFF], r);
+        for (int i = (KGCMUtil_256.SIZE << 3) - 2; i >= 0; --i)
+        {
+            KGCMUtil_256.multiplyX8(r, r);
+            KGCMUtil_256.add(T[(int)(z[i >>> 3] >>> ((i & 7) << 3)) & 0xFF], r, r);
+        }
+        KGCMUtil_256.copy(r, z);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
index 63e29d8..076ad58 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
@@ -2,6 +2,7 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 
 /**
@@ -26,7 +27,7 @@
         }
         else
         {
-            this.random = new SecureRandom();
+            this.random = CryptoServicesRegistrar.getSecureRandom();
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
index 9a9272b..c064815 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.crypto.params;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
 
 public class AEADParameters
     implements CipherParameters
@@ -33,9 +34,9 @@
     public AEADParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
     {
         this.key = key;
-        this.nonce = nonce;
+        this.nonce = Arrays.clone(nonce);
         this.macSize = macSize;
-        this.associatedText = associatedText;
+        this.associatedText = Arrays.clone(associatedText);
     }
 
     public KeyParameter getKey()
@@ -50,11 +51,11 @@
 
     public byte[] getAssociatedText()
     {
-        return associatedText;
+        return Arrays.clone(associatedText);
     }
 
     public byte[] getNonce()
     {
-        return nonce;
+        return Arrays.clone(nonce);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Argon2Parameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Argon2Parameters.java
new file mode 100644
index 0000000..89bef26
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Argon2Parameters.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CharToByteConverter;
+import org.bouncycastle.crypto.PasswordConverter;
+import org.bouncycastle.util.Arrays;
+
+public class Argon2Parameters
+{
+    public static final int ARGON2_d = 0x00;
+    public static final int ARGON2_i = 0x01;
+    public static final int ARGON2_id = 0x02;
+
+    public static final int ARGON2_VERSION_10 = 0x10;
+    public static final int ARGON2_VERSION_13 = 0x13;
+
+    private static final int DEFAULT_ITERATIONS = 3;
+    private static final int DEFAULT_MEMORY_COST = 12;
+    private static final int DEFAULT_LANES = 1;
+    private static final int DEFAULT_TYPE = ARGON2_i;
+    private static final int DEFAULT_VERSION = ARGON2_VERSION_13;
+
+    public static class Builder
+    {
+        private byte[] salt;
+        private byte[] secret;
+        private byte[] additional;
+
+        private int iterations;
+        private int memory;
+        private int lanes;
+
+        private int version;
+        private final int type;
+        
+        private CharToByteConverter converter = PasswordConverter.UTF8;
+
+        public Builder()
+        {
+            this(DEFAULT_TYPE);
+        }
+
+        public Builder(int type)
+        {
+            this.type = type;
+            this.lanes = DEFAULT_LANES;
+            this.memory = 1 << DEFAULT_MEMORY_COST;
+            this.iterations = DEFAULT_ITERATIONS;
+            this.version = DEFAULT_VERSION;
+        }
+
+        public Builder withParallelism(int parallelism)
+        {
+            this.lanes = parallelism;
+            return this;
+        }
+
+        public Builder withSalt(byte[] salt)
+        {
+            this.salt = Arrays.clone(salt);
+            return this;
+        }
+
+        public Builder withSecret(byte[] secret)
+        {
+            this.secret = Arrays.clone(secret);
+            return this;
+        }
+
+        public Builder withAdditional(byte[] additional)
+        {
+            this.additional = Arrays.clone(additional);
+            return this;
+        }
+
+        public Builder withIterations(int iterations)
+        {
+            this.iterations = iterations;
+            return this;
+        }
+
+
+        public Builder withMemoryAsKB(int memory)
+        {
+            this.memory = memory;
+            return this;
+        }
+
+
+        public Builder withMemoryPowOfTwo(int memory)
+        {
+            this.memory = 1 << memory;
+            return this;
+        }
+
+        public Builder withVersion(int version)
+        {
+            this.version = version;
+            return this;
+        }
+        
+        public Builder withCharToByteConverter(CharToByteConverter converter)
+        {
+            this.converter = converter;
+            return this;
+        }
+
+        public Argon2Parameters build()
+        {
+            return new Argon2Parameters(type, salt, secret, additional, iterations, memory, lanes, version, converter);
+        }
+
+        public void clear()
+        {
+            Arrays.clear(salt);
+            Arrays.clear(secret);
+            Arrays.clear(additional);
+        }
+    }
+
+    private final byte[] salt;
+    private final byte[] secret;
+    private final byte[] additional;
+
+    private final int iterations;
+    private final int memory;
+    private final int lanes;
+
+    private final int version;
+    private final int type;
+    private final CharToByteConverter converter;
+
+    private Argon2Parameters(
+        int type,
+        byte[] salt,
+        byte[] secret,
+        byte[] additional,
+        int iterations,
+        int memory,
+        int lanes,
+        int version,
+        CharToByteConverter converter)
+    {
+
+        this.salt = Arrays.clone(salt);
+        this.secret = Arrays.clone(secret);
+        this.additional = Arrays.clone(additional);
+        this.iterations = iterations;
+        this.memory = memory;
+        this.lanes = lanes;
+        this.version = version;
+        this.type = type;
+        this.converter = converter;
+    }
+
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
+
+    public byte[] getSecret()
+    {
+        return Arrays.clone(secret);
+    }
+
+    public byte[] getAdditional()
+    {
+        return Arrays.clone(additional);
+    }
+
+    public int getIterations()
+    {
+        return iterations;
+    }
+
+    public int getMemory()
+    {
+        return memory;
+    }
+
+    public int getLanes()
+    {
+        return lanes;
+    }
+
+    public int getVersion()
+    {
+        return version;
+    }
+
+    public int getType()
+    {
+        return type;
+    }
+
+    public CharToByteConverter getCharToByteConverter()
+    {
+        return converter;
+    }
+
+    public void clear()
+    {
+        Arrays.clear(salt);
+        Arrays.clear(secret);
+        Arrays.clear(additional);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java
index 24264b6..1e9e90b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java
@@ -4,50 +4,61 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Memoable;
 
-public class CramerShoupParameters implements CipherParameters {
+public class CramerShoupParameters
+    implements CipherParameters
+{
 
-	private BigInteger p; // prime order of G
-	private BigInteger g1, g2; // generate G
-	
-	private Digest H; // hash function
+    private BigInteger p; // prime order of G
+    private BigInteger g1, g2; // generate G
 
-	public CramerShoupParameters(BigInteger p, BigInteger g1, BigInteger g2, Digest H) {
-		this.p = p;
-		this.g1 = g1;
-		this.g2 = g2;
-		this.H = H;
-	}
+    private Digest H; // hash function
 
-	public boolean equals(Object obj) {
-		if (!(obj instanceof DSAParameters)) {
-			return false;
-		}
+    public CramerShoupParameters(BigInteger p, BigInteger g1, BigInteger g2, Digest H)
+    {
+        this.p = p;
+        this.g1 = g1;
+        this.g2 = g2;
+        this.H = (Digest)((Memoable)H).copy();
+        this.H.reset();
+    }
 
-		CramerShoupParameters pm = (CramerShoupParameters) obj;
+    public boolean equals(Object obj)
+    {
+        if (!(obj instanceof CramerShoupParameters))
+        {
+            return false;
+        }
 
-		return (pm.getP().equals(p) && pm.getG1().equals(g1) && pm.getG2().equals(g2));
-	}
+        CramerShoupParameters pm = (CramerShoupParameters)obj;
 
-	public int hashCode() {
-		return getP().hashCode() ^ getG1().hashCode() ^ getG2().hashCode();
-	}
-	
-	public BigInteger getG1() {
-		return g1;
-	}
-	
-	public BigInteger getG2() {
-		return g2;
-	}
-	
-	public BigInteger getP() {
-		return p;
-	}
-	
-	public Digest getH() {
-		H.reset();
-		return H;
-	}
-	
+        return (pm.getP().equals(p) && pm.getG1().equals(g1) && pm.getG2().equals(g2));
+    }
+
+    public int hashCode()
+    {
+        return getP().hashCode() ^ getG1().hashCode() ^ getG2().hashCode();
+    }
+
+    public BigInteger getG1()
+    {
+        return g1;
+    }
+
+    public BigInteger getG2()
+    {
+        return g2;
+    }
+
+    public BigInteger getP()
+    {
+        return p;
+    }
+
+    public Digest getH()
+    {
+        return (Digest)((Memoable)H).copy();
+    }
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPrivateParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPrivateParameters.java
new file mode 100644
index 0000000..9f5d73a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPrivateParameters.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class DHMQVPrivateParameters
+    implements CipherParameters
+{
+    private DHPrivateKeyParameters staticPrivateKey;
+    private DHPrivateKeyParameters ephemeralPrivateKey;
+    private DHPublicKeyParameters ephemeralPublicKey;
+
+    public DHMQVPrivateParameters(
+        DHPrivateKeyParameters  staticPrivateKey,
+        DHPrivateKeyParameters  ephemeralPrivateKey)
+    {
+        this(staticPrivateKey, ephemeralPrivateKey, null);
+    }
+
+    public DHMQVPrivateParameters(
+        DHPrivateKeyParameters  staticPrivateKey,
+        DHPrivateKeyParameters  ephemeralPrivateKey,
+        DHPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPrivateKey == null)
+        {
+            throw new NullPointerException("staticPrivateKey cannot be null");
+        }
+        if (ephemeralPrivateKey == null)
+        {
+            throw new NullPointerException("ephemeralPrivateKey cannot be null");
+        }
+
+        DHParameters parameters = staticPrivateKey.getParameters();
+        if (!parameters.equals(ephemeralPrivateKey.getParameters()))
+        {
+            throw new IllegalArgumentException("Static and ephemeral private keys have different domain parameters");
+        }
+
+        if (ephemeralPublicKey == null)
+        {
+            ephemeralPublicKey = new DHPublicKeyParameters(
+                parameters.getG().multiply(ephemeralPrivateKey.getX()),
+                parameters);
+        }
+        else if (!parameters.equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("Ephemeral public key has different domain parameters");
+        }
+
+        this.staticPrivateKey = staticPrivateKey;
+        this.ephemeralPrivateKey = ephemeralPrivateKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public DHPrivateKeyParameters getStaticPrivateKey()
+    {
+        return staticPrivateKey;
+    }
+
+    public DHPrivateKeyParameters getEphemeralPrivateKey()
+    {
+        return ephemeralPrivateKey;
+    }
+
+    public DHPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPublicParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPublicParameters.java
new file mode 100644
index 0000000..ca21a67
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHMQVPublicParameters.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class DHMQVPublicParameters
+    implements CipherParameters
+{
+    private DHPublicKeyParameters staticPublicKey;
+    private DHPublicKeyParameters ephemeralPublicKey;
+
+    public DHMQVPublicParameters(
+        DHPublicKeyParameters   staticPublicKey,
+        DHPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPublicKey == null)
+        {
+            throw new NullPointerException("staticPublicKey cannot be null");
+        }
+        if (ephemeralPublicKey == null)
+        {
+            throw new NullPointerException("ephemeralPublicKey cannot be null");
+        }
+        if (!staticPublicKey.getParameters().equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("Static and ephemeral public keys have different domain parameters");
+        }
+
+        this.staticPublicKey = staticPublicKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public DHPublicKeyParameters getStaticPublicKey()
+    {
+        return staticPublicKey;
+    }
+
+    public DHPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
index fec6dfd..5bdb10a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
@@ -94,6 +94,11 @@
             }
         }
 
+        if (m > p.bitLength())
+        {
+            throw new IllegalArgumentException("unsafe p value so small specific l required");
+        }
+
         this.g = g;
         this.p = p;
         this.q = q;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPrivateParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPrivateParameters.java
new file mode 100644
index 0000000..31836ce
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPrivateParameters.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for private unified static/ephemeral agreement as described in NIST SP 800-56A.
+ */
+public class DHUPrivateParameters
+    implements CipherParameters
+{
+    private DHPrivateKeyParameters staticPrivateKey;
+    private DHPrivateKeyParameters ephemeralPrivateKey;
+    private DHPublicKeyParameters ephemeralPublicKey;
+
+    public DHUPrivateParameters(
+        DHPrivateKeyParameters  staticPrivateKey,
+        DHPrivateKeyParameters  ephemeralPrivateKey)
+    {
+        this(staticPrivateKey, ephemeralPrivateKey, null);
+    }
+
+    public DHUPrivateParameters(
+        DHPrivateKeyParameters  staticPrivateKey,
+        DHPrivateKeyParameters  ephemeralPrivateKey,
+        DHPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPrivateKey == null)
+        {
+            throw new NullPointerException("staticPrivateKey cannot be null");
+        }
+        if (ephemeralPrivateKey == null)
+        {
+            throw new NullPointerException("ephemeralPrivateKey cannot be null");
+        }
+
+        DHParameters parameters = staticPrivateKey.getParameters();
+        if (!parameters.equals(ephemeralPrivateKey.getParameters()))
+        {
+            throw new IllegalArgumentException("static and ephemeral private keys have different domain parameters");
+        }
+
+        if (ephemeralPublicKey == null)
+        {
+            ephemeralPublicKey = new DHPublicKeyParameters(
+                parameters.getG().modPow(ephemeralPrivateKey.getX(), parameters.getP()),
+                parameters);
+        }
+        else if (!parameters.equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("ephemeral public key has different domain parameters");
+        }
+
+        this.staticPrivateKey = staticPrivateKey;
+        this.ephemeralPrivateKey = ephemeralPrivateKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public DHPrivateKeyParameters getStaticPrivateKey()
+    {
+        return staticPrivateKey;
+    }
+
+    public DHPrivateKeyParameters getEphemeralPrivateKey()
+    {
+        return ephemeralPrivateKey;
+    }
+
+    public DHPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPublicParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPublicParameters.java
new file mode 100644
index 0000000..7b2a14d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHUPublicParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for static/ephemeral agreement as described in NIST SP 800-56A.
+ */
+public class DHUPublicParameters
+    implements CipherParameters
+{
+    private DHPublicKeyParameters staticPublicKey;
+    private DHPublicKeyParameters ephemeralPublicKey;
+
+    public DHUPublicParameters(
+        DHPublicKeyParameters   staticPublicKey,
+        DHPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPublicKey == null)
+        {
+            throw new NullPointerException("staticPublicKey cannot be null");
+        }
+        if (ephemeralPublicKey == null)
+        {
+            throw new NullPointerException("ephemeralPublicKey cannot be null");
+        }
+        if (!staticPublicKey.getParameters().equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("Static and ephemeral public keys have different domain parameters");
+        }
+
+        this.staticPublicKey = staticPublicKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public DHPublicKeyParameters getStaticPublicKey()
+    {
+        return staticPublicKey;
+    }
+
+    public DHPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
index b22f7a0..8c4b187 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
@@ -11,7 +11,7 @@
         byte[]  seed,
         int     counter)
     {
-        this.seed = seed;
+        this.seed = Arrays.clone(seed);
         this.counter = counter;
     }
 
@@ -22,7 +22,7 @@
 
     public byte[] getSeed()
     {
-        return seed;
+        return Arrays.clone(seed);
     }
 
     public boolean equals(
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
index 07d93d0..11613e8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
@@ -20,7 +20,7 @@
         int     counter,
         int     usageIndex)
     {
-        this.seed = seed;
+        this.seed = Arrays.clone(seed);
         this.counter = counter;
         this.usageIndex = usageIndex;
     }
@@ -32,7 +32,7 @@
 
     public byte[] getSeed()
     {
-        return seed;
+        return Arrays.clone(seed);
     }
 
     public int getUsageIndex()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DSTU4145Parameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSTU4145Parameters.java
new file mode 100644
index 0000000..8afd2b3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSTU4145Parameters.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.util.Arrays;
+
+public class DSTU4145Parameters
+    extends ECDomainParameters
+{
+    private final byte[] dke;
+
+    public DSTU4145Parameters(ECDomainParameters ecParameters, byte[] dke)
+    {
+        super(ecParameters.getCurve(), ecParameters.getG(), ecParameters.getN(), ecParameters.getH(), ecParameters.getSeed());
+
+        this.dke = Arrays.clone(dke);
+    }
+
+    public byte[] getDKE()
+    {
+        return Arrays.clone(dke);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPrivateParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPrivateParameters.java
new file mode 100644
index 0000000..ca9deaf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPrivateParameters.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for private unified static/ephemeral agreement as described in NIST SP 800-56A.
+ */
+public class ECDHUPrivateParameters
+    implements CipherParameters
+{
+    private ECPrivateKeyParameters staticPrivateKey;
+    private ECPrivateKeyParameters ephemeralPrivateKey;
+    private ECPublicKeyParameters ephemeralPublicKey;
+
+    public ECDHUPrivateParameters(
+        ECPrivateKeyParameters  staticPrivateKey,
+        ECPrivateKeyParameters  ephemeralPrivateKey)
+    {
+        this(staticPrivateKey, ephemeralPrivateKey, null);
+    }
+
+    public ECDHUPrivateParameters(
+        ECPrivateKeyParameters  staticPrivateKey,
+        ECPrivateKeyParameters  ephemeralPrivateKey,
+        ECPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPrivateKey == null)
+        {
+            throw new NullPointerException("staticPrivateKey cannot be null");
+        }
+        if (ephemeralPrivateKey == null)
+        {
+            throw new NullPointerException("ephemeralPrivateKey cannot be null");
+        }
+
+        ECDomainParameters parameters = staticPrivateKey.getParameters();
+        if (!parameters.equals(ephemeralPrivateKey.getParameters()))
+        {
+            throw new IllegalArgumentException("static and ephemeral private keys have different domain parameters");
+        }
+
+        if (ephemeralPublicKey == null)
+        {
+            ephemeralPublicKey = new ECPublicKeyParameters(
+                parameters.getG().multiply(ephemeralPrivateKey.getD()),
+                parameters);
+        }
+        else if (!parameters.equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("ephemeral public key has different domain parameters");
+        }
+
+        this.staticPrivateKey = staticPrivateKey;
+        this.ephemeralPrivateKey = ephemeralPrivateKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public ECPrivateKeyParameters getStaticPrivateKey()
+    {
+        return staticPrivateKey;
+    }
+
+    public ECPrivateKeyParameters getEphemeralPrivateKey()
+    {
+        return ephemeralPrivateKey;
+    }
+
+    public ECPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPublicParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPublicParameters.java
new file mode 100644
index 0000000..f7754e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDHUPublicParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for public unified static/ephemeral agreement as described in NIST SP 800-56A using EC DH/CDH.
+ */
+public class ECDHUPublicParameters
+    implements CipherParameters
+{
+    private ECPublicKeyParameters staticPublicKey;
+    private ECPublicKeyParameters ephemeralPublicKey;
+
+    public ECDHUPublicParameters(
+        ECPublicKeyParameters   staticPublicKey,
+        ECPublicKeyParameters   ephemeralPublicKey)
+    {
+        if (staticPublicKey == null)
+        {
+            throw new NullPointerException("staticPublicKey cannot be null");
+        }
+        if (ephemeralPublicKey == null)
+        {
+            throw new NullPointerException("ephemeralPublicKey cannot be null");
+        }
+        if (!staticPublicKey.getParameters().equals(ephemeralPublicKey.getParameters()))
+        {
+            throw new IllegalArgumentException("static and ephemeral public keys have different domain parameters");
+        }
+
+        this.staticPublicKey = staticPublicKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public ECPublicKeyParameters getStaticPublicKey()
+    {
+        return staticPublicKey;
+    }
+
+    public ECPublicKeyParameters getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
index c97f2e7..1cead6f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
@@ -2,6 +2,7 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
@@ -15,6 +16,7 @@
     private ECPoint     G;
     private BigInteger  n;
     private BigInteger  h;
+    private BigInteger  hInv = null;
 
     public ECDomainParameters(
         ECCurve     curve,
@@ -40,11 +42,21 @@
         BigInteger  h,
         byte[]      seed)
     {
+        if (curve == null)
+        {
+            throw new NullPointerException("curve");
+        }
+        if (n == null)
+        {
+            throw new NullPointerException("n");
+        }
+        // we can't check for h == null here as h is optional in X9.62 as it is not required for ECDSA
+
         this.curve = curve;
-        this.G = G.normalize();
+        this.G = validate(curve, G);
         this.n = n;
         this.h = h;
-        this.seed = seed;
+        this.seed = Arrays.clone(seed);
     }
 
     public ECCurve getCurve()
@@ -67,6 +79,15 @@
         return h;
     }
 
+    public synchronized BigInteger getHInv()
+    {
+        if (hInv == null)
+        {
+            hInv = h.modInverse(n);
+        }
+        return hInv;
+    }
+
     public byte[] getSeed()
     {
         return Arrays.clone(seed);
@@ -101,4 +122,26 @@
         hc ^= h.hashCode();
         return hc;
     }
+
+    static ECPoint validate(ECCurve c, ECPoint q)
+    {
+        if (q == null)
+        {
+            throw new IllegalArgumentException("Point has null value");
+        }
+
+        q = ECAlgorithms.importPoint(c, q).normalize();
+
+        if (q.isInfinity())
+        {
+            throw new IllegalArgumentException("Point at infinity");
+        }
+
+        if (!q.isValid())
+        {
+            throw new IllegalArgumentException("Point not on curve");
+        }
+
+        return q;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECGOST3410Parameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECGOST3410Parameters.java
new file mode 100644
index 0000000..e90d30f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECGOST3410Parameters.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public class ECGOST3410Parameters
+    extends ECNamedDomainParameters
+{
+    private final ASN1ObjectIdentifier  publicKeyParamSet;
+    private final ASN1ObjectIdentifier  digestParamSet;
+    private final ASN1ObjectIdentifier  encryptionParamSet;
+
+    public ECGOST3410Parameters(ECDomainParameters ecParameters, ASN1ObjectIdentifier publicKeyParamSet, ASN1ObjectIdentifier digestParamSet)
+    {
+        this(ecParameters, publicKeyParamSet, digestParamSet, null);
+    }
+
+    public ECGOST3410Parameters(ECDomainParameters ecParameters, ASN1ObjectIdentifier publicKeyParamSet, ASN1ObjectIdentifier digestParamSet, ASN1ObjectIdentifier encryptionParamSet)
+    {
+        super(publicKeyParamSet, ecParameters.getCurve(), ecParameters.getG(), ecParameters.getN(), ecParameters.getH(), ecParameters.getSeed());
+
+        if (ecParameters instanceof ECNamedDomainParameters)
+        {
+            if (!publicKeyParamSet.equals(((ECNamedDomainParameters)ecParameters).getName()))
+            {
+                throw new IllegalArgumentException("named parameters do not match publicKeyParamSet value");
+            }
+        }
+        this.publicKeyParamSet = publicKeyParamSet;
+        this.digestParamSet = digestParamSet;
+        this.encryptionParamSet = encryptionParamSet;
+    }
+
+    public ASN1ObjectIdentifier getPublicKeyParamSet()
+    {
+        return publicKeyParamSet;
+    }
+
+    public ASN1ObjectIdentifier getDigestParamSet()
+    {
+        return digestParamSet;
+    }
+
+    public ASN1ObjectIdentifier getEncryptionParamSet()
+    {
+        return encryptionParamSet;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java
index 5b694be..ed89589 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java
@@ -3,6 +3,7 @@
 import java.math.BigInteger;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 
@@ -13,7 +14,7 @@
 
     public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECCurve curve, ECPoint G, BigInteger n)
     {
-        this(name, curve, G, n, null, null);
+        this(name, curve, G, n, ECConstants.ONE, null);
     }
 
     public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECCurve curve, ECPoint G, BigInteger n, BigInteger h)
@@ -28,6 +29,12 @@
         this.name = name;
     }
 
+    public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECDomainParameters domainParameters)
+    {
+        super(domainParameters.getCurve(), domainParameters.getG(), domainParameters.getN(), domainParameters.getH(), domainParameters.getSeed());
+        this.name = name;
+    }
+
     public ASN1ObjectIdentifier getName()
     {
         return name;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
index 036bf4a..6f097ad 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
@@ -13,29 +13,7 @@
     {
         super(false, params);
 
-        this.Q = validate(Q);
-    }
-
-    private ECPoint validate(ECPoint q)
-    {
-        if (q == null)
-        {
-            throw new IllegalArgumentException("point has null value");
-        }
-
-        if (q.isInfinity())
-        {
-            throw new IllegalArgumentException("point at infinity");
-        }
-
-        q = q.normalize();
-
-        if (!q.isValid())
-        {
-            throw new IllegalArgumentException("point not on curve");
-        }
-
-        return q;
+        this.Q = ECDomainParameters.validate(params.getCurve(), Q);
     }
 
     public ECPoint getQ()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java
new file mode 100644
index 0000000..593bff0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class Ed25519KeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    public Ed25519KeyGenerationParameters(SecureRandom random)
+    {
+        super(random, 256);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java
new file mode 100644
index 0000000..d5d3ed5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class Ed25519PrivateKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = Ed25519.SECRET_KEY_SIZE;
+    public static final int SIGNATURE_SIZE = Ed25519.SIGNATURE_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public Ed25519PrivateKeyParameters(SecureRandom random)
+    {
+        super(true);
+
+        Ed25519.generatePrivateKey(random, data);
+    }
+
+    public Ed25519PrivateKeyParameters(byte[] buf, int off)
+    {
+        super(true);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public Ed25519PrivateKeyParameters(InputStream input) throws IOException
+    {
+        super(true);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of Ed25519 private key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+
+    public Ed25519PublicKeyParameters generatePublicKey()
+    {
+        byte[] publicKey = new byte[Ed25519.PUBLIC_KEY_SIZE];
+        Ed25519.generatePublicKey(data, 0, publicKey, 0);
+        return new Ed25519PublicKeyParameters(publicKey, 0);
+    }
+
+    public void sign(int algorithm, Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen, byte[] sig, int sigOff)
+    {
+        byte[] pk = new byte[Ed25519.PUBLIC_KEY_SIZE];
+        if (null == publicKey)
+        {
+            Ed25519.generatePublicKey(data, 0, pk, 0);
+        }
+        else
+        {
+            publicKey.encode(pk, 0);
+        }
+
+        switch (algorithm)
+        {
+        case Ed25519.Algorithm.Ed25519:
+        {
+            if (null != ctx)
+            {
+                throw new IllegalArgumentException("ctx");
+            }
+
+            Ed25519.sign(data, 0, pk, 0, msg, msgOff, msgLen, sig, sigOff);
+            break;
+        }
+        case Ed25519.Algorithm.Ed25519ctx:
+        {
+            Ed25519.sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff);
+            break;
+        }
+        case Ed25519.Algorithm.Ed25519ph:
+        {
+            if (Ed25519.PREHASH_SIZE != msgLen)
+            {
+                throw new IllegalArgumentException("msgLen");
+            }
+
+            Ed25519.signPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff);
+            break;
+        }
+        default:
+        {
+            throw new IllegalArgumentException("algorithm");
+        }
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java
new file mode 100644
index 0000000..783382f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class Ed25519PublicKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = Ed25519.PUBLIC_KEY_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public Ed25519PublicKeyParameters(byte[] buf, int off)
+    {
+        super(false);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public Ed25519PublicKeyParameters(InputStream input) throws IOException
+    {
+        super(false);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of Ed25519 public key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java
new file mode 100644
index 0000000..34de47b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class Ed448KeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    public Ed448KeyGenerationParameters(SecureRandom random)
+    {
+        super(random, 448);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java
new file mode 100644
index 0000000..be50146
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class Ed448PrivateKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = Ed448.SECRET_KEY_SIZE;
+    public static final int SIGNATURE_SIZE = Ed448.SIGNATURE_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public Ed448PrivateKeyParameters(SecureRandom random)
+    {
+        super(true);
+
+        Ed448.generatePrivateKey(random, data);
+    }
+
+    public Ed448PrivateKeyParameters(byte[] buf, int off)
+    {
+        super(true);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public Ed448PrivateKeyParameters(InputStream input) throws IOException
+    {
+        super(true);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of Ed448 private key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+
+    public Ed448PublicKeyParameters generatePublicKey()
+    {
+        byte[] publicKey = new byte[Ed448.PUBLIC_KEY_SIZE];
+        Ed448.generatePublicKey(data, 0, publicKey, 0);
+        return new Ed448PublicKeyParameters(publicKey, 0);
+    }
+
+    public void sign(int algorithm, Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen, byte[] sig, int sigOff)
+    {
+        byte[] pk = new byte[Ed448.PUBLIC_KEY_SIZE];
+        if (null == publicKey)
+        {
+            Ed448.generatePublicKey(data, 0, pk, 0);
+        }
+        else
+        {
+            publicKey.encode(pk, 0);
+        }
+
+        switch (algorithm)
+        {
+        case Ed448.Algorithm.Ed448:
+        {
+            Ed448.sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff);
+            break;
+        }
+        case Ed448.Algorithm.Ed448ph:
+        {
+            if (Ed448.PREHASH_SIZE != msgLen)
+            {
+                throw new IllegalArgumentException("msgLen");
+            }
+
+            Ed448.signPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff);
+            break;
+        }
+        default:
+        {
+            throw new IllegalArgumentException("algorithm");
+        }
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java
new file mode 100644
index 0000000..44cbf87
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class Ed448PublicKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = Ed448.PUBLIC_KEY_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public Ed448PublicKeyParameters(byte[] buf, int off)
+    {
+        super(false);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public Ed448PublicKeyParameters(InputStream input) throws IOException
+    {
+        super(false);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of Ed448 public key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/IESParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
index 0600b34..f0940f8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.crypto.params;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
 
 /**
  * parameters for using an integrated cipher in stream mode.
@@ -22,19 +23,19 @@
         byte[]  encoding,
         int     macKeySize)
     {
-        this.derivation = derivation;
-        this.encoding = encoding;
+        this.derivation = Arrays.clone(derivation);
+        this.encoding = Arrays.clone(encoding);
         this.macKeySize = macKeySize;
     }
 
     public byte[] getDerivationV()
     {
-        return derivation;
+        return Arrays.clone(derivation);
     }
 
     public byte[] getEncodingV()
     {
-        return encoding;
+        return Arrays.clone(encoding);
     }
 
     public int getMacKeySize()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
index 29d8b36..6b5e150 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
@@ -14,13 +14,13 @@
  * <li>3b: K(i) := PRF( KI, Label || 0x00 || [i]_2 || [L]_2 || Context ) OR:</li>
  * <li>3c: K(i) := PRF( KI, Label || [i]_2 || 0x00 || Context || [L]_2 ) etc... with the counter somewhere in the 'middle' of the fixedInputData.</li>
  * </ul>
- * <p>
  * This function must be called with the following KDFCounterParameters():
- *  - KI     <br/>
- *  - The part of the fixedInputData that comes BEFORE the counter OR null  <br/>
- *  - the part of the fixedInputData that comes AFTER the counter OR null  <br/>
- *  - the length of the counter in bits (not bytes) <br/>
- * </p>
+ * <ul>
+ *  <li>KI</li>
+ *  <li>The part of the fixedInputData that comes BEFORE the counter OR null</li>
+ *  <li>the part of the fixedInputData that comes AFTER the counter OR null</li>
+ *  <li>the length of the counter in bits (not bytes)</li>
+ *  </ul>
  * Resulting function calls assuming an 8 bit counter.
  * <ul>
  * <li>1.  KDFCounterParameters(ki, 	null, 									"Label || 0x00 || Context || [L]_2]",	8); </li>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
index a7b18e5..9c6ed02 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
@@ -1,9 +1,10 @@
 package org.bouncycastle.crypto.params;
 
-import org.bouncycastle.crypto.CipherParameters;
-
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+
 public class ParametersWithRandom
     implements CipherParameters
 {
@@ -21,7 +22,7 @@
     public ParametersWithRandom(
         CipherParameters    parameters)
     {
-        this(parameters, new SecureRandom());
+        this(parameters, CryptoServicesRegistrar.getSecureRandom());
     }
 
     public SecureRandom getRandom()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithUKM.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithUKM.java
new file mode 100644
index 0000000..55001e3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithUKM.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ParametersWithUKM
+    implements CipherParameters
+{
+    private byte[] ukm;
+    private CipherParameters    parameters;
+
+    public ParametersWithUKM(
+        CipherParameters    parameters,
+        byte[] ukm)
+    {
+        this(parameters, ukm, 0, ukm.length);
+    }
+
+    public ParametersWithUKM(
+        CipherParameters    parameters,
+        byte[] ukm,
+        int                 ivOff,
+        int                 ivLen)
+    {
+        this.ukm = new byte[ivLen];
+        this.parameters = parameters;
+
+        System.arraycopy(ukm, ivOff, this.ukm, 0, ivLen);
+    }
+
+    public byte[] getUKM()
+    {
+        return ukm;
+    }
+
+    public CipherParameters getParameters()
+    {
+        return parameters;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePrivateParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePrivateParameters.java
index fe8038c..860cf6d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePrivateParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePrivateParameters.java
@@ -4,7 +4,7 @@
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
- * Public parameters for an SM2 key exchange. The ephemeralPrivateKey is used to calculate the random point used in the algorithm.
+ * Private parameters for an SM2 key exchange. The ephemeralPrivateKey is used to calculate the random point used in the algorithm.
  */
 public class SM2KeyExchangePrivateParameters
     implements CipherParameters
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePublicParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePublicParameters.java
index dadcee9..fa5dc00 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePublicParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SM2KeyExchangePublicParameters.java
@@ -8,8 +8,8 @@
 public class SM2KeyExchangePublicParameters
     implements CipherParameters
 {
-    private ECPublicKeyParameters staticPublicKey;
-    private ECPublicKeyParameters ephemeralPublicKey;
+    private final ECPublicKeyParameters staticPublicKey;
+    private final ECPublicKeyParameters ephemeralPublicKey;
 
     public SM2KeyExchangePublicParameters(
         ECPublicKeyParameters   staticPublicKey,
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
index ef3d0fc..6451b96 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
@@ -196,7 +196,7 @@
                 throw new IllegalArgumentException("Parameter value must not be null.");
             }
             if ((type != PARAM_TYPE_KEY)
-                && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+                && (type < PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
             {
                 throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519KeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519KeyGenerationParameters.java
new file mode 100644
index 0000000..ac6ea70
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519KeyGenerationParameters.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class X25519KeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    public X25519KeyGenerationParameters(SecureRandom random)
+    {
+        super(random, 255);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PrivateKeyParameters.java
new file mode 100644
index 0000000..7535ca9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PrivateKeyParameters.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc7748.X25519;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class X25519PrivateKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = X25519.SCALAR_SIZE;
+    public static final int SECRET_SIZE = X25519.POINT_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public X25519PrivateKeyParameters(SecureRandom random)
+    {
+        super(true);
+
+        X25519.generatePrivateKey(random, data);
+    }
+
+    public X25519PrivateKeyParameters(byte[] buf, int off)
+    {
+        super(true);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public X25519PrivateKeyParameters(InputStream input) throws IOException
+    {
+        super(true);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of X25519 private key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+
+    public X25519PublicKeyParameters generatePublicKey()
+    {
+        byte[] publicKey = new byte[X25519.POINT_SIZE];
+        X25519.generatePublicKey(data, 0, publicKey, 0);
+        return new X25519PublicKeyParameters(publicKey, 0);
+    }
+
+    public void generateSecret(X25519PublicKeyParameters publicKey, byte[] buf, int off)
+    {
+        byte[] encoded = new byte[X25519.POINT_SIZE];
+        publicKey.encode(encoded, 0);
+        if (!X25519.calculateAgreement(data, 0, encoded, 0, buf, off))
+        {
+            throw new IllegalStateException("X25519 agreement failed");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PublicKeyParameters.java
new file mode 100644
index 0000000..33d1701
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X25519PublicKeyParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.math.ec.rfc7748.X25519;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class X25519PublicKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = X25519.POINT_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public X25519PublicKeyParameters(byte[] buf, int off)
+    {
+        super(false);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public X25519PublicKeyParameters(InputStream input) throws IOException
+    {
+        super(false);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of X25519 public key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X448KeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448KeyGenerationParameters.java
new file mode 100644
index 0000000..9472244
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448KeyGenerationParameters.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class X448KeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    public X448KeyGenerationParameters(SecureRandom random)
+    {
+        super(random, 448);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PrivateKeyParameters.java
new file mode 100644
index 0000000..c4e0195
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PrivateKeyParameters.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc7748.X448;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class X448PrivateKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = X448.SCALAR_SIZE;
+    public static final int SECRET_SIZE = X448.POINT_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public X448PrivateKeyParameters(SecureRandom random)
+    {
+        super(true);
+
+        X448.generatePrivateKey(random, data);
+    }
+
+    public X448PrivateKeyParameters(byte[] buf, int off)
+    {
+        super(true);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public X448PrivateKeyParameters(InputStream input) throws IOException
+    {
+        super(true);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of X448 private key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+
+    public X448PublicKeyParameters generatePublicKey()
+    {
+        byte[] publicKey = new byte[X448.POINT_SIZE];
+        X448.generatePublicKey(data, 0, publicKey, 0);
+        return new X448PublicKeyParameters(publicKey, 0);
+    }
+
+    public void generateSecret(X448PublicKeyParameters publicKey, byte[] buf, int off)
+    {
+        byte[] encoded = new byte[X448.POINT_SIZE];
+        publicKey.encode(encoded, 0);
+        if (!X448.calculateAgreement(data, 0, encoded, 0, buf, off))
+        {
+            throw new IllegalStateException("X448 agreement failed");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PublicKeyParameters.java
new file mode 100644
index 0000000..a0da720
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/X448PublicKeyParameters.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.math.ec.rfc7748.X448;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public final class X448PublicKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final int KEY_SIZE = X448.POINT_SIZE;
+
+    private final byte[] data = new byte[KEY_SIZE];
+
+    public X448PublicKeyParameters(byte[] buf, int off)
+    {
+        super(false);
+
+        System.arraycopy(buf, off, data, 0, KEY_SIZE);
+    }
+
+    public X448PublicKeyParameters(InputStream input) throws IOException
+    {
+        super(false);
+
+        if (KEY_SIZE != Streams.readFully(input, data))
+        {
+            throw new EOFException("EOF encountered in middle of X448 public key");
+        }
+    }
+
+    public void encode(byte[] buf, int off)
+    {
+        System.arraycopy(data, 0, buf, off, KEY_SIZE);
+    }
+
+    public byte[] getEncoded()
+    {
+        return Arrays.clone(data);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPrivateParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPrivateParameters.java
new file mode 100644
index 0000000..7b991ea
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPrivateParameters.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for private unified static/ephemeral agreement using Edwards Curves.
+ */
+public class XDHUPrivateParameters
+    implements CipherParameters
+{
+    private AsymmetricKeyParameter staticPrivateKey;
+    private AsymmetricKeyParameter ephemeralPrivateKey;
+    private AsymmetricKeyParameter ephemeralPublicKey;
+
+    public XDHUPrivateParameters(
+        AsymmetricKeyParameter  staticPrivateKey,
+        AsymmetricKeyParameter  ephemeralPrivateKey)
+    {
+        this(staticPrivateKey, ephemeralPrivateKey, null);
+    }
+
+    public XDHUPrivateParameters(
+        AsymmetricKeyParameter  staticPrivateKey,
+        AsymmetricKeyParameter  ephemeralPrivateKey,
+        AsymmetricKeyParameter  ephemeralPublicKey)
+    {
+        if (staticPrivateKey == null)
+        {
+            throw new NullPointerException("staticPrivateKey cannot be null");
+        }
+        if (!(staticPrivateKey instanceof X448PrivateKeyParameters || staticPrivateKey instanceof X25519PrivateKeyParameters))
+        {
+            throw new IllegalArgumentException("only X25519 and X448 paramaters can be used");
+        }
+        if (ephemeralPrivateKey == null)
+        {
+            throw new NullPointerException("ephemeralPrivateKey cannot be null");
+        }
+
+        if (!staticPrivateKey.getClass().isAssignableFrom(ephemeralPrivateKey.getClass()))
+        {
+            throw new IllegalArgumentException("static and ephemeral private keys have different domain parameters");
+        }
+
+        if (ephemeralPublicKey == null)
+        {
+            if (ephemeralPrivateKey instanceof X448PrivateKeyParameters)
+            {
+                ephemeralPublicKey = ((X448PrivateKeyParameters)ephemeralPrivateKey).generatePublicKey();
+            }
+            else
+            {
+                ephemeralPublicKey = ((X25519PrivateKeyParameters)ephemeralPrivateKey).generatePublicKey();
+            }
+        }
+        else
+        {
+            if (ephemeralPublicKey instanceof X448PublicKeyParameters && !(staticPrivateKey instanceof X448PrivateKeyParameters))
+            {
+                throw new IllegalArgumentException("ephemeral public key has different domain parameters");
+            }
+            if (ephemeralPublicKey instanceof X25519PublicKeyParameters && !(staticPrivateKey instanceof X25519PrivateKeyParameters))
+            {
+                throw new IllegalArgumentException("ephemeral public key has different domain parameters");
+            }
+        }
+
+        this.staticPrivateKey = staticPrivateKey;
+        this.ephemeralPrivateKey = ephemeralPrivateKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public AsymmetricKeyParameter getStaticPrivateKey()
+    {
+        return staticPrivateKey;
+    }
+
+    public AsymmetricKeyParameter getEphemeralPrivateKey()
+    {
+        return ephemeralPrivateKey;
+    }
+
+    public AsymmetricKeyParameter getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPublicParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPublicParameters.java
new file mode 100644
index 0000000..ac6c063
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/XDHUPublicParameters.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * Parameters holder for public unified static/ephemeral agreement using Edwards Curves.
+ */
+public class XDHUPublicParameters
+    implements CipherParameters
+{
+    private AsymmetricKeyParameter staticPublicKey;
+    private AsymmetricKeyParameter ephemeralPublicKey;
+
+    public XDHUPublicParameters(
+        AsymmetricKeyParameter   staticPublicKey,
+        AsymmetricKeyParameter   ephemeralPublicKey)
+    {
+        if (staticPublicKey == null)
+        {
+            throw new NullPointerException("staticPublicKey cannot be null");
+        }
+        if (!(staticPublicKey instanceof X448PublicKeyParameters || staticPublicKey instanceof X25519PublicKeyParameters))
+        {
+            throw new IllegalArgumentException("only X25519 and X448 paramaters can be used");
+        }
+        if (ephemeralPublicKey == null)
+        {
+            throw new NullPointerException("ephemeralPublicKey cannot be null");
+        }
+        if (!staticPublicKey.getClass().isAssignableFrom(ephemeralPublicKey.getClass()))
+        {
+            throw new IllegalArgumentException("static and ephemeral public keys have different domain parameters");
+        }
+
+        this.staticPublicKey = staticPublicKey;
+        this.ephemeralPublicKey = ephemeralPublicKey;
+    }
+
+    public AsymmetricKeyParameter getStaticPublicKey()
+    {
+        return staticPublicKey;
+    }
+
+    public AsymmetricKeyParameter getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
index 7ebeece..000b0c4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
@@ -75,6 +75,14 @@
      */
     public void reseed(byte[] additionalInput)
     {
-        drbg.reseed(additionalInput);
+        synchronized (this)
+        {
+            if (drbg == null)
+            {
+                drbg = drbgProvider.get(entropySource);
+            }
+
+            drbg.reseed(additionalInput);
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
index 9c09680..420cf12 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
@@ -3,14 +3,14 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG;
-import org.bouncycastle.crypto.prng.drbg.DualECPoints;
-import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG;
 import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG;
 import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG;
 import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Builder class for making SecureRandom objects based on SP 800-90A Deterministic Random Bit Generators (DRBG).
@@ -34,7 +34,7 @@
      */
     public SP800SecureRandomBuilder()
     {
-        this(new SecureRandom(), false);
+        this(CryptoServicesRegistrar.getSecureRandom(), false);
     }
 
     /**
@@ -73,7 +73,7 @@
      */
     public SP800SecureRandomBuilder setPersonalizationString(byte[] personalizationString)
     {
-        this.personalizationString = personalizationString;
+        this.personalizationString = Arrays.clone(personalizationString);
 
         return this;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java
index 89cd34c..9764b6b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java
@@ -3,7 +3,9 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 public class X931SecureRandomBuilder
@@ -23,7 +25,7 @@
      */
     public X931SecureRandomBuilder()
     {
-        this(new SecureRandom(), false);
+        this(CryptoServicesRegistrar.getSecureRandom(), false);
     }
 
     /**
@@ -57,7 +59,7 @@
 
     public X931SecureRandomBuilder setDateTimeVector(byte[] dateTimeVector)
     {
-        this.dateTimeVector = dateTimeVector;
+        this.dateTimeVector = Arrays.clone(dateTimeVector);
 
         return this;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
index f30dbfe..db0cffd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
@@ -260,7 +260,7 @@
         System.arraycopy(temp, 0, K, 0, K.length);
         System.arraycopy(temp, K.length, X, 0, X.length);
 
-        temp = new byte[bitLength / 2];
+        temp = new byte[bitLength / 8];
 
         i = 0;
         _engine.init(true, new KeyParameter(expandKey(K)));
@@ -380,7 +380,7 @@
         }
         else
         {
-            additionalInput = new byte[_seedLength];
+            additionalInput = new byte[_seedLength / 8];
         }
 
         byte[] out = new byte[_V.length];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
index 684eb9c..b9343db 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
@@ -1,16 +1,10 @@
 package org.bouncycastle.crypto.signers;
 
-import java.io.IOException;
 import java.math.BigInteger;
 
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
@@ -19,16 +13,28 @@
 public class DSADigestSigner
     implements Signer
 {
+    private final DSA dsa;
     private final Digest digest;
-    private final DSA dsaSigner;
+    private final DSAEncoding encoding;
     private boolean forSigning;
 
     public DSADigestSigner(
-        DSA    signer,
-        Digest digest)
+        DSA     dsa,
+        Digest  digest)
     {
+        this.dsa = dsa;
         this.digest = digest;
-        this.dsaSigner = signer;
+        this.encoding = StandardDSAEncoding.INSTANCE;
+    }
+
+    public DSADigestSigner(
+        DSAExt      dsa,
+        Digest      digest,
+        DSAEncoding encoding)
+    {
+        this.dsa = dsa;
+        this.digest = digest;
+        this.encoding = encoding;
     }
 
     public void init(
@@ -60,7 +66,7 @@
 
         reset();
 
-        dsaSigner.init(forSigning, parameters);
+        dsa.init(forSigning, parameters);
     }
 
     /**
@@ -97,13 +103,13 @@
         byte[] hash = new byte[digest.getDigestSize()];
         digest.doFinal(hash, 0);
 
-        BigInteger[] sig = dsaSigner.generateSignature(hash);
+        BigInteger[] sig = dsa.generateSignature(hash);
 
         try
         {
-            return derEncode(sig[0], sig[1]);
+            return encoding.encode(getOrder(), sig[0], sig[1]);
         }
-        catch (IOException e)
+        catch (Exception e)
         {
             throw new IllegalStateException("unable to encode signature");
         }
@@ -122,10 +128,11 @@
 
         try
         {
-            BigInteger[] sig = derDecode(signature);
-            return dsaSigner.verifySignature(hash, sig[0], sig[1]);
+            BigInteger[] sig = encoding.decode(getOrder(), signature);
+
+            return dsa.verifySignature(hash, sig[0], sig[1]);
         }
-        catch (IOException e)
+        catch (Exception e)
         {
             return false;
         }
@@ -136,28 +143,8 @@
         digest.reset();
     }
 
-    private byte[] derEncode(
-        BigInteger  r,
-        BigInteger  s)
-        throws IOException
+    protected BigInteger getOrder()
     {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        v.add(new ASN1Integer(r));
-        v.add(new ASN1Integer(s));
-
-        return new DERSequence(v).getEncoded(ASN1Encoding.DER);
-    }
-
-    private BigInteger[] derDecode(
-        byte[] encoding)
-        throws IOException
-    {
-        ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
-
-        return new BigInteger[]
-        {
-            ((ASN1Integer)s.getObjectAt(0)).getValue(),
-            ((ASN1Integer)s.getObjectAt(1)).getValue()
-        };
+        return dsa instanceof DSAExt ? ((DSAExt)dsa).getOrder() : null;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAEncoding.java
new file mode 100644
index 0000000..9ee166f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAEncoding.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+/**
+ * An interface for different encoding formats for DSA signatures.
+ */
+public interface DSAEncoding
+{
+    /**
+     * Decode the (r, s) pair of a DSA signature.
+     * 
+     * @param n the order of the group that r, s belong to.
+     * @param encoding an encoding of the (r, s) pair of a DSA signature.
+     * @return the (r, s) of a DSA signature, stored in an array of exactly two elements, r followed by s.
+     * @throws IOException
+     */
+    BigInteger[] decode(BigInteger n, byte[] encoding) throws IOException;
+
+    /**
+     * Encode the (r, s) pair of a DSA signature.
+     * 
+     * @param n the order of the group that r, s belong to.
+     * @param r the r value of a DSA signature.
+     * @param s the s value of a DSA signature.
+     * @return an encoding of the DSA signature given by the provided (r, s) pair.
+     * @throws IOException
+     */
+    byte[] encode(BigInteger n, BigInteger r, BigInteger s) throws IOException;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
index 920611b..668ac27 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -4,19 +4,21 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.params.DSAKeyParameters;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * The Digital Signature Algorithm - as described in "Handbook of Applied
  * Cryptography", pages 452 - 453.
  */
 public class DSASigner
-    implements DSA
+    implements DSAExt
 {
     private final DSAKCalculator kCalculator;
 
@@ -69,6 +71,11 @@
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getQ();
+    }
+
     /**
      * generate a signature for the given message using the key we were
      * initialised with. For conventional DSA the message should be a SHA-1
@@ -162,7 +169,7 @@
 
     protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
     {
-        return !needed ? null : (provided != null) ? provided : new SecureRandom();
+        return !needed ? null : (provided != null) ? provided : CryptoServicesRegistrar.getSecureRandom();
     }
 
     private BigInteger getRandomizer(BigInteger q, SecureRandom provided)
@@ -170,6 +177,6 @@
         // Calculate a random multiple of q to add to k. Note that g^q = 1 (mod p), so adding multiple of q to k does not change r.
         int randomBits = 7;
 
-        return new BigInteger(randomBits, provided != null ? provided : new SecureRandom()).add(BigInteger.valueOf(128)).multiply(q);
+        return BigIntegers.createRandomBigInteger(randomBits, provided != null ? provided : CryptoServicesRegistrar.getSecureRandom()).add(BigInteger.valueOf(128)).multiply(q);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
index bceb822..34f6718 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
@@ -4,7 +4,8 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -17,6 +18,7 @@
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * DSTU 4145-2002
@@ -25,7 +27,7 @@
  * </p>
  */
 public class DSTU4145Signer
-    implements DSA
+    implements DSAExt
 {
     private static final BigInteger ONE = BigInteger.valueOf(1);
 
@@ -45,7 +47,7 @@
             }
             else
             {
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             this.key = (ECPrivateKeyParameters)param;
@@ -57,6 +59,11 @@
 
     }
 
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getN();
+    }
+
     public BigInteger[] generateSignature(byte[] message)
     {
         ECDomainParameters ec = key.getParameters();
@@ -145,7 +152,7 @@
      */
     private static BigInteger generateRandomInteger(BigInteger n, SecureRandom random)
     {
-        return new BigInteger(n.bitLength() - 1, random);
+        return BigIntegers.createRandomBigInteger(n.bitLength() - 1, random);
     }
 
     private static ECFieldElement hash2FieldElement(ECCurve curve, byte[] hash)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
index adb2558..057beed 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -4,7 +4,8 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -22,7 +23,7 @@
  * EC-DSA as described in X9.62
  */
 public class ECDSASigner
-    implements ECConstants, DSA
+    implements ECConstants, DSAExt
 {
     private final DSAKCalculator kCalculator;
 
@@ -75,6 +76,11 @@
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getN();
+    }
+
     // 5.3 pg 28
     /**
      * generate a signature for the given message using the key we were
@@ -247,6 +253,6 @@
 
     protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
     {
-        return !needed ? null : (provided != null) ? provided : new SecureRandom();
+        return !needed ? null : (provided != null) ? provided : CryptoServicesRegistrar.getSecureRandom();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
index 0ef8876..1a7d59f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
@@ -1,7 +1,11 @@
 package org.bouncycastle.crypto.signers;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -12,15 +16,13 @@
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * GOST R 34.10-2001 Signature Algorithm
  */
 public class ECGOST3410Signer
-    implements DSA
+    implements DSAExt
 {
     ECKeyParameters key;
 
@@ -41,7 +43,7 @@
             }
             else
             {
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (ECPrivateKeyParameters)param;
             }
         }
@@ -51,6 +53,11 @@
         }
     }
 
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getN();
+    }
+
     /**
      * generate a signature for the given message using the key we were
      * initialised with. For conventional GOST3410 the message should be a GOST3411
@@ -84,7 +91,7 @@
             {
                 do
                 {
-                    k = new BigInteger(n.bitLength(), random);
+                    k = BigIntegers.createRandomBigInteger(n.bitLength(), random);
                 }
                 while (k.equals(ECConstants.ZERO));
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410_2012Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410_2012Signer.java
new file mode 100644
index 0000000..8efe9ad
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410_2012Signer.java
@@ -0,0 +1,167 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * GOST R 34.10-2012 Signature Algorithm
+ */
+public class ECGOST3410_2012Signer
+    implements DSAExt
+{
+    ECKeyParameters key;
+
+    SecureRandom    random;
+
+    public void init(
+        boolean                 forSigning,
+        CipherParameters        param)
+    {
+        if (forSigning)
+        {
+            if (param instanceof ParametersWithRandom)
+            {
+                ParametersWithRandom    rParam = (ParametersWithRandom)param;
+
+                this.random = rParam.getRandom();
+                this.key = (ECPrivateKeyParameters)rParam.getParameters();
+            }
+            else
+            {
+                this.random = CryptoServicesRegistrar.getSecureRandom();
+                this.key = (ECPrivateKeyParameters)param;
+            }
+        }
+        else
+        {
+            this.key = (ECPublicKeyParameters)param;
+        }
+    }
+
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getN();
+    }
+
+    /**
+     * generate a signature for the given message using the key we were
+     * initialised with. For conventional GOST3410 2012 the message should be a GOST3411 2012
+     * hash of the message of interest.
+     *
+     * @param message the message that will be verified later.
+     */
+    public BigInteger[] generateSignature(
+        byte[] message)
+    {
+        byte[] mRev = new byte[message.length]; // conversion is little-endian
+        for (int i = 0; i != mRev.length; i++)
+        {
+            mRev[i] = message[mRev.length - 1 - i];
+        }
+        BigInteger e = new BigInteger(1, mRev);
+
+        ECDomainParameters ec = key.getParameters();
+        BigInteger n = ec.getN();
+        BigInteger d = ((ECPrivateKeyParameters)key).getD();
+
+        BigInteger r, s;
+
+        ECMultiplier basePointMultiplier = createBasePointMultiplier();
+
+        do // generate s
+        {
+            BigInteger k;
+            do // generate r
+            {
+                do
+                {
+                    k = BigIntegers.createRandomBigInteger(n.bitLength(), random);
+                }
+                while (k.equals(ECConstants.ZERO));
+
+                ECPoint p = basePointMultiplier.multiply(ec.getG(), k).normalize();
+
+                r = p.getAffineXCoord().toBigInteger().mod(n);
+            }
+            while (r.equals(ECConstants.ZERO));
+
+            s = (k.multiply(e)).add(d.multiply(r)).mod(n);
+        }
+        while (s.equals(ECConstants.ZERO));
+
+        return new BigInteger[]{ r, s };
+    }
+
+    /**
+     * return true if the value r and s represent a GOST3410 2012 signature for
+     * the passed in message (for standard GOST3410 2012 the message should be
+     * a GOST3411 2012 hash of the real message to be verified).
+     */
+    public boolean verifySignature(
+        byte[]      message,
+        BigInteger  r,
+        BigInteger  s)
+    {
+
+
+        byte[] mRev = new byte[message.length]; // conversion is little-endian
+        for (int i = 0; i != mRev.length; i++)
+        {
+            mRev[i] = message[mRev.length - 1 - i];
+        }
+        BigInteger e = new BigInteger(1, mRev);
+        BigInteger n = key.getParameters().getN();
+
+        // r in the range [1,n-1]
+        if (r.compareTo(ECConstants.ONE) < 0 || r.compareTo(n) >= 0)
+        {
+            return false;
+        }
+
+        // s in the range [1,n-1]
+        if (s.compareTo(ECConstants.ONE) < 0 || s.compareTo(n) >= 0)
+        {
+            return false;
+        }
+
+        BigInteger v = e.modInverse(n);
+
+        BigInteger z1 = s.multiply(v).mod(n);
+        BigInteger z2 = (n.subtract(r)).multiply(v).mod(n);
+
+        ECPoint G = key.getParameters().getG(); // P
+        ECPoint Q = ((ECPublicKeyParameters)key).getQ();
+
+        ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2).normalize();
+
+        // components must be bogus.
+        if (point.isInfinity())
+        {
+            return false;
+        }
+
+        BigInteger R = point.getAffineXCoord().toBigInteger().mod(n);
+
+        return R.equals(r);
+    }
+
+    protected ECMultiplier createBasePointMultiplier()
+    {
+        return new FixedPointCombMultiplier();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
index 3e83916..f7d1b55 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
@@ -1,8 +1,12 @@
 package org.bouncycastle.crypto.signers;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
@@ -14,14 +18,11 @@
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECPoint;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
 /**
  * EC-NR as described in IEEE 1363-2000
  */
 public class ECNRSigner
-    implements DSA
+    implements DSAExt
 {
     private boolean             forSigning;
     private ECKeyParameters     key;
@@ -44,7 +45,7 @@
             }
             else
             {
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (ECPrivateKeyParameters)param;
             }
         }
@@ -54,6 +55,11 @@
         }
     }
 
+    public BigInteger getOrder()
+    {
+        return key.getParameters().getN();
+    }
+
     // Section 7.2.5 ECSP-NR, pg 34
     /**
      * generate a signature for the given message using the key we were
@@ -67,12 +73,12 @@
     public BigInteger[] generateSignature(
         byte[] digest)
     {
-        if (! this.forSigning) 
+        if (!this.forSigning) 
         {
             throw new IllegalStateException("not initialised for signing");
         }
         
-        BigInteger n = ((ECPrivateKeyParameters)this.key).getParameters().getN();
+        BigInteger n = getOrder();
         int nBitLength = n.bitLength();
         
         BigInteger e = new BigInteger(1, digest);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519Signer.java
new file mode 100644
index 0000000..966b626
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519Signer.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+
+public class Ed25519Signer
+    implements Signer
+{
+    private final Buffer buffer = new Buffer();
+
+    private boolean forSigning;
+    private Ed25519PrivateKeyParameters privateKey;
+    private Ed25519PublicKeyParameters publicKey;
+
+    public Ed25519Signer()
+    {
+    }
+
+    public void init(boolean forSigning, CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+
+        if (forSigning)
+        {
+            // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+            this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+            this.publicKey = privateKey.generatePublicKey();
+        }
+        else
+        {
+            this.privateKey = null;
+            this.publicKey = (Ed25519PublicKeyParameters)parameters;
+        }
+
+        reset();
+    }
+
+    public void update(byte b)
+    {
+        buffer.write(b);
+    }
+
+    public void update(byte[] buf, int off, int len)
+    {
+        buffer.write(buf, off, len);
+    }
+
+    public byte[] generateSignature()
+    {
+        if (!forSigning || null == privateKey)
+        {
+            throw new IllegalStateException("Ed25519Signer not initialised for signature generation.");
+        }
+
+        return buffer.generateSignature(privateKey, publicKey);
+    }
+
+    public boolean verifySignature(byte[] signature)
+    {
+        if (forSigning || null == publicKey)
+        {
+            throw new IllegalStateException("Ed25519Signer not initialised for verification");
+        }
+
+        return buffer.verifySignature(publicKey, signature);
+    }
+
+    public void reset()
+    {
+        buffer.reset();
+    }
+
+    private static class Buffer extends ByteArrayOutputStream
+    {
+        synchronized byte[] generateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey)
+        {
+            byte[] signature = new byte[Ed25519PrivateKeyParameters.SIGNATURE_SIZE];
+            privateKey.sign(Ed25519.Algorithm.Ed25519, publicKey, null, buf, 0, count, signature, 0);
+            reset();
+            return signature;
+        }
+
+        synchronized boolean verifySignature(Ed25519PublicKeyParameters publicKey, byte[] signature)
+        {
+            byte[] pk = publicKey.getEncoded();
+            boolean result = Ed25519.verify(signature, 0, pk, 0, buf, 0, count);
+            reset();
+            return result;
+        }
+
+        public synchronized void reset()
+        {
+            Arrays.fill(buf, 0, count, (byte)0);
+            this.count = 0;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519ctxSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519ctxSigner.java
new file mode 100644
index 0000000..959ddf0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519ctxSigner.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+
+public class Ed25519ctxSigner
+    implements Signer
+{
+    private final Buffer buffer = new Buffer();
+    private final byte[] context;
+
+    private boolean forSigning;
+    private Ed25519PrivateKeyParameters privateKey;
+    private Ed25519PublicKeyParameters publicKey;
+
+    public Ed25519ctxSigner(byte[] context)
+    {
+        this.context = Arrays.clone(context);
+    }
+
+    public void init(boolean forSigning, CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+
+        if (forSigning)
+        {
+            // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+            this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+            this.publicKey = privateKey.generatePublicKey();
+        }
+        else
+        {
+            this.privateKey = null;
+            this.publicKey = (Ed25519PublicKeyParameters)parameters;
+        }
+
+        reset();
+    }
+
+    public void update(byte b)
+    {
+        buffer.write(b);
+    }
+
+    public void update(byte[] buf, int off, int len)
+    {
+        buffer.write(buf, off, len);
+    }
+
+    public byte[] generateSignature()
+    {
+        if (!forSigning || null == privateKey)
+        {
+            throw new IllegalStateException("Ed25519ctxSigner not initialised for signature generation.");
+        }
+
+        return buffer.generateSignature(privateKey, publicKey, context);
+    }
+
+    public boolean verifySignature(byte[] signature)
+    {
+        if (forSigning || null == publicKey)
+        {
+            throw new IllegalStateException("Ed25519ctxSigner not initialised for verification");
+        }
+
+        return buffer.verifySignature(publicKey, context, signature);
+    }
+
+    public void reset()
+    {
+        buffer.reset();
+    }
+
+    private static class Buffer extends ByteArrayOutputStream
+    {
+        synchronized byte[] generateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey, byte[] ctx)
+        {
+            byte[] signature = new byte[Ed25519PrivateKeyParameters.SIGNATURE_SIZE];
+            privateKey.sign(Ed25519.Algorithm.Ed25519ctx, publicKey, ctx, buf, 0, count, signature, 0);
+            reset();
+            return signature;
+        }
+
+        synchronized boolean verifySignature(Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] signature)
+        {
+            byte[] pk = publicKey.getEncoded();
+            boolean result = Ed25519.verify(signature, 0, pk, 0, ctx, buf, 0, count);
+            reset();
+            return result;
+        }
+
+        public synchronized void reset()
+        {
+            Arrays.fill(buf, 0, count, (byte)0);
+            this.count = 0;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519phSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519phSigner.java
new file mode 100644
index 0000000..eec7d1c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed25519phSigner.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+
+public class Ed25519phSigner
+    implements Signer
+{
+    private final Digest prehash = Ed25519.createPrehash();
+    private final byte[] context;
+
+    private boolean forSigning;
+    private Ed25519PrivateKeyParameters privateKey;
+    private Ed25519PublicKeyParameters publicKey;
+
+    public Ed25519phSigner(byte[] context)
+    {
+        this.context = Arrays.clone(context);
+    }
+
+    public void init(boolean forSigning, CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+
+        if (forSigning)
+        {
+            // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+            this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+            this.publicKey = privateKey.generatePublicKey();
+        }
+        else
+        {
+            this.privateKey = null;
+            this.publicKey = (Ed25519PublicKeyParameters)parameters;
+        }
+
+        reset();
+    }
+
+    public void update(byte b)
+    {
+        prehash.update(b);
+    }
+
+    public void update(byte[] buf, int off, int len)
+    {
+        prehash.update(buf, off, len);
+    }
+
+    public byte[] generateSignature()
+    {
+        if (!forSigning || null == privateKey)
+        {
+            throw new IllegalStateException("Ed25519phSigner not initialised for signature generation.");
+        }
+
+        byte[] msg = new byte[Ed25519.PREHASH_SIZE];
+        if (Ed25519.PREHASH_SIZE != prehash.doFinal(msg, 0))
+        {
+            throw new IllegalStateException("Prehash digest failed");
+        }
+
+        byte[] signature = new byte[Ed25519PrivateKeyParameters.SIGNATURE_SIZE];
+        privateKey.sign(Ed25519.Algorithm.Ed25519ph, publicKey, context, msg, 0, Ed25519.PREHASH_SIZE, signature, 0);
+        return signature;
+    }
+
+    public boolean verifySignature(byte[] signature)
+    {
+        if (forSigning || null == publicKey)
+        {
+            throw new IllegalStateException("Ed25519phSigner not initialised for verification");
+        }
+
+        byte[] pk = publicKey.getEncoded();
+        return Ed25519.verifyPrehash(signature, 0, pk, 0, context, prehash);
+    }
+
+    public void reset()
+    {
+        prehash.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448Signer.java
new file mode 100644
index 0000000..f5122d0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448Signer.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.Arrays;
+
+
+public class Ed448Signer
+    implements Signer
+{
+    private final Buffer buffer = new Buffer();
+    private final byte[] context;
+
+    private boolean forSigning;
+    private Ed448PrivateKeyParameters privateKey;
+    private Ed448PublicKeyParameters publicKey;
+
+    public Ed448Signer(byte[] context)
+    {
+        this.context = Arrays.clone(context);
+    }
+
+    public void init(boolean forSigning, CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+
+        if (forSigning)
+        {
+            // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+            this.privateKey = (Ed448PrivateKeyParameters)parameters;
+            this.publicKey = privateKey.generatePublicKey();
+        }
+        else
+        {
+            this.privateKey = null;
+            this.publicKey = (Ed448PublicKeyParameters)parameters;
+        }
+
+        reset();
+    }
+
+    public void update(byte b)
+    {
+        buffer.write(b);
+    }
+
+    public void update(byte[] buf, int off, int len)
+    {
+        buffer.write(buf, off, len);
+    }
+
+    public byte[] generateSignature()
+    {
+        if (!forSigning || null == privateKey)
+        {
+            throw new IllegalStateException("Ed448Signer not initialised for signature generation.");
+        }
+
+        return buffer.generateSignature(privateKey, publicKey, context);
+    }
+
+    public boolean verifySignature(byte[] signature)
+    {
+        if (forSigning || null == publicKey)
+        {
+            throw new IllegalStateException("Ed448Signer not initialised for verification");
+        }
+
+        return buffer.verifySignature(publicKey, context, signature);
+    }
+
+    public void reset()
+    {
+        buffer.reset();
+    }
+
+    private static class Buffer extends ByteArrayOutputStream
+    {
+        synchronized byte[] generateSignature(Ed448PrivateKeyParameters privateKey, Ed448PublicKeyParameters publicKey, byte[] ctx)
+        {
+            byte[] signature = new byte[Ed448PrivateKeyParameters.SIGNATURE_SIZE];
+            privateKey.sign(Ed448.Algorithm.Ed448, publicKey, ctx, buf, 0, count, signature, 0);
+            reset();
+            return signature;
+        }
+
+        synchronized boolean verifySignature(Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] signature)
+        {
+            byte[] pk = publicKey.getEncoded();
+            boolean result = Ed448.verify(signature, 0, pk, 0, ctx, buf, 0, count);
+            reset();
+            return result;
+        }
+
+        public synchronized void reset()
+        {
+            Arrays.fill(buf, 0, count, (byte)0);
+            this.count = 0;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448phSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448phSigner.java
new file mode 100644
index 0000000..2db2465
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Ed448phSigner.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.Arrays;
+
+public class Ed448phSigner
+    implements Signer
+{
+    private final Xof prehash = Ed448.createPrehash();
+    private final byte[] context;
+
+    private boolean forSigning;
+    private Ed448PrivateKeyParameters privateKey;
+    private Ed448PublicKeyParameters publicKey;
+
+    public Ed448phSigner(byte[] context)
+    {
+        this.context = Arrays.clone(context);
+    }
+
+    public void init(boolean forSigning, CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+
+        if (forSigning)
+        {
+            // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+            this.privateKey = (Ed448PrivateKeyParameters)parameters;
+            this.publicKey = privateKey.generatePublicKey();
+        }
+        else
+        {
+            this.privateKey = null;
+            this.publicKey = (Ed448PublicKeyParameters)parameters;
+        }
+
+        reset();
+    }
+
+    public void update(byte b)
+    {
+        prehash.update(b);
+    }
+
+    public void update(byte[] buf, int off, int len)
+    {
+        prehash.update(buf, off, len);
+    }
+
+    public byte[] generateSignature()
+    {
+        if (!forSigning || null == privateKey)
+        {
+            throw new IllegalStateException("Ed448phSigner not initialised for signature generation.");
+        }
+
+        byte[] msg = new byte[Ed448.PREHASH_SIZE];
+        if (Ed448.PREHASH_SIZE != prehash.doFinal(msg, 0, Ed448.PREHASH_SIZE))
+        {
+            throw new IllegalStateException("Prehash digest failed");
+        }
+
+        byte[] signature = new byte[Ed448PrivateKeyParameters.SIGNATURE_SIZE];
+        privateKey.sign(Ed448.Algorithm.Ed448ph, publicKey, context, msg, 0, Ed448.PREHASH_SIZE, signature, 0);
+        return signature;
+    }
+
+    public boolean verifySignature(byte[] signature)
+    {
+        if (forSigning || null == publicKey)
+        {
+            throw new IllegalStateException("Ed448phSigner not initialised for verification");
+        }
+
+        byte[] pk = publicKey.getEncoded();
+        return Ed448.verifyPrehash(signature, 0, pk, 0, context, prehash);
+    }
+
+    public void reset()
+    {
+        prehash.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
index 135f185..6719448 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
@@ -4,18 +4,20 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.params.GOST3410KeyParameters;
 import org.bouncycastle.crypto.params.GOST3410Parameters;
 import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
 import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * GOST R 34.10-94 Signature Algorithm
  */
 public class GOST3410Signer
-        implements DSA
+        implements DSAExt
 {
         GOST3410KeyParameters key;
 
@@ -36,7 +38,7 @@
                 }
                 else
                 {
-                    this.random = new SecureRandom();
+                    this.random = CryptoServicesRegistrar.getSecureRandom();
                     this.key = (GOST3410PrivateKeyParameters)param;
                 }
             }
@@ -46,6 +48,11 @@
             }
         }
 
+        public BigInteger getOrder()
+        {
+            return key.getParameters().getQ();
+        }
+
         /**
          * generate a signature for the given message using the key we were
          * initialised with. For conventional GOST3410 the message should be a GOST3411
@@ -68,7 +75,7 @@
 
             do
             {
-                k = new BigInteger(params.getQ().bitLength(), random);
+                k = BigIntegers.createRandomBigInteger(params.getQ().bitLength(), random);
             }
             while (k.compareTo(params.getQ()) >= 0);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
index 0fb7375..f5cabd9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
@@ -52,12 +52,13 @@
         Arrays.fill(V, (byte)0x01);
         Arrays.fill(K, (byte)0);
 
-        byte[] x = new byte[(n.bitLength() + 7) / 8];
+        int size = BigIntegers.getUnsignedByteLength(n);
+        byte[] x = new byte[size];
         byte[] dVal = BigIntegers.asUnsignedByteArray(d);
 
         System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length);
 
-        byte[] m = new byte[(n.bitLength() + 7) / 8];
+        byte[] m = new byte[size];
 
         BigInteger mInt = bitsToInt(message);
 
@@ -101,7 +102,7 @@
 
     public BigInteger nextK()
     {
-        byte[] t = new byte[((n.bitLength() + 7) / 8)];
+        byte[] t = new byte[BigIntegers.getUnsignedByteLength(n)];
 
         for (;;)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
index 1c56142..2ee1808 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.SignerWithRecovery;
@@ -158,7 +159,7 @@
             kParam = (RSAKeyParameters)param;
             if (forSigning)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
         }
 
@@ -248,9 +249,13 @@
 
             if (trailerObj != null)
             {
-                if (sigTrail != trailerObj.intValue())
+                int trailer = trailerObj.intValue();
+                if (sigTrail != trailer)
                 {
-                    throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    if (!(trailer == ISOTrailers.TRAILER_SHA512_256 && sigTrail == 0x40CC))
+                    {
+                        throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    }
                 }
             }
             else
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
index dd63a21..1e046d5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
@@ -200,9 +200,13 @@
 
             if (trailerObj != null)
             {
-                if (sigTrail != trailerObj.intValue())
+                int trailer = trailerObj.intValue();
+                if (sigTrail != trailer)
                 {
-                    throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    if (!(trailer == ISOTrailers.TRAILER_SHA512_256 && sigTrail == 0x40CC))
+                    {
+                        throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    }
                 }
             }
             else
@@ -461,9 +465,13 @@
 
             if (trailerObj != null)
             {
-                if (sigTrail != trailerObj.intValue())
+                int trailer = trailerObj.intValue();
+                if (sigTrail != trailer)
                 {
-                    throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    if (!(trailer == ISOTrailers.TRAILER_SHA512_256 && sigTrail == 0x40CC))
+                    {
+                        throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail);
+                    }
                 }
             }
             else
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
index 2793d60..acc5863 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java
@@ -22,7 +22,7 @@
     static final public int   TRAILER_WHIRLPOOL   = 0x37CC;
     static final public int   TRAILER_SHA224      = 0x38CC;
     static final public int   TRAILER_SHA512_224  = 0x39CC;
-    static final public int   TRAILER_SHA512_256  = 0x40CC;
+    static final public int   TRAILER_SHA512_256  = 0x3aCC;
 
     static
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
index 7b6d69a..7e155a8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
@@ -144,7 +145,7 @@
             params = param;
             if (forSigning)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/PlainDSAEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/PlainDSAEncoding.java
new file mode 100644
index 0000000..9bb30dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/PlainDSAEncoding.java
@@ -0,0 +1,62 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+public class PlainDSAEncoding
+    implements DSAEncoding
+{
+    public static final PlainDSAEncoding INSTANCE = new PlainDSAEncoding();
+
+    public byte[] encode(BigInteger n, BigInteger r, BigInteger s)
+    {
+        int valueLength = BigIntegers.getUnsignedByteLength(n);
+        byte[] result = new byte[valueLength * 2];
+        encodeValue(n, r, result, 0, valueLength);
+        encodeValue(n, s, result, valueLength, valueLength);
+        return result;
+    }
+
+    public BigInteger[] decode(BigInteger n, byte[] encoding)
+    {
+        int valueLength = BigIntegers.getUnsignedByteLength(n);
+        if (encoding.length != valueLength * 2)
+        {
+            throw new IllegalArgumentException("Encoding has incorrect length");
+        }
+
+        return new BigInteger[] {
+            decodeValue(n, encoding, 0, valueLength),
+            decodeValue(n, encoding, valueLength, valueLength),
+        };
+    }
+
+    protected BigInteger checkValue(BigInteger n, BigInteger x)
+    {
+        if (x.signum() < 0 || x.compareTo(n) >= 0)
+        {
+            throw new IllegalArgumentException("Value out of range");
+        }
+
+        return x;
+    }
+
+    protected BigInteger decodeValue(BigInteger n, byte[] buf, int off, int len)
+    {
+        byte[] bs = Arrays.copyOfRange(buf, off, off + len);
+        return checkValue(n, new BigInteger(1, bs));
+    }
+
+    private void encodeValue(BigInteger n, BigInteger x, byte[] buf, int off, int len)
+    {
+        byte[] bs = checkValue(n, x).toByteArray();
+        int bsOff = Math.max(0, bs.length - len);
+        int bsLen = bs.length - bsOff;
+
+        int pos = len - bsLen;
+        Arrays.fill(buf, off, off + pos, (byte)0);
+        System.arraycopy(bs, bsOff, buf, off + pos, bsLen);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
index 6a69308..cf54b85 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
@@ -3,6 +3,8 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.util.BigIntegers;
+
 public class RandomDSAKCalculator
     implements DSAKCalculator
 {
@@ -34,7 +36,7 @@
         BigInteger k;
         do
         {
-            k = new BigInteger(qBitLength, random);
+            k = BigIntegers.createRandomBigInteger(qBitLength, random);
         }
         while (k.equals(ZERO) || k.compareTo(q) >= 0);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java
index ce1b9a4..97f9dbc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java
@@ -1,11 +1,12 @@
 package org.bouncycastle.crypto.signers;
 
 import java.math.BigInteger;
-import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
 import org.bouncycastle.crypto.digests.SM3Digest;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
@@ -13,30 +14,43 @@
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithID;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECFieldElement;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
-import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.encoders.Hex;
 
+/**
+ * The SM2 Digital Signature algorithm.
+ */
 public class SM2Signer
-    implements DSA, ECConstants
+    implements Signer, ECConstants
 {
     private final DSAKCalculator kCalculator = new RandomDSAKCalculator();
+    private final SM3Digest digest = new SM3Digest();
+    private final DSAEncoding encoding;
 
-    private byte[] userID;
-
-    private int curveLength;
     private ECDomainParameters ecParams;
     private ECPoint pubPoint;
     private ECKeyParameters ecKey;
+    private byte[] z;
 
-    private SecureRandom random;
+    public SM2Signer()
+    {
+        this(StandardDSAEncoding.INSTANCE);
+    }
+
+    public SM2Signer(DSAEncoding encoding)
+    {
+        this.encoding = encoding;
+    }
 
     public void init(boolean forSigning, CipherParameters param)
     {
         CipherParameters baseParam;
+        byte[] userID;
 
         if (param instanceof ParametersWithID)
         {
@@ -46,7 +60,7 @@
         else
         {
             baseParam = param;
-            userID = new byte[0];
+            userID = Hex.decode("31323334353637383132333435363738"); // the default value
         }
 
         if (forSigning)
@@ -63,9 +77,9 @@
             {
                 ecKey = (ECKeyParameters)baseParam;
                 ecParams = ecKey.getParameters();
-                kCalculator.init(ecParams.getN(), new SecureRandom());
+                kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom());
             }
-            pubPoint = ecParams.getG().multiply(((ECPrivateKeyParameters)ecKey).getD()).normalize();
+            pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), ((ECPrivateKeyParameters)ecKey).getD()).normalize();
         }
         else
         {
@@ -74,21 +88,50 @@
             pubPoint = ((ECPublicKeyParameters)ecKey).getQ();
         }
 
-        curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;
+        z = getZ(userID);
+        
+        digest.update(z, 0, z.length);
     }
 
-    public BigInteger[] generateSignature(byte[] message)
+    public void update(byte b)
     {
-        SM3Digest digest = new SM3Digest();
+        digest.update(b);
+    }
 
-        byte[] z = getZ(digest);
+    public void update(byte[] in, int off, int len)
+    {
+        digest.update(in, off, len);
+    }
 
-        digest.update(z, 0, z.length);
-        digest.update(message, 0, message.length);
+    public boolean verifySignature(byte[] signature)
+    {
+        try
+        {
+            BigInteger[] rs = encoding.decode(ecParams.getN(), signature);
 
-        byte[] eHash = new byte[digest.getDigestSize()];
+            return verifySignature(rs[0], rs[1]);
+        }
+        catch (Exception e)
+        {
+        }
 
-        digest.doFinal(eHash, 0);
+        return false;
+    }
+
+    public void reset()
+    {
+        digest.reset();
+
+        if (z != null)
+        {
+            digest.update(z, 0, z.length);
+        }
+    }
+
+    public byte[] generateSignature()
+        throws CryptoException
+    {
+        byte[] eHash = digestDoFinal();
 
         BigInteger n = ecParams.getN();
         BigInteger e = calculateE(eHash);
@@ -124,39 +167,35 @@
         while (s.equals(ZERO));
 
         // A7
-        return new BigInteger[]{ r, s };
+        try
+        {
+            return encoding.encode(ecParams.getN(), r, s);
+        }
+        catch (Exception ex)
+        {
+            throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex);
+        }
     }
 
-    public boolean verifySignature(byte[] message, BigInteger r, BigInteger s)
+    private boolean verifySignature(BigInteger r, BigInteger s)
     {
         BigInteger n = ecParams.getN();
 
         // 5.3.1 Draft RFC:  SM2 Public Key Algorithms
         // B1
-        if (r.compareTo(ONE) < 0 || r.compareTo(n) > 0)
+        if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0)
         {
             return false;
         }
 
         // B2
-        if (s.compareTo(ONE) < 0 || s.compareTo(n) > 0)
+        if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0)
         {
             return false;
         }
 
-        ECPoint q = ((ECPublicKeyParameters)ecKey).getQ();
-
-        SM3Digest digest = new SM3Digest();
-
-        byte[] z = getZ(digest);
-
-        digest.update(z, 0, z.length);
-        digest.update(message, 0, message.length);
-
-        byte[] eHash = new byte[digest.getDigestSize()];
-
         // B3
-        digest.doFinal(eHash, 0);
+        byte[] eHash = digestDoFinal();
 
         // B4
         BigInteger e = calculateE(eHash);
@@ -167,19 +206,35 @@
         {
             return false;
         }
-        else
-        {
-            // B6
-            ECPoint x1y1 = ecParams.getG().multiply(s);
-            x1y1 = x1y1.add(q.multiply(t)).normalize();
 
-            // B7
-            return r.equals(e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n));
+        // B6
+        ECPoint q = ((ECPublicKeyParameters)ecKey).getQ();
+        ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize();
+        if (x1y1.isInfinity())
+        {
+            return false;
         }
+
+        // B7
+        BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n);
+
+        return expectedR.equals(r);
     }
 
-    private byte[] getZ(Digest digest)
+    private byte[] digestDoFinal()
     {
+        byte[] result = new byte[digest.getDigestSize()];
+        digest.doFinal(result, 0);
+
+        reset();
+        
+        return result;
+    }
+
+    private byte[] getZ(byte[] userID)
+    {
+        digest.reset();
+
         addUserID(digest, userID);
 
         addFieldElement(digest, ecParams.getCurve().getA());
@@ -189,11 +244,11 @@
         addFieldElement(digest, pubPoint.getAffineXCoord());
         addFieldElement(digest, pubPoint.getAffineYCoord());
 
-        byte[] rv = new byte[digest.getDigestSize()];
+        byte[] result = new byte[digest.getDigestSize()];
 
-        digest.doFinal(rv, 0);
+        digest.doFinal(result, 0);
 
-        return rv;
+        return result;
     }
 
     private void addUserID(Digest digest, byte[] userID)
@@ -206,7 +261,7 @@
 
     private void addFieldElement(Digest digest, ECFieldElement v)
     {
-        byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());
+        byte[] p = v.getEncoded();
         digest.update(p, 0, p.length);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/StandardDSAEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/StandardDSAEncoding.java
new file mode 100644
index 0000000..c92b976
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/StandardDSAEncoding.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.crypto.signers;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+public class StandardDSAEncoding
+    implements DSAEncoding
+{
+    public static final StandardDSAEncoding INSTANCE = new StandardDSAEncoding();
+
+    public byte[] encode(BigInteger n, BigInteger r, BigInteger s) throws IOException
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        encodeValue(n, v, r);
+        encodeValue(n, v, s);
+        return new DERSequence(v).getEncoded(ASN1Encoding.DER);
+    }
+
+    public BigInteger[] decode(BigInteger n, byte[] encoding) throws IOException
+    {
+        ASN1Sequence seq = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
+        if (seq.size() == 2)
+        {
+            BigInteger r = decodeValue(n, seq, 0);
+            BigInteger s = decodeValue(n, seq, 1);
+
+            byte[] expectedEncoding = encode(n, r, s);
+            if (Arrays.areEqual(expectedEncoding,  encoding))
+            {
+                return new BigInteger[]{ r, s };
+            }
+        }
+
+        throw new IllegalArgumentException("Malformed signature");
+    }
+
+    protected BigInteger checkValue(BigInteger n, BigInteger x)
+    {
+        if (x.signum() < 0 || (null != n && x.compareTo(n) >= 0))
+        {
+            throw new IllegalArgumentException("Value out of range");
+        }
+
+        return x;
+    }
+
+    protected BigInteger decodeValue(BigInteger n, ASN1Sequence s, int pos)
+    {
+        return checkValue(n, ((ASN1Integer)s.getObjectAt(pos)).getValue());
+    }
+
+    protected void encodeValue(BigInteger n, ASN1EncodableVector v, BigInteger x)
+    {
+        v.add(new ASN1Integer(checkValue(n, x)));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java
index 6efc9ec..eaba04a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java
@@ -159,17 +159,18 @@
     public byte[] generateSignature()
         throws CryptoException
     {
-        createSignatureBlock();
+        createSignatureBlock(trailer);
 
         BigInteger t = new BigInteger(1, cipher.processBlock(block, 0, block.length));
         clearBlock(block);
 
         t = t.min(kParam.getModulus().subtract(t));
 
-        return BigIntegers.asUnsignedByteArray((kParam.getModulus().bitLength() + 7) / 8, t);
+        int size = BigIntegers.getUnsignedByteLength(kParam.getModulus());
+        return BigIntegers.asUnsignedByteArray(size, t);
     }
 
-    private void createSignatureBlock()
+    private void createSignatureBlock(int trailer)
     {
         int     digSize = digest.getDigestSize();
 
@@ -198,7 +199,7 @@
     }
 
     /**
-     * return true if the signature represents a ISO9796-2 signature
+     * return true if the signature represents a X9.31 signature
      * for the passed in message.
      */
     public boolean verifySignature(
@@ -233,12 +234,19 @@
             }
         }
 
-        createSignatureBlock();
+        createSignatureBlock(trailer);
 
         byte[] fBlock = BigIntegers.asUnsignedByteArray(block.length, f);
 
         boolean rv = Arrays.constantTimeAreEqual(block, fBlock);
 
+        // check for old NIST tool value
+        if (trailer == ISOTrailers.TRAILER_SHA512_256 && !rv)
+        {
+            block[block.length - 2] = (byte)0x40;   // old NIST CAVP tool value
+            rv = Arrays.constantTimeAreEqual(block, fBlock);
+        }
+        
         clearBlock(block);
         clearBlock(fBlock);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ARIATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ARIATest.java
index 027e6bd..6d8f27d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/ARIATest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ARIATest.java
@@ -43,14 +43,11 @@
     public void performTest() throws Exception
     {
         checkTestVectors_RFC5794();
-        long before = System.currentTimeMillis();
-        for (int i = 0; i < 10000; ++i)
+
+        for (int i = 0; i < 100; ++i)
         {
-        checkRandomRoundtrips();
+            checkRandomRoundtrips();
         }
-        long after = System.currentTimeMillis();
-        long elapsed = after - before;
-        System.out.println("Elapsed: " + elapsed + "ms.");
 
         new MyARIAEngine().checkImplementation();
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Argon2Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Argon2Test.java
new file mode 100644
index 0000000..6fa1eab
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Argon2Test.java
@@ -0,0 +1,227 @@
+package org.bouncycastle.crypto.test;
+
+
+import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
+import org.bouncycastle.crypto.params.Argon2Parameters;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Tests from https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03
+ *
+ */
+public class Argon2Test
+    extends SimpleTest
+{
+    private static final int DEFAULT_OUTPUTLEN = 32;
+
+    public String getName()
+    {
+        return "ArgonTest";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        if (getJvmVersion() < 7)
+        {
+            return;
+        }
+
+        testVectorsFromInternetDraft();
+
+        int version = Argon2Parameters.ARGON2_VERSION_10;
+
+
+        /* Multiple test cases for various input values */
+        hashTest(version, 2, 16, 1, "password", "somesalt",
+            "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 20, 1, "password", "somesalt",
+            "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9",
+            DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 18, 1, "password", "somesalt",
+            "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467",
+            DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 8, 1, "password", "somesalt",
+            "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06",
+            DEFAULT_OUTPUTLEN);
+        hashTest(version, 2, 8, 2, "password", "somesalt",
+            "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb", DEFAULT_OUTPUTLEN);
+        hashTest(version, 1, 16, 1, "password", "somesalt",
+            "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2", DEFAULT_OUTPUTLEN);
+        hashTest(version, 4, 16, 1, "password", "somesalt",
+            "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b", DEFAULT_OUTPUTLEN);
+        hashTest(version, 2, 16, 1, "differentpassword", "somesalt",
+            "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3", DEFAULT_OUTPUTLEN);
+        hashTest(version, 2, 16, 1, "password", "diffsalt",
+            "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497", DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 16, 1, "password", "diffsalt",
+            "1a097a5d1c80e579583f6e19c7e4763ccb7c522ca85b7d58143738e12ca39f8e6e42734c950ff2463675b97c37ba" +
+                "39feba4a9cd9cc5b4c798f2aaf70eb4bd044c8d148decb569870dbd923430b82a083f284beae777812cce18cdac68ee8ccef" +
+                "c6ec9789f30a6b5a034591f51af830f4",
+            112);
+
+
+        version = Argon2Parameters.ARGON2_VERSION_13;
+
+
+        /* Multiple test cases for various input values */
+        hashTest(version, 2, 16, 1, "password", "somesalt",
+            "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
+            DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 20, 1, "password", "somesalt",
+            "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41", DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 18, 1, "password", "somesalt",
+            "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb", DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 8, 1, "password", "somesalt",
+            "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f", DEFAULT_OUTPUTLEN);
+
+        hashTest(version, 2, 8, 2, "password", "somesalt",
+            "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61", DEFAULT_OUTPUTLEN);
+        hashTest(version, 1, 16, 1, "password", "somesalt",
+            "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf", DEFAULT_OUTPUTLEN);
+        hashTest(version, 4, 16, 1, "password", "somesalt",
+            "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b", DEFAULT_OUTPUTLEN);
+        hashTest(version, 2, 16, 1, "differentpassword", "somesalt",
+            "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee", DEFAULT_OUTPUTLEN);
+        hashTest(version, 2, 16, 1, "password", "diffsalt",
+            "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271", DEFAULT_OUTPUTLEN);
+
+    }
+
+
+    private void hashTest(int version, int iterations, int memory, int parallelism,
+                          String password, String salt, String passwordRef, int outputLength)
+    {
+        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i)
+            .withVersion(version)
+            .withIterations(iterations)
+            .withMemoryPowOfTwo(memory)
+            .withParallelism(parallelism)
+            .withSalt(Strings.toByteArray(salt));
+
+        //
+        // Set the password.
+        //
+        Argon2BytesGenerator gen = new Argon2BytesGenerator();
+
+        gen.init(builder.build());
+
+        byte[] result = new byte[outputLength];
+        gen.generateBytes(password.toCharArray(), result, 0, result.length);
+
+
+        isTrue(passwordRef + " Failed", areEqual(result, Hex.decode(passwordRef)));
+    }
+
+
+    /**
+     * Tests from https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03
+     *
+     * @throws Exception
+     */
+    private void testVectorsFromInternetDraft()
+        throws Exception
+    {
+        byte[] ad = Hex.decode("040404040404040404040404");
+        byte[] secret = Hex.decode("0303030303030303");
+        byte[] salt = Hex.decode("02020202020202020202020202020202");
+        byte[] password = Hex.decode("0101010101010101010101010101010101010101010101010101010101010101");
+
+        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d)
+            .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
+            .withIterations(3)
+            .withMemoryAsKB(32)
+            .withParallelism(4)
+            .withAdditional(ad)
+            .withSecret(secret)
+            .withSalt(salt);
+
+        Argon2BytesGenerator dig = new Argon2BytesGenerator();
+
+        dig.init(builder.build());
+
+        byte[] result = new byte[32];
+        dig.generateBytes(password, result);
+        isTrue("Argon 2d Failed", areEqual(result, Hex.decode("512b391b6f1162975371d30919734294f" +
+            "868e3be3984f3c1a13a4db9fabe4acb")));
+
+
+        builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i)
+            .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
+            .withIterations(3)
+            .withMemoryAsKB(32)
+            .withParallelism(4)
+            .withAdditional(ad)
+            .withSecret(secret)
+            .withSalt(salt);
+
+        dig = new Argon2BytesGenerator();
+
+        dig.init(builder.build());
+
+        result = new byte[32];
+        dig.generateBytes(password, result);
+        isTrue("Argon 2i Failed", areEqual(result, Hex.decode("c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016" +
+            "dd388d29952a4c4672b6ce8")));
+
+
+        builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
+            .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
+            .withIterations(3)
+            .withMemoryAsKB(32)
+            .withParallelism(4)
+            .withAdditional(ad)
+            .withSecret(secret)
+            .withSalt(salt);
+        
+        dig = new Argon2BytesGenerator();
+
+        dig.init(builder.build());
+
+        result = new byte[32];
+        dig.generateBytes(password, result);
+        isTrue("Argon 2id Failed", areEqual(result, Hex.decode("0d640df58d78766c08c037a34a8b53c9d01ef0452" +
+            "d75b65eb52520e96b01e659")));
+
+    }
+
+    private static int getJvmVersion()
+    {
+        String version = System.getProperty("java.version");
+
+        if (version.startsWith("1.7"))
+        {
+            return 7;
+        }
+        if (version.startsWith("1.8"))
+        {
+            return 8;
+        }
+        if (version.startsWith("1.9"))
+        {
+            return 9;
+        }
+        if (version.startsWith("1.1"))
+        {
+            return 10;
+        }
+
+        return -1;
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        runTest(new Argon2Test());
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java
index 6a4f777..521cde8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java
@@ -132,6 +132,8 @@
             test(password, salt, cost, expected);
         }
 
+        isTrue(areEqual(BCrypt.generate(BCrypt.passwordToByteArray("12341234".toCharArray()), Hex.decode("01020304050607080102030405060708"), 5), Hex.decode("cdd19088721c50e5cb49a7b743d93b5a6e67bef0f700cd78")));
+        isTrue(areEqual(BCrypt.generate(BCrypt.passwordToByteArray("1234".toCharArray()), Hex.decode("01020304050607080102030405060708"), 5), Hex.decode("02a3269aca2732484057b40c614204814cbfc2becd8e093e")));
     }
 
     private void test(byte[] password, byte[] salt, int cost, byte[] expected)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
index 7f4a6d3..f940131 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java
@@ -5,244 +5,324 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.Blake2bDigest;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
-public class Blake2bDigestTest 
-	extends SimpleTest
+public class Blake2bDigestTest
+    extends SimpleTest
 {
 
-	private static final String[][] keyedTestVectors =
-	{ // input/message, key, hash
+    private static final String[][] keyedTestVectors =
+        { // input/message, key, hash
 
-			// Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
-			{
-					"",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" },
+            // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
+            {
+                "",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"},
 
-			{
-					"00",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" },
+            {
+                "00",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"},
 
-			{
-					"0001",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" },
+            {
+                "0001",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"},
 
-			{
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" },
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd"},
 
-			{
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" },
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f"},
 
-			{
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
-					"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					"142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" } };
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461"}};
 
-	private final static String[][] unkeyedTestVectors =
-	{ // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
-			// hash, input/message
-			// digests without leading $BLAKE2$
-			{
-					"4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
-					"blake2" },
-			{
-					"021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
-					"hello world" },
-			{
-					"1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
-					"verystrongandlongpassword" },
-			{
-					"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
-					"The quick brown fox jumps over the lazy dog" },
-			{
-					"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
-					"" },
-		{
-				"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
-				"abc" },
-	};
+    private final static String[][] unkeyedTestVectors =
+        { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
+            // hash, input/message
+            // digests without leading $BLAKE2$
+            {
+                "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
+                "blake2"},
+            {
+                "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
+                "hello world"},
+            {
+                "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
+                "verystrongandlongpassword"},
+            {
+                "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+                "The quick brown fox jumps over the lazy dog"},
+            {
+                "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+                ""},
+            {
+                "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
+                "abc"},
+        };
 
-	public String getName()
-	{
-		return "Blake2b";
-	}
+    public String getName()
+    {
+        return "BLAKE2b";
+    }
 
-	private void offsetTest(
-		Digest digest,
-		byte[] input,
-		byte[] expected)
-	{
-		byte[] resBuf = new byte[expected.length + 11];
+    private void offsetTest(
+        Digest digest,
+        byte[] input,
+        byte[] expected)
+    {
+        byte[] resBuf = new byte[expected.length + 11];
 
-		digest.update(input, 0, input.length);
+        digest.update(input, 0, input.length);
 
         digest.doFinal(resBuf, 11);
 
-		if (!areEqual(Arrays.copyOfRange(resBuf, 11, resBuf.length), expected))
-		{
-			fail("Offset failed got " + new String(Hex.encode(resBuf)));
-		}
-	}
+        if (!areEqual(Arrays.copyOfRange(resBuf, 11, resBuf.length), expected))
+        {
+            fail("Offset failed got " + new String(Hex.encode(resBuf)));
+        }
+    }
 
-	public void performTest() throws Exception
-	{
-		// test keyed test vectors:
-		
-		Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.decode(keyedTestVectors[0][1]));
-		for (int tv = 0; tv < keyedTestVectors.length; tv++)
-		{						
+    public void performTest()
+        throws Exception
+    {
+        // test keyed test vectors:
 
-			byte[] input = Hex.decode(keyedTestVectors[tv][0]);
-			blake2bkeyed.reset();
+        Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.decode(keyedTestVectors[0][1]));
+        for (int tv = 0; tv < keyedTestVectors.length; tv++)
+        {
 
-			blake2bkeyed.update(input, 0, input.length);
-			byte[] keyedHash = new byte[64];
-			blake2bkeyed.doFinal(keyedHash, 0);
+            byte[] input = Hex.decode(keyedTestVectors[tv][0]);
+            blake2bkeyed.reset();
 
-			if (!Arrays.areEqual(Hex.decode(keyedTestVectors[tv][2]), keyedHash))
-			{
-				fail("Blake2b mismatch on test vector ",
-						keyedTestVectors[tv][2],
-						new String(Hex.encode(keyedHash)));
-			}
+            blake2bkeyed.update(input, 0, input.length);
+            byte[] keyedHash = new byte[64];
+            blake2bkeyed.doFinal(keyedHash, 0);
 
-			offsetTest(blake2bkeyed, input, keyedHash);
-		}
+            if (!Arrays.areEqual(Hex.decode(keyedTestVectors[tv][2]), keyedHash))
+            {
+                fail("BLAKE2b mismatch on test vector ",
+                    keyedTestVectors[tv][2],
+                    new String(Hex.encode(keyedHash)));
+            }
 
-		Blake2bDigest blake2bunkeyed = new Blake2bDigest();
-		// test unkeyed test vectors:
-		for (int i = 0; i < unkeyedTestVectors.length; i++)
-		{
+            offsetTest(blake2bkeyed, input, keyedHash);
+        }
 
-			try
-			{
-				// blake2bunkeyed.update(
-				// unkeyedTestVectors[i][1].getBytes("UTF-8"));
-				// test update(byte b)
-				byte[] unkeyedInput = unkeyedTestVectors[i][1]
-						.getBytes("UTF-8");
-				for (int j = 0; j < unkeyedInput.length; j++)
-				{
-					blake2bunkeyed.update(unkeyedInput[j]);
-				}
-			}
+        Blake2bDigest blake2bunkeyed = new Blake2bDigest();
+        // test unkeyed test vectors:
+        for (int i = 0; i < unkeyedTestVectors.length; i++)
+        {
+
+            try
+            {
+                // blake2bunkeyed.update(
+                // unkeyedTestVectors[i][1].getBytes("UTF-8"));
+                // test update(byte b)
+                byte[] unkeyedInput = unkeyedTestVectors[i][1]
+                    .getBytes("UTF-8");
+                for (int j = 0; j < unkeyedInput.length; j++)
+                {
+                    blake2bunkeyed.update(unkeyedInput[j]);
+                }
+            }
             catch (UnsupportedEncodingException e)
-			{
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-			}
-			byte[] unkeyedHash = new byte[64];
-			blake2bunkeyed.doFinal(unkeyedHash, 0);
-			blake2bunkeyed.reset();
+            {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+            byte[] unkeyedHash = new byte[64];
+            blake2bunkeyed.doFinal(unkeyedHash, 0);
+            blake2bunkeyed.reset();
 
-			if (!Arrays.areEqual(Hex.decode(unkeyedTestVectors[i][0]),
-					unkeyedHash))
-			{
-				fail("Blake2b mismatch on test vector ",
-						unkeyedTestVectors[i][0],
-						new String(Hex.encode(unkeyedHash)));
-			}
-		}
+            if (!Arrays.areEqual(Hex.decode(unkeyedTestVectors[i][0]),
+                unkeyedHash))
+            {
+                fail("BLAKE2b mismatch on test vector ",
+                    unkeyedTestVectors[i][0],
+                    new String(Hex.encode(unkeyedHash)));
+            }
+        }
 
-		cloneTest();
-		resetTest();
-	}
+        cloneTest();
+        resetTest();
+        testNullKeyVsUnkeyed();
+        testLengthConstruction();
+    }
 
-	private void cloneTest()
-	{
-		Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, Hex.decode("000102030405060708090a0b0c0d0e0f"), Hex.decode("101112131415161718191a1b1c1d1e1f"));
-		byte[] expected = Hex.decode("b6d48ed5771b17414c4e08bd8d8a3bc4");
+    private void cloneTest()
+    {
+        Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, Hex.decode("000102030405060708090a0b0c0d0e0f"), Hex.decode("101112131415161718191a1b1c1d1e1f"));
+        byte[] expected = Hex.decode("b6d48ed5771b17414c4e08bd8d8a3bc4");
 
-		checkClone(blake2bCloneSource, expected);
+        checkClone(blake2bCloneSource, expected);
 
-		// just digest size
-		blake2bCloneSource = new Blake2bDigest(160);
-		expected = Hex.decode("64202454e538279b21cea0f5a7688be656f8f484");
-		checkClone(blake2bCloneSource, expected);
+        // just digest size
+        blake2bCloneSource = new Blake2bDigest(160);
+        expected = Hex.decode("64202454e538279b21cea0f5a7688be656f8f484");
+        checkClone(blake2bCloneSource, expected);
 
-		// null salt and personalisation
-		blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, null, null);
-		expected = Hex.decode("2b4a081fae2d7b488f5eed7e83e42a20");
-		checkClone(blake2bCloneSource, expected);
+        // null salt and personalisation
+        blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, null, null);
+        expected = Hex.decode("2b4a081fae2d7b488f5eed7e83e42a20");
+        checkClone(blake2bCloneSource, expected);
 
-		// null personalisation
-		blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, Hex.decode("000102030405060708090a0b0c0d0e0f"), null);
-		expected = Hex.decode("00c3a2a02fcb9f389857626e19d706f6");
-		checkClone(blake2bCloneSource, expected);
+        // null personalisation
+        blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, Hex.decode("000102030405060708090a0b0c0d0e0f"), null);
+        expected = Hex.decode("00c3a2a02fcb9f389857626e19d706f6");
+        checkClone(blake2bCloneSource, expected);
 
-		// null salt
-		blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, null, Hex.decode("101112131415161718191a1b1c1d1e1f"));
-		expected = Hex.decode("f445ec9c062a3c724f8fdef824417abb");
-		checkClone(blake2bCloneSource, expected);
-	}
+        // null salt
+        blake2bCloneSource = new Blake2bDigest(Hex.decode(keyedTestVectors[3][1]), 16, null, Hex.decode("101112131415161718191a1b1c1d1e1f"));
+        expected = Hex.decode("f445ec9c062a3c724f8fdef824417abb");
+        checkClone(blake2bCloneSource, expected);
+    }
 
-	private void checkClone(Blake2bDigest blake2bCloneSource, byte[] expected)
-	{
-		byte[] message = Hex.decode(keyedTestVectors[3][0]);
+    private void checkClone(Blake2bDigest blake2bCloneSource, byte[] expected)
+    {
+        byte[] message = Hex.decode(keyedTestVectors[3][0]);
 
-		blake2bCloneSource.update(message, 0, message.length);
+        blake2bCloneSource.update(message, 0, message.length);
 
-		byte[] hash = new byte[blake2bCloneSource.getDigestSize()];
+        byte[] hash = new byte[blake2bCloneSource.getDigestSize()];
 
-		Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource);
+        Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource);
 
-		blake2bCloneSource.doFinal(hash, 0);
-		if (!areEqual(expected, hash))
-		{
-			fail("clone source not correct");
-		}
+        blake2bCloneSource.doFinal(hash, 0);
+        if (!areEqual(expected, hash))
+        {
+            fail("clone source not correct");
+        }
 
-		digClone.doFinal(hash, 0);
-		if (!areEqual(expected, hash))
-		{
-			fail("clone not correct");
-		}
-	}
+        digClone.doFinal(hash, 0);
+        if (!areEqual(expected, hash))
+        {
+            fail("clone not correct");
+        }
+    }
 
-	private void resetTest()
-	{
-		// Generate a non-zero key
-		byte[] key = new byte[32];
-		for (byte i = 0; i < key.length; i++)
-		{
-			key[i] = i;
-		}
-		// Generate some non-zero input longer than the key
-		byte[] input = new byte[key.length + 1];
-		for (byte i = 0; i < input.length; i++)
-		{
-			input[i] = i;
-		}
-		// Hash the input
-		Blake2bDigest digest = new Blake2bDigest(key);
-		digest.update(input, 0, input.length);
-		byte[] hash = new byte[digest.getDigestSize()];
-		digest.doFinal(hash, 0);
-		// Using a second instance, hash the input without calling doFinal()
-		Blake2bDigest digest1 = new Blake2bDigest(key);
-		digest1.update(input, 0, input.length);
-		// Reset the second instance and hash the input again
-		digest1.reset();
-		digest1.update(input, 0, input.length);
-		byte[] hash1 = new byte[digest.getDigestSize()];
-		digest1.doFinal(hash1, 0);
-		// The hashes should be identical
-		if (!Arrays.areEqual(hash, hash1))
-		{
-			fail("state was not reset");
-		}
-	}
+    private void testLengthConstruction()
+    {
+        try
+        {
+            new Blake2bDigest(-1);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.getMessage());
+        }
 
-	public static void main(String[] args) throws Exception
-	{
-		runTest(new Blake2bDigestTest());
-	}
+        try
+        {
+            new Blake2bDigest(9);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2bDigest(520);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2bDigest(null, -1, null, null);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("Invalid digest length (required: 1 - 64)", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2bDigest(null, 65, null, null);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("Invalid digest length (required: 1 - 64)", e.getMessage());
+        }
+    }
+
+    private void testNullKeyVsUnkeyed()
+    {
+        byte[] abc = Strings.toByteArray("abc");
+
+        for (int i = 1; i != 64; i++)
+        {
+            Blake2bDigest dig1 = new Blake2bDigest(i * 8);
+            Blake2bDigest dig2 = new Blake2bDigest(null, i, null, null);
+
+            byte[] out1 = new byte[i];
+            byte[] out2 = new byte[i];
+
+            dig1.update(abc, 0, abc.length);
+            dig2.update(abc, 0, abc.length);
+
+            dig1.doFinal(out1, 0);
+            dig2.doFinal(out2, 0);
+
+            isTrue(Arrays.areEqual(out1, out2));
+        }
+    }
+
+    private void resetTest()
+    {
+        // Generate a non-zero key
+        byte[] key = new byte[32];
+        for (byte i = 0; i < key.length; i++)
+        {
+            key[i] = i;
+        }
+        // Generate some non-zero input longer than the key
+        byte[] input = new byte[key.length + 1];
+        for (byte i = 0; i < input.length; i++)
+        {
+            input[i] = i;
+        }
+        // Hash the input
+        Blake2bDigest digest = new Blake2bDigest(key);
+        digest.update(input, 0, input.length);
+        byte[] hash = new byte[digest.getDigestSize()];
+        digest.doFinal(hash, 0);
+        // Using a second instance, hash the input without calling doFinal()
+        Blake2bDigest digest1 = new Blake2bDigest(key);
+        digest1.update(input, 0, input.length);
+        // Reset the second instance and hash the input again
+        digest1.reset();
+        digest1.update(input, 0, input.length);
+        byte[] hash1 = new byte[digest.getDigestSize()];
+        digest1.doFinal(hash1, 0);
+        // The hashes should be identical
+        if (!Arrays.areEqual(hash, hash1))
+        {
+            fail("state was not reset");
+        }
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        runTest(new Blake2bDigestTest());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2sDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2sDigestTest.java
new file mode 100644
index 0000000..583eb47
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2sDigestTest.java
@@ -0,0 +1,311 @@
+package org.bouncycastle.crypto.test;
+
+import java.util.Random;
+
+import org.bouncycastle.crypto.digests.Blake2sDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Blake2sDigestTest
+    extends SimpleTest
+{
+
+    // Vectors from BLAKE2 web site: https://blake2.net/blake2s-test.txt
+    private static final String[][] keyedTestVectors = {
+        // input/message, key, hash
+        {
+            "",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49",
+        },
+        {
+            "00",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "40d15fee7c328830166ac3f918650f807e7e01e177258cdc0a39b11f598066f1",
+        },
+        {
+            "0001",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "6bb71300644cd3991b26ccd4d274acd1adeab8b1d7914546c1198bbe9fc9d803",
+        },
+        {
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "172ffc67153d12e0ca76a8b6cd5d4731885b39ce0cac93a8972a18006c8b8baf",
+        },
+        {
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "4f8ce1e51d2fe7f24043a904d898ebfc91975418753413aa099b795ecb35cedb",
+        },
+        {
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+            "3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd",
+        },
+    };
+
+    public String getName()
+    {
+        return "BLAKE2s";
+    }
+
+    public void testDigestWithKeyedTestVectors()
+    {
+        Blake2sDigest digest = new Blake2sDigest(Hex.decode(
+            keyedTestVectors[0][1]));
+        for (int i = 0; i != keyedTestVectors.length; i++)
+        {
+            String[] keyedTestVector = keyedTestVectors[i];
+            byte[] input = Hex.decode(keyedTestVector[0]);
+            digest.reset();
+
+            digest.update(input, 0, input.length);
+            byte[] hash = new byte[32];
+            digest.doFinal(hash, 0);
+
+            if (!areEqual(Hex.decode(keyedTestVector[2]), hash))
+            {
+                fail("BLAKE2s mismatch on test vector ",
+                    keyedTestVector[2],
+                    new String(Hex.encode(hash)));
+            }
+        }
+    }
+
+    public void testDigestWithKeyedTestVectorsAndRandomUpdate()
+    {
+        Blake2sDigest digest = new Blake2sDigest(Hex.decode(
+            keyedTestVectors[0][1]));
+        Random random = new Random();
+        for (int i = 0; i < 100; i++)
+        {
+            for (int j = 0; j != keyedTestVectors.length; j++)
+            {
+                String[] keyedTestVector = keyedTestVectors[j];
+                byte[] input = Hex.decode(keyedTestVector[0]);
+                if (input.length < 3)
+                {
+                    continue;
+                }
+                digest.reset();
+
+                int pos = (random.nextInt() & 0xffff) % input.length;
+                if (pos > 0)
+                {
+                    digest.update(input, 0, pos);
+                }
+                digest.update(input[pos]);
+                if (pos < (input.length - 1))
+                {
+                    digest.update(input, pos + 1, input.length - (pos + 1));
+                }
+
+                byte[] hash = new byte[32];
+                digest.doFinal(hash, 0);
+
+                if (!areEqual(Hex.decode(keyedTestVector[2]), hash))
+                {
+                    fail("BLAKE2s mismatch on test vector ",
+                        keyedTestVector[2],
+                        new String(Hex.encode(hash)));
+                }
+            }
+        }
+    }
+
+    private void testLengthConstruction()
+    {
+        try
+        {
+            new Blake2sDigest(-1);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2sDigest(9);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.getMessage());
+        }
+        
+        try
+        {
+            new Blake2sDigest(512);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2sDigest(null, -1, null, null);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("Invalid digest length (required: 1 - 32)", e.getMessage());
+        }
+
+        try
+        {
+            new Blake2sDigest(null, 33, null, null);
+            fail("no exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("Invalid digest length (required: 1 - 32)", e.getMessage());
+        }
+    }
+
+    private void testNullKeyVsUnkeyed()
+    {
+        byte[] abc = Strings.toByteArray("abc");
+
+        for (int i = 1; i != 32; i++)
+        {
+            Blake2sDigest dig1 = new Blake2sDigest(i * 8);
+            Blake2sDigest dig2 = new Blake2sDigest(null, i, null, null);
+
+            byte[] out1 = new byte[i];
+            byte[] out2 = new byte[i];
+
+            dig1.update(abc, 0, abc.length);
+            dig2.update(abc, 0, abc.length);
+
+            dig1.doFinal(out1, 0);
+            dig2.doFinal(out2, 0);
+
+            isTrue(Arrays.areEqual(out1, out2));
+        }
+    }
+
+    public void testReset()
+    {
+        // Generate a non-zero key
+        byte[] key = new byte[32];
+        for (byte i = 0; i < key.length; i++)
+        {
+            key[i] = i;
+        }
+        // Generate some non-zero input longer than the key
+        byte[] input = new byte[key.length + 1];
+        for (byte i = 0; i < input.length; i++)
+        {
+            input[i] = i;
+        }
+        // Hash the input
+        Blake2sDigest digest = new Blake2sDigest(key);
+        digest.update(input, 0, input.length);
+        byte[] hash = new byte[digest.getDigestSize()];
+        digest.doFinal(hash, 0);
+        // Create a second instance, hash the input without calling doFinal()
+        Blake2sDigest digest1 = new Blake2sDigest(key);
+        digest1.update(input, 0, input.length);
+        // Reset the second instance and hash the input again
+        digest1.reset();
+        digest1.update(input, 0, input.length);
+        byte[] hash1 = new byte[digest.getDigestSize()];
+        digest1.doFinal(hash1, 0);
+        // The hashes should be identical
+        if (!areEqual(hash, hash1))
+        {
+            fail("BLAKE2s mismatch on test vector ",
+                new String(Hex.encode(hash)),
+                new String(Hex.encode(hash1)));
+        }
+    }
+
+    // Self-test routine from https://tools.ietf.org/html/rfc7693#appendix-E
+    private static final String SELF_TEST_RESULT =
+        "6A411F08CE25ADCDFB02ABA641451CEC53C598B24F4FC787FBDC88797F4C1DFE";
+    private static final int[] SELF_TEST_DIGEST_LEN = {16, 20, 28, 32};
+    private static final int[] SELF_TEST_INPUT_LEN = {0, 3, 64, 65, 255, 1024};
+
+    private static byte[] selfTestSequence(int len, int seed)
+    {
+        int a = 0xDEAD4BAD * seed;
+        int b = 1;
+        int t;
+        byte[] out = new byte[len];
+
+        for (int i = 0; i < len; i++)
+        {
+            t = a + b;
+            a = b;
+            b = t;
+            out[i] = (byte)((t >> 24) & 0xFF);
+        }
+
+        return out;
+    }
+
+    public void runSelfTest()
+    {
+        Blake2sDigest testDigest = new Blake2sDigest();
+        byte[] md = new byte[32];
+
+        for (int i = 0; i < 4; i++)
+        {
+            int outlen = SELF_TEST_DIGEST_LEN[i];
+            for (int j = 0; j < 6; j++)
+            {
+                int inlen = SELF_TEST_INPUT_LEN[j];
+
+                // unkeyed hash
+                byte[] in = selfTestSequence(inlen, inlen);
+                Blake2sDigest unkeyedDigest = new Blake2sDigest(outlen * 8);
+                unkeyedDigest.update(in, 0, inlen);
+                unkeyedDigest.doFinal(md, 0);
+                // hash the hash
+                testDigest.update(md, 0, outlen);
+
+                // keyed hash
+                byte[] key = selfTestSequence(outlen, outlen);
+                Blake2sDigest keyedDigest = new Blake2sDigest(key, outlen, null,
+                    null);
+                keyedDigest.update(in, 0, inlen);
+                keyedDigest.doFinal(md, 0);
+                // hash the hash
+                testDigest.update(md, 0, outlen);
+            }
+        }
+
+        byte[] hash = new byte[32];
+        testDigest.doFinal(hash, 0);
+        if (!areEqual(Hex.decode(SELF_TEST_RESULT), hash))
+        {
+            fail("BLAKE2s mismatch on test vector ",
+                SELF_TEST_RESULT,
+                new String(Hex.encode(hash)));
+        }
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        testDigestWithKeyedTestVectors();
+        testDigestWithKeyedTestVectorsAndRandomUpdate();
+        testReset();
+        runSelfTest();
+        testNullKeyVsUnkeyed();
+        testLengthConstruction();
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        runTest(new Blake2sDigestTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java
index 36b729c..3eced59 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java
@@ -65,7 +65,9 @@
 
         if (!areEqual(input, out))
         {
-            fail("failed reversal got " + new String(Hex.encode(out)));
+            System.out.println(" got " + new String(Hex.encode(out)));
+
+            fail("failed reversal - " + "expected " + new String(Hex.encode(input)));
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CSHAKETest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CSHAKETest.java
new file mode 100644
index 0000000..72ba40d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CSHAKETest.java
@@ -0,0 +1,193 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.CSHAKEDigest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * CSHAKE test vectors from:
+ *
+ * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/cSHAKE_samples.pdf
+ */
+public class CSHAKETest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "CSHAKE";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        CSHAKEDigest cshake = new CSHAKEDigest(128, new byte[0], Strings.toByteArray("Email Signature"));
+
+        cshake.update(Hex.decode("00010203"), 0, 4);
+
+        byte[] res = new byte[32];
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue("oops!", Arrays.areEqual(Hex.decode("c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5"), res));
+
+        cshake = new CSHAKEDigest(128, new byte[0], Strings.toByteArray("Email Signature"));
+
+        cshake.update(Hex.decode(
+            "000102030405060708090A0B0C0D0E0F" +
+                "101112131415161718191A1B1C1D1E1F" +
+                "202122232425262728292A2B2C2D2E2F" +
+                "303132333435363738393A3B3C3D3E3F" +
+                "404142434445464748494A4B4C4D4E4F" +
+                "505152535455565758595A5B5C5D5E5F" +
+                "606162636465666768696A6B6C6D6E6F" +
+                "707172737475767778797A7B7C7D7E7F" +
+                "808182838485868788898A8B8C8D8E8F" +
+                "909192939495969798999A9B9C9D9E9F" +
+                "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF" +
+                "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF" +
+                "C0C1C2C3C4C5C6C7"), 0, 1600 / 8);
+
+        res = new byte[32];
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode("C5221D50E4F822D96A2E8881A961420F294B7B24FE3D2094BAED2C6524CC166B "), res));
+
+        cshake = new CSHAKEDigest(256, new byte[0], Strings.toByteArray("Email Signature"));
+
+        cshake.update(Hex.decode("00010203"), 0, 4);
+
+        res = new byte[64];
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode(
+        "D008828E2B80AC9D2218FFEE1D070C48"+
+            "B8E4C87BFF32C9699D5B6896EEE0EDD1"+
+            "64020E2BE0560858D9C00C037E34A969"+
+            "37C561A74C412BB4C746469527281C8C"),res));
+
+        cshake = new CSHAKEDigest(256, new byte[0], Strings.toByteArray("Email Signature"));
+
+        cshake.update(Hex.decode(
+            "000102030405060708090A0B0C0D0E0F" +
+                "101112131415161718191A1B1C1D1E1F" +
+                "202122232425262728292A2B2C2D2E2F" +
+                "303132333435363738393A3B3C3D3E3F" +
+                "404142434445464748494A4B4C4D4E4F" +
+                "505152535455565758595A5B5C5D5E5F" +
+                "606162636465666768696A6B6C6D6E6F" +
+                "707172737475767778797A7B7C7D7E7F" +
+                "808182838485868788898A8B8C8D8E8F" +
+                "909192939495969798999A9B9C9D9E9F" +
+                "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF" +
+                "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF" +
+                "C0C1C2C3C4C5C6C7"), 0, 1600 / 8);
+
+        res = new byte[64];
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode(
+                "07DC27B11E51FBAC75BC7B3C1D983E8B"+
+                    "4B85FB1DEFAF218912AC864302730917"+
+                    "27F42B17ED1DF63E8EC118F04B23633C"+
+                    "1DFB1574C8FB55CB45DA8E25AFB092BB"), res));
+
+        doFinalTest();
+        longBlockTest();
+        
+        checkSHAKE(128, new CSHAKEDigest(128, new byte[0], new byte[0]), Hex.decode("eeaabeef"));
+        checkSHAKE(256, new CSHAKEDigest(256, new byte[0], null), Hex.decode("eeaabeef"));
+        checkSHAKE(128, new CSHAKEDigest(128, null, new byte[0]), Hex.decode("eeaabeef"));
+        checkSHAKE(128, new CSHAKEDigest(128, null, null), Hex.decode("eeaabeef"));
+        checkSHAKE(256, new CSHAKEDigest(256, null, null), Hex.decode("eeaabeef"));
+    }
+
+    private void doFinalTest()
+    {
+        CSHAKEDigest cshake = new CSHAKEDigest(128, new byte[0], Strings.toByteArray("Email Signature"));
+
+        cshake.update(Hex.decode("00010203"), 0, 4);
+
+        byte[] res = new byte[32];
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode("c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5"), res));
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(!Arrays.areEqual(Hex.decode("c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5"), res));
+
+        cshake.doFinal(res, 0, res.length);
+
+        cshake.update(Hex.decode("00010203"), 0, 4);
+
+        cshake.doFinal(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode("c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5"), res));
+
+        cshake.update(Hex.decode("00010203"), 0, 4);
+
+        cshake.doOutput(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode("c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5"), res));
+        
+        cshake.doFinal(res, 0, res.length);
+
+        isTrue(Arrays.areEqual(Hex.decode("9cbce830079c452abdeb875366a49ebfe75b89ef17396e34898e904830b0e136"), res));
+    }
+
+    private void longBlockTest()
+    {
+        byte[] data = new byte[16000];
+        byte[] res = new byte[32];
+
+        for (int i = 0; i != data.length; i++)
+        {
+            data[i] = (byte)i;
+        }
+
+        for (int i = 10000; i != data.length; i++)
+        {
+            CSHAKEDigest cshake = new CSHAKEDigest(128, new byte[0], Arrays.copyOfRange(data, 0, i));
+
+            cshake.update(Hex.decode("00010203"), 0, 4);
+
+            cshake.doFinal(res, 0);
+        }
+
+        CSHAKEDigest cshake = new CSHAKEDigest(256, new byte[0], new byte[200]);
+
+        cshake.update(Arrays.copyOfRange(data, 0, 200), 0, 200);
+
+        cshake.doFinal(res, 0);
+
+        isTrue(Arrays.areEqual(Hex.decode("4a899b5be460d85a9789215bc17f88b8f8ac049bd3b519f561e7b5d3870dafa3"), res));
+    }
+
+    private void checkSHAKE(int bitSize, CSHAKEDigest cshake, byte[] msg)
+    {
+        SHAKEDigest ref = new SHAKEDigest(bitSize);
+
+        ref.update(msg, 0, msg.length);
+        cshake.update(msg, 0, msg.length);
+
+        byte[] res1 = new byte[32];
+        byte[] res2 = new byte[32];
+
+        ref.doFinal(res1, 0, res1.length);
+        cshake.doFinal(res2, 0, res2.length);
+
+        isTrue(Arrays.areEqual(res1, res2));
+    }
+    public static void main(
+        String[] args)
+    {
+        runTest(new CSHAKETest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java
index 862b9f2..9c4413c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java
@@ -6,6 +6,7 @@
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.agreement.DHAgreement;
 import org.bouncycastle.crypto.agreement.DHBasicAgreement;
+import org.bouncycastle.crypto.agreement.DHUnifiedAgreement;
 import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
 import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
 import org.bouncycastle.crypto.generators.DHParametersGenerator;
@@ -13,7 +14,10 @@
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DHUPrivateParameters;
+import org.bouncycastle.crypto.params.DHUPublicParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
 public class DHTest
@@ -295,6 +299,7 @@
             fail("basic with " + size + " bit 2-way test failed");
         }
     }
+
     private void testBounds()
     {
          BigInteger p1 = new BigInteger("00C8028E9151C6B51BCDB35C1F6B2527986A72D8546AE7A4BF41DC4289FF9837EE01592D36C324A0F066149B8B940C86C87D194206A39038AE3396F8E12435BB74449B70222D117B8A2BB77CB0D67A5D664DDE7B75E0FEC13CE0CAF258DAF3ADA0773F6FF0F2051D1859929AAA53B07809E496B582A89C3D7DA8B6E38305626621", 16);
@@ -314,6 +319,124 @@
         kpGen.init(params2);
     }
 
+    private void testCombinedTestVector1()
+    {
+        // Test Vector from NIST sample data
+
+        BigInteger P = new BigInteger("eedb3431b31d30851ddcd4dce57e1b8fc3b83cc7913bc049281d713d9f8fa91bfd0fde2e1ec5eb45a0d6483cfa6b5055ffa88622a1aa83b9f9c1df561e88b702866f17af2defea0b04cf3fbdd817140ad49c415909fc2bb2c5d160b77273e958a181bf73cf72118e1c8670d53d0e459d14d61ecb5b7c7f63a9cb019cd66aecb3a01d0402f1c18218f142653f4bc922e5baa35964b7432f311fa5a9b34e3b91582db366ad1493f25ea659540f87758ae34678dc864fb2c9d4aba18cb757285292c7d0bac73cc4632a2d54b89f2dc9656d1c50edd49dcbe2102510c70563a96f35dd8a21f0fdc5a1e23ce31fce0ee3023eafdca623508ffd2412fe4dc5b5dd0f75", 16);
+        BigInteger Q = new BigInteger("e90a78d5da01e926462e5c17a61ff97b09b6ac18f9137e7b99298705", 16);
+        BigInteger G = new BigInteger("9da3567e2f7396dd2ee4716d3477a53a47f811b2275a95ed07024d7231b739c79e88e5377479b23d460a41f981b1af619915e4d8b2dabf2cb716168d02dfb81e76048e23fff6c773f496b2ac3ae06e2eb12c39787a8244452aef404ce631aec9cf4027eefae492ce55517db0af3939354c5414e23205ae3bcd17faedecf80101fa75c619249a43b41aa15ee2d7699ee32e227b641129fe1c78b20c6655b09fa7fead338e179b4b4416c359b16e3773d141e1a876b7ee4281b61120607717f7edc8da8de42b16b54d0802d67d41fc173cd33227436f7c66bd2fe711b37fb0162543c268857414f4188f243fbf92e128388329c9f2df8db4e7808ab539891da798", 16);
+
+        DHParameters p = new DHParameters(P, G, Q);
+        
+        AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("e485cd4b82e82dafd35f89d40361049e6100c16b17ca156d072832319a40bf7a3f5081182397b8fbd9d33391896bb35d9cc890d8c0a9e5b642b773ce0690f1bbd4596a9604708edb9c27f45117a7395b7407b43eebd8b82bef4a925e2a93185df21fbf012ec9059a9c9efc0b64afe0505aa1864d79a2a9833863c16163b48c9fcc26a9b9e2741097bdeabc2b7208589e4154e1de7ecf77e928668b28abb8113b322c6d426701df979d47ccd50d493b7fb6f20050c3e67cb876c1550d8c8677527600eab07196213252bd9a48d5023788fdb4b65f85144cf6654e092550646be4882125b286ced6578eedc981304ff88725e4138f90a7a4a07c94105d796b038f", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("8a10c0be8f4efaf3019b99698bc4c102f2dac93b993d52ab10ae93f0", 16), p));
+
+        AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("3e84fbbb785bbdc43881b04ec6221b69a557b8b708d72cec8627a8342787554702d5021153ff1246ba5311553f740835c4b82ebc28c5fac05ad37f6c619649750e8dc41af9176af0099f18d36ee43535e7f35fb5f70a37b25dedd87cb6035bb938531c0430cee9c5c8f4321eae72590122bff1f636dcd6a32116ea3945d23a17acc1bfd1e7ad12390e6e13b456bc4a613b1356a7ca95c2660ac5c9f064a6b9c6d584c7e23bc1ff56745d92d0efc06384b3f59125f7c0918ae3a40074d229e22d8ca7573f9fbe89bc7afb344498d6a85b823e1fa20c3d6eccdd69abafe5e43273e71b6d32aa8dc3a349ec4ae41304e6e159c2e5c4b1555a538d58b46a4c8c87d9", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("5cb398bfbc3f69744de1f9611e03ab97aba0c5dbe1f6d74ac60fecdf", 16), p));
+
+        AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("2d6e1bb1ed6cc967027f2eb76d069369ac26f38fc87110fe55cc6487988a7d7bf2525a1b65cd02e30fcaa12d626f3b18d9191e6dcbf9fbe4b1f421dd2cb8ca804a7ca535c05bcb850561edb477eafe0a1e1e2468e89bb58899293d65cde98db5200b5eb32b1d80d4489fbab14a68f74453513658bda56067e8b41add0f13f5980ceb77c52f205e3d8b36f436ff0b313860197972de0da8b554b47091b8a69cf6ce7efd6cae6e17f090e0f71fc5332a9999cf880ff5c031132463b0eb56083885cee842f85540418b68d0250b18181b0dfb9487e39aad1d0402dc910cf679fd87d765222812ec66cf0a981f950de94b0fd1f45370bc2176748d20fe099c1f498c", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("9b6038b952d3491d937a41e1bf8857bd79b80a96c99783a96ff1ef93", 16), p));
+
+        AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("991805c775da39e0b92dc71f212e332cbab2b62a86114836bbe091c5ba2ce12cca5011483e220c0f24bba23f24a32c2c11b966064beba99b0b21eb19c7f46b328dc30af094ec116248e6f3f856aab622da4eb36b6056d7c5a3e0a0f1c45acc24321fccd1d0e0f4503e3e3aae3748ae6adeb1b85e0f708b4877b7a8d97acab093a57820b9d861da6d919126ae1c0b2d28dccca03a1808c03d5c5b6847d5e43a70b0a07190ced3ccf419e9f790281cf4676cad5dc6c7d3591a9fde2251850e072ffbc0411d8559460303c56738a1dbf76c8dd165b62a407e8cac9455c9257016fa0c7892cbcb978489a909f74d38d10746c1d5756329607ab0479c994c5d6f30e3", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("2775ab7578d5c0e18d12ed02f8c38ddfe272712902ee6a256270b041", 16), p));
+        
+        byte[] x = calculateUnifiedAgreement(U1, U2, V1, V2);
+
+        if (x == null
+            || !areEqual(Hex.decode("0f028c915a5ff77f5997791b66f08261995f7b459a574d66412f00afe5af4b838da0b9a4ed371077f1160f063844bca86ae83838cce0974d130f489532a8aeee5d55df17c13a15f79f27144aa3533665a47867f3eb43feb963ac2201d2766fb62a3979c19411c94cedf2c283b59fc616fbeeca585deb726fc7002900dc300e7b9bc055261708fee0f1f9b90de4f3720b7ec85d68745f41d495f1001dd7ccbbacf42ff2edc28e33454c5c59897d9782142db3f47972e2a79f16028f5fc6cdce4c729c57e9f63b55e25e80e3663528942b79749d7d66f7d84d4c8c4e877e221a8e06c7f001cd50b008086a4b0981e5fe000b7896dee152b24ed9cdb9907a5d64f0e4225b3cba8268c45c0846a60a697218a683e1b33843cb0153d8634769882a7fef5db4653d827bd75b54dda96666944b5d836b875d76936f73520e57be069f6aba7c36d42fc07be3e7ea49d0dabfad3177aa673553ba93a990cb79df9bcd8fa979f81c75b280cb99ff8e09713546cae8dbaea1021d2c29902793d483f29c1153f432e8b00e039286b085df0260d4949703a4a7a46492d1cb586d1845182c5b5461a432c5ebe60650de40e9e25502a0dfb931c4d5e5d9b624dcab3cbb5bf7cc51e5dbf35cd7029e724840c660dd4a6014de92a2bbb8a1b6ce28f6448d28cf1975017f66bc6904d244fe91ec39e509568d1c8256fa79931875b7ab69e29e432cce"), x))
+        {
+            fail("DH Combined Test Vector #1 agreement failed");
+        }
+    }
+
+    private void testCombinedTestVector2()
+    {
+        // Test Vector from NIST sample data
+
+        BigInteger P = new BigInteger("ea40cd647d0a1d3bcbdfa721a837e4d4dfd328340892a00aa2317f2fc532fe1e185d4ef0718281959943fc949964e542310deb687f7fcc45696c829a491b7dc5c46fff01673e71d92520465b4115dbb7edaeb32ec2688d0a5a9be93a322f3023b96d5f54e02d4a72dec479f68b40caff79f810f3a5cdaa3bda9eb87151b4c0663ceef4b50ca22ac63e4ab1343978e8ec148b5523734b23aa9ca92a21ca1cbe652c9a01b1724a1b10285778287cb5bf87c45e45dc54998e5e5308c00003131be4a62add4f5acbb0c4e2229e0fccd1633e4cf024f96dcbf012e5b629394500b1b5ceb6707957bde445671ba9a1d5b9a7d1dfe2f1419d1abf236b4b49bcfd7563df", 16);
+        BigInteger Q = new BigInteger("aa6b31da31408f637670a1fc36ca3625a5eebea9bdcc4398124bb9a006ac21f1", 16);
+        BigInteger G = new BigInteger("380ad19f75e5c666aa24daf545d74c51a4374f9002de09744bc338a33a3ab2017fdeb59f1f8552125ade4dceb7094d125ffad694662e3fe924d23c7a404806631e353887bbc4bf9f892f581880975918aca5b8a7d5108b791469f2e35f0a4095ce253bec246a8cdce190507018a4f844685eb2e0ba0146d5bb2d7ff7f1c5624fa2d7f6d20834c453457eb0227c26ae5d422cde461cfe1cd2f5ff909388dcd6ccdbfb8617b54d9038c1b9b1b2f15febbd5215db893f3a8f340bd18ac74d025a63b321ec537fa5d2c04c651f0431f75bc490ddd2a846595c6d10d0a085ab3835d025a334cdb0b25c3d993fa22aecaf5f87ca417a7aa278cb765344195f2a45201b", 16);
+
+        DHParameters p = new DHParameters(P, G, Q);
+
+        AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("a2c43dc18321063dac3cd7793fb3a6cc3b38cbce99f233ba295660aa6cbba1449b0783acb7da1118bd0530f022336a2bb8845ac26bb71c3647369e8aa29ef7b5ddc4a3b4fe70291c9acf1bc1ce5666a3401b885fd7b1906ed27a985efdb643464398036ed79eb1a79cd7b88c5bfa4418df6439ac2297b946f125f7086537082f2144545da570835b23f27ebd400ceae6670168fece4ce3780a59d6eebb3a76f91de308d4aa9a1617b4005b6b089af5c5247af6a5dea1693861151e0a5aaa4b86884ab2969f5bc3008f19ac54118939b2efccf307dc2e3aa675aea0d80dcaec7160408d6e12b0b041544c831b9ae3d06b5d51e2e77035f0b5439fb375a9bd7664", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("7b4957b799a08816f9c48c2aff5dcc0aa6ad93a765a664e67899f09d1fa8949e", 16), p));
+
+        AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("5ceca3f30cb6eb8bef123518d9b569fb9df47ac54944e381da3f69ab4a3f0484e49ea8e54d87b2bcad6f78c82f9a1969b72c7b1314ccf2ff7aa857e69ae24dbbce023f8d3cfcb2b5fe942750597b1ada12b685bb12c6ddfddf0a9d2b95e0692d431f5735b71d456fabc7362581cad88ca97b69cf185ec2d6097b07a1da80291c4d93285b21604540dc1da0807009b8f708e4eb4bdd40672b875076d5f4e712b54922c6506de4280f2cf8b34d78ea59a91dd45c7eee8cd77d8640af48342ea348abed040f7dd085181bda8f9ce88cc602407ae91b4fcb051cfcff7e7479fb6e24f6b7fb013d5b3d2ccc3dc3088c331fc9644b73e1b47e3f585f97e6f2c57e9983", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("8f9fe1ecd00b427a211f9d52b973aad9451b5985757a2204473f06de07eb39e2", 16), p));
+
+        AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("b823ca4d470c714efb57420cc50acbb56eb4a4664abb3fe233496c2a0f70e52a0af08f87490724819d8bfc10203dc62b38ee032f5e14e612e1b23d5b014359ab4fe3584f49475c9d117f9ad89511d88c79dcc284d39d722939b0b5d24ad7374af70db712344755fc54502d0ae428860f63fcdcd9537c0f89f451ada1a30676481154129de022019e5a6ac1c117820896ebd97d06db887d6fd088ab71ad0fd2f3c87a015abe428aeadee7a8a65a7b823edcf4b7d9b2faf98691126b885e5804bac1a8fa1d05c186de218816e0aa75e939b731621a424b39d19e47a81d3638ff3d663e38a802361fb9bd1e79b2f3d1f4955b3d7d63bcb373f2ee70659a270f5087", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("2c8c6202bb519b17361418f48ef346328db6be65b4aa6a25561e165b6958682e", 16), p));
+
+        AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+            new DHPublicKeyParameters(
+                new BigInteger("dae7f69a4a318f506181bd97320777e9256dcf992057eb951585fbd5b6e22e0a57255f316315e462ee38e15a0e5c5b9fc3f6f0611929bfe681f0d093cdcda18e35e13f09d5c73cbab5f659b2c55669410e5a772b621acbfa365db6046b08bce1bac4c379ad0f2bee10eddb040645ab75d5888c93e91efdc442053e5e935541b80afa911881daa91488bceab9585facbbdb010575387eac4f6657fbdc85a37dedbfccbd9b37d671861c5853de9078bdf905f15b191f2dcc1c93ee7258dc6854a8d3882ff4f03753373305fa4a00a839c3853e128f51004a17641f37ed9035665c4a6d6240cbeefb9c36b618a50e3b75d6128f732b34d81ce8b316ddefe8c0630d", 16), p),
+            new DHPrivateKeyParameters(
+                new BigInteger("35f5bf981241cff39e43b93bf31f5612a364595a881e75315de0b42b82f0d596", 16), p));
+
+        byte[] x = calculateUnifiedAgreement(U1, U2, V1, V2);
+
+        if (x == null
+            || !areEqual(Hex.decode("6d1eae28340c2095ab915b6655c96d23986c49e53f38de42a9c906eeeae3686744855b940de8377ad23053d923116f6dce7c91eea69714092a4e182cef01b362937c9bc66cc892948e79bac85bf0b9ee5c402c7725def46f754e5cd743e89247e84a4fe6e50b249c7aecf62114cb3beb6a0f8af8b0f3a19799c67372109fe0e01af6517d4108888cd3864b801a8566516b454219ee74b86a2e1a4cfbb2407198a1382858b947f9258404764fee9a0a99198c594fee426e04453b41051cfa22359d2b10d425142045b1a186056413203f4553ce0d7977012f1d3aa3df571f041f7422d4518da7abdf5a32bbbc86615cd2217b73719cb0b5ee5228a74ed0cb8202b862c68e46ab8282a482a9c94365e3dcb3b9b511bc65e7741f7d90f1180ef9c926ed9209cb10291d0ea472e675ac7704244723d788985aa6f5a73c83be4cdaba402453dfa572ac6d5bafb51b130556481e98a5ab5ede13364b886fbbf57f282b8f560f4ceafb2f29d953c8244aa3fea0c227a1a88e012e814267ecf36ac72793acf2ee02713d8980f30bc9231aae91a8181ed4645aa969625990cbdc7f4f646929132ef73354950c2490f91847a3350ece763a1869f6e446e4995296d4c024bf6998dd11aea59220e81e1aade984ba650150621f17e4bbca5f0f49fd21924c3a605d1e7e4fd3e32b93e1df6cd6a0d28cd9105537b513144e8ad1d3007bffbb15"), x))
+        {
+            fail("DH Combined Test Vector #2 agreement failed");
+        }
+    }
+
+    private byte[] calculateUnifiedAgreement(
+        AsymmetricCipherKeyPair U1,
+        AsymmetricCipherKeyPair U2,
+        AsymmetricCipherKeyPair V1,
+        AsymmetricCipherKeyPair V2)
+    {
+        DHUnifiedAgreement u = new DHUnifiedAgreement();
+        u.init(new DHUPrivateParameters(
+            (DHPrivateKeyParameters)U1.getPrivate(),
+            (DHPrivateKeyParameters)U2.getPrivate(),
+            (DHPublicKeyParameters)U2.getPublic()));
+        byte[] ux = u.calculateAgreement(new DHUPublicParameters(
+            (DHPublicKeyParameters)V1.getPublic(),
+            (DHPublicKeyParameters)V2.getPublic()));
+
+        DHUnifiedAgreement v = new DHUnifiedAgreement();
+        v.init(new DHUPrivateParameters(
+            (DHPrivateKeyParameters)V1.getPrivate(),
+            (DHPrivateKeyParameters)V2.getPrivate(),
+            (DHPublicKeyParameters)V2.getPublic()));
+        byte[] vx = v.calculateAgreement(new DHUPublicParameters(
+            (DHPublicKeyParameters)U1.getPublic(),
+            (DHPublicKeyParameters)U2.getPublic()));
+
+        if (areEqual(ux, vx))
+        {
+            return ux;
+        }
+
+        return null;
+    }
+    
     public void performTest()
     {
         testDHBasic(512, 0, g512, p512);
@@ -330,6 +453,9 @@
 
         testBounds();
 
+        testCombinedTestVector1();
+        testCombinedTestVector2();
+        
         //
         // generation test.
         //
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7564Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7564Test.java
new file mode 100644
index 0000000..3589b3b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7564Test.java
@@ -0,0 +1,624 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.DSTU7564Digest;
+import org.bouncycastle.crypto.macs.DSTU7564Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class DSTU7564Test
+    extends DigestTest
+{
+
+    private static String[] messages =
+        {
+            "",
+            "a",
+            "abc",
+            "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+        };
+
+    private static String[] digests =
+        {
+            "cd5101d1ccdf0d1d1f4ada56e888cd724ca1a0838a3521e7131d4fb78d0f5eb6",
+            "c51a1d639596fb613d86557314a150c40f8fff3de48bc93a3b03c161f4105ee4",
+            "0bd1b36109f1318411a0517315aa46b8839df06622a278676f5487996c9cfc04",
+            "02621dbb53f2c7001be64d7308ecb80d21ba7797c92e98d1efc240d41e4c414b"
+        };
+
+    protected Digest cloneDigest(Digest digest)
+    {
+        return new DSTU7564Digest((DSTU7564Digest)digest);
+    }
+
+    public DSTU7564Test()
+    {
+        super(new DSTU7564Digest(256), messages, digests);
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new DSTU7564Test());
+    }
+
+    public void performTest()
+    {
+        super.performTest();
+
+        hash256Tests();
+        hash384Tests();
+        hash512Tests();
+        macTests();
+        overflowTest();
+    }
+
+    private void overflowTest()
+    {
+        int macBitSize = 256;
+        byte[] input = new byte[1024];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+        byte[] key = Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100");
+
+        byte[] expectedMac = Hex.decode("165382df70adcb040b17c1aced117d26d598b239ab631271a05f6d0f875ae9ea");
+        byte[] mac = new byte[macBitSize / 8];
+
+        DSTU7564Mac dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed overflow test 1 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        macBitSize = 256;
+        input = new byte[1023];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+        key = Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100");
+
+        expectedMac = Hex.decode("ed45f163e694d990d2d835dca2f3f869a55a31396c8138161b190d5914d50686");
+        mac = new byte[macBitSize / 8];
+
+        dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed overflow test 2 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        DSTU7564Digest digest = new DSTU7564Digest(macBitSize);
+        byte[] expectedDigest = Hex.decode("6bfc5ec8c1f5963fbed89da115d86e9330634eca341dd42fd94a7007e4af7942");
+        byte[] digestBuf = new byte[macBitSize / 8];
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 3 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        expectedDigest = Hex.decode("6f8f0a3f8261af77581ab01cb89d4cb5ed87ca1d9954f11d5586e94b45c82fb8");
+
+        input = new byte[51];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 4 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[52];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("8b6fe2ba77e684b2a1ac82232f4efc49f681cd18c82a0cfff530186a2fc642d2");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 5 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+
+        input = new byte[53];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("837f2b0cbe39a4defdfcb44272288d4091cab850161c70695d7831fc5f00e171");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 6 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[54];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("21d423d5b8c7f18a0da42cdd95b36b66344125e2adc6edeab5899926442113bc");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 7 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[55];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("0e7bf74464b81b3ae7d904170776d29f4b02a7227da578dd562d01027af7fd0e");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 8 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[56];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("badea1f49cbcec94acec52b4c695acdddd786cca5a6763929f341a58c5134b3b");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 9 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[57];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("a13b5f6f53ee043292ed65b66c1d49759be4d2fe0c2f6148f2416487965f7bde");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 10 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[63];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("03a44a02c9ffafb43addb290bbcf3b8168f624e8cbd332dc6a9dc7df9d39cbc2");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 11 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[64];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("08f4ee6f1be6903b324c4e27990cb24ef69dd58dbe84813ee0a52f6631239875");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 12 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+
+        input = new byte[65];
+        for (int i = 0; i != input.length; i++)
+        {
+            input[i] = (byte)(i & 0xff);
+        }
+
+        expectedDigest = Hex.decode("a81c2fb92351f370050b7c36cd51736d5603a50ec1106cbd5fe1c9be2e5c77a6");
+
+        digest.update(input, 0, input.length);
+        digest.doFinal(digestBuf, 0);
+
+        if (!Arrays.areEqual(expectedDigest, digestBuf))
+        {
+            fail("Failed overflow test 13 - expected "
+                + Hex.toHexString(expectedDigest)
+                + " got " + Hex.toHexString(digestBuf));
+        }
+    }
+
+    private void macTests()
+    {
+
+        //test1
+        int macBitSize = 256;
+        byte[] input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E");
+        byte[] key = Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100");
+
+        byte[] expectedMac = Hex.decode("B60594D56FA79BA210314C72C2495087CCD0A99FC04ACFE2A39EF669925D98EE");
+        byte[] mac = new byte[macBitSize / 8];
+
+        DSTU7564Mac dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed mac test 1 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        //test1a
+        input = Hex.decode("0001020304050607");
+        key = Hex.decode("08F4EE6F1BE6903B324C4E27990CB24EF69DD58DBE84813EE0A52F6631239875");
+
+        expectedMac = Hex.decode("383A0B11989ABF61B2CF3EB489351EB7C9AEF70CF5A9D6DBD90F340FF151BA2D");
+        mac = new byte[macBitSize / 8];
+
+        dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed mac test 1a - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        //test 2
+        macBitSize = 384;
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E");
+        key = Hex.decode("2F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100");
+
+        expectedMac = Hex.decode("BEBFD8D730336F043ABACB41829E79A4D320AEDDD8D14024D5B805DA70C396FA295C281A38B30AE728A304B3F5AE490E");
+        mac = new byte[macBitSize / 8];
+
+        dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed mac test 2 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        //test 3
+        macBitSize = 512;
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E");
+        key = Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100");
+
+        expectedMac = Hex.decode("F270043C06A5C37E65D9D791C5FBFB966E5EE709F8F54019C9A55B76CA40B70100579F269CEC24E347A9D864614CF3ABBF6610742E4DB3BD2ABC000387C49D24");
+        mac = new byte[macBitSize / 8];
+
+        dstu7564mac = new DSTU7564Mac(macBitSize);
+
+        dstu7564mac.init(new KeyParameter(key));
+        dstu7564mac.update(input, 0, input.length);
+        dstu7564mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(expectedMac, mac))
+        {
+            fail("Failed mac test 3 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+    }
+
+    private void hash512Tests()
+    {
+
+        int hashBitSize = 512;
+
+        //test 1
+        byte[] input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        byte[] expectedHash = Hex.decode("3813E2109118CDFB5A6D5E72F7208DCCC80A2DFB3AFDFB02F46992B5EDBE536B3560DD1D7E29C6F53978AF58B444E37BA685C0DD910533BA5D78EFFFC13DE62A");
+        byte[] hash = new byte[hashBitSize / 8];
+
+
+        DSTU7564Digest dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 1 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 2
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+        expectedHash = Hex.decode("76ED1AC28B1D0143013FFA87213B4090B356441263C13E03FA060A8CADA32B979635657F256B15D5FCA4A174DE029F0B1B4387C878FCC1C00E8705D783FD7FFE");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 2 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 3
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+        expectedHash = Hex.decode("0DD03D7350C409CB3C29C25893A0724F6B133FA8B9EB90A64D1A8FA93B56556611EB187D715A956B107E3BFC76482298133A9CE8CBC0BD5E1436A5B197284F7E");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 3 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 4
+        input = Hex.decode("FF");
+        expectedHash = Hex.decode("871B18CF754B72740307A97B449ABEB32B64444CC0D5A4D65830AE5456837A72D8458F12C8F06C98C616ABE11897F86263B5CB77C420FB375374BEC52B6D0292");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 4 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 5
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+        expectedHash = Hex.decode("B189BFE987F682F5F167F0D7FA565330E126B6E592B1C55D44299064EF95B1A57F3C2D0ECF17869D1D199EBBD02E8857FB8ADD67A8C31F56CD82C016CF743121");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 5 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+
+        //test 6
+        input = Hex.decode("");
+        expectedHash = Hex.decode("656B2F4CD71462388B64A37043EA55DBE445D452AECD46C3298343314EF04019BCFA3F04265A9857F91BE91FCE197096187CEDA78C9C1C021C294A0689198538");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-512 test 6 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+    }
+
+    private void hash384Tests()
+    {
+
+        int hashBitSize = 384;
+
+        //test 1
+        byte[] input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E");
+        byte[] expectedHash = Hex.decode("D9021692D84E5175735654846BA751E6D0ED0FAC36DFBC0841287DCB0B5584C75016C3DECC2A6E47C50B2F3811E351B8");
+        byte[] hash = new byte[hashBitSize / 8];
+
+
+        DSTU7564Digest dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-384 test 1 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+    }
+
+    private void hash256Tests()
+    {
+
+        int hashBitSize = 256;
+
+        //test 1
+        byte[] input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        byte[] expectedHash = Hex.decode("08F4EE6F1BE6903B324C4E27990CB24EF69DD58DBE84813EE0A52F6631239875");
+        byte[] hash = new byte[hashBitSize / 8];
+
+
+        DSTU7564Digest dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 1 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 2
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+        expectedHash = Hex.decode("0A9474E645A7D25E255E9E89FFF42EC7EB31349007059284F0B182E452BDA882");
+        hash = new byte[hashBitSize / 8];
+
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 2 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 3
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+        expectedHash = Hex.decode("D305A32B963D149DC765F68594505D4077024F836C1BF03806E1624CE176C08F");
+        hash = new byte[hashBitSize / 8];
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 3 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 4
+        input = Hex.decode("FF");
+        expectedHash = Hex.decode("EA7677CA4526555680441C117982EA14059EA6D0D7124D6ECDB3DEEC49E890F4");
+        hash = new byte[hashBitSize / 8];
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 4 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 5
+        input = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E");
+        expectedHash = Hex.decode("1075C8B0CB910F116BDA5FA1F19C29CF8ECC75CAFF7208BA2994B68FC56E8D16");
+        hash = new byte[hashBitSize / 8];
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 5 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+
+        //test 6
+        input = Hex.decode("");
+        expectedHash = Hex.decode("CD5101D1CCDF0D1D1F4ADA56E888CD724CA1A0838A3521E7131D4FB78D0F5EB6");
+        hash = new byte[hashBitSize / 8];
+
+        dstu7564 = new DSTU7564Digest(hashBitSize);
+        dstu7564.update(input, 0, input.length);
+        dstu7564.doFinal(hash, 0);
+
+        if (!Arrays.areEqual(expectedHash, hash))
+        {
+            fail("Failed hash-256 test 6 - expected "
+                + Hex.toHexString(expectedHash)
+                + " got " + Hex.toHexString(hash));
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7624Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7624Test.java
new file mode 100644
index 0000000..b8b73aa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU7624Test.java
@@ -0,0 +1,1439 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.engines.DSTU7624Engine;
+import org.bouncycastle.crypto.engines.DSTU7624WrapEngine;
+import org.bouncycastle.crypto.macs.DSTU7624Mac;
+import org.bouncycastle.crypto.macs.KGMac;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.KCCMBlockCipher;
+import org.bouncycastle.crypto.modes.KCTRBlockCipher;
+import org.bouncycastle.crypto.modes.KGCMBlockCipher;
+import org.bouncycastle.crypto.modes.KXTSBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class DSTU7624Test
+    extends CipherTest
+{
+    private static final SecureRandom RANDOM = new SecureRandom();
+    
+    private static byte[] randomBytes(int min, int max)
+    {
+        int count = min + RNGUtils.nextInt(RANDOM,max - min);
+        byte[] result = new byte[count];
+        RANDOM.nextBytes(result);
+        return result;
+    }
+
+    static SimpleTest[] tests =
+        {
+            //ECB mode
+            new BlockCipherVectorTest(0, new DSTU7624Engine(128), new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), "101112131415161718191A1B1C1D1E1F", "81BF1C7D779BAC20E1C9EA39B4D2AD06"),
+            new BlockCipherVectorTest(1, new DSTU7624Engine(128), new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F", "58EC3E091000158A1148F7166F334F14"),
+            new BlockCipherVectorTest(2, new DSTU7624Engine(256), new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F", "F66E3D570EC92135AEDAE323DCBD2A8CA03963EC206A0D5A88385C24617FD92C"),
+            new BlockCipherVectorTest(3, new DSTU7624Engine(256), new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F", "606990E9E6B7B67A4BD6D893D72268B78E02C83C3CD7E102FD2E74A8FDFE5DD9"),
+            new BlockCipherVectorTest(4, new DSTU7624Engine(512), new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F", "4A26E31B811C356AA61DD6CA0596231A67BA8354AA47F3A13E1DEEC320EB56B895D0F417175BAB662FD6F134BB15C86CCB906A26856EFEB7C5BC6472940DD9D9"),
+
+            //CBC mode
+            new BlockCipherVectorTest(5, new CBCBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F", "A73625D7BE994E85469A9FAABCEDAAB6DBC5F65DD77BB35E06BD7D1D8EAFC8624D6CB31CE189C82B8979F2936DE9BF14"),
+            new BlockCipherVectorTest(6, new CBCBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("0F0E0D0C0B0A09080706050403020100")), Hex.decode("1F1E1D1C1B1A19181716151413121110")), "88F2F048BA696170E3818915E0DBC0AFA6F141FEBC2F817138DA4AAB2DBF9CE490A488C9C82AC83FB0A6C0EEB64CFD22", "4F4E4D4C4B4A494847464544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A29282726252423222120"),
+            new BlockCipherVectorTest(7, new CBCBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")), Hex.decode("202122232425262728292A2B2C2D2E2F")), "303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D8000", "13EA15843AD14C50BC03ECEF1F43E398E4217752D3EB046AC393DACC5CA1D6FA0EB9FCEB229362B4F1565527EE3D8433"),
+            new BlockCipherVectorTest(8, new CBCBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("2F2E2D2C2B2A29282726252423222120")), "BC8F026FC603ECE05C24FDE87542730999B381870882AC0535D4368C4BABD81B884E96E853EE7E055262D9D204FBE212", "5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A39383736353433323130"),
+            new BlockCipherVectorTest(9, new CBCBlockCipher(new DSTU7624Engine(256)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")), Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F", "9CDFDAA75929E7C2A5CFC1BF16B42C5AE3886D0258E8C577DC01DAF62D185FB999B9867736B87110F5F1BC7481912C593F48FF79E2AFDFAB9F704A277EC3E557B1B0A9F223DAE6ED5AF591C4F2D6FB22E48334F5E9B96B1A2EA5200F30A406CE"),
+            new BlockCipherVectorTest(10, new CBCBlockCipher(new DSTU7624Engine(256)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")), "606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF", "B8A2474578C2FEBF3F94703587BD5FDC3F4A4D2F43575B6144A1E1031FB3D1452B7FD52F5E3411461DAC506869FF8D2FAEF4FEE60379AE00B33AA3EAF911645AF8091CD8A45D141D1FB150E5A01C1F26FF3DBD26AC4225EC7577B2CE57A5B0FF"),
+            new BlockCipherVectorTest(11, new CBCBlockCipher(new DSTU7624Engine(256)), new ParametersWithIV(new KeyParameter(Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746454443424140")), "C69A59E10D00F087319B62288A57417C074EAD07C732A87055F0A5AD2BB288105705C45E091A9A6726E9672DC7D8C76FC45C782BCFEF7C39D94DEB84B17035BC8651255A0D34373451B6E1A2C827DB97566C9FF5506C5579F982A0EFC5BA7C28", "BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160"),
+            new BlockCipherVectorTest(12, new CBCBlockCipher(new DSTU7624Engine(512)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")), "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF", "D4739B829EF901B24C1162AE4FDEF897EDA41FAC7F5770CDC90E1D1CDF124E8D7831E06B4498A4B6F6EC815DF2461DC99BB0449B0F09FCAA2C84090534BCC9329626FD74EF8F0A0BCB5765184629C3CBF53B0FB134F6D0421174B1C4E884D1CD1069A7AD19752DCEBF655842E79B7858BDE01390A760D85E88925BFE38B0FA57"),
+            new BlockCipherVectorTest(13, new CBCBlockCipher(new DSTU7624Engine(512)), new ParametersWithIV(new KeyParameter(Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("7F7E7D7C7B7A797877767574737271706F6E6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746454443424140")), "5D5B3E3DE5BAA70E0A0684D458856CE759C6018D0B3F087FC1DAC101D380236DD934F2880B02D56A575BCA35A0CE4B0D9BA1F4A39C16CA7D80D59956630F09E54EC91E32B6830FE08323ED393F8028D150BF03CAD0629A5AFEEFF6E44257980618DB2F32B7B2B65B96E8451F1090829D2FFFC615CC1581E9221438DCEAD1FD12", "FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A89888786858483828180"),
+
+            //CFB mode
+            new BlockCipherVectorTest(14, new CFBBlockCipher(new DSTU7624Engine(128), 128), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F", "A19E3E5E53BE8A07C9E0C01298FF83291F8EE6212110BE3FA5C72C88A082520B265570FE28680719D9B4465E169BC37A"),
+
+            //OFB mode
+            new BlockCipherVectorTest(15, new OFBBlockCipher(new DSTU7624Engine(128), 128), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F", "A19E3E5E53BE8A07C9E0C01298FF832953205C661BD85A51F3A94113BC785CAB634B36E89A8FDD16A12E4467F5CC5A26"),
+            new BlockCipherVectorTest(16, new OFBBlockCipher(new DSTU7624Engine(128), 128), new ParametersWithIV(new KeyParameter(Hex.decode("0F0E0D0C0B0A09080706050403020100")), Hex.decode("1F1E1D1C1B1A19181716151413121110")), "649A1EAAE160AF20F5B3EF2F58D66C1178B82E00D26F30689C8EC22E8E86E9CBB0BD4FFEE39EB13C2311276A906DD636", "4F4E4D4C4B4A494847464544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A29282726252423222120"),
+            new BlockCipherVectorTest(17, new OFBBlockCipher(new DSTU7624Engine(128), 128), new ParametersWithIV(new KeyParameter(Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("2F2E2D2C2B2A29282726252423222120")), "1A66CFBFEC00C6D52E39923E858DD64B214AB787798D3D5059A6B498AD66B34EAC48C4074BEC0D98C6", "5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A393837"),
+            new BlockCipherVectorTest(18, new OFBBlockCipher(new DSTU7624Engine(256), 256), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")), Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90", "B62F7F144A8C6772E693A96890F064C3F06831BF743F5B0DD061067F3D22877331AA6A99D939F05B7550E9402BD1615CC7B2D4A167E83EC0D8A894F92C72E176F3880B61C311D69CE1210C59184E818E19"),
+            new BlockCipherVectorTest(19, new OFBBlockCipher(new DSTU7624Engine(256), 256), new ParametersWithIV(new KeyParameter(Hex.decode("1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A29282726252423222120")), "7758A939DD6BD00CAF9153E5A5D5A66129105CA1EA54A97C06FA4A40960A068F55E34F9339A14436216948F92FA2FB5286D3AB1E81543FC0018A0C4E8C493475F4D35DCFB0A7A5377F6669B857CDC978E4", "9F9E9D9C9B9A999897969594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F"),
+            new BlockCipherVectorTest(20, new OFBBlockCipher(new DSTU7624Engine(256), 256), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")), "606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0", "0008F28A82D2D01D23BFB2F8BB4F06D8FE73BA4F48A2977585570ED3818323A668883C9DCFF610CC7E3EA5C025FBBC5CA6520F8F11CA35CEB9B07031E6DBFABE39001E9A3CC0A24BBC565939592B4DEDBD"),
+            new BlockCipherVectorTest(21, new OFBBlockCipher(new DSTU7624Engine(256), 256), new ParametersWithIV(new KeyParameter(Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746454443424140")), "98E122708FDABB1B1A5765C396DC79D7573221EC486ADDABD1770B147A6DD00B5FBC4F1EC68C59775B7AAA4D43C4CCE4F396D982DF64D30B03EF6C3B997BA0ED940BBC590BD30D64B5AE207147D71086B5", "BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F"),
+            new BlockCipherVectorTest(22, new OFBBlockCipher(new DSTU7624Engine(512), 512), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F")), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")), "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0", "CAA761980599B3ED2E945C41891BAD95F72B11C73ED26536A6847458BC76C827357156B4B3FE0DC1877F5B9F17B866C37B21D89531DB48007D05DEC928B06766C014BB9080385EDF0677E48A0A39B5E7489E28E82FFFD1F84694F17296CB701656"),
+            new BlockCipherVectorTest(23, new OFBBlockCipher(new DSTU7624Engine(512), 512), new ParametersWithIV(new KeyParameter(Hex.decode("3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100")), Hex.decode("7F7E7D7C7B7A797877767574737271706F6E6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746454443424140")), "06C061A4A66DFC0910034B3CFBDC4206D8908241C56BF41C4103CFD6DF322210B87F57EAE9F9AD815E606A7D1E8E6BD7CB1EBFBDBCB085C2D06BF3CC1586CB2EE1D81D38437F425131321647E42F5DE309D33F25B89DE37124683E4B44824FC56D", "EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F"),
+
+            //CTR mode
+            new BlockCipherVectorTest(24, new KCTRBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748", "A90A6B9780ABDFDFF64D14F5439E88F266DC50EDD341528DD5E698E2F000CE21F872DAF9FE1811844A"),
+            new BlockCipherVectorTest(25, new KCTRBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F", "B91A7B8790BBCFCFE65D04E5538E98E216AC209DA33122FDA596E8928070BE51"),
+            new StreamCipherVectorTest(26, new KCTRBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748", "A90A6B9780ABDFDFF64D14F5439E88F266DC50EDD341528DD5E698E2F000CE21F872DAF9FE1811844A"),
+            new StreamCipherVectorTest(27, new KCTRBlockCipher(new DSTU7624Engine(128)), new ParametersWithIV(new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F")), Hex.decode("101112131415161718191A1B1C1D1E1F")), "303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F", "B91A7B8790BBCFCFE65D04E5538E98E216AC209DA33122FDA596E8928070BE51")
+        };
+
+
+    public DSTU7624Test()
+    {
+        super(tests, new DSTU7624Engine(128), new KeyParameter(new byte[16]));
+    }
+
+    public String getName()
+    {
+        return "DSTU7624";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        super.performTest();
+
+        MacTests();
+        KeyWrapTests();
+        CCMModeTests();
+        XTSModeTests();
+        GCMModeTests();
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new DSTU7624Test());
+    }
+
+
+    private void MacTests()
+    {
+
+        //test 1
+        byte[] key = Hex.decode("000102030405060708090A0B0C0D0E0F");
+
+        byte[] authtext = Hex.decode("202122232425262728292A2B2C2D2E2F" +
+            "303132333435363738393A3B3C3D3E3F" +
+            "404142434445464748494A4B4C4D4E4F");
+
+        byte[] expectedMac = Hex.decode("123B4EAB8E63ECF3E645A99C1115E241");
+
+        byte[] mac = new byte[expectedMac.length];
+
+        DSTU7624Mac dstu7624Mac = new DSTU7624Mac(128, 128);
+        dstu7624Mac.init(new KeyParameter(key));
+        dstu7624Mac.update(authtext, 0, authtext.length);
+        dstu7624Mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed MAC test 1 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+
+        //test 2
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F" +
+            "101112131415161718191A1B1C1D1E1F" +
+            "202122232425262728292A2B2C2D2E2F" +
+            "303132333435363738393A3B3C3D3E3F");
+
+        authtext = Hex.decode("404142434445464748494A4B4C4D4E4F" +
+            "505152535455565758595A5B5C5D5E5F" +
+            "606162636465666768696A6B6C6D6E6F" +
+            "707172737475767778797A7B7C7D7E7F" +
+            "808182838485868788898A8B8C8D8E8F" +
+            "909192939495969798999A9B9C9D9E9F" +
+            "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF" +
+            "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        expectedMac = Hex.decode("7279FA6BC8EF7525B2B35260D00A1743");
+
+        dstu7624Mac = new DSTU7624Mac(512, 128);
+        dstu7624Mac.init(new KeyParameter(key));
+        dstu7624Mac.update(authtext, 0, authtext.length);
+        dstu7624Mac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed MAC test 2 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+    }
+
+    private void KeyWrapTests()
+        throws Exception
+    {
+        //test 1
+        /*
+         * Initial implementation had bugs handling offset and length correctly, so for
+         * this first test case we embed the input inside a larger buffer.
+         */
+        byte[] textA = randomBytes(1, 64);
+        byte[] textB = randomBytes(1, 64);
+        byte[] textToWrap = Arrays.concatenate(new byte[][]{ textA, Hex.decode("101112131415161718191A1B1C1D1E1F"), textB });
+
+        byte[] key = Hex.decode("000102030405060708090A0B0C0D0E0F");
+        byte[] expectedWrappedText = Hex.decode("1DC91DC6E52575F6DBED25ADDA95A1B6AD3E15056E489738972C199FB9EE2913");
+        byte[] output = new byte[expectedWrappedText.length];
+
+        DSTU7624WrapEngine wrapper = new DSTU7624WrapEngine(128);
+        wrapper.init(true, new KeyParameter(key));
+        output = wrapper.wrap(textToWrap, textA.length, textToWrap.length - textA.length - textB.length);
+
+        if (!Arrays.areEqual(output, expectedWrappedText))
+        {
+            fail("Failed KW (wrapping) test 1 - expected "
+                + Hex.toHexString(expectedWrappedText)
+                + " got " + Hex.toHexString(output));
+        }
+
+        output = Arrays.concatenate(new byte[][]{ textB, output, textA });
+
+        wrapper.init(false, new KeyParameter(key));
+        output = wrapper.unwrap(output, textB.length, output.length - textB.length - textA.length);
+
+        byte[] expected = Arrays.copyOfRange(textToWrap, textA.length, textToWrap.length - textB.length);
+        if (!Arrays.areEqual(output, expected))
+        {
+            fail("Failed KW (unwrapping) test 1 - expected "
+                + Hex.toHexString(expected)
+                + " got " + Hex.toHexString(output));
+        }
+
+        //test 2
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F");
+        textToWrap = Hex.decode("101112131415161718191A1B1C1D1E1F20219000000000000000800000000000");
+        expectedWrappedText = Hex.decode("0EA983D6CE48484D51462C32CC61672210FCC44196ABE635BAF878FDB83E1A63114128585D49DB355C5819FD38039169");
+
+        output = new byte[expectedWrappedText.length];
+
+        wrapper.init(true, new KeyParameter(key));
+        output = wrapper.wrap(textToWrap, 0, textToWrap.length);
+
+
+        if (!Arrays.areEqual(output, expectedWrappedText))
+        {
+            fail("Failed KW (wrapping) test 2 - expected "
+                + Hex.toHexString(expectedWrappedText)
+                + " got " + Hex.toHexString(output));
+        }
+
+
+        wrapper.init(false, new KeyParameter(key));
+
+        output = wrapper.unwrap(expectedWrappedText, 0, expectedWrappedText.length);
+        if (!Arrays.areEqual(output, textToWrap))
+        {
+            fail("Failed KW (unwrapping) test 2 - expected "
+                + Hex.toHexString(textToWrap)
+                + " got " + Hex.toHexString(output));
+        }
+
+        //test 3
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        textToWrap = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F");
+        expectedWrappedText = Hex.decode("2D09A7C18E6A5A0816331EC27CEA596903F77EC8D63F3BDB73299DE7FD9F4558E05992B0B24B39E02EA496368E0841CC1E3FA44556A3048C5A6E9E335717D17D");
+
+        output = new byte[expectedWrappedText.length];
+
+        wrapper = new DSTU7624WrapEngine(128);
+        wrapper.init(true, new KeyParameter(key));
+        output = wrapper.wrap(textToWrap, 0, textToWrap.length);
+
+
+        if (!Arrays.areEqual(output, expectedWrappedText))
+        {
+            fail("Failed KW (wrapping) test 3 - expected "
+                + Hex.toHexString(expectedWrappedText)
+                + " got " + Hex.toHexString(output));
+        }
+
+        wrapper.init(false, new KeyParameter(key));
+
+        output = wrapper.unwrap(expectedWrappedText, 0, expectedWrappedText.length);
+
+        if (!Arrays.areEqual(output, textToWrap))
+        {
+            fail("Failed KW (unwrapping) test 3 - expected "
+                + Hex.toHexString(textToWrap)
+                + " got " + Hex.toHexString(output));
+        }
+
+        //test 4
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        textToWrap = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464E8040000000000020");
+        expectedWrappedText = Hex.decode("37E3EECB91150C6FA04CFD19D6FC57B7168C9FA5C5ED18601C68EE4AFD7301F8C8C51D7A0A5CD34F6FAB0D8AF11845CC1E4B16E0489FDA1D76BA4EFCFD161F76");
+
+        output = new byte[expectedWrappedText.length];
+
+        wrapper = new DSTU7624WrapEngine(128);
+        wrapper.init(true, new KeyParameter(key));
+        output = wrapper.wrap(textToWrap, 0, textToWrap.length);
+
+
+        if (!Arrays.areEqual(output, expectedWrappedText))
+        {
+            fail("Failed KW (wrapping) test 4 - expected "
+                + Hex.toHexString(expectedWrappedText)
+                + " got " + Hex.toHexString(output));
+        }
+
+        wrapper.init(false, new KeyParameter(key));
+
+        output = wrapper.unwrap(expectedWrappedText, 0, expectedWrappedText.length);
+
+        if (!Arrays.areEqual(output, textToWrap))
+        {
+            fail("Failed KW (unwrapping) test 4 - expected "
+                + Hex.toHexString(textToWrap)
+                + " got " + Hex.toHexString(output));
+        }
+
+        //test 5
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        textToWrap = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+        expectedWrappedText = Hex.decode("BE59D3C3C31B2685A8FA57CD000727F16AF303F0D87BC2D7ABD80DC2796BBC4CDBC4E0408943AF4DAF7DE9084DC81BFEF15FDCDD0DF399983DF69BF730D7AE2A199CA4F878E4723B7171DD4D1E8DF59C0F25FA0C20946BA64F9037D724BB1D50B6C2BD9788B2AF83EF6163087CD2D4488BC19F3A858D813E3A8947A529B6D65D");
+
+        output = new byte[expectedWrappedText.length];
+
+        wrapper = new DSTU7624WrapEngine(256);
+        wrapper.init(true, new KeyParameter(key));
+        output = wrapper.wrap(textToWrap, 0, textToWrap.length);
+
+
+        if (!Arrays.areEqual(output, expectedWrappedText))
+        {
+            fail("Failed KW (wrapping) test 5 - expected "
+                + Hex.toHexString(expectedWrappedText)
+                + " got " + Hex.toHexString(output));
+        }
+
+        wrapper.init(false, new KeyParameter(key));
+
+        output = wrapper.unwrap(expectedWrappedText, 0, expectedWrappedText.length);
+
+        if (!Arrays.areEqual(output, textToWrap))
+        {
+            fail("Failed KW (unwrapping) test 5 - expected "
+                + Hex.toHexString(textToWrap)
+                + " got " + Hex.toHexString(output));
+        }
+    }
+
+    private void CCMModeTests()
+        throws Exception
+    {
+        //test 1
+        byte[] key = Hex.decode("000102030405060708090a0b0c0d0e0f");
+        byte[] iv = Hex.decode("101112131415161718191a1b1c1d1e1f");
+        byte[] input = Hex.decode("303132333435363738393a3b3c3d3e3f");
+        byte[] authText = Hex.decode("202122232425262728292a2b2c2d2e2f");
+
+        byte[] expectedMac = Hex.decode("26a936173a4dc9160d6e3fda3a974060");
+        byte[] expectedEncrypted = Hex.decode("b91a7b8790bbcfcfe65d04e5538e98e2704454c9dd39adace0b19d03f6aab07e");
+
+        byte[] mac;
+        byte[] encrypted = new byte[expectedEncrypted.length];
+
+        byte[] decrypted = new byte[encrypted.length];
+        byte[] expectedDecrypted = new byte[input.length + expectedMac.length];
+        System.arraycopy(input, 0, expectedDecrypted, 0, input.length);
+        System.arraycopy(expectedMac, 0, expectedDecrypted, input.length, expectedMac.length);
+        int len;
+
+
+        AEADParameters param = new AEADParameters(new KeyParameter(key), 128, iv);
+
+        KCCMBlockCipher dstu7624ccm = new KCCMBlockCipher(new DSTU7624Engine(128));
+
+        dstu7624ccm.init(true, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(input, 0, input.length, encrypted, 0);
+
+
+        dstu7624ccm.doFinal(encrypted, len);
+
+        mac = dstu7624ccm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed CCM mac test 1 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedEncrypted))
+        {
+            fail("Failed CCM encrypt test 1 - expected "
+                + Hex.toHexString(expectedEncrypted)
+                + " got " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624ccm.init(false, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(expectedEncrypted, 0, expectedEncrypted.length, decrypted, 0);
+
+        dstu7624ccm.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, expectedDecrypted))
+        {
+            fail("Failed CCM decrypt/verify mac test 1 - expected "
+                + Hex.toHexString(expectedDecrypted)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 2
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        input = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F");
+        authText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        expectedMac = Hex.decode("9AB831B4B0BF0FDBC36E4B4FD58F0F00");
+        expectedEncrypted = Hex.decode("7EC15C54BB553CB1437BE0EFDD2E810F6058497EBCE4408A08A73FADF3F459D56B0103702D13AB73ACD2EB33A8B5E9CFFF5EB21865A6B499C10C810C4BAEBE809C48AD90A9E12A68380EF1C1B7C83EE1");
+
+        mac = new byte[expectedMac.length];
+        encrypted = new byte[expectedEncrypted.length];
+
+        decrypted = new byte[encrypted.length];
+        expectedDecrypted = new byte[input.length + expectedMac.length];
+        System.arraycopy(input, 0, expectedDecrypted, 0, input.length);
+        System.arraycopy(expectedMac, 0, expectedDecrypted, input.length, expectedMac.length);
+
+
+        param = new AEADParameters(new KeyParameter(key), 128, iv);
+
+        dstu7624ccm = new KCCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624ccm.init(true, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(input, 0, input.length, encrypted, 0);
+
+        dstu7624ccm.doFinal(encrypted, len);
+
+        mac = dstu7624ccm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed CCM mac test 2 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedEncrypted))
+        {
+            fail("Failed CCM encrypt test 2 - expected "
+                + Hex.toHexString(expectedEncrypted)
+                + " got " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624ccm.init(false, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(expectedEncrypted, 0, expectedEncrypted.length, decrypted, 0);
+
+        dstu7624ccm.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, expectedDecrypted))
+        {
+            fail("Failed CCM decrypt/verify mac test 2 - expected "
+                + Hex.toHexString(expectedDecrypted)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 3
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+        input = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+        authText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+
+        expectedMac = Hex.decode("924FA0326824355595C98028E84D86279CEA9135FAB35F22054AE3203E68AE46");
+        expectedEncrypted = Hex.decode("3EBDB4584B5169A26FBEBA0295B4223F58D5D8A031F2950A1D7764FAB97BA058E9E2DAB90FF0C519AA88435155A71B7B53BB100F5D20AFFAC0552F5F2813DEE8DD3653491737B9615A5CCD83DB32F1E479BF227C050325BBBFF60BCA9558D7FE");
+
+        mac = new byte[expectedMac.length];
+        encrypted = new byte[expectedEncrypted.length];
+
+        decrypted = new byte[encrypted.length];
+        expectedDecrypted = new byte[input.length + expectedMac.length];
+        System.arraycopy(input, 0, expectedDecrypted, 0, input.length);
+        System.arraycopy(expectedMac, 0, expectedDecrypted, input.length, expectedMac.length);
+
+
+        param = new AEADParameters(new KeyParameter(key), 256, iv);
+
+        dstu7624ccm = new KCCMBlockCipher(new DSTU7624Engine(256), 6);
+
+        dstu7624ccm.init(true, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(input, 0, input.length, encrypted, 0);
+
+        dstu7624ccm.doFinal(encrypted, len);
+
+        mac = dstu7624ccm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed CCM mac test 3 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedEncrypted))
+        {
+            fail("Failed CCM encrypt test 3 - expected "
+                + Hex.toHexString(expectedEncrypted)
+                + " got " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624ccm.init(false, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(expectedEncrypted, 0, expectedEncrypted.length, decrypted, 0);
+
+        dstu7624ccm.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, expectedDecrypted))
+        {
+            fail("Failed CCM decrypt/verify mac test 3 - expected "
+                + Hex.toHexString(expectedDecrypted)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 4
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+        input = Hex.decode("C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+        authText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        expectedMac = Hex.decode("D4155EC3D888C8D32FE184AC260FD60F567705E1DF362A6F1F9C287156AA96D91BC4C56F9709E72F3D79CF0A9AC8BDC2BA836BE50E823AB50FB1B39080390923");
+        expectedEncrypted = Hex.decode("220642D7277D104788CF97B10210984F506435512F7BF153C5CDABFECC10AFB4A2E2FC51F616AF80FFDD0607FAD4F542B8EF0667717CE3EAAA8FBC303CE76C99BD8F80CE149143C04FC2490272A31B029DDADA82F055FE4ABEF452A7D438B21E59C1D8B3DD4606BAD66A6F36300EF3CE0E5F3BB59F11416E80B7FC5A8E8B057A");
+
+        mac = new byte[expectedMac.length];
+        encrypted = new byte[expectedEncrypted.length];
+
+        decrypted = new byte[encrypted.length];
+        expectedDecrypted = new byte[input.length + expectedMac.length];
+        System.arraycopy(input, 0, expectedDecrypted, 0, input.length);
+        System.arraycopy(expectedMac, 0, expectedDecrypted, input.length, expectedMac.length);
+
+
+        param = new AEADParameters(new KeyParameter(key), 512, iv);
+
+        dstu7624ccm = new KCCMBlockCipher(new DSTU7624Engine(512), 8);
+
+        dstu7624ccm.init(true, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(input, 0, input.length, encrypted, 0);
+
+        dstu7624ccm.doFinal(encrypted, len);
+
+        mac = dstu7624ccm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed CCM mac test 4 - expected "
+                + Hex.toHexString(expectedMac)
+                + " got " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedEncrypted))
+        {
+            fail("Failed CCM encrypt test 4 - expected "
+                + Hex.toHexString(expectedEncrypted)
+                + " got " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624ccm.init(false, param);
+
+        dstu7624ccm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624ccm.processBytes(expectedEncrypted, 0, expectedEncrypted.length, decrypted, 0);
+
+        dstu7624ccm.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, expectedDecrypted))
+        {
+            fail("Failed CCM decrypt/verify mac test 4 - expected "
+                + Hex.toHexString(expectedDecrypted)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        doFinalTest(new KCCMBlockCipher(new DSTU7624Engine(512), 8), key, iv, authText, input, expectedEncrypted);
+    }
+
+    private void XTSModeTests()
+        throws Exception
+    {
+
+        //test 1
+        byte[] key = Hex.decode("000102030405060708090A0B0C0D0E0F");
+        byte[] iv = Hex.decode("101112131415161718191A1B1C1D1E1F");
+        byte[] plainText = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        byte[] output = new byte[plainText.length];
+        byte[] expectedCipherText = Hex.decode("B3E431B3FBAF31108C302669EE7116D1CF518B6D329D30618DF5628E426BDEF1");
+
+        byte[] decrypted = new byte[plainText.length];
+
+
+        int len;
+
+        KXTSBlockCipher dstu7624xts = new KXTSBlockCipher(new DSTU7624Engine(128));
+        ParametersWithIV param = new ParametersWithIV(new KeyParameter(key), iv);
+
+        dstu7624xts.init(true, param);
+        len = dstu7624xts.processBytes(plainText, 0, plainText.length, output, 0);
+
+        dstu7624xts.doFinal(output, len);
+
+        if (!Arrays.areEqual(output, expectedCipherText))
+        {
+            fail("Failed XTS encrypt test 1 - expected "
+                + Hex.toHexString(expectedCipherText)
+                + " got " + Hex.toHexString(output));
+        }
+
+
+        dstu7624xts.init(false, param);
+        len = dstu7624xts.processBytes(expectedCipherText, 0, expectedCipherText.length, decrypted, 0);
+        dstu7624xts.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed XTS decrypt test 1 - expected "
+                + Hex.toHexString(plainText)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 2
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F");
+        plainText = Hex.decode("303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F");
+
+        output = new byte[plainText.length];
+        expectedCipherText = Hex.decode("830AC78A6F629CB4C7D5D156FD84955BD0998CA1E0BC1FF135676BF2A2598FA1");
+
+        decrypted = new byte[plainText.length];
+
+
+        dstu7624xts = new KXTSBlockCipher(new DSTU7624Engine(128));
+        param = new ParametersWithIV(new KeyParameter(key), iv);
+
+        dstu7624xts.init(true, param);
+        len = dstu7624xts.processBytes(plainText, 0, plainText.length, output, 0);
+        dstu7624xts.doFinal(output, len);
+
+        if (!Arrays.areEqual(output, expectedCipherText))
+        {
+            fail("Failed XTS encrypt test 2 - expected "
+                + Hex.toHexString(expectedCipherText)
+                + " got " + Hex.toHexString(output));
+        }
+
+
+        dstu7624xts.init(false, param);
+        len = dstu7624xts.processBytes(expectedCipherText, 0, expectedCipherText.length, decrypted, 0);
+        dstu7624xts.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed XTS decrypt test 2 - expected "
+                + Hex.toHexString(plainText)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+
+        //test 3
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        plainText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F");
+
+        output = new byte[plainText.length];
+        expectedCipherText = Hex.decode("E0E51EAEA6A3134600758EA7F87E88025D8B82897C8DB099B843054C3A51883756913571530BA8FA23003E337627E698674B807E847EC6B2292627736562F9F62B2DE9E6AAC5DF74C09A0C5CF80280174AEC9BDD4E73F7D63EDBC29A6922637A");
+
+        decrypted = new byte[plainText.length];
+
+        dstu7624xts = new KXTSBlockCipher(new DSTU7624Engine(256));
+        param = new ParametersWithIV(new KeyParameter(key), iv);
+
+        dstu7624xts.init(true, param);
+        len = dstu7624xts.processBytes(plainText, 0, plainText.length, output, 0);
+        dstu7624xts.doFinal(output, len);
+
+        if (!Arrays.areEqual(output, expectedCipherText))
+        {
+            fail("Failed XTS encrypt test 3 - expected "
+                + Hex.toHexString(expectedCipherText)
+                + " got " + Hex.toHexString(output));
+        }
+
+        dstu7624xts.init(false, param);
+        len = dstu7624xts.processBytes(expectedCipherText, 0, expectedCipherText.length, decrypted, 0);
+        dstu7624xts.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed XTS decrypt test 3 - expected "
+                + Hex.toHexString(plainText)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 4
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+        plainText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        output = new byte[plainText.length];
+        expectedCipherText = Hex.decode("30663E4686574B343A1898E46973CD37DB9D775D356512EB59E723397F2A333CE2C0E96538781FF48EA1D93BDF88FFF8BB7BC4FB80A609881220C7FE21881C7374F65B232A8F94CD0E3DDC7614830C23CFCE98ADC5113496F9E106E8C8BFF3AB");
+
+        decrypted = new byte[plainText.length];
+
+        dstu7624xts = new KXTSBlockCipher(new DSTU7624Engine(256));
+        param = new ParametersWithIV(new KeyParameter(key), iv);
+
+        dstu7624xts.init(true, param);
+        len = dstu7624xts.processBytes(plainText, 0, plainText.length, output, 0);
+        dstu7624xts.doFinal(output, len);
+
+        if (!Arrays.areEqual(output, expectedCipherText))
+        {
+            fail("Failed XTS encrypt test 4 - expected "
+                + Hex.toHexString(expectedCipherText)
+                + " got " + Hex.toHexString(output));
+        }
+
+
+        dstu7624xts.init(false, param);
+        len = dstu7624xts.processBytes(expectedCipherText, 0, expectedCipherText.length, decrypted, 0);
+        dstu7624xts.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed XTS decrypt test 4 - expected "
+                + Hex.toHexString(plainText)
+                + " got " + Hex.toHexString(decrypted));
+        }
+
+        //test 5
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+        plainText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+
+        output = new byte[plainText.length];
+        expectedCipherText = Hex.decode("5C6250BD2E40AAE27E1E57512CD38E6A51D0C2B04F0D6A50E0CB43358B8C4E8BA361331436C6FFD38D77BBBBF5FEC56A234108A6CC8CB298360943E849E5BD64D26ECA2FA8AEAD070656C3777BA412BCAF3D2F08C26CF86CA8F0921043A15D709AE1112611E22D4396E582CCB661E0F778B6F38561BC338AFD5D1036ED8B322D");
+
+        decrypted = new byte[plainText.length];
+
+        dstu7624xts = new KXTSBlockCipher(new DSTU7624Engine(512));
+        param = new ParametersWithIV(new KeyParameter(key), iv);
+
+        dstu7624xts.init(true, param);
+        len = dstu7624xts.processBytes(plainText, 0, plainText.length, output, 0);
+        dstu7624xts.doFinal(output, len);
+
+        if (!Arrays.areEqual(output, expectedCipherText))
+        {
+            fail("Failed XTS encrypt test 5 - expected "
+                + Hex.toHexString(expectedCipherText)
+                + " got " + Hex.toHexString(output));
+        }
+
+
+        dstu7624xts.init(false, param);
+        len = dstu7624xts.processBytes(expectedCipherText, 0, expectedCipherText.length, decrypted, 0);
+        dstu7624xts.doFinal(decrypted, len);
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed XTS decrypt test 5 - expected "
+                + Hex.toHexString(plainText)
+                + " got " + Hex.toHexString(decrypted));
+        }
+    }
+
+    private void GCMModeTests()
+        throws Exception
+    {
+        //test 1
+        byte[] key = Hex.decode("000102030405060708090A0B0C0D0E0F");
+
+        byte[] iv = Hex.decode("101112131415161718191A1B1C1D1E1F");
+
+        byte[] authText = Hex.decode("202122232425262728292A2B2C2D2E2F");
+
+        byte[] plainText = Hex.decode("303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F");
+
+        byte[] expectedEncrypted = Hex.decode("B91A7B8790BBCFCFE65D04E5538E98E216AC209DA33122FDA596E8928070BE51");
+
+        byte[] expectedMac = Hex.decode("C8310571CD60F9584B45C1B4ECE179AF");
+
+        byte[] expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+        byte[] mac = new byte[expectedMac.length];
+
+        byte[] encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        byte[] decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+        int len;
+
+        AEADParameters parameters = new AEADParameters(new KeyParameter(key), 128, iv);
+
+        KGCMBlockCipher dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(128));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 1 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 1 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+        dstu7624gcm.doFinal(decrypted, len);
+
+
+        mac = dstu7624gcm.getMac();
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 1 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        byte[] tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 1 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        //test 2
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F");
+
+        authText = Hex.decode("303132333435363738393A3B3C3D3E3F");
+
+        plainText = Hex.decode("505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F");
+
+        expectedEncrypted = Hex.decode("FF83F27C6D4EA26101B1986235831406A297940D6C0E695596D612623E0E7CDC");
+
+        expectedMac = Hex.decode("3C474281AFEAE4FD6D61E995258747AB");
+
+        expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+
+        mac = new byte[expectedMac.length];
+
+        encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+        parameters = new AEADParameters(new KeyParameter(key), 128, iv);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(128));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 2 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 2 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+
+        dstu7624gcm.doFinal(decrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 2 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 2 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        //test 3
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        authText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        plainText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F");
+
+        expectedEncrypted = Hex.decode("7EC15C54BB553CB1437BE0EFDD2E810F6058497EBCE4408A08A73FADF3F459D56B0103702D13AB73ACD2EB33A8B5E9CFFF5EB21865A6B499C10C810C4BAEBE80");
+
+        expectedMac = Hex.decode("1D61B0A3018F6B849CBA20AF1DDDA245");
+
+        expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+
+        mac = new byte[expectedMac.length];
+
+        encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+
+        parameters = new AEADParameters(new KeyParameter(key), 128, iv);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 3 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 3 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+
+        dstu7624gcm.doFinal(decrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 3 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 3 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        //test 4
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        iv = Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        authText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        plainText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F");
+
+        expectedEncrypted = Hex.decode("7EC15C54BB553CB1437BE0EFDD2E810F6058497EBCE4408A08A73FADF3F459D56B0103702D13AB73ACD2EB33A8B5E9CFFF5EB21865A6B499C10C810C4BAEBE80");
+
+        expectedMac = Hex.decode("1D61B0A3018F6B849CBA20AF1DDDA245B1B296258AC0352A52D3F372E72224CE");
+
+        expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+
+        mac = new byte[expectedMac.length];
+
+        encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+        parameters = new AEADParameters(new KeyParameter(key), 256, iv);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 4 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 4 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+
+        dstu7624gcm.doFinal(decrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 4 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 4 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        //test 5
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        authText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+
+        plainText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        expectedEncrypted = Hex.decode("3EBDB4584B5169A26FBEBA0295B4223F58D5D8A031F2950A1D7764FAB97BA058E9E2DAB90FF0C519AA88435155A71B7B53BB100F5D20AFFAC0552F5F2813DEE8");
+
+        expectedMac = Hex.decode("8555FD3D9B02C2325ACA3CC9309D6B4B9AFC697D13BBBFF067198D5D86CB9820");
+
+        expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+
+        mac = new byte[expectedMac.length];
+
+        encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+
+        parameters = new AEADParameters(new KeyParameter(key), 256, iv);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 5 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 5 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+
+        dstu7624gcm.doFinal(decrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 5 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 5 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        //test 6
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        iv = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+
+        authText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        plainText = Hex.decode("C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+
+        expectedEncrypted = Hex.decode("220642D7277D104788CF97B10210984F506435512F7BF153C5CDABFECC10AFB4A2E2FC51F616AF80FFDD0607FAD4F542B8EF0667717CE3EAAA8FBC303CE76C99");
+
+        expectedMac = Hex.decode("78A77E5948F5DC05F551486FDBB44898C9AB1BD439D7519841AE31007C09E1B312E5EA5929F952F6A3EEF5CBEAEF262B8EC1884DFCF4BAAF7B5C9291A22489E1");
+
+        expectedOutput = new byte[expectedEncrypted.length + expectedMac.length];
+        System.arraycopy(expectedEncrypted, 0, expectedOutput, 0, expectedEncrypted.length);
+        System.arraycopy(expectedMac, 0, expectedOutput, expectedEncrypted.length, expectedMac.length);
+
+
+        mac = new byte[expectedMac.length];
+
+        encrypted = new byte[expectedEncrypted.length + mac.length];
+
+        decrypted = new byte[plainText.length + mac.length];
+
+        System.arraycopy(expectedMac, 0, decrypted, plainText.length, mac.length);
+
+        parameters = new AEADParameters(new KeyParameter(key), 512, iv);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(512));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(plainText, 0, plainText.length, encrypted, 0);
+
+        dstu7624gcm.doFinal(encrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 6 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        if (!Arrays.areEqual(encrypted, expectedOutput))
+        {
+            fail("Failed GCM/GMAC test 6 - expected encrypted: "
+                + Hex.toHexString(expectedOutput)
+                + " got encrypted: " + Hex.toHexString(encrypted));
+        }
+
+        dstu7624gcm.init(false, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        len = dstu7624gcm.processBytes(expectedOutput, 0, expectedOutput.length, decrypted, 0);
+
+        dstu7624gcm.doFinal(decrypted, len);
+
+        mac = dstu7624gcm.getMac();
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 6 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //remove mac at the end of decrypted data
+        tempDecrypted = new byte[plainText.length];
+        System.arraycopy(decrypted, 0, tempDecrypted, 0, plainText.length);
+        decrypted = tempDecrypted;
+
+        if (!Arrays.areEqual(decrypted, plainText))
+        {
+            fail("Failed GCM/GMAC test 6 - expected decrypted: "
+                + Hex.toHexString(plainText)
+                + " got decrypted: " + Hex.toHexString(decrypted));
+        }
+
+        /* Testing mac producing without encryption */
+        //test 7
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        authText = Hex.decode("303132333435363738393A3B3C3D3E3F");
+
+        expectedMac = Hex.decode("5AE309EE80B583C6523397ADCB5704C4");
+
+        mac = new byte[expectedMac.length];
+
+        parameters = new AEADParameters(new KeyParameter(key), 128, new byte[16]);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(128));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        dstu7624gcm.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 7 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //test 8
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        authText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        expectedMac = Hex.decode("FF48B56F2C26CC484B8F5952D7B3E1FE");
+
+        mac = new byte[expectedMac.length];
+
+        parameters = new AEADParameters(new KeyParameter(key), 128, new byte[16]);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        dstu7624gcm.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 8 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //test 9
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+        authText = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
+
+        expectedMac = Hex.decode("FF48B56F2C26CC484B8F5952D7B3E1FE69577701C50BE96517B33921E44634CD");
+
+        mac = new byte[expectedMac.length];
+
+        parameters = new AEADParameters(new KeyParameter(key), 256, new byte[32]);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        dstu7624gcm.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 9 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //test 10
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        authText = Hex.decode("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F");
+
+        expectedMac = Hex.decode("96F61FA0FDE92883C5041D748F9AE91F3A0A50415BFA1466855340A5714DC01F");
+
+        mac = new byte[expectedMac.length];
+
+        parameters = new AEADParameters(new KeyParameter(key), 256, new byte[32]);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(256));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        dstu7624gcm.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 10 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        //test 11
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        authText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        expectedMac = Hex.decode("897C32E05E776FD988C5171FE70BB72949172E514E3308A871BA5BD898FB6EBD6E3897D2D55697D90D6428216C08052E3A5E7D4626F4DBBF1546CE21637357A3");
+
+        mac = new byte[expectedMac.length];
+
+        parameters = new AEADParameters(new KeyParameter(key), 512, new byte[32]);
+
+        dstu7624gcm = new KGCMBlockCipher(new DSTU7624Engine(512));
+
+        dstu7624gcm.init(true, parameters);
+        dstu7624gcm.processAADBytes(authText, 0, authText.length);
+        dstu7624gcm.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 11 - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+
+        doFinalTest(new KGCMBlockCipher(new DSTU7624Engine(512)), key, new byte[32], authText, null, expectedMac);
+
+        //test 11 - as KGMac
+        key = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F");
+
+        authText = Hex.decode("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF");
+
+        expectedMac = Hex.decode("897C32E05E776FD988C5171FE70BB72949172E514E3308A871BA5BD898FB6EBD6E3897D2D55697D90D6428216C08052E3A5E7D4626F4DBBF1546CE21637357A3");
+
+        mac = new byte[expectedMac.length];
+
+        KGMac dstuGmac = new KGMac(new KGCMBlockCipher(new DSTU7624Engine(512)));
+
+        dstuGmac.init(new ParametersWithIV(new KeyParameter(key), new byte[32]));
+
+        dstuGmac.update(authText, 0, authText.length);
+
+        dstuGmac.doFinal(mac, 0);
+
+        if (!Arrays.areEqual(mac, expectedMac))
+        {
+            fail("Failed GCM/GMAC test 11 (mac) - expected mac: "
+                + Hex.toHexString(expectedMac)
+                + " got mac: " + Hex.toHexString(mac));
+        }
+    }
+
+    private void doFinalTest(AEADBlockCipher cipher, byte[] key, byte[] iv, byte[] authText, byte[] input, byte[] expected)
+        throws Exception
+    {
+        byte[] output = new byte[expected.length];
+
+        AEADParameters parameters = new AEADParameters(new KeyParameter(key), cipher.getUnderlyingCipher().getBlockSize() * 8, iv);
+
+        cipher.init(true, parameters);
+        cipher.processAADBytes(authText, 0, authText.length);
+
+        int off = 0;
+        if (input != null)
+        {
+            off = cipher.processBytes(input, 0, input.length, output, 0);
+        }
+
+        cipher.doFinal(output, off);
+
+        if (!Arrays.areEqual(output, expected))
+        {
+            System.err.println(Hex.toHexString(output));
+            System.err.println(Hex.toHexString(expected));
+            fail("Failed doFinal test - init: " + cipher.getAlgorithmName());
+        }
+
+        cipher.processAADBytes(authText, 0, authText.length);
+
+        off = 0;
+        if (input != null)
+        {
+            off = cipher.processBytes(input, 0, input.length, output, 0);
+        }
+
+        cipher.doFinal(output, off);
+
+        if (!Arrays.areEqual(output, expected))
+        {
+            fail("Failed doFinal test - after: " + cipher.getAlgorithmName());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java
index ab30f4f..738aa58 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java
@@ -3,6 +3,9 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
@@ -11,25 +14,30 @@
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
 import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.engines.IESEngine;
 import org.bouncycastle.crypto.engines.TwofishEngine;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
 import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.kems.ECIESKeyEncapsulation;
 import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.IESParameters;
 import org.bouncycastle.crypto.params.IESWithCipherParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.crypto.parsers.ECIESPublicKeyParser;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -480,6 +488,53 @@
         doEphemeralTest(null, true);
         doEphemeralTest(TWOFISH_IV, false);
         doEphemeralTest(TWOFISH_IV, true);
+
+        doCofactorTest(true, false);
+        doCofactorTest(false, false);
+        doCofactorTest(false, true);
+        doCofactorTest(true, true);
+    }
+
+    private void doCofactorTest(boolean newCofactorMode, boolean oldCofactorMode)
+    {
+
+        /* Create the generator */
+        ECKeyPairGenerator myGenerator = new ECKeyPairGenerator();
+        SecureRandom myRandom = new SecureRandom();
+        String myCurve = "sect571k1"; /* Any curve will do */
+
+        /* Lookup the parameters */
+        X9ECParameters x9 = ECNamedCurveTable.getByName(myCurve);
+
+        /* Initialise the generator */
+        ASN1ObjectIdentifier myOid = ECNamedCurveTable.getOID(myCurve);
+        ECNamedDomainParameters myDomain = new ECNamedDomainParameters(myOid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+        ECKeyGenerationParameters myParams = new ECKeyGenerationParameters(myDomain, myRandom);
+        myGenerator.init(myParams);
+
+        /* Create the key Pair */
+        AsymmetricCipherKeyPair myPair = myGenerator.generateKeyPair();
+
+        /* Determine message length */
+        int myFieldSize = x9.getCurve().getFieldSize();
+        myFieldSize = (myFieldSize + 8 - 1) / 8;
+        int myLen = 2 * myFieldSize + 1;
+        byte[] myMessage = new byte[myLen];
+        int myKeyLen = 256 / 8;
+
+        /* Create agreement */
+        ECIESKeyEncapsulation myAgreement = new ECIESKeyEncapsulation(new KDF2BytesGenerator(new SHA512Digest()), myRandom, newCofactorMode, oldCofactorMode, false);
+        myAgreement.init(myPair.getPublic());
+        KeyParameter mySender = (KeyParameter) myAgreement.encrypt(myMessage, myKeyLen);
+        byte[] mySenderKey = mySender.getKey();
+
+        /* Accept agreement */
+        myAgreement.init(myPair.getPrivate());
+        KeyParameter myReceiver = (KeyParameter) myAgreement.decrypt(myMessage, myKeyLen);
+        byte[] myReceiverKey = myReceiver.getKey();
+
+        /* Check that keys match  */
+        isTrue("new " + newCofactorMode + " old " + oldCofactorMode, Arrays.areEqual(mySenderKey, myReceiverKey));
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java
index 61e4fc8..2e733ce 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java
@@ -7,14 +7,19 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
 import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.bouncycastle.crypto.agreement.ECDHCUnifiedAgreement;
 import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement;
 import org.bouncycastle.crypto.digests.SHA3Digest;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDHUPrivateParameters;
+import org.bouncycastle.crypto.params.ECDHUPublicParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -107,12 +112,8 @@
 
     private void decodeTest()
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
-            new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
-            new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
-
-        ECPoint p = curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")).normalize();
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime192v1");
+        ECPoint p = x9.getG();
 
         if (!p.getAffineXCoord().toBigInteger().equals(new BigInteger("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", 16)))
         {
@@ -124,7 +125,7 @@
             fail("y uncompressed incorrectly");
         }
 
-        byte[] encoding = p.getEncoded();
+        byte[] encoding = p.getEncoded(true);
 
         if (!areEqual(encoding, Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")))
         {
@@ -734,7 +735,7 @@
     /**
      * Basic Key Agreement Test
      */
-    private void testECBasicAgreementTest()
+    private void testECDHBasicAgreement()
     {
         SecureRandom random = new SecureRandom();
 
@@ -796,6 +797,34 @@
         }
     }
 
+    private void testECDHBasicAgreementCofactor()
+    {
+        SecureRandom random = new SecureRandom();
+
+        X9ECParameters x9 = CustomNamedCurves.getByName("curve25519");
+        ECDomainParameters ec = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+        ECKeyPairGenerator kpg = new ECKeyPairGenerator();
+        kpg.init(new ECKeyGenerationParameters(ec, random));
+
+        AsymmetricCipherKeyPair p1 = kpg.generateKeyPair();
+        AsymmetricCipherKeyPair p2 = kpg.generateKeyPair();
+
+        BasicAgreement e1 = new ECDHBasicAgreement();
+        BasicAgreement e2 = new ECDHBasicAgreement();
+
+        e1.init(p1.getPrivate());
+        e2.init(p2.getPrivate());
+
+        BigInteger k1 = e1.calculateAgreement(p2.getPublic());
+        BigInteger k2 = e2.calculateAgreement(p1.getPublic());
+
+        if (!k1.equals(k2))
+        {
+            fail("calculated agreement test failed");
+        }
+    }
+
     private void testECMQVTestVector1()
     {
         // Test Vector from GEC-2
@@ -948,6 +977,120 @@
         return null;
     }
 
+    private void testECUnifiedTestVector1()
+    {
+        // Test Vector from NIST sample data
+
+        X9ECParameters x9 = NISTNamedCurves.getByName("P-224");
+        ECDomainParameters p = new ECDomainParameters(
+            x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+        AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("040784e946ef1fae0cfe127042a310a018ba639d3f6b41f265904f0a7b21b7953efe638b45e6c0c0d34a883a510ce836d143d831daa9ce8a12")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("86d1735ca357890aeec8eccb4859275151356ecee9f1b2effb76b092", 16), p));
+
+        AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("04b33713dc0d56215be26ee6c5e60ad36d12e02e78529ae3ff07873c6b39598bda41c1cf86ee3981f40e102333c15fef214bda034291c1aca6")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("764010b3137ef8d34a3552955ada572a4fa1bb1f5289f27c1bf18344", 16), p));
+
+        AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("0484c22d9575d09e280613c8758467f84869c6eede4f6c1b644517d6a72c4fc5c68fa12b4c259032fc5949c630259948fca38fb3342d9cb0a8")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("e37964e391f5058fb43435352a9913438a1ec10831f755273285230a", 16), p));
+
+        AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("044b917e9ce693b277c8095e535ea81c2dea089446a8c55438eda750fb6170c85b86390481fff2dff94b7dff3e42d35ff623921cb558967b48")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("ab40d67f59ba7265d8ad33ade8f704d13a7ba2298b69172a7cd02515", 16), p));
+
+        byte[] x = calculateUnifiedAgreement(U1, U2, V1, V2);
+
+        if (x == null
+            || !areEqual(Hex.decode("80315a208b1cd6119264e5c03242b7db96379986fdc4c2f06bf88d0655cda75d4dc7e94a8df9f03239d5da9a18d364cebc6c63f01b6f4378"), x))
+        {
+            fail("EC combined Test Vector #1 agreement failed");
+        }
+    }
+
+    private void testECUnifiedTestVector2()
+    {
+        // Test Vector from NIST sample data
+
+        X9ECParameters x9 = NISTNamedCurves.getByName("P-256");
+        ECDomainParameters p = new ECDomainParameters(
+            x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+        AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("047581b35964a983414ebdd56f4ebb1ddcad10881b200666a51ae41306e1ecf1db368468a5e8a65ca10ccea526472c8982db68316c468800e171c11f4ee694fce4")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("2eb7ef76d4936123b6f13035045aedf45c1c7731f35d529d25941926b5bb38bb", 16), p));
+
+        AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("045b1e4cdeb0728333c0a51631b1a75269e4878d10732f4cb94d600483db4bd9ee625c374592c3db7e9f8b4f2c91a0098a158bc37b922e4243bd9cbdefe67d6ab0")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("78acde388a022261767e6b3dd6dd016c53b70a084260ec87d395aec761c082de", 16), p));
+
+        AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("04e4916d616803ff1bd9569f35b7d06f792f19c1fb4e6fa916d686c027a17d8dffd570193d8e101624ac2ea0bcb762d5613f05452670f09af66ef70861fb528868")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("9c85898640a1b1de8ce7f557492dc1460530b9e17afaaf742eb953bb644e9c5a", 16), p));
+
+        AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+            new ECPublicKeyParameters(
+                p.getCurve().decodePoint(Hex.decode("04d1cd23c29d0fc865c316d44a1fd5adb6605ee47c9ddfec3a9b0a5e532d52704e74ff5d149aeb50856fefb38d5907b6dbb580fe6dc166bcfcbee4eb376d77e95c")), p),
+            new ECPrivateKeyParameters(
+                new BigInteger("d6e11d5d3b85b201b8f4c12dadfad3000e267961a806a0658a2b859d44389599", 16), p));
+
+        byte[] x = calculateUnifiedAgreement(U1, U2, V1, V2);
+
+        if (x == null
+            || !areEqual(Hex.decode("02886e53998b06d92f04e4579cbfa5f35c96334d3890298264e7f956da70966af07bf1b3abbaa8d76fbaf435508bdabbbbbdae1a191d91480ed88374c3552233"), x))
+        {
+            fail("EC combined Test Vector #2 agreement failed");
+        }
+    }
+
+    private byte[] calculateUnifiedAgreement(
+        AsymmetricCipherKeyPair U1,
+        AsymmetricCipherKeyPair U2,
+        AsymmetricCipherKeyPair V1,
+        AsymmetricCipherKeyPair V2)
+    {
+        ECDHCUnifiedAgreement u = new ECDHCUnifiedAgreement();
+        u.init(new ECDHUPrivateParameters(
+            (ECPrivateKeyParameters)U1.getPrivate(),
+            (ECPrivateKeyParameters)U2.getPrivate(),
+            (ECPublicKeyParameters)U2.getPublic()));
+        byte[] ux = u.calculateAgreement(new ECDHUPublicParameters(
+            (ECPublicKeyParameters)V1.getPublic(),
+            (ECPublicKeyParameters)V2.getPublic()));
+
+        ECDHCUnifiedAgreement v = new ECDHCUnifiedAgreement();
+        v.init(new ECDHUPrivateParameters(
+            (ECPrivateKeyParameters)V1.getPrivate(),
+            (ECPrivateKeyParameters)V2.getPrivate(),
+            (ECPublicKeyParameters)V2.getPublic()));
+        byte[] vx = v.calculateAgreement(new ECDHUPublicParameters(
+            (ECPublicKeyParameters)U1.getPublic(),
+            (ECPublicKeyParameters)U2.getPublic()));
+
+        if (areEqual(ux, vx))
+        {
+            return ux;
+        }
+
+        return null;
+    }
+
     public String getName()
     {
         return "EC";
@@ -961,7 +1104,8 @@
         testECDSA191bitBinary();
         testECDSA239bitBinary();
         testECDSAKeyGenTest();
-        testECBasicAgreementTest();
+        testECDHBasicAgreement();
+        testECDHBasicAgreementCofactor();
 
         testECDSAP224sha224();
         testECDSAP224OneByteOver();
@@ -978,6 +1122,9 @@
         testECMQVTestVector1();
         testECMQVTestVector2();
         testECMQVRandom();
+
+        testECUnifiedTestVector1();
+        testECUnifiedTestVector2();
     }
 
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed25519Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed25519Test.java
new file mode 100644
index 0000000..c040d9c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed25519Test.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+import org.bouncycastle.crypto.signers.Ed25519ctxSigner;
+import org.bouncycastle.crypto.signers.Ed25519phSigner;
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Ed25519Test
+    extends SimpleTest
+{
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+    public String getName()
+    {
+        return "Ed25519";
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new Ed25519Test());
+    }
+
+    public void performTest() throws Exception
+    {
+        for (int i = 0; i < 10; ++i)
+        {
+            testConsistency(Ed25519.Algorithm.Ed25519, null);
+
+            byte[] context = randomContext(RANDOM.nextInt() & 255);
+            testConsistency(Ed25519.Algorithm.Ed25519ctx, context);
+            testConsistency(Ed25519.Algorithm.Ed25519ph, context);
+        }
+
+        basicSigTest();
+    }
+
+    private void basicSigTest()
+        throws Exception
+    {
+        Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(
+            Hex.decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"), 0);
+        Ed25519PublicKeyParameters publicKey = new Ed25519PublicKeyParameters(
+            Hex.decode("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"), 0);
+
+        byte[] sig = Hex.decode("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
+
+        Signer signer = new Ed25519Signer();
+
+        signer.init(true, privateKey);
+
+        areEqual(sig, signer.generateSignature());
+
+        signer.init(false, publicKey);
+
+        isTrue(signer.verifySignature(sig));
+    }
+
+    private Signer createSigner(int algorithm, byte[] context)
+    {
+        switch (algorithm)
+        {
+        case Ed25519.Algorithm.Ed25519:
+            return new Ed25519Signer();
+        case Ed25519.Algorithm.Ed25519ctx:
+            return new Ed25519ctxSigner(context);
+        case Ed25519.Algorithm.Ed25519ph:
+            return new Ed25519phSigner(context);
+        default:
+            throw new IllegalArgumentException("algorithm");
+        }
+    }
+
+    private byte[] randomContext(int length)
+    {
+        byte[] context = new byte[length];
+        RANDOM.nextBytes(context);
+        return context;
+    }
+
+    private void testConsistency(int algorithm, byte[] context) throws Exception
+    {
+        Ed25519KeyPairGenerator kpg = new Ed25519KeyPairGenerator();
+        kpg.init(new Ed25519KeyGenerationParameters(RANDOM));
+
+        AsymmetricCipherKeyPair kp = kpg.generateKeyPair();
+        Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters)kp.getPrivate();
+        Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters)kp.getPublic();
+
+        byte[] msg = new byte[RANDOM.nextInt() & 255];
+        RANDOM.nextBytes(msg);
+
+        Signer signer = createSigner(algorithm, context);
+        signer.init(true, privateKey);
+        signer.update(msg, 0, msg.length);
+        byte[] signature = signer.generateSignature();
+
+        Signer verifier = createSigner(algorithm, context);
+        verifier.init(false, publicKey);
+        verifier.update(msg, 0, msg.length);
+        boolean shouldVerify = verifier.verifySignature(signature);
+
+        if (!shouldVerify)
+        {
+            fail("Ed25519(" + algorithm + ") signature failed to verify");
+        }
+
+        signature[(RANDOM.nextInt() >>> 1) % signature.length] ^= 1 << (RANDOM.nextInt() & 7);
+
+        verifier.init(false, publicKey);
+        verifier.update(msg, 0, msg.length);
+        boolean shouldNotVerify = verifier.verifySignature(signature);
+
+        if (shouldNotVerify)
+        {
+            fail("Ed25519(" + algorithm + ") bad signature incorrectly verified");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed448Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed448Test.java
new file mode 100644
index 0000000..123b373
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Ed448Test.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.crypto.signers.Ed448Signer;
+import org.bouncycastle.crypto.signers.Ed448phSigner;
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Ed448Test
+    extends SimpleTest
+{
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+    public String getName()
+    {
+        return "Ed448";
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new Ed448Test());
+    }
+
+    public void performTest() throws Exception
+    {
+        basicSigTest();
+
+        for (int i = 0; i < 10; ++i)
+        {
+            byte[] context = randomContext(RANDOM.nextInt() & 255);
+            testConsistency(Ed448.Algorithm.Ed448, context);
+            testConsistency(Ed448.Algorithm.Ed448ph, context);
+        }
+    }
+
+    private void basicSigTest()
+        throws Exception
+    {
+        Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(
+            Hex.decode(
+                "6c82a562cb808d10d632be89c8513ebf" +
+                "6c929f34ddfa8c9f63c9960ef6e348a3" +
+                "528c8a3fcc2f044e39a3fc5b94492f8f" +
+                "032e7549a20098f95b"), 0);
+        Ed448PublicKeyParameters publicKey = new Ed448PublicKeyParameters(
+            Hex.decode("5fd7449b59b461fd2ce787ec616ad46a" +
+                "1da1342485a70e1f8a0ea75d80e96778" +
+                "edf124769b46c7061bd6783df1e50f6c" +
+                "d1fa1abeafe8256180"), 0);
+
+        byte[] sig = Hex.decode("533a37f6bbe457251f023c0d88f976ae" +
+            "2dfb504a843e34d2074fd823d41a591f" +
+            "2b233f034f628281f2fd7a22ddd47d78" +
+            "28c59bd0a21bfd3980ff0d2028d4b18a" +
+            "9df63e006c5d1c2d345b925d8dc00b41" +
+            "04852db99ac5c7cdda8530a113a0f4db" +
+            "b61149f05a7363268c71d95808ff2e65" +
+            "2600");
+
+        Signer signer = new Ed448Signer(new byte[0]);
+
+        signer.init(true, privateKey);
+
+        areEqual(sig, signer.generateSignature());
+
+        signer.init(false, publicKey);
+
+        isTrue(signer.verifySignature(sig));
+    }
+    
+    private Signer createSigner(int algorithm, byte[] context)
+    {
+        switch (algorithm)
+        {
+        case Ed448.Algorithm.Ed448:
+            return new Ed448Signer(context);
+        case Ed448.Algorithm.Ed448ph:
+            return new Ed448phSigner(context);
+        default:
+            throw new IllegalArgumentException("algorithm");
+        }
+    }
+
+    private byte[] randomContext(int length)
+    {
+        byte[] context = new byte[length];
+        RANDOM.nextBytes(context);
+        return context;
+    }
+
+    private void testConsistency(int algorithm, byte[] context) throws Exception
+    {
+        Ed448KeyPairGenerator kpg = new Ed448KeyPairGenerator();
+        kpg.init(new Ed448KeyGenerationParameters(RANDOM));
+
+        AsymmetricCipherKeyPair kp = kpg.generateKeyPair();
+        Ed448PrivateKeyParameters privateKey = (Ed448PrivateKeyParameters)kp.getPrivate();
+        Ed448PublicKeyParameters publicKey = (Ed448PublicKeyParameters)kp.getPublic();
+
+        byte[] msg = new byte[RANDOM.nextInt() & 255];
+        RANDOM.nextBytes(msg);
+
+        Signer signer = createSigner(algorithm, context);
+        signer.init(true, privateKey);
+        signer.update(msg, 0, msg.length);
+        byte[] signature = signer.generateSignature();
+
+        Signer verifier = createSigner(algorithm, context);
+        verifier.init(false, publicKey);
+        verifier.update(msg, 0, msg.length);
+        boolean shouldVerify = verifier.verifySignature(signature);
+
+        if (!shouldVerify)
+        {
+            fail("Ed448(" + algorithm + ") signature failed to verify");
+        }
+
+        signature[(RANDOM.nextInt() >>> 1) % signature.length] ^= 1 << (RANDOM.nextInt() & 7);
+
+        verifier.init(false, publicKey);
+        verifier.update(msg, 0, msg.length);
+        boolean shouldNotVerify = verifier.verifySignature(signature);
+
+        if (shouldNotVerify)
+        {
+            fail("Ed448(" + algorithm + ") bad signature incorrectly verified");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java
index efac338..23e3692 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java
@@ -3,21 +3,22 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 
-import junit.framework.TestCase;
 import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
 import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
 import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
-import org.bouncycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 import org.bouncycastle.util.encoders.Hex;
 
+import junit.framework.TestCase;
+
 public class GCMReorderTest
     extends TestCase
 {
     private static final byte[] H;
     private static final SecureRandom random = new SecureRandom(); 
-    private static final GCMMultiplier mul = new Tables64kGCMMultiplier();
+    private static final GCMMultiplier mul = new Tables4kGCMMultiplier();
     private static final GCMExponentiator exp = new Tables1kGCMExponentiator();
     private static final byte[] EMPTY = new byte[0];
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java
index 3f09fc0..f13456c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java
@@ -9,6 +9,7 @@
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.crypto.modes.gcm.BasicGCMMultiplier;
 import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
 import org.bouncycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
 import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
 import org.bouncycastle.crypto.params.AEADParameters;
@@ -402,6 +403,7 @@
         runTestCase(null, null, testName, K, IV, A, P, C, T);
 
         runTestCase(new BasicGCMMultiplier(), new BasicGCMMultiplier(), testName, K, IV, A, P, C, T);
+        runTestCase(new Tables4kGCMMultiplier(), new Tables4kGCMMultiplier(), testName, K, IV, A, P, C, T);
         runTestCase(new Tables8kGCMMultiplier(), new Tables8kGCMMultiplier(), testName, K, IV, A, P, C, T);
         runTestCase(new Tables64kGCMMultiplier(), new Tables64kGCMMultiplier(), testName, K, IV, A, P, C, T);
     }
@@ -540,6 +542,7 @@
         srng.setSeed(Times.nanoTime());
         randomTests(srng, null);
         randomTests(srng, new BasicGCMMultiplier());
+        randomTests(srng, new Tables4kGCMMultiplier());
         randomTests(srng, new Tables8kGCMMultiplier());
         randomTests(srng, new Tables64kGCMMultiplier());
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java
index 58c9e35..d1cc6e2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java
@@ -3,13 +3,30 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
 import org.bouncycastle.crypto.generators.GOST3410ParametersGenerator;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
 import org.bouncycastle.crypto.params.GOST3410Parameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.GOST3410Signer;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.FixedSecureRandom;
@@ -20,17 +37,893 @@
 import org.bouncycastle.util.test.TestResult;
 
 public class GOST3410Test
-        implements Test
+    implements Test
 {
     byte[] hashmessage = Hex.decode("3042453136414534424341374533364339313734453431443642453241453435");
-    
+
     private byte[] zeroTwo(int length)
     {
         byte[] data = new byte[length];
         data[data.length - 1] = 0x02;
         return data;
     }
-    
+
+
+    Test tests[] =
+        {
+            new GOST3410EncodingDecoding(),
+            new GOST3410_TEST1_512(),
+            new GOST3410_TEST2_512(),
+            new GOST3410_TEST1_1024(),
+            new GOST3410_TEST2_1024(),
+            new GOST3410_AParam(),
+            new GOST3410_BParam(),
+            new GOST3410_CParam(),
+            new GOST3410_DParam(),
+            new GOST3410_AExParam(),
+            new GOST3410_BExParam(),
+            new GOST3410_CExParam()
+        };
+
+    public static void main(
+        String[] args)
+    {
+        GOST3410Test test = new GOST3410Test();
+        TestResult result = test.perform();
+
+        System.out.println(result);
+    }
+
+    public TestResult perform()
+    {
+        for (int i = 0; i != tests.length; i++)
+        {
+            TestResult result = tests[i].perform();
+
+            if (!result.isSuccessful())
+            {
+                return result;
+            }
+        }
+
+        return new SimpleTestResult(true, "GOST3410: Okay");
+    }
+
+    private class GOST3410EncodingDecoding
+        implements Test
+    {
+
+        public String getName()
+        {
+            return "GOST3410ParameterEncodeDecode";
+        }
+
+
+        private SimpleTestResult encodeRecodePrivateKeyGost2006()
+        {
+            try
+            {
+                ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID("GostR3410-2001-CryptoPro-A");
+                ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+                ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, CryptoProObjectIdentifiers.gostR3411);
+                ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+                ECKeyPairGenerator engine = new ECKeyPairGenerator();
+                engine.init(params);
+                AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+                ECPrivateKeyParameters generatedKeyParameters = (ECPrivateKeyParameters)pair.getPrivate();
+                ECPrivateKeyParameters keyParameters = generatedKeyParameters;
+
+
+                //
+                // Continuously encode/decode the key and check for loss of information.
+                //
+
+
+                for (int t = 0; t < 3; t++)
+                {
+                    PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(keyParameters);
+                    keyParameters = (ECPrivateKeyParameters)PrivateKeyFactory.createKey(info);
+
+                    { // Specifically cast and test gost parameters.
+                        ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                        ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.getParameters();
+
+                        boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                            safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                            safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                        if (!ok)
+                        {
+                            return new SimpleTestResult(false, "GOST parameters does not match");
+                        }
+
+                    }
+
+                    if (keyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                    {
+                        return new SimpleTestResult(false, "isPrivate does not match");
+                    }
+
+                    if (!keyParameters.getD().equals(generatedKeyParameters.getD()))
+                    {
+                        return new SimpleTestResult(false, "D does not match");
+                    }
+
+                    if (!((ECGOST3410Parameters)keyParameters.getParameters()).getName().equals(
+                        ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                    {
+                        return new SimpleTestResult(false, "Name does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                    {
+                        return new SimpleTestResult(false, "Curve does not match");
+                    }
+
+                    if (!Arrays.areEqual(
+                        keyParameters.getParameters().getG().getEncoded(true),
+                        generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "G does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                    {
+                        return new SimpleTestResult(false, "H does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                    {
+                        return new SimpleTestResult(false, "Hinv does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                    {
+                        return new SimpleTestResult(false, "N does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                    {
+                        return new SimpleTestResult(false, "Seed does not match");
+                    }
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+            return new SimpleTestResult(true, null);
+        }
+
+
+        public SimpleTestResult encodeRecodePublicKeyGost2006()
+        {
+            ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID("GostR3410-2001-CryptoPro-A");
+            ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+            ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, CryptoProObjectIdentifiers.gostR3411);
+            ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+            ECKeyPairGenerator engine = new ECKeyPairGenerator();
+            engine.init(params);
+            AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+            ECPublicKeyParameters generatedKeyParameters = (ECPublicKeyParameters)pair.getPublic();
+            ECPublicKeyParameters keyParameters = generatedKeyParameters;
+
+            try
+            {
+                for (int t = 0; t < 3; t++)
+                {
+
+                    SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParameters);
+                    keyParameters = (ECPublicKeyParameters)PublicKeyFactory.createKey(info);
+
+                    { // Specifically cast and test gost parameters.
+                        ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                        ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.getParameters();
+
+
+                        boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                            safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                            safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                        if (!ok)
+                        {
+                            return new SimpleTestResult(false, "GOST parameters does not match");
+                        }
+
+                    }
+
+                    if (!((ECGOST3410Parameters)keyParameters.getParameters()).getName().equals(
+                        ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                    {
+                        return new SimpleTestResult(false, "Name does not match");
+                    }
+
+
+                    if (keyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                    {
+                        return new SimpleTestResult(false, "isPrivate does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getQ().getEncoded(true), generatedKeyParameters.getQ().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "Q does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                    {
+                        return new SimpleTestResult(false, "Curve does not match");
+                    }
+
+                    if (!Arrays.areEqual(
+                        keyParameters.getParameters().getG().getEncoded(true),
+                        generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "G does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                    {
+                        return new SimpleTestResult(false, "H does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                    {
+                        return new SimpleTestResult(false, "Hinv does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                    {
+                        return new SimpleTestResult(false, "N does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                    {
+                        return new SimpleTestResult(false, "Seed does not match");
+                    }
+                }
+                return new SimpleTestResult(true, null);
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+
+        }
+
+
+        public SimpleTestResult encodeRecodePublicKey()
+        {
+
+            ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID("Tc26-Gost-3410-12-512-paramSetA");
+            ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+            ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+            ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+            ECKeyPairGenerator engine = new ECKeyPairGenerator();
+            engine.init(params);
+            AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+            ECPublicKeyParameters generatedKeyParameters = (ECPublicKeyParameters)pair.getPublic();
+            ECPublicKeyParameters keyParameters = generatedKeyParameters;
+
+
+            //
+            // Continuously encode/decode the key and check for loss of information.
+            //
+            try
+            {
+                for (int t = 0; t < 3; t++)
+                {
+
+                    SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParameters);
+                    keyParameters = (ECPublicKeyParameters)PublicKeyFactory.createKey(info);
+
+                    { // Specifically cast and test gost parameters.
+                        ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                        ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.getParameters();
+
+
+                        boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                            safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                            safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                        if (!ok)
+                        {
+                            return new SimpleTestResult(false, "GOST parameters does not match");
+                        }
+
+                    }
+
+                    if (!((ECGOST3410Parameters)keyParameters.getParameters()).getName().equals(
+                        ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                    {
+                        return new SimpleTestResult(false, "Name does not match");
+                    }
+
+
+                    if (keyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                    {
+                        return new SimpleTestResult(false, "isPrivate does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getQ().getEncoded(true), generatedKeyParameters.getQ().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "Q does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                    {
+                        return new SimpleTestResult(false, "Curve does not match");
+                    }
+
+                    if (!Arrays.areEqual(
+                        keyParameters.getParameters().getG().getEncoded(true),
+                        generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "G does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                    {
+                        return new SimpleTestResult(false, "H does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                    {
+                        return new SimpleTestResult(false, "Hinv does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                    {
+                        return new SimpleTestResult(false, "N does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                    {
+                        return new SimpleTestResult(false, "Seed does not match");
+                    }
+                }
+                return new SimpleTestResult(true, null);
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+
+        }
+
+
+        private SimpleTestResult encodeRecodePrivateKey()
+        {
+            try
+            {
+                ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID("Tc26-Gost-3410-12-512-paramSetA");
+                ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+                ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+                ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+                ECKeyPairGenerator engine = new ECKeyPairGenerator();
+                engine.init(params);
+                AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+                ECPrivateKeyParameters generatedKeyParameters = (ECPrivateKeyParameters)pair.getPrivate();
+                ECPrivateKeyParameters keyParameters = generatedKeyParameters;
+
+
+                //
+                // Continuously encode/decode the key and check for loss of information.
+                //
+
+
+                for (int t = 0; t < 3; t++)
+                {
+                    PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(keyParameters);
+                    keyParameters = (ECPrivateKeyParameters)PrivateKeyFactory.createKey(info);
+
+                    { // Specifically cast and test gost parameters.
+                        ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                        ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.getParameters();
+
+                        boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                            safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                            safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                        if (!ok)
+                        {
+                            return new SimpleTestResult(false, "GOST parameters does not match");
+                        }
+
+                    }
+
+                    if (keyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                    {
+                        return new SimpleTestResult(false, "isPrivate does not match");
+                    }
+
+                    if (!keyParameters.getD().equals(generatedKeyParameters.getD()))
+                    {
+                        return new SimpleTestResult(false, "D does not match");
+                    }
+
+                    if (!((ECGOST3410Parameters)keyParameters.getParameters()).getName().equals(
+                        ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                    {
+                        return new SimpleTestResult(false, "Name does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                    {
+                        return new SimpleTestResult(false, "Curve does not match");
+                    }
+
+                    if (!Arrays.areEqual(
+                        keyParameters.getParameters().getG().getEncoded(true),
+                        generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                    {
+                        return new SimpleTestResult(false, "G does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                    {
+                        return new SimpleTestResult(false, "H does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                    {
+                        return new SimpleTestResult(false, "Hinv does not match");
+                    }
+
+                    if (!keyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                    {
+                        return new SimpleTestResult(false, "N does not match");
+                    }
+
+                    if (!Arrays.areEqual(keyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                    {
+                        return new SimpleTestResult(false, "Seed does not match");
+                    }
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+            return new SimpleTestResult(true, null);
+        }
+
+
+        private SimpleTestResult decodeJCEPublic()
+        {
+            byte[] pub256 = Hex.decode("3068302106082a85030701010101301506092a850307010201010106082a850307010102020343000440292335c87d892510c35a033819a13e2b0dc606d911676af2bad8872d74a4b7bae6c729e98ace04c3dee626343f794731e1489edb7bc26f1c8c56e1448c96501a");
+
+            try
+            {
+                ECPublicKeyParameters pkInfo = (ECPublicKeyParameters)PublicKeyFactory.createKey(pub256);
+
+                if (pkInfo.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate should be false");
+                }
+
+                if (
+                    !Arrays.areEqual(
+                        pkInfo.getQ().getEncoded(true),
+                        Hex.decode("02bab7a4742d87d8baf26a6711d906c60d2b3ea11938035ac31025897dc8352329")))
+                {
+                    return new SimpleTestResult(false, "Q does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getPublicKeyParamSet().toString().equals("1.2.643.7.1.2.1.1.1"))
+                {
+                    return new SimpleTestResult(false, "PublicKeyParamSet does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getDigestParamSet().toString().equals("1.2.643.7.1.1.2.2"))
+                {
+                    return new SimpleTestResult(false, "DigestParamSet does not match");
+                }
+
+                if (((ECGOST3410Parameters)pkInfo.getParameters()).getEncryptionParamSet() != null)
+                {
+                    return new SimpleTestResult(false, "EncryptionParamSet is not null");
+                }
+
+
+                byte[] pub512 = Hex.decode("3081aa302106082a85030701010102301506092a850307010201020106082a850307010102030381840004818043ccc22692ee8a1870c7c9de0566d7e3a494cf0e3c80f9e8852a3d1ec10d2a829d357253e0864aee2eaacd5e2d327578dee771f62f24decfd6358e06199efe540e7912db43c4c80fe0fd31f7f67a862f9d44fd0075cfee6e3d638c7520063d26311ef962547e8129fb8c5b194e129370cd30313884b4a60872254a10772fe595");
+
+                pkInfo = (ECPublicKeyParameters)PublicKeyFactory.createKey(pub512);
+
+                if (pkInfo.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate should be true");
+                }
+
+                if (
+                    !Arrays.areEqual(
+                        pkInfo.getQ().getEncoded(true),
+                        Hex.decode("0254fe9e19068e35d6cfde242ff671e7de7875322d5ecdaa2eee4a86e05372359d822a0dc11e3d2a85e8f9803c0ecf94a4e3d76605dec9c770188aee9226c2cc43")))
+                {
+                    return new SimpleTestResult(false, "Q does not match");
+                }
+
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getPublicKeyParamSet().toString().equals("1.2.643.7.1.2.1.2.1"))
+                {
+                    return new SimpleTestResult(false, "PublicKeyParamSet does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getDigestParamSet().toString().equals("1.2.643.7.1.1.2.3"))
+                {
+                    return new SimpleTestResult(false, "DigestParamSet does not match");
+                }
+
+                if (((ECGOST3410Parameters)pkInfo.getParameters()).getEncryptionParamSet() != null)
+                {
+                    return new SimpleTestResult(false, "EncryptionParamSet is not null");
+                }
+
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+            return new SimpleTestResult(true, null);
+        }
+
+
+        private SimpleTestResult decodeJCEPrivate()
+        {
+            byte[] priv256 = Hex.decode("304a020100302106082a85030701010101301506092a850307010201010106082a8503070101020204220420fe75ba328d5439ed4859e6dc7e6ca2e9aab0818f094eddeb0d57d1c16a90762b");
+
+            try
+            {
+                ECPrivateKeyParameters pkInfo = (ECPrivateKeyParameters)PrivateKeyFactory.createKey(priv256);
+
+                if (!pkInfo.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate should be true");
+                }
+
+                if (
+                    !Arrays.areEqual(
+                        Hex.decode("2b76906ac1d1570debdd4e098f81b0aae9a26c7edce65948ed39548d32ba75fe"),
+                        pkInfo.getD().toByteArray()))
+                {
+                    return new SimpleTestResult(false, "D does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getPublicKeyParamSet().toString().equals("1.2.643.7.1.2.1.1.1"))
+                {
+                    return new SimpleTestResult(false, "PublicKeyParamSet does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getDigestParamSet().toString().equals("1.2.643.7.1.1.2.2"))
+                {
+                    return new SimpleTestResult(false, "DigestParamSet does not match");
+                }
+
+                if (((ECGOST3410Parameters)pkInfo.getParameters()).getEncryptionParamSet() != null)
+                {
+                    return new SimpleTestResult(false, "EncryptionParamSet is not null");
+                }
+
+
+                byte[] priv512 = Hex.decode("306a020100302106082a85030701010102301506092a850307010201020106082a85030701010203044204402fc35576152f6e873236608b592b4b98d0793bf5184f8dc4a99512be703716991a96061ef46aceeae5319b5c69e6fcbfa7e339207878597ce50f9b7cbf857ff1");
+
+                pkInfo = (ECPrivateKeyParameters)PrivateKeyFactory.createKey(priv512);
+
+                if (!pkInfo.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate should be true");
+                }
+
+                if (
+                    !Arrays.areEqual(
+                        Hex.decode("00f17f85bf7c9b0fe57c5978782039e3a7bffce6695c9b31e5eace6af41e06961a99163770be1295a9c48d4f18f53b79d0984b2b598b603632876e2f157655c32f"),
+                        pkInfo.getD().toByteArray()))
+                {
+                    return new SimpleTestResult(false, "D does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getPublicKeyParamSet().toString().equals("1.2.643.7.1.2.1.2.1"))
+                {
+                    return new SimpleTestResult(false, "PublicKeyParamSet does not match");
+                }
+
+                if (!((ECGOST3410Parameters)pkInfo.getParameters()).getDigestParamSet().toString().equals("1.2.643.7.1.1.2.3"))
+                {
+                    return new SimpleTestResult(false, "DigestParamSet does not match");
+                }
+
+                if (((ECGOST3410Parameters)pkInfo.getParameters()).getEncryptionParamSet() != null)
+                {
+                    return new SimpleTestResult(false, "EncryptionParamSet is not null");
+                }
+
+            }
+            catch (Exception ex)
+            {
+                return new SimpleTestResult(false, ex.getMessage(), ex);
+            }
+
+            return new SimpleTestResult(true, null);
+        }
+
+
+        private SimpleTestResult encodeDecodePrivateLW(String oidStr, ASN1ObjectIdentifier digest)
+        {
+            try
+            {
+                ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID(oidStr);
+                ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+                ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, digest);
+                ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+                ECKeyPairGenerator engine = new ECKeyPairGenerator();
+                engine.init(params);
+                AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+                ECPrivateKeyParameters generatedKeyParameters = (ECPrivateKeyParameters)pair.getPrivate();
+
+                PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(generatedKeyParameters);
+
+                ECPrivateKeyParameters recoveredKeyParameters = (ECPrivateKeyParameters)PrivateKeyFactory.createKey(info);
+
+                { // Specifically cast and test gost parameters.
+                    ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                    ECGOST3410Parameters rParam = (ECGOST3410Parameters)recoveredKeyParameters.getParameters();
+
+                    boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                        safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                        safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                    if (!ok)
+                    {
+                        return new SimpleTestResult(false, "GOST parameters does not match");
+                    }
+
+                }
+
+
+                if (recoveredKeyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate does not match");
+                }
+
+                if (!((ECGOST3410Parameters)recoveredKeyParameters.getParameters()).getName().equals(
+                    ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                {
+                    return new SimpleTestResult(false, "Name does not match");
+                }
+
+
+                if (!recoveredKeyParameters.getD().equals(generatedKeyParameters.getD()))
+                {
+                    return new SimpleTestResult(false, "D does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                {
+                    return new SimpleTestResult(false, "Curve does not match");
+                }
+
+                if (!Arrays.areEqual(
+                    recoveredKeyParameters.getParameters().getG().getEncoded(true),
+                    generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                {
+                    return new SimpleTestResult(false, "G does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                {
+                    return new SimpleTestResult(false, "H does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                {
+                    return new SimpleTestResult(false, "Hinv does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                {
+                    return new SimpleTestResult(false, "N does not match");
+                }
+
+                if (!Arrays.areEqual(recoveredKeyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                {
+                    return new SimpleTestResult(false, "Seed does not match");
+                }
+
+                return new SimpleTestResult(true, null);
+            }
+            catch (Exception t)
+            {
+                // Any exception is bad.
+                return new SimpleTestResult(false, t.getMessage(), t);
+            }
+        }
+
+
+        private SimpleTestResult encodeDecodePublicLW(String oidStr, ASN1ObjectIdentifier digest)
+        {
+            try
+            {
+                ASN1ObjectIdentifier oid = ECGOST3410NamedCurves.getOID(oidStr);
+                ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGOST3410NamedCurves.getByOID(oid));
+                ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, digest);
+                ECKeyGenerationParameters params = new ECKeyGenerationParameters(gostParams, new SecureRandom());
+                ECKeyPairGenerator engine = new ECKeyPairGenerator();
+                engine.init(params);
+                AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+
+                ECPublicKeyParameters generatedKeyParameters = (ECPublicKeyParameters)pair.getPublic();
+
+                SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(generatedKeyParameters);
+
+                ECPublicKeyParameters recoveredKeyParameters = (ECPublicKeyParameters)PublicKeyFactory.createKey(info);
+
+                { // Specifically cast and test gost parameters.
+                    ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.getParameters();
+                    ECGOST3410Parameters rParam = (ECGOST3410Parameters)recoveredKeyParameters.getParameters();
+
+
+                    boolean ok = safeEquals(gParam.getDigestParamSet(), rParam.getDigestParamSet()) &&
+                        safeEquals(gParam.getEncryptionParamSet(), rParam.getEncryptionParamSet()) &&
+                        safeEquals(gParam.getPublicKeyParamSet(), rParam.getPublicKeyParamSet());
+
+                    if (!ok)
+                    {
+                        return new SimpleTestResult(false, "GOST parameters does not match");
+                    }
+
+                }
+
+                if (!((ECGOST3410Parameters)recoveredKeyParameters.getParameters()).getName().equals(
+                    ((ECGOST3410Parameters)generatedKeyParameters.getParameters()).getName()))
+                {
+                    return new SimpleTestResult(false, "Name does not match");
+                }
+
+
+                if (recoveredKeyParameters.isPrivate() != generatedKeyParameters.isPrivate())
+                {
+                    return new SimpleTestResult(false, "isPrivate does not match");
+                }
+
+                if (!Arrays.areEqual(recoveredKeyParameters.getQ().getEncoded(true), generatedKeyParameters.getQ().getEncoded(true)))
+                {
+                    return new SimpleTestResult(false, "Q does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getCurve().equals(generatedKeyParameters.getParameters().getCurve()))
+                {
+                    return new SimpleTestResult(false, "Curve does not match");
+                }
+
+                if (!Arrays.areEqual(
+                    recoveredKeyParameters.getParameters().getG().getEncoded(true),
+                    generatedKeyParameters.getParameters().getG().getEncoded(true)))
+                {
+                    return new SimpleTestResult(false, "G does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getH().equals(generatedKeyParameters.getParameters().getH()))
+                {
+                    return new SimpleTestResult(false, "H does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getHInv().equals(generatedKeyParameters.getParameters().getHInv()))
+                {
+                    return new SimpleTestResult(false, "Hinv does not match");
+                }
+
+                if (!recoveredKeyParameters.getParameters().getN().equals(generatedKeyParameters.getParameters().getN()))
+                {
+                    return new SimpleTestResult(false, "N does not match");
+                }
+
+                if (!Arrays.areEqual(recoveredKeyParameters.getParameters().getSeed(), generatedKeyParameters.getParameters().getSeed()))
+                {
+                    return new SimpleTestResult(false, "Seed does not match");
+                }
+
+                return new SimpleTestResult(true, null);
+            }
+            catch (Exception t)
+            {
+                // Any exception is bad.
+                return new SimpleTestResult(false, t.getMessage(), t);
+            }
+        }
+
+
+        private boolean safeEquals(Object left, Object right)
+        {
+            if (left == null || right == null)
+            {
+                return left == null && right == null;
+            }
+
+            return left.equals(right);
+        }
+
+        public TestResult perform()
+        {
+
+            SimpleTestResult str = encodeDecodePublicLW("Tc26-Gost-3410-12-512-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = encodeDecodePrivateLW("Tc26-Gost-3410-12-512-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+
+            str = encodeDecodePublicLW("Tc26-Gost-3410-12-256-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = encodeDecodePrivateLW("Tc26-Gost-3410-12-256-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = decodeJCEPrivate();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = decodeJCEPublic();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = encodeRecodePrivateKey();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = encodeRecodePublicKey();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+
+            str = encodeRecodePublicKeyGost2006();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            str = encodeRecodePrivateKeyGost2006();
+            if (!str.isSuccessful())
+            {
+                return str;
+            }
+
+            return new SimpleTestResult(true, getName() + ": Okay");
+        }
+    }
+
     private class GOST3410_TEST1_512
         implements Test
     {
@@ -39,37 +932,37 @@
             return "GOST3410-TEST1-512";
         }
 
-        FixedSecureRandom    init_random = new FixedSecureRandom(
-            new FixedSecureRandom.Source[] { new FixedSecureRandom.Data(Hex.decode("00005EC900007341")), new FixedSecureRandom.Data(zeroTwo(64)) });
-        FixedSecureRandom    random = new TestRandomData(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
-        FixedSecureRandom    keyRandom = new TestRandomData(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
+        FixedSecureRandom init_random = new FixedSecureRandom(
+            new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(Hex.decode("00005EC900007341")), new FixedSecureRandom.Data(zeroTwo(64))});
+        FixedSecureRandom random = new TestRandomData(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
+        FixedSecureRandom keyRandom = new TestRandomData(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
 
-        BigInteger  pValue = new BigInteger("EE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3", 16);
-        BigInteger  qValue = new BigInteger("98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D", 16);
+        BigInteger pValue = new BigInteger("EE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3", 16);
+        BigInteger qValue = new BigInteger("98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("3e5f895e276d81d2d52c0763270a458157b784c57abdbd807bc44fd43a32ac06",16);
-            BigInteger              s = new BigInteger("3f0dd5d4400d47c08e4ce505ff7434b6dbf729592e37c74856dab85115a60955",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("3e5f895e276d81d2d52c0763270a458157b784c57abdbd807bc44fd43a32ac06", 16);
+            BigInteger s = new BigInteger("3f0dd5d4400d47c08e4ce505ff7434b6dbf729592e37c74856dab85115a60955", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(512, 1, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (params.getValidationParameters() == null)
             {
                 return new SimpleTestResult(false, getName() + "validation parameters wrong");
             }
             if (params.getValidationParameters().getC() != 29505
-                ||  params.getValidationParameters().getX0() != 24265)
+                || params.getValidationParameters().getX0() != 24265)
             {
                 return new SimpleTestResult(false, getName() + "validation parameters values wrong");
             }
             if (!init_random.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'init_random'.");
+                    + ": unexpected number of bytes used from 'init_random'.");
             }
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
@@ -77,17 +970,17 @@
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             if (!keyRandom.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'keyRandom'.");
+                    + ": unexpected number of bytes used from 'keyRandom'.");
             }
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
@@ -95,13 +988,13 @@
             GOST3410Signer gost3410 = new GOST3410Signer();
 
             gost3410.init(true, param);
-            
+
             BigInteger[] sig = gost3410.generateSignature(hashmessage);
 
             if (!random.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'random'.");
+                    + ": unexpected number of bytes used from 'random'.");
             }
 
             if (!r.equals(sig[0]))
@@ -146,23 +1039,23 @@
         FixedSecureRandom random = new TestRandomData(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
         FixedSecureRandom keyRandom = new TestRandomData(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
 
-        BigInteger  pValue = new BigInteger("8b08eb135af966aab39df294538580c7da26765d6d38d30cf1c06aae0d1228c3316a0e29198460fad2b19dc381c15c888c6dfd0fc2c565abb0bf1faff9518f85", 16);
-        BigInteger  qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+        BigInteger pValue = new BigInteger("8b08eb135af966aab39df294538580c7da26765d6d38d30cf1c06aae0d1228c3316a0e29198460fad2b19dc381c15c888c6dfd0fc2c565abb0bf1faff9518f85", 16);
+        BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("7c07c8cf035c2a1cb2b7fae5807ac7cd623dfca7a1a68f6d858317822f1ea00d",16);
-            BigInteger              s = new BigInteger("7e9e036a6ff87dbf9b004818252b1f6fc310bdd4d17cb8c37d9c36c7884de60c",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("7c07c8cf035c2a1cb2b7fae5807ac7cd623dfca7a1a68f6d858317822f1ea00d", 16);
+            BigInteger s = new BigInteger("7e9e036a6ff87dbf9b004818252b1f6fc310bdd4d17cb8c37d9c36c7884de60c", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(512, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!init_random.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'init_random'.");
+                    + ": unexpected number of bytes used from 'init_random'.");
             }
 
             if (params.getValidationParameters() == null)
@@ -171,7 +1064,7 @@
             }
 
             if (params.getValidationParameters().getCL() != 13
-                ||  params.getValidationParameters().getX0L() != 1039943409)
+                || params.getValidationParameters().getX0L() != 1039943409)
             {
                 return new SimpleTestResult(false, getName() + ": validation parameters values wrong");
             }
@@ -181,17 +1074,17 @@
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             if (!keyRandom.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'keyRandom'.");
+                    + ": unexpected number of bytes used from 'keyRandom'.");
             }
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
@@ -199,13 +1092,13 @@
             GOST3410Signer GOST3410 = new GOST3410Signer();
 
             GOST3410.init(true, param);
-            
+
             BigInteger[] sig = GOST3410.generateSignature(hashmessage);
 
             if (!random.isExhausted())
             {
                 return new SimpleTestResult(false, getName()
-                        + ": unexpected number of bytes used from 'random'.");
+                    + ": unexpected number of bytes used from 'random'.");
             }
 
             if (!r.equals(sig[0]))
@@ -245,14 +1138,14 @@
             return "GOST3410-TEST1-1024";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstInt = true;
 
             public int nextInt()
             {
                 String x0 = "0xA565";
-                String c =  "0x538B";
+                String c = "0x538B";
 
                 if (firstInt)
                 {
@@ -267,11 +1160,11 @@
 
                 byte[] d = Hex.decode("02");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -295,7 +1188,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -319,37 +1212,37 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("ab8f37938356529e871514c1f48c5cbce77b2f4fc9a2673ac2c1653da8984090c0ac73775159a26bef59909d4c9846631270e16653a6234668f2a52a01a39b921490e694c0f104b58d2e14970fccb478f98d01e975a1028b9536d912de5236d2dd2fc396b77153594d4178780e5f16f718471e2111c8ce64a7d7e196fa57142d", 16);
-        BigInteger  qValue = new BigInteger("bcc02ca0ce4f0753ec16105ee5d530aa00d39f3171842ab2c334a26b5f576e0f", 16);
+        BigInteger pValue = new BigInteger("ab8f37938356529e871514c1f48c5cbce77b2f4fc9a2673ac2c1653da8984090c0ac73775159a26bef59909d4c9846631270e16653a6234668f2a52a01a39b921490e694c0f104b58d2e14970fccb478f98d01e975a1028b9536d912de5236d2dd2fc396b77153594d4178780e5f16f718471e2111c8ce64a7d7e196fa57142d", 16);
+        BigInteger qValue = new BigInteger("bcc02ca0ce4f0753ec16105ee5d530aa00d39f3171842ab2c334a26b5f576e0f", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("a8790aabbd5a998ff524bad048ac69cd1faff2dab048265c8d60d1471c44a9ee",16);
-            BigInteger              s = new BigInteger("30df5ba32ac77170b9632559bef7d37620017756dff3fea1088b4267db0944b8",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("a8790aabbd5a998ff524bad048ac69cd1faff2dab048265c8d60d1471c44a9ee", 16);
+            BigInteger s = new BigInteger("30df5ba32ac77170b9632559bef7d37620017756dff3fea1088b4267db0944b8", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 1, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
             GOST3410Signer GOST3410 = new GOST3410Signer();
 
             GOST3410.init(true, param);
-            
+
             BigInteger[] sig = GOST3410.generateSignature(hashmessage);
 
             if (!r.equals(sig[0]))
@@ -389,14 +1282,14 @@
             return "GOST3410-TEST2-1024";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x3DFC46F1";
-                String c =  "0xD";
+                String c = "0xD";
 
                 if (firstLong)
                 {
@@ -411,11 +1304,11 @@
 
                 byte[] d = Hex.decode("02");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -439,7 +1332,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -463,37 +1356,37 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("e2c4191c4b5f222f9ac2732562f6d9b4f18e7fb67a290ea1e03d750f0b9806755fc730d975bf3faa606d05c218b35a6c3706919aab92e0c58b1de4531c8fa8e7af43c2bff016251e21b2870897f6a27ac4450bca235a5b748ad386e4a0e4dfcb09152435abcfe48bd0b126a8122c7382f285a9864615c66decddf6afd355dfb7", 16);
-        BigInteger  qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+        BigInteger pValue = new BigInteger("e2c4191c4b5f222f9ac2732562f6d9b4f18e7fb67a290ea1e03d750f0b9806755fc730d975bf3faa606d05c218b35a6c3706919aab92e0c58b1de4531c8fa8e7af43c2bff016251e21b2870897f6a27ac4450bca235a5b748ad386e4a0e4dfcb09152435abcfe48bd0b126a8122c7382f285a9864615c66decddf6afd355dfb7", 16);
+        BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("81d69a192e9c7ac21fc07da41bd07e230ba6a94eb9f3c1fd104c7bd976733ca5",16);
-            BigInteger              s = new BigInteger("315c879c8414f35feb4deb15e7cc0278c48e6ca1596325d6959338d860b0c47a",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("81d69a192e9c7ac21fc07da41bd07e230ba6a94eb9f3c1fd104c7bd976733ca5", 16);
+            BigInteger s = new BigInteger("315c879c8414f35feb4deb15e7cc0278c48e6ca1596325d6959338d860b0c47a", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
             GOST3410Signer GOST3410 = new GOST3410Signer();
 
             GOST3410.init(true, param);
-           
+
             BigInteger[] sig = GOST3410.generateSignature(hashmessage);
 
             if (!r.equals(sig[0]))
@@ -533,14 +1426,14 @@
             return "GOST3410-AParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x520874F5";
-                String c =  "0xEE39ADB3";
+                String c = "0xEE39ADB3";
 
                 if (firstLong)
                 {
@@ -555,11 +1448,11 @@
 
                 byte[] d = Hex.decode("02");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -583,7 +1476,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -607,30 +1500,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("b4e25efb018e3c8b87505e2a67553c5edc56c2914b7e4f89d23f03f03377e70a2903489dd60e78418d3d851edb5317c4871e40b04228c3b7902963c4b7d85d52b9aa88f2afdbeb28da8869d6df846a1d98924e925561bd69300b9ddd05d247b5922d967cbb02671881c57d10e5ef72d3e6dad4223dc82aa1f7d0294651a480df", 16);
-        BigInteger  qValue = new BigInteger("972432a437178b30bd96195b773789ab2fff15594b176dd175b63256ee5af2cf", 16);
+        BigInteger pValue = new BigInteger("b4e25efb018e3c8b87505e2a67553c5edc56c2914b7e4f89d23f03f03377e70a2903489dd60e78418d3d851edb5317c4871e40b04228c3b7902963c4b7d85d52b9aa88f2afdbeb28da8869d6df846a1d98924e925561bd69300b9ddd05d247b5922d967cbb02671881c57d10e5ef72d3e6dad4223dc82aa1f7d0294651a480df", 16);
+        BigInteger qValue = new BigInteger("972432a437178b30bd96195b773789ab2fff15594b176dd175b63256ee5af2cf", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("64a8856628e5669d85f62cd763dd4a99bc56d33dc0e1859122855d141e9e4774",16);
-            BigInteger              s = new BigInteger("319ebac97092b288d469a4b988248794f60c865bc97858d9a3135c6d1a1bf2dd",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("64a8856628e5669d85f62cd763dd4a99bc56d33dc0e1859122855d141e9e4774", 16);
+            BigInteger s = new BigInteger("319ebac97092b288d469a4b988248794f60c865bc97858d9a3135c6d1a1bf2dd", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -677,14 +1570,14 @@
             return "GOST3410-BParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x5B977CDB";
-                String c =  "0x6E9692DD";
+                String c = "0x6E9692DD";
 
                 if (firstLong)
                 {
@@ -698,11 +1591,11 @@
             {
                 byte[] d = Hex.decode("bc3cbbdb7e6f848286e19ad9a27a8e297e5b71c53dd974cdf60f937356df69cbc97a300ccc71685c553046147f11568c4fddf363d9d886438345a62c3b75963d6546adfabf31b31290d12cae65ecb8309ef66782");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -726,7 +1619,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -750,30 +1643,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("c6971fc57524b30c9018c5e621de15499736854f56a6f8aee65a7a404632b3540f09020f67f04dc2e6783b141dceffd21a703035b7d0187c6e12cb4229922bafdb2225b73e6b23a0de36e20047065aea000c1a374283d0ad8dc1981e3995f0bb8c72526041fcb98ae6163e1e71a669d8364e9c4c3188f673c5f8ee6fadb41abf", 16);
-        BigInteger  qValue = new BigInteger("b09d634c10899cd7d4c3a7657403e05810b07c61a688bab2c37f475e308b0607", 16);
+        BigInteger pValue = new BigInteger("c6971fc57524b30c9018c5e621de15499736854f56a6f8aee65a7a404632b3540f09020f67f04dc2e6783b141dceffd21a703035b7d0187c6e12cb4229922bafdb2225b73e6b23a0de36e20047065aea000c1a374283d0ad8dc1981e3995f0bb8c72526041fcb98ae6163e1e71a669d8364e9c4c3188f673c5f8ee6fadb41abf", 16);
+        BigInteger qValue = new BigInteger("b09d634c10899cd7d4c3a7657403e05810b07c61a688bab2c37f475e308b0607", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("860d82c60e9502cd00c0e9e1f6563feafec304801974d745c5e02079946f729e",16);
-            BigInteger              s = new BigInteger("7ef49264ef022801aaa03033cd97915235fbab4c823ed936b0f360c22114688a",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("860d82c60e9502cd00c0e9e1f6563feafec304801974d745c5e02079946f729e", 16);
+            BigInteger s = new BigInteger("7ef49264ef022801aaa03033cd97915235fbab4c823ed936b0f360c22114688a", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -820,14 +1713,14 @@
             return "GOST3410-CParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x43848744";
-                String c =  "0xB50A826D";
+                String c = "0xB50A826D";
 
                 if (firstLong)
                 {
@@ -841,11 +1734,11 @@
             {
                 byte[] d = Hex.decode("7F575E8194BC5BDF");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -869,7 +1762,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -893,30 +1786,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("9d88e6d7fe3313bd2e745c7cdd2ab9ee4af3c8899e847de74a33783ea68bc30588ba1f738c6aaf8ab350531f1854c3837cc3c860ffd7e2e106c3f63b3d8a4c034ce73942a6c3d585b599cf695ed7a3c4a93b2b947b7157bb1a1c043ab41ec8566c6145e938a611906de0d32e562494569d7e999a0dda5c879bdd91fe124df1e9", 16);
-        BigInteger  qValue = new BigInteger("fadd197abd19a1b4653eecf7eca4d6a22b1f7f893b641f901641fbb555354faf", 16);
+        BigInteger pValue = new BigInteger("9d88e6d7fe3313bd2e745c7cdd2ab9ee4af3c8899e847de74a33783ea68bc30588ba1f738c6aaf8ab350531f1854c3837cc3c860ffd7e2e106c3f63b3d8a4c034ce73942a6c3d585b599cf695ed7a3c4a93b2b947b7157bb1a1c043ab41ec8566c6145e938a611906de0d32e562494569d7e999a0dda5c879bdd91fe124df1e9", 16);
+        BigInteger qValue = new BigInteger("fadd197abd19a1b4653eecf7eca4d6a22b1f7f893b641f901641fbb555354faf", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("4deb95a0b35e7ed7edebe9bef5a0f93739e16b7ff27fe794d989d0c13159cfbc",16);
-            BigInteger              s = new BigInteger("e1d0d30345c24cfeb33efde3deee5fbbda78ddc822b719d860cd0ba1fb6bd43b",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("4deb95a0b35e7ed7edebe9bef5a0f93739e16b7ff27fe794d989d0c13159cfbc", 16);
+            BigInteger s = new BigInteger("e1d0d30345c24cfeb33efde3deee5fbbda78ddc822b719d860cd0ba1fb6bd43b", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -963,14 +1856,14 @@
             return "GOST3410-DParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x13DA8B9D";
-                String c =  "0xA0E9DE4B";
+                String c = "0xA0E9DE4B";
 
                 if (firstLong)
                 {
@@ -985,11 +1878,11 @@
 
                 byte[] d = Hex.decode("41ab97857f42614355d32db0b1069f109a4da283676c7c53a68185b4");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1013,7 +1906,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1037,30 +1930,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("80f102d32b0fd167d069c27a307adad2c466091904dbaa55d5b8cc7026f2f7a1919b890cb652c40e054e1e9306735b43d7b279eddf9102001cd9e1a831fe8a163eed89ab07cf2abe8242ac9dedddbf98d62cddd1ea4f5f15d3a42a6677bdd293b24260c0f27c0f1d15948614d567b66fa902baa11a69ae3bceadbb83e399c9b5", 16);
-        BigInteger  qValue = new BigInteger("f0f544c418aac234f683f033511b65c21651a6078bda2d69bb9f732867502149", 16);
+        BigInteger pValue = new BigInteger("80f102d32b0fd167d069c27a307adad2c466091904dbaa55d5b8cc7026f2f7a1919b890cb652c40e054e1e9306735b43d7b279eddf9102001cd9e1a831fe8a163eed89ab07cf2abe8242ac9dedddbf98d62cddd1ea4f5f15d3a42a6677bdd293b24260c0f27c0f1d15948614d567b66fa902baa11a69ae3bceadbb83e399c9b5", 16);
+        BigInteger qValue = new BigInteger("f0f544c418aac234f683f033511b65c21651a6078bda2d69bb9f732867502149", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("712592d285b792e33b8a9a11e8e6c4f512ddf0042972bbfd1abb0a93e8fc6f54",16);
-            BigInteger              s = new BigInteger("2cf26758321258b130d5612111339f09ceb8668241f3482e38baa56529963f07",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("712592d285b792e33b8a9a11e8e6c4f512ddf0042972bbfd1abb0a93e8fc6f54", 16);
+            BigInteger s = new BigInteger("2cf26758321258b130d5612111339f09ceb8668241f3482e38baa56529963f07", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -1107,14 +2000,14 @@
             return "GOST3410-AExParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0xD05E9F14";
-                String c =  "0x46304C5F";
+                String c = "0x46304C5F";
 
                 if (firstLong)
                 {
@@ -1128,11 +2021,11 @@
             {
                 byte[] d = Hex.decode("35ab875399cda33c146ca629660e5a5e5c07714ca326db032dd6751995cdb90a612b9228932d8302704ec24a5def7739c5813d83");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1156,7 +2049,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1180,30 +2073,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("ca3b3f2eee9fd46317d49595a9e7518e6c63d8f4eb4d22d10d28af0b8839f079f8289e603b03530784b9bb5a1e76859e4850c670c7b71c0df84ca3e0d6c177fe9f78a9d8433230a883cd82a2b2b5c7a3306980278570cdb79bf01074a69c9623348824b0c53791d53c6a78cab69e1cfb28368611a397f50f541e16db348dbe5f", 16);
-        BigInteger  qValue = new BigInteger("cae4d85f80c147704b0ca48e85fb00a9057aa4acc44668e17f1996d7152690d9", 16);
+        BigInteger pValue = new BigInteger("ca3b3f2eee9fd46317d49595a9e7518e6c63d8f4eb4d22d10d28af0b8839f079f8289e603b03530784b9bb5a1e76859e4850c670c7b71c0df84ca3e0d6c177fe9f78a9d8433230a883cd82a2b2b5c7a3306980278570cdb79bf01074a69c9623348824b0c53791d53c6a78cab69e1cfb28368611a397f50f541e16db348dbe5f", 16);
+        BigInteger qValue = new BigInteger("cae4d85f80c147704b0ca48e85fb00a9057aa4acc44668e17f1996d7152690d9", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("90892707282f433398488f19d31ac48523a8e2ded68944e0da91c6895ee7045e",16);
-            BigInteger              s = new BigInteger("3be4620ee88f1ee8f9dd63c7d145b7e554839feeca125049118262ea4651e9de",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("90892707282f433398488f19d31ac48523a8e2ded68944e0da91c6895ee7045e", 16);
+            BigInteger s = new BigInteger("3be4620ee88f1ee8f9dd63c7d145b7e554839feeca125049118262ea4651e9de", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -1242,6 +2135,11 @@
         }
     }
 
+    public String getName()
+    {
+        return "GOST3410";
+    }
+
     private class GOST3410_BExParam
         implements Test
     {
@@ -1250,14 +2148,14 @@
             return "GOST3410-BExParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x7A007804";
-                String c =  "0xD31A4FF7";
+                String c = "0xD31A4FF7";
 
                 if (firstLong)
                 {
@@ -1271,11 +2169,11 @@
             {
                 byte[] d = Hex.decode("7ec123d161477762838c2bea9dbdf33074af6d41d108a066a1e7a07ab3048de2");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1299,7 +2197,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1323,30 +2221,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("9286dbda91eccfc3060aa5598318e2a639f5ba90a4ca656157b2673fb191cd0589ee05f4cef1bd13508408271458c30851ce7a4ef534742bfb11f4743c8f787b11193ba304c0e6bca25701bf88af1cb9b8fd4711d89f88e32b37d95316541bf1e5dbb4989b3df13659b88c0f97a3c1087b9f2d5317d557dcd4afc6d0a754e279", 16);
-        BigInteger  qValue = new BigInteger("c966e9b3b8b7cdd82ff0f83af87036c38f42238ec50a876cd390e43d67b6013f", 16);
+        BigInteger pValue = new BigInteger("9286dbda91eccfc3060aa5598318e2a639f5ba90a4ca656157b2673fb191cd0589ee05f4cef1bd13508408271458c30851ce7a4ef534742bfb11f4743c8f787b11193ba304c0e6bca25701bf88af1cb9b8fd4711d89f88e32b37d95316541bf1e5dbb4989b3df13659b88c0f97a3c1087b9f2d5317d557dcd4afc6d0a754e279", 16);
+        BigInteger qValue = new BigInteger("c966e9b3b8b7cdd82ff0f83af87036c38f42238ec50a876cd390e43d67b6013f", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("8f79a582513df84dc247bcb624340cc0e5a34c4324a20ce7fe3ab8ff38a9db71",16);
-            BigInteger              s = new BigInteger("7508d22fd6cbb45efd438cb875e43f137247088d0f54b29a7c91f68a65b5fa85",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("8f79a582513df84dc247bcb624340cc0e5a34c4324a20ce7fe3ab8ff38a9db71", 16);
+            BigInteger s = new BigInteger("7508d22fd6cbb45efd438cb875e43f137247088d0f54b29a7c91f68a65b5fa85", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -1393,14 +2291,14 @@
             return "GOST3410-CExParam";
         }
 
-        SecureRandom    init_random = new SecureRandom()
+        SecureRandom init_random = new SecureRandom()
         {
             boolean firstLong = true;
 
             public long nextLong()
             {
                 String x0 = "0x162AB910";
-                String c =  "0x93F828D3";
+                String c = "0x93F828D3";
 
                 if (firstLong)
                 {
@@ -1414,11 +2312,11 @@
             {
                 byte[] d = Hex.decode("ca82cce78a738bc46f103d53b9bf809745ec845e4f6da462606c51f60ecf302e31204b81");
 
-                System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+                System.arraycopy(d, 0, bytes, bytes.length - d.length, d.length);
             }
         };
 
-        SecureRandom    random = new SecureRandom()
+        SecureRandom random = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1442,7 +2340,7 @@
             }
         };
 
-        SecureRandom    keyRandom = new SecureRandom()
+        SecureRandom keyRandom = new SecureRandom()
         {
             public void nextBytes(byte[] bytes)
             {
@@ -1466,30 +2364,30 @@
             }
         };
 
-        BigInteger  pValue = new BigInteger("b194036ace14139d36d64295ae6c50fc4b7d65d8b340711366ca93f383653908ee637be428051d86612670ad7b402c09b820fa77d9da29c8111a8496da6c261a53ed252e4d8a69a20376e6addb3bdcd331749a491a184b8fda6d84c31cf05f9119b5ed35246ea4562d85928ba1136a8d0e5a7e5c764ba8902029a1336c631a1d", 16);
-        BigInteger  qValue = new BigInteger("96120477df0f3896628e6f4a88d83c93204c210ff262bccb7dae450355125259", 16);
+        BigInteger pValue = new BigInteger("b194036ace14139d36d64295ae6c50fc4b7d65d8b340711366ca93f383653908ee637be428051d86612670ad7b402c09b820fa77d9da29c8111a8496da6c261a53ed252e4d8a69a20376e6addb3bdcd331749a491a184b8fda6d84c31cf05f9119b5ed35246ea4562d85928ba1136a8d0e5a7e5c764ba8902029a1336c631a1d", 16);
+        BigInteger qValue = new BigInteger("96120477df0f3896628e6f4a88d83c93204c210ff262bccb7dae450355125259", 16);
 
         public TestResult perform()
         {
-            BigInteger              r = new BigInteger("169fdb2dc09f690b71332432bfec806042e258fa9a21dafe73c6abfbc71407d9",16);
-            BigInteger              s = new BigInteger("9002551808ae40d19f6f31fb67e4563101243cf07cffd5f2f8ff4c537b0c9866",16);
-            GOST3410ParametersGenerator  pGen = new GOST3410ParametersGenerator();
+            BigInteger r = new BigInteger("169fdb2dc09f690b71332432bfec806042e258fa9a21dafe73c6abfbc71407d9", 16);
+            BigInteger s = new BigInteger("9002551808ae40d19f6f31fb67e4563101243cf07cffd5f2f8ff4c537b0c9866", 16);
+            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
 
             pGen.init(1024, 2, init_random);
 
-            GOST3410Parameters           params = pGen.generateParameters();
+            GOST3410Parameters params = pGen.generateParameters();
 
             if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
             {
                 return new SimpleTestResult(false, getName() + ": p or q wrong");
             }
 
-            GOST3410KeyPairGenerator         GOST3410KeyGen = new GOST3410KeyPairGenerator();
-            GOST3410KeyGenerationParameters  genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+            GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+            GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
 
             GOST3410KeyGen.init(genParam);
 
-            AsymmetricCipherKeyPair  pair = GOST3410KeyGen.generateKeyPair();
+            AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
 
             ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
 
@@ -1527,48 +2425,4 @@
             }
         }
     }
-
-    Test tests[] =
-    {
-        new GOST3410_TEST1_512(),
-        new GOST3410_TEST2_512(),
-//        new GOST3410_TEST1_1024(),
-//        new GOST3410_TEST2_1024(),
-//        new GOST3410_AParam(),
-//        new GOST3410_BParam(),
-//        new GOST3410_CParam(),
-//        new GOST3410_DParam(),
-//        new GOST3410_AExParam(),
-//        new GOST3410_BExParam(),
-//        new GOST3410_CExParam()
-    };
-
-    public String getName()
-    {
-        return "GOST3410";
-    }
-
-    public TestResult perform()
-    {
-        for (int i = 0; i != tests.length; i++)
-        {
-            TestResult  result = tests[i].perform();
-
-            if (!result.isSuccessful())
-            {
-                return result;
-            }
-        }
-
-        return new SimpleTestResult(true, "GOST3410: Okay");
-    }
-
-    public static void main(
-        String[]    args)
-    {
-        GOST3410Test         test = new GOST3410Test();
-        TestResult      result = test.perform();
-
-        System.out.println(result);
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412MacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412MacTest.java
new file mode 100644
index 0000000..3c4fe3b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412MacTest.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.GOST3412_2015Engine;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * see GOST_R_3413-2015
+ */
+public class GOST3412MacTest
+    implements Test
+{
+
+    public String getName()
+    {
+        return "GOST 3412 2015 MAC test";
+    }
+
+    public TestResult perform()
+    {
+
+
+        byte[][] inputs = new byte[][]{
+            Hex.decode("1122334455667700ffeeddccbbaa9988"),
+            Hex.decode("00112233445566778899aabbcceeff0a"),
+            Hex.decode("112233445566778899aabbcceeff0a00"),
+            Hex.decode("2233445566778899aabbcceeff0a0011"),
+        };
+        Mac mac = new CMac(new GOST3412_2015Engine(), 64);
+
+        byte[] output = Hex.decode("336f4d296059fbe3");
+
+        KeyParameter key =
+            new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef"));
+        mac.init(key);
+
+        for (int i = 0; i != inputs.length; i++)
+        {
+            mac.update(inputs[i], 0, inputs[i].length);
+        }
+
+        byte[] out = new byte[8];
+
+        mac.doFinal(out, 0);
+
+        if (!Arrays.areEqual(out, output))
+        {
+            return new SimpleTestResult(false, getName() + ": Failed test 1 - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+        }
+
+        return new SimpleTestResult(true, getName() + ": Okay");
+
+    }
+
+
+    public static void main(String[] args)
+    {
+        GOST3412MacTest test = new GOST3412MacTest();
+        TestResult result = test.perform();
+
+        System.out.println(result);
+    }
+
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412Test.java
new file mode 100644
index 0000000..48a78e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3412Test.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.GOST3412_2015Engine;
+import org.bouncycastle.crypto.modes.G3413CBCBlockCipher;
+import org.bouncycastle.crypto.modes.G3413CFBBlockCipher;
+import org.bouncycastle.crypto.modes.G3413CTRBlockCipher;
+import org.bouncycastle.crypto.modes.G3413OFBBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class GOST3412Test
+    extends CipherTest
+{
+
+    private byte[][] inputs = new byte[][]{
+        Hex.decode("1122334455667700ffeeddccbbaa9988"),
+        Hex.decode("00112233445566778899aabbcceeff0a"),
+        Hex.decode("112233445566778899aabbcceeff0a00"),
+        Hex.decode("2233445566778899aabbcceeff0a0011")
+    };
+
+
+    static SimpleTest[] tests = {
+
+//         ECB
+        new BlockCipherVectorTest(1, new GOST3412_2015Engine(),
+            new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+            "1122334455667700ffeeddccbbaa9988", "7f679d90bebc24305a468d42b9d4edcd"),
+
+        // CFB
+        new BlockCipherVectorTest(2, new G3413CFBBlockCipher(new GOST3412_2015Engine()),
+            new ParametersWithIV(new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+                Hex.decode("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "81800a59b1842b24ff1f795e897abd95ed5b47a7048cfab48fb521369d9326bf79f2a8eb5cc68d38842d264e97a238b54ffebecd4e922de6c75bd9dd44fbf4d1"),
+
+        new BlockCipherVectorTest(3, new G3413CFBBlockCipher(new GOST3412_2015Engine(), 8),
+            new ParametersWithIV(
+                new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+                Hex.decode("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "819b19c5867e61f1cf1b16f664f66e46ed8fcb82b1110b1e7ec03bfa6611f2eabd7a32363691cbdc3bbe403bc80552d822c2cdf483981cd71d5595453d7f057d"),
+
+        // OFB
+        new BlockCipherVectorTest(4, new G3413OFBBlockCipher(new GOST3412_2015Engine()),
+            new ParametersWithIV(
+                new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+                Hex.decode("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "81800a59b1842b24ff1f795e897abd95ed5b47a7048cfab48fb521369d9326bf66a257ac3ca0b8b1c80fe7fc10288a13203ebbc066138660a0292243f6903150"),
+
+//CBC
+        new BlockCipherVectorTest(5, new G3413CBCBlockCipher(new GOST3412_2015Engine()),
+            new ParametersWithIV(new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")), Hex.decode("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "689972d4a085fa4d90e52e3d6d7dcc272826e661b478eca6af1e8e448d5ea5acfe7babf1e91999e85640e8b0f49d90d0167688065a895c631a2d9a1560b63970"),
+//CTR
+        new BlockCipherVectorTest(6, new G3413CTRBlockCipher(new GOST3412_2015Engine()),
+            new ParametersWithIV(new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+                Hex.decode("1234567890abcef0")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "f195d8bec10ed1dbd57b5fa240bda1b885eee733f6a13e5df33ce4b33c45dee4a5eae88be6356ed3d5e877f13564a3a5cb91fab1f20cbab6d1c6d15820bdba73"),
+        new BlockCipherVectorTest(7, new G3413CTRBlockCipher(new GOST3412_2015Engine(), 8),
+            new ParametersWithIV(new KeyParameter(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef")),
+                Hex.decode("1234567890abcef0")),
+            "1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011",
+            "f1a787ad3a88f9a0bc735293f98c12c3eb31621b9b2e6461c7ef73a2e6a6b1793ddf722f7b1d22a722ec4d3edbc313bcd356b313d37af9e5ef934fa223c13fe2")
+
+
+    };
+
+
+    protected GOST3412Test()
+    {
+        super(tests, new GOST3412_2015Engine(), new KeyParameter(new byte[32]));
+    }
+
+    public String getName()
+    {
+        return "GOST 34.12 2015";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        super.performTest();
+
+//        cfbTest();
+//        ofbTest();
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new GOST3412Test());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GSKKDFTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GSKKDFTest.java
new file mode 100644
index 0000000..30264ff
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GSKKDFTest.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.agreement.kdf.GSKKDFParameters;
+import org.bouncycastle.crypto.agreement.kdf.GSKKFDGenerator;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class GSKKDFTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "GSKKDFTest";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        GSKKFDGenerator gen = new GSKKFDGenerator(new SHA256Digest());
+
+        byte[] key = new byte[16];
+
+        gen.init(new GSKKDFParameters(Hex.decode("0102030405060708090a"), 1, Hex.decode("27252622")));
+
+        gen.generateBytes(key, 0, key.length);
+        areEqual(Hex.decode("bd9ff24b9cc4d91b70af951989b4d719"), key);
+        
+        gen.generateBytes(key, 0, key.length);
+        areEqual(Hex.decode("d5934f681ad1e860981eb1792af68e20"), key);
+
+        gen = new GSKKFDGenerator(new SHA256Digest());
+        
+        gen.init(new GSKKDFParameters(Hex.decode("0102030405060708090a"), 2, Hex.decode("27252622")));
+
+        gen.generateBytes(key, 0, key.length);
+        areEqual(Hex.decode("d5934f681ad1e860981eb1792af68e20"), key);
+
+        gen.init(new GSKKDFParameters(Hex.decode("0102030405060708090a"), 1));
+
+        gen.generateBytes(key, 0, key.length);
+        areEqual(Hex.decode("3c6e999b2cb08d8d8dd261cd23f15ed6"), key);
+
+        gen.generateBytes(key, 0, key.length);
+        areEqual(Hex.decode("019ce1fcf81b94602f2f8678be905e0e"), key);
+
+        try
+        {
+            gen.generateBytes(key, 1, key.length);
+        }
+        catch (DataLengthException e)
+        {
+            isEquals("output buffer too small", e.getMessage());
+        }
+    }
+    
+    public static void main(
+        String[]    args)
+    {
+        runTest(new GSKKDFTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/IsoTrailerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/IsoTrailerTest.java
new file mode 100644
index 0000000..6815af5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/IsoTrailerTest.java
@@ -0,0 +1,122 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.digests.SHA512tDigest;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.signers.ISO9796d2PSSSigner;
+import org.bouncycastle.crypto.signers.ISO9796d2Signer;
+import org.bouncycastle.crypto.signers.X931Signer;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class IsoTrailerTest
+    extends SimpleTest
+{
+    private static final byte[] x931SigOld = Hex.decode("33156b44e30640d14940d5f4534de6a91c945fe8355b01f66a896c41c78482ba02457d079327bd7015a875c353c6a0db356d6c568edb07dbbdb0500705e3f8aff9269f8535c1ed27edb09a1c246a366c4f638fd224389bcaebeb2dedc400990b91cddfda4ee0abc67ae1e39b139183dd6193aee9aa3616285ba928af20f89d5c");
+    private static final byte[] x931SigCorrect =  Hex.decode("3ff8d8c371503a153bb99edb1064984680ef7f70f73d08d28205b8e1ae90d7a00d78d6f16994b872bf613aafb41dd7b60fc9e964d280bde07637ec278b9491ddeeecae53e2b55302801577b20cda4ef2d4a42868de579fa5a50f2f2feb50688d893d9210469bb47134475515a7745744ba99e23d4490400e7a734e00ef2c5476");
+
+    private static final byte[] iso9796d2Old = Hex.decode("07f7c6a8726aeed821ce7af09b5eb260ade66913c5548438f5f7a613f5e96e1638c36d22d968c24abfa7b5879cbdf55985e7928553fe33a9c7e53b85daab87cc661e33cc1290832c3cfda59256f9501657efead29ee45cc06df1e1c0f3e06110e46377c6ed5ec78327d1c7787af79287e50c810ed17a2b43c56d27ec695b4dbf");
+    private static final byte[] iso9796d2Correct = Hex.decode("530c8949deb24d138b175db5be846481f8b22598fcc44476caf87fe4f5f8c9b71f9456791bf47aaafa20650fedc000251f4eee1bcaed57bd5d1b1e64b5d0e460df88e4a5266eb3969577d29a80d7d0038044247ae6fe7705f9d20d0ef42f525445de0560c9c3972c6443be779c762cfb08e403fc2f06bc8e2d7b8f3bf022160a");
+
+    private static final byte[] iso9796d2PSSOld = Hex.decode("274dbd6e3d93672ee5121022843e37f66b1ff12bb7f04cff059d76932ce9116e8b12efcd19d98a78f8c9d3f262fd3ce7c3bca0edc223f3af54e1401b37f807ef5b6d71591a22a40a34ce8abc10164138835bb63ac8eeb0223d1e1d8c5d18da2acac7f7061023597aa338c4af96bebe6c7935e0b5603cb87977b9e345f697ff98");
+    private static final byte[] iso9796d2PSSCorrect = Hex.decode("3a5c1248652cd6fd4419064b894379ad48c8596a3a5a0bdf98b6d6a9d25f5df164591beddff9e2ae88100dd165053f0edd2e4154834ea7b7c1f56312c4fe23a5407cf73a2c4540c8c19e91187f709529ebb779db2f8fa39f6bef923c392abecf9e7596927a71f62990dafd8bf00d298863d07680e75b1bb9bb655ba25ff48d1e");
+    
+    public String getName()
+    {
+        return "IsoTrailerTest";
+    }
+
+    private void x931Sha512_256Test()
+    {
+        BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+        BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+
+        RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+
+        byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+        X931Signer signer = new X931Signer(new RSAEngine(), new SHA512tDigest(256));
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(x931SigCorrect))
+        {
+            fail("X9.31 Signer failed.");
+        }
+
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(x931SigOld))
+        {
+            fail("X9.31 old Signer failed.");
+        }
+    }
+
+    private void iso9796_2Sha512_256Test()
+    {
+        BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+        BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+
+        RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+
+        byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+        ISO9796d2Signer signer = new ISO9796d2Signer(new RSAEngine(), new SHA512tDigest(256));
+
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(iso9796d2Correct))
+        {
+            fail("ISO9796-2 Signer failed.");
+        }
+
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(iso9796d2Old))
+        {
+            fail("ISO9796-2 old Signer failed.");
+        }
+    }
+
+    private void iso9796_2PSSSha512_256Test()
+    {
+        BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+        BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+
+        RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+
+        byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+        ISO9796d2PSSSigner signer = new ISO9796d2PSSSigner(new RSAEngine(), new SHA512tDigest(256), 32);
+
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(iso9796d2PSSCorrect))
+        {
+            fail("ISO9796-2PSS Signer failed.");
+        }
+
+        signer.init(false, rsaPublic);
+        signer.update(msg, 0, msg.length);
+        if (!signer.verifySignature(iso9796d2PSSOld))
+        {
+            fail("ISO9796-2PSS old Signer failed.");
+        }
+    }
+    
+    public void performTest()
+        throws Exception
+    {
+        x931Sha512_256Test();
+        iso9796_2Sha512_256Test();
+        iso9796_2PSSSha512_256Test();
+    }
+
+    public static void main(
+        String[]    args)
+    {
+        runTest(new IsoTrailerTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/JournalingSecureRandomTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/JournalingSecureRandomTest.java
new file mode 100644
index 0000000..a0623e0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/JournalingSecureRandomTest.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.util.JournalingSecureRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class JournalingSecureRandomTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "JournalingSecureRandom";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        SecureRandom rand = new SecureRandom();
+
+        JournalingSecureRandom jRandom1 = new JournalingSecureRandom(rand);
+
+        byte[] base = new byte[1024];
+
+        jRandom1.nextBytes(base);
+
+        byte[] transcript = jRandom1.getTranscript();
+
+        byte[] block = new byte[512];
+
+        JournalingSecureRandom jRandom2 = new JournalingSecureRandom(transcript, rand);
+
+        jRandom2.nextBytes(block);
+
+        areEqual(Arrays.copyOfRange(base, 0, 512), block);
+
+        jRandom2.nextBytes(block);
+
+        areEqual(Arrays.copyOfRange(base, 512, 1024), block);
+
+        jRandom2.nextBytes(block);
+
+        isTrue(!Arrays.areEqual(Arrays.copyOfRange(base, 0, 512), block));
+
+    }
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new JournalingSecureRandomTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
index 7d9bdf1..fc566d3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java
@@ -257,6 +257,7 @@
         //
         // extremely long data test
         //
+//        long start = System.currentTimeMillis();
 //        System.out.println("Starting very long");
 //        for (int i = 0; i != 16384; i++)
 //        {
@@ -272,7 +273,7 @@
 //        {
 //            fail("Keccak mismatch on " + digest.getAlgorithmName() + " extreme data test");
 //        }
-//        System.out.println("Done");
+//        System.out.println("Done " + (System.currentTimeMillis() - start));
     }
 
     private void testDigestDoFinal(Digest digest)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java
index 7091c42..010a8cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java
@@ -10,14 +10,19 @@
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.encodings.OAEPEncoding;
 import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -779,8 +784,10 @@
         oaepVecTest(1027, 5, pubParam, privParam, seed_1027_5, input_1027_5, output_1027_5);
         oaepVecTest(1027, 6, pubParam, privParam, seed_1027_6, input_1027_6, output_1027_6);
 
+        testForHighByteError("invalidCiphertextOaepTest 1024", 1024);
+
         //
-        // OAEP - public encrypt, private decrypt  differring hashes
+        // OAEP - public encrypt, private decrypt, differing hashes
         //
         AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine(), new SHA256Digest(), new SHA1Digest(), new byte[10]);
 
@@ -821,6 +828,77 @@
         }
     }
 
+    private void testForHighByteError(String label, int keySizeBits) throws Exception
+    {
+        // draw a key of the size asked
+        BigInteger e = BigIntegers.ONE.shiftLeft(16).add(BigIntegers.ONE);
+
+        AsymmetricCipherKeyPairGenerator kpGen = new RSAKeyPairGenerator();
+
+        kpGen.init(new RSAKeyGenerationParameters(e, new SecureRandom(), keySizeBits, 100));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine());
+
+        // obtain a known good ciphertext
+        cipher.init(true, new ParametersWithRandom(kp.getPublic(), new VecRand(seed)));
+        byte[] m = { 42 };
+        byte[] c = cipher.processBlock(m, 0, m.length);
+        int keySizeBytes = (keySizeBits+7)>>>3;
+        if (c.length!=keySizeBytes)
+        {
+            fail(label + " failed ciphertext size");
+        }
+
+        BigInteger n  = ((RSAPrivateCrtKeyParameters)kp.getPrivate()).getModulus();
+
+        // decipher
+        cipher.init(false, kp.getPrivate());
+        byte[] r = cipher.processBlock(c, 0, keySizeBytes);
+        if (r.length!=1 || r[0]!=42)
+        {
+            fail(label + " failed first decryption of test message");
+        }
+
+        // decipher again
+        r = cipher.processBlock(c, 0, keySizeBytes);
+        if (r.length!=1 || r[0]!=42)
+        {
+            fail(label + " failed second decryption of test message");
+        }
+
+        // check hapazard incorrect ciphertexts
+        for(int i=keySizeBytes*8; --i>=0;)
+        {
+            c[i>>>3] ^= 1<<(i&7);
+            boolean ko = true;
+            try
+            {
+                BigInteger cV = new BigInteger(1, c);
+
+                // don't pass in c if it will be rejected trivially
+                if (cV.compareTo(n) < 0)
+                {
+                    r = cipher.processBlock(c, 0, keySizeBytes);
+                }
+                else
+                {
+                    ko = false; // size errors are picked up at start
+                }
+            }
+            catch (InvalidCipherTextException exception)
+            {
+                ko = false;
+            }
+            if (ko)
+            {
+                fail(label + " invalid ciphertext caused no exception");
+            }
+            c[i>>>3] ^= 1<<(i&7);
+        }
+    }
+
     public static void main(
         String[]    args)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java
index 1355f3a..679f4e5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java
@@ -74,6 +74,28 @@
         {"8nv;PAN~-FQ]Emh@.TKG=^.t8R0EQC0T?x9|9g4xzxYmSbBO1qDx8kv-ehh0IBv>3KWhz.Z~jUF0tt8[5U@8;5:=[v6pf.IEJ", "$2a$08$eXo9KDc1BZyybBgMurpcD.GA1/ch3XhgBnIH10Xvjc2ogZaGg3t/m"},
     };
 
+
+    // 2y vectors generated from htpasswd -nB -C 12, nb leading username was removed.
+    private static final String[][] twoYVec = new String[][]{
+        {"a", "$2y$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"},
+        {"abc", "$2y$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"},
+        {"hello world", "$2y$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"},
+        {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2y$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"}
+    };
+
+    // Same as 2y vectors only version changed to 2b to verify handling of that version.
+    private static final String[][] twoBVec = new String[][]{
+        {"a", "$2b$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"},
+        {"abc", "$2b$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"},
+        {"hello world", "$2b$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"},
+        {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2b$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"}
+    };
+
+    public static void main(String[] args)
+    {
+        runTest(new OpenBSDBCryptTest());
+    }
+
     public String getName()
     {
         return "OpenBSDBCrypt";
@@ -131,11 +153,28 @@
                 fail("test4 mismatch: " + "[" + i + "] " + password);
             }
         }
-    }
 
-    public static void main(String[] args)
-    {
-        runTest(new OpenBSDBCryptTest());
+        for (int i = 0; i < twoYVec.length; i++)
+        {
+            password = twoYVec[i][0];
+            encoded = twoYVec[i][1];
+
+            if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+            {
+                fail("twoYVec mismatch: " + "[" + i + "] " + password);
+            }
+        }
+
+        for (int i = 0; i < twoBVec.length; i++)
+        {
+            password = twoBVec[i][0];
+            encoded = twoBVec[i][1];
+
+            if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+            {
+                fail("twoBVec mismatch: " + "[" + i + "] " + password);
+            }
+        }
     }
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java
new file mode 100644
index 0000000..c4c9364
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java
@@ -0,0 +1,233 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.StringReader;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.signers.DSASigner;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
+import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class OpenSSHKeyParsingTests
+    extends SimpleTest
+{
+    private static SecureRandom secureRandom = new SecureRandom();
+
+
+    public static void main(
+        String[] args)
+    {
+        runTest(new OpenSSHKeyParsingTests());
+    }
+
+
+    public void testDSA()
+        throws Exception
+    {
+        CipherParameters pubSpec = OpenSSHPublicKeyUtil.parsePublicKey(Base64.decode("AAAAB3NzaC1kc3MAAACBAJBB5+S4kZZYZLswaQ/zm3GM7YWmHsumwo/Xxu+z6Cg2l5PUoiBBZ4ET9EhhQuL2ja/zrCMCi0ZwiSRuSp36ayPrHLbNJb3VdOuJg8xExRa6F3YfVZfcTPUEKh6FU72fI31HrQmi4rpyHnWxL/iDX496ZG2Hdq6UkPISQpQwj4TtAAAAFQCP9TXcVahR/2rpfEhvdXR0PfhbRwAAAIBdXzAVqoOtb9zog6lNF1cGS1S06W9W/clvuwq2xF1s3bkoI/xUbFSc0IAPsGl2kcB61PAZqcop50lgpvYzt8cq/tbqz3ypq1dCQ0xdmJHj975QsRFax+w6xQ0kgpBhwcS2EOizKb+C+tRzndGpcDSoSMuVXp9i4wn5pJSTZxAYFQAAAIEAhQZc687zYxrEDR/1q6m4hw5GFxuVvLsC+bSHtMF0c11Qy4IPg7mBeP7K5Kq4WyJPtmZhuc5Bb12bJQR6qgd1uLn692fe1UK2kM6eWXBzhlzZ54BslfSKHGNN4qH+ln3Zaf/4rpKE7fvoinkrgkOZmj0PMx9D6wlpHKkXMUxeXtc="));
+
+        CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(new PemReader(new StringReader("-----BEGIN DSA PRIVATE KEY-----\n" +
+            "MIIBuwIBAAKBgQCQQefkuJGWWGS7MGkP85txjO2Fph7LpsKP18bvs+goNpeT1KIg\n" +
+            "QWeBE/RIYULi9o2v86wjAotGcIkkbkqd+msj6xy2zSW91XTriYPMRMUWuhd2H1WX\n" +
+            "3Ez1BCoehVO9nyN9R60JouK6ch51sS/4g1+PemRth3aulJDyEkKUMI+E7QIVAI/1\n" +
+            "NdxVqFH/aul8SG91dHQ9+FtHAoGAXV8wFaqDrW/c6IOpTRdXBktUtOlvVv3Jb7sK\n" +
+            "tsRdbN25KCP8VGxUnNCAD7BpdpHAetTwGanKKedJYKb2M7fHKv7W6s98qatXQkNM\n" +
+            "XZiR4/e+ULERWsfsOsUNJIKQYcHEthDosym/gvrUc53RqXA0qEjLlV6fYuMJ+aSU\n" +
+            "k2cQGBUCgYEAhQZc687zYxrEDR/1q6m4hw5GFxuVvLsC+bSHtMF0c11Qy4IPg7mB\n" +
+            "eP7K5Kq4WyJPtmZhuc5Bb12bJQR6qgd1uLn692fe1UK2kM6eWXBzhlzZ54BslfSK\n" +
+            "HGNN4qH+ln3Zaf/4rpKE7fvoinkrgkOZmj0PMx9D6wlpHKkXMUxeXtcCFELnLOJ8\n" +
+            "D0akSCUFY/iDLo/KnOIH\n" +
+            "-----END DSA PRIVATE KEY-----\n")).readPemObject().getContent());
+
+        DSASigner signer = new DSASigner();
+        signer.init(true, privSpec);
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+
+        BigInteger[] rs = signer.generateSignature(originalMessage);
+
+        signer.init(false, pubSpec);
+
+        isTrue("DSA test", signer.verifySignature(originalMessage, rs[0], rs[1]));
+
+    }
+
+
+    public void testECDSA()
+        throws Exception
+    {
+        CipherParameters pubSpec = OpenSSHPublicKeyUtil.parsePublicKey(Base64.decode("AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHq5qxGqnh93Gpbj2w1Avx1UwBl6z5bZC3Viog1yNHDZYcV6Da4YQ3i0/hN7xY7sUy9dNF6g16tJSYXQQ4tvO3g="));
+
+        CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(new PemReader(new StringReader("-----BEGIN EC PRIVATE KEY-----\n" +
+            "MHcCAQEEIHeg/+m02j6nr4bO8ubfbzhs0fqOjiuIoWbvGnVg+FmpoAoGCCqGSM49\n" +
+            "AwEHoUQDQgAEermrEaqeH3caluPbDUC/HVTAGXrPltkLdWKiDXI0cNlhxXoNrhhD\n" +
+            "eLT+E3vFjuxTL100XqDXq0lJhdBDi287eA==\n" +
+            "-----END EC PRIVATE KEY-----\n")).readPemObject().getContent());
+
+        ECDSASigner signer = new ECDSASigner();
+        signer.init(true, privSpec);
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+
+        BigInteger[] rs = signer.generateSignature(originalMessage);
+
+        signer.init(false, pubSpec);
+
+        isTrue("ECDSA test", signer.verifySignature(originalMessage, rs[0], rs[1]));
+
+    }
+
+
+    public void testED25519()
+        throws Exception
+    {
+
+        CipherParameters pubSpec = OpenSSHPublicKeyUtil.parsePublicKey(Base64.decode("AAAAC3NzaC1lZDI1NTE5AAAAIM4CaV7WQcy0lht0hclgXf4Olyvzvv2fnUvQ3J8IYsWF"));
+
+        CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(new PemReader(new StringReader("-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" +
+            "QyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQAAAKBTr4PvU6+D\n" +
+            "7wAAAAtzc2gtZWQyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQ\n" +
+            "AAAED4BTHeR3YD7CFQqusztfL5K+YSD4mRGLBwb7jHiXxIJM4CaV7WQcy0lht0hclgXf4O\n" +
+            "lyvzvv2fnUvQ3J8IYsWFAAAAG21lZ2Fud29vZHNAdHljaGUtMzI2NS5sb2NhbAEC\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n")).readPemObject().getContent());
+
+        Ed25519Signer signer = new Ed25519Signer();
+        signer.init(true, privSpec);
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+        signer.update(originalMessage, 0, originalMessage.length);
+
+        byte[] sig = signer.generateSignature();
+
+        signer.init(false, pubSpec);
+
+        signer.update(originalMessage, 0, originalMessage.length);
+
+
+        isTrue("ED25519Signer test", signer.verifySignature(sig));
+
+    }
+
+
+    public void testFailures()
+        throws Exception
+    {
+        byte[] blob = new PemReader(new StringReader("-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" +
+            "QyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQAAAKBTr4PvU6+D\n" +
+            "7wAAAAtzc2gtZWQyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQ\n" +
+            "AAAED4BTHeR3YD7CFQqusztfL5K+YSD4mRGLBwb7jHiXxIJM4CaV7WQcy0lht0hclgXf4O\n" +
+            "lyvzvv2fnUvQ3J8IYsWFAAAAG21lZ2Fud29vZHNAdHljaGUtMzI2NS5sb2NhbAEC\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n")).readPemObject().getContent();
+
+
+        //
+        // Altering the check value.
+        //
+
+        blob[98] ^= 1;
+
+        try
+        {
+            CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(blob);
+            fail("Change should trigger failure.");
+        }
+        catch (IllegalStateException iles)
+        {
+            isEquals("Check value mismatch ", iles.getMessage(), "private key check values are not the same");
+        }
+
+
+        //
+        // Altering the cipher name.
+        //
+
+
+        blob = new PemReader(new StringReader("-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" +
+            "QyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQAAAKBTr4PvU6+D\n" +
+            "7wAAAAtzc2gtZWQyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQ\n" +
+            "AAAED4BTHeR3YD7CFQqusztfL5K+YSD4mRGLBwb7jHiXxIJM4CaV7WQcy0lht0hclgXf4O\n" +
+            "lyvzvv2fnUvQ3J8IYsWFAAAAG21lZ2Fud29vZHNAdHljaGUtMzI2NS5sb2NhbAEC\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n")).readPemObject().getContent();
+
+
+        blob[19] = (byte)'C';
+
+        try
+        {
+            CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(blob);
+            fail("Change should trigger failure.");
+        }
+        catch (IllegalStateException iles)
+        {
+            isEquals("enc keys not supported ", iles.getMessage(), "encrypted keys not supported");
+        }
+    }
+
+    public String getName()
+    {
+        return "OpenSSHParsing";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        testDSA();
+        testECDSA();
+        testRSA();
+        testED25519();
+        testFailures();
+    }
+
+    public void testRSA()
+        throws Exception
+    {
+        CipherParameters pubSpec = OpenSSHPublicKeyUtil.parsePublicKey(Base64.decode("AAAAB3NzaC1yc2EAAAADAQABAAAAgQDvh2BophdIp8ojwGZQR0FQ/awowXnV24nAPm+/na8MOUrdySNhOnlek4LAZl82/+Eu2t21XD6hQUiHKAj6XaNFBthTuss7Cz/tA348DLEMHD9wUtT0FXVmsxqN4BfusunbcULxxVWG2z8FvqeaGgc/Unkp9y7/kyf54pPUCBcClw=="));
+
+        CipherParameters privSpec = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(new PemReader(new StringReader("-----BEGIN RSA PRIVATE KEY-----\n" +
+            "MIICXgIBAAKBgQDvh2BophdIp8ojwGZQR0FQ/awowXnV24nAPm+/na8MOUrdySNh\n" +
+            "Onlek4LAZl82/+Eu2t21XD6hQUiHKAj6XaNFBthTuss7Cz/tA348DLEMHD9wUtT0\n" +
+            "FXVmsxqN4BfusunbcULxxVWG2z8FvqeaGgc/Unkp9y7/kyf54pPUCBcClwIDAQAB\n" +
+            "AoGBAOMXYEoXHgAeREE9CkOWKtDUkEJbnF0rNSB0kZIDt5BJSTeYmNh3jdYi2FX9\n" +
+            "OMx2MFIx4v0tJZvQvyiUxl5IJJ9ZJsYUWF+6VbcTVwYYfdVzZzP2TNyGmF9/ADZW\n" +
+            "wBehqP04uRlYjt94kqb4HoOKF3gJ3LC4uW9xcEltTBeHWCfhAkEA/2biF5St9/Ya\n" +
+            "540E4zu/FKPsxLSaT8LWCo9+X7IqIzlBQCB4GjM+nZeTm7eZOkfAFZoxwfiNde/9\n" +
+            "qleXXf6B2QJBAPAW+jDBC3QF4/g8n9cDxm/A3ICmcOFSychLSrydk9ZyRPbTRyQC\n" +
+            "YlC2mf/pCrO/yO7h189BXyQ3PXOEhnujce8CQQD7gDy0K90EiH0F94AQpA0OLj5B\n" +
+            "lfc/BAXycEtpwPBtrzvqAg9C/aNzXIgmly10jqNAoo7NDA2BTcrlq0uLa8xBAkBl\n" +
+            "7Hs+I1XnZXDIO4Rn1VRysN9rRj15ipnbDAuoUwUl7tDUMBFteg2e0kZCW/6NHIgC\n" +
+            "0aG6fLgVOdY+qi4lYtfFAkEAqqiBgEgSrDmnJLTm6j/Pv1mBA6b9bJbjOqomrDtr\n" +
+            "AWTXe+/kSCv/jYYdpNA/tDgAwEmtkWWEie6+SwJB5cXXqg==\n" +
+            "-----END RSA PRIVATE KEY-----\n")).readPemObject().getContent());
+
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+
+        originalMessage[0] |= 1;
+
+        RSAEngine rsaEngine = new RSAEngine();
+        rsaEngine.init(true, privSpec);
+
+        byte[] ct = rsaEngine.processBlock(originalMessage, 0, originalMessage.length);
+
+        rsaEngine.init(false, pubSpec);
+        byte[] result = rsaEngine.processBlock(ct, 0, ct.length);
+
+        isTrue("Result did not match original message", Arrays.areEqual(originalMessage, result));
+
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java
index 57688a5..b424e6e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java
@@ -110,7 +110,7 @@
         }
         catch (InvalidCipherTextException e)
         {
-            if (!e.getMessage().equals("wrapped key fails checksum"))
+            if (!e.getMessage().equals("wrapped key corrupted"))
             {
                 fail("wrong exception");
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java
index 54faa94..bc62770 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java
@@ -24,6 +24,83 @@
 public class RSATest
     extends SimpleTest
 {
+    /*
+     * Based on https://github.com/crocs-muni/roca/blob/master/java/BrokenKey.java
+     * Credits: ported to Java by Martin Paljak
+     */
+    static class BrokenKey_CVE_2017_15361
+    {
+        private static final int[] prims = new int[]{ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
+            67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167 };
+        private static final BigInteger[] primes = new BigInteger[prims.length];
+
+        static
+        {
+            for (int i = 0; i < prims.length; i++)
+            {
+                primes[i] = BigInteger.valueOf(prims[i]);
+            }
+        }
+
+        private static final BigInteger[] markers = new BigInteger[]
+        {
+            new BigInteger("6"),
+            new BigInteger("30"),
+            new BigInteger("126"),
+            new BigInteger("1026"),
+            new BigInteger("5658"),
+            new BigInteger("107286"),
+            new BigInteger("199410"),
+            new BigInteger("8388606"),
+            new BigInteger("536870910"),
+            new BigInteger("2147483646"),
+            new BigInteger("67109890"),
+            new BigInteger("2199023255550"),
+            new BigInteger("8796093022206"),
+            new BigInteger("140737488355326"),
+            new BigInteger("5310023542746834"),
+            new BigInteger("576460752303423486"),
+            new BigInteger("1455791217086302986"),
+            new BigInteger("147573952589676412926"),
+            new BigInteger("20052041432995567486"),
+            new BigInteger("6041388139249378920330"),
+            new BigInteger("207530445072488465666"),
+            new BigInteger("9671406556917033397649406"),
+            new BigInteger("618970019642690137449562110"),
+            new BigInteger("79228162521181866724264247298"),
+            new BigInteger("2535301200456458802993406410750"),
+            new BigInteger("1760368345969468176824550810518"),
+            new BigInteger("50079290986288516948354744811034"),
+            new BigInteger("473022961816146413042658758988474"),
+            new BigInteger("10384593717069655257060992658440190"),
+            new BigInteger("144390480366845522447407333004847678774"),
+            new BigInteger("2722258935367507707706996859454145691646"),
+            new BigInteger("174224571863520493293247799005065324265470"),
+            new BigInteger("696898287454081973172991196020261297061886"),
+            new BigInteger("713623846352979940529142984724747568191373310"),
+            new BigInteger("1800793591454480341970779146165214289059119882"),
+            new BigInteger("126304807362733370595828809000324029340048915994"),
+            new BigInteger("11692013098647223345629478661730264157247460343806"),
+            new BigInteger("187072209578355573530071658587684226515959365500926")
+        };
+
+        public static boolean isAffected(RSAKeyParameters publicKey)
+        {
+            BigInteger modulus = publicKey.getModulus();
+
+            for (int i = 0; i < primes.length; i++)
+            {
+                int remainder = modulus.remainder(primes[i]).intValue();
+                if (!markers[i].testBit(remainder))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+
     static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
     static BigInteger pubExp = new BigInteger("11", 16);
     static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
@@ -273,6 +350,26 @@
         }
     }
 
+    private void test_CVE_2017_15361()
+    {
+        SecureRandom random = new SecureRandom();
+        RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+        BigInteger e = BigInteger.valueOf(0x11);
+
+        for (int strength = 512; strength <= 2048; strength += 32)
+        {
+            pGen.init(new RSAKeyGenerationParameters(
+                e, random, strength, 100));
+
+            RSAKeyParameters pubKey = (RSAKeyParameters)pGen.generateKeyPair().getPublic();
+
+            if (BrokenKey_CVE_2017_15361.isAffected(pubKey))
+            {
+                fail("failed CVE-2017-15361 vulnerability test for generated RSA key");
+            }
+        }
+    }
+
     public void performTest()
     {
         RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
@@ -551,6 +648,7 @@
         testMissingDataPKCS1Block(pubParameters, privParameters);
         testTruncatedPKCS1Block(pubParameters, privParameters);
         testWrongPaddingPKCS1Block(pubParameters, privParameters);
+        test_CVE_2017_15361();
 
         try
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java
index 427bb5f..98a859e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java
@@ -5,167 +5,181 @@
 
 public class RegressionTest
 {
-    public static Test[]    tests = 
-    {
-        new AESTest(),
-        new AESLightTest(),
-        new AESFastTest(),
-        new AESWrapTest(),
-        new AESWrapPadTest(),
-        new ARIATest(),
-        new DESTest(),
-        new DESedeTest(),
-        new ModeTest(),
-        new PaddingTest(),
-        new DHTest(),
-        new ElGamalTest(),
-        new DSATest(),
-        new ECTest(),
-        new DeterministicDSATest(),
-        new GOST3410Test(),
-        new ECGOST3410Test(),
-        new ECIESTest(),
-        new ECNRTest(),
-        new MacTest(),
-        new GOST28147MacTest(),
-        new RC2Test(),
-        new RC2WrapTest(),
-        new RC4Test(),
-        new RC5Test(),
-        new RC6Test(),
-        new RijndaelTest(),
-        new SerpentTest(),
-        new TnepresTest(),
-        new CamelliaTest(),
-        new CamelliaLightTest(),
-        new DigestRandomNumberTest(),
-        new SkipjackTest(),
-        new BlowfishTest(),
-        new TwofishTest(),
-        new Threefish256Test(),
-        new Threefish512Test(),
-        new Threefish1024Test(),
-        new SkeinDigestTest(),
-        new SkeinMacTest(),
-        new CAST5Test(),
-        new CAST6Test(),
-        new GOST28147Test(),
-        new IDEATest(),
-        new RSATest(),
-        new RSABlindedTest(),
-        new RSADigestSignerTest(),
-        new PSSBlindTest(),
-        new ISO9796Test(),
-        new ISO9797Alg3MacTest(),
-        new MD2DigestTest(),
-        new MD4DigestTest(),
-        new MD5DigestTest(),
-        new SHA1DigestTest(),
-        new SHA224DigestTest(),
-        new SHA256DigestTest(),
-        new SHA384DigestTest(),
-        new SHA512DigestTest(),
-        new SHA512t224DigestTest(),
-        new SHA512t256DigestTest(),
-        new SHA3DigestTest(),
-        new RIPEMD128DigestTest(),
-        new RIPEMD160DigestTest(),
-        new RIPEMD256DigestTest(),
-        new RIPEMD320DigestTest(),
-        new TigerDigestTest(),
-        new GOST3411DigestTest(),
-        new GOST3411_2012_256DigestTest(),
-        new GOST3411_2012_512DigestTest(),
-        new WhirlpoolDigestTest(),
-        new MD5HMacTest(),
-        new SHA1HMacTest(),
-        new SHA224HMacTest(),
-        new SHA256HMacTest(),
-        new SHA384HMacTest(),
-        new SHA512HMacTest(),
-        new RIPEMD128HMacTest(),
-        new RIPEMD160HMacTest(),
-        new OAEPTest(),
-        new PSSTest(),
-        new CTSTest(),
-        new NISTCTSTest(),
-        new CCMTest(),
-        new PKCS5Test(),
-        new PKCS12Test(),
-        new KDF1GeneratorTest(),
-        new KDF2GeneratorTest(),
-        new MGF1GeneratorTest(),
-        new HKDFGeneratorTest(),
-        new DHKEKGeneratorTest(),
-        new ECDHKEKGeneratorTest(),
-        new ShortenedDigestTest(),
-        new EqualsHashCodeTest(),
-        new TEATest(),
-        new XTEATest(),
-        new RFC3211WrapTest(),
-        new SEEDTest(),
-        new Salsa20Test(),
-        new XSalsa20Test(),
-        new ChaChaTest(),
-        new CMacTest(),
-        new EAXTest(),
-        new GCMTest(),
-        new GMacTest(),
-        new HCFamilyTest(),
-        new HCFamilyVecTest(),
-        new ISAACTest(),
-        new NoekeonTest(),
-        new VMPCKSA3Test(),
-        new VMPCMacTest(),
-        new VMPCTest(),
-        new Grainv1Test(),
-        new Grain128Test(),
-        //new NaccacheSternTest(),
-        new SRP6Test(),
-        new SCryptTest(),
-        new ResetTest(),
-        new NullTest(),
-        new DSTU4145Test(),
-        new SipHashTest(),
-        new Poly1305Test(),
-        new OCBTest(),
-        new NonMemoableDigestTest(),
-        new RSAKeyEncapsulationTest(),
-        new ECIESKeyEncapsulationTest(),
-        new HashCommitmentTest(),
-        new CipherStreamTest(),
-        new BlockCipherResetTest(),
-        new StreamCipherResetTest(),
-        new SM3DigestTest(),
-        new Shacal2Test(),
-        new KDFCounterGeneratorTest(),
-        new KDFDoublePipelineIteratorGeneratorTest(),
-        new KDFFeedbackGeneratorTest(),
-        new CramerShoupTest(),
-        new BCryptTest(),
-        new OpenBSDBCryptTest(),
-        new X931SignerTest(),
-        new Blake2bDigestTest(),
-        new KeccakDigestTest(),
-        new SHAKEDigestTest(),
-        new SM2EngineTest(),
-        new SM2KeyExchangeTest(),
-        new SM2SignerTest(),
-        new SM4Test()
-    };
+    public static Test[] tests =
+        {
+            new AESTest(),
+            new AESLightTest(),
+            new AESFastTest(),
+            new AESWrapTest(),
+            new AESWrapPadTest(),
+            new ARIATest(),
+            new DESTest(),
+            new DESedeTest(),
+            new ModeTest(),
+            new PaddingTest(),
+            new DHTest(),
+            new ElGamalTest(),
+            new DSATest(),
+            new ECTest(),
+            new DeterministicDSATest(),
+            new GOST3410Test(),
+            new ECGOST3410Test(),
+            new ECIESTest(),
+            new ECNRTest(),
+            new MacTest(),
+            new GOST28147MacTest(),
+            new RC2Test(),
+            new RC2WrapTest(),
+            new RC4Test(),
+            new RC5Test(),
+            new RC6Test(),
+            new RijndaelTest(),
+            new SerpentTest(),
+            new TnepresTest(),
+            new CamelliaTest(),
+            new CamelliaLightTest(),
+            new DigestRandomNumberTest(),
+            new SkipjackTest(),
+            new BlowfishTest(),
+            new TwofishTest(),
+            new Threefish256Test(),
+            new Threefish512Test(),
+            new Threefish1024Test(),
+            new SkeinDigestTest(),
+            new SkeinMacTest(),
+            new CAST5Test(),
+            new CAST6Test(),
+            new GOST28147Test(),
+            new IDEATest(),
+            new RSATest(),
+            new RSABlindedTest(),
+            new RSADigestSignerTest(),
+            new PSSBlindTest(),
+            new ISO9796Test(),
+            new ISO9797Alg3MacTest(),
+            new MD2DigestTest(),
+            new MD4DigestTest(),
+            new MD5DigestTest(),
+            new SHA1DigestTest(),
+            new SHA224DigestTest(),
+            new SHA256DigestTest(),
+            new SHA384DigestTest(),
+            new SHA512DigestTest(),
+            new SHA512t224DigestTest(),
+            new SHA512t256DigestTest(),
+            new SHA3DigestTest(),
+            new RIPEMD128DigestTest(),
+            new RIPEMD160DigestTest(),
+            new RIPEMD256DigestTest(),
+            new RIPEMD320DigestTest(),
+            new TigerDigestTest(),
+            new GOST3411DigestTest(),
+            new GOST3411_2012_256DigestTest(),
+            new GOST3411_2012_512DigestTest(),
+            new WhirlpoolDigestTest(),
+            new MD5HMacTest(),
+            new SHA1HMacTest(),
+            new SHA224HMacTest(),
+            new SHA256HMacTest(),
+            new SHA384HMacTest(),
+            new SHA512HMacTest(),
+            new RIPEMD128HMacTest(),
+            new RIPEMD160HMacTest(),
+            new OAEPTest(),
+            new PSSTest(),
+            new CTSTest(),
+            new NISTCTSTest(),
+            new CCMTest(),
+            new PKCS5Test(),
+            new PKCS12Test(),
+            new KDF1GeneratorTest(),
+            new KDF2GeneratorTest(),
+            new MGF1GeneratorTest(),
+            new HKDFGeneratorTest(),
+            new DHKEKGeneratorTest(),
+            new ECDHKEKGeneratorTest(),
+            new ShortenedDigestTest(),
+            new EqualsHashCodeTest(),
+            new TEATest(),
+            new XTEATest(),
+            new RFC3211WrapTest(),
+            new SEEDTest(),
+            new Salsa20Test(),
+            new XSalsa20Test(),
+            new ChaChaTest(),
+            new CMacTest(),
+            new EAXTest(),
+            new GCMTest(),
+            new GMacTest(),
+            new HCFamilyTest(),
+            new HCFamilyVecTest(),
+            new ISAACTest(),
+            new NoekeonTest(),
+            new VMPCKSA3Test(),
+            new VMPCMacTest(),
+            new VMPCTest(),
+            new Grainv1Test(),
+            new Grain128Test(),
+            //new NaccacheSternTest(),
+            new SRP6Test(),
+            new SCryptTest(),
+            new ResetTest(),
+            new NullTest(),
+            new DSTU4145Test(),
+            new SipHashTest(),
+            new Poly1305Test(),
+            new OCBTest(),
+            new NonMemoableDigestTest(),
+            new RSAKeyEncapsulationTest(),
+            new ECIESKeyEncapsulationTest(),
+            new HashCommitmentTest(),
+            new CipherStreamTest(),
+            new BlockCipherResetTest(),
+            new StreamCipherResetTest(),
+            new SM3DigestTest(),
+            new Shacal2Test(),
+            new KDFCounterGeneratorTest(),
+            new KDFDoublePipelineIteratorGeneratorTest(),
+            new KDFFeedbackGeneratorTest(),
+            new CramerShoupTest(),
+            new BCryptTest(),
+            new OpenBSDBCryptTest(),
+            new X931SignerTest(),
+            new Blake2bDigestTest(),
+            new Blake2sDigestTest(),
+            new KeccakDigestTest(),
+            new SHAKEDigestTest(),
+            new SM2EngineTest(),
+            new SM2KeyExchangeTest(),
+            new SM2SignerTest(),
+            new SM4Test(),
+            new DSTU7624Test(),
+            new DSTU7564Test(),
+            new IsoTrailerTest(),
+            new GOST3412Test(),
+            new GOST3412MacTest(),
+            new GSKKDFTest(),
+            new X25519Test(),
+            new X448Test(),
+            new Ed25519Test(),
+            new Ed448Test(),
+            new CSHAKETest(),
+            new Argon2Test(),
+            new OpenSSHKeyParsingTests()
+        };
 
     public static void main(
-        String[]    args)
+        String[] args)
     {
         for (int i = 0; i != tests.length; i++)
         {
-            TestResult  result = tests[i].perform();
-            
+            TestResult result = tests[i].perform();
+
             if (result.getException() != null)
             {
                 result.getException().printStackTrace();
             }
-            
+
             System.out.println(result);
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java
index b017a1d..9e547e5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java
@@ -12,14 +12,16 @@
  * scrypt test vectors from "Stronger Key Derivation Via Sequential Memory-hard Functions" Appendix B.
  * (http://www.tarsnap.com/scrypt/scrypt.pdf)
  */
-public class SCryptTest extends SimpleTest
+public class SCryptTest
+    extends SimpleTest
 {
     public String getName()
     {
         return "SCrypt";
     }
 
-    public void performTest() throws Exception
+    public void performTest()
+        throws Exception
     {
         testParameters();
         testVectors();
@@ -29,8 +31,8 @@
     {
         checkOK("Minimal values", new byte[0], new byte[0], 2, 1, 1, 1);
         checkIllegal("Cost parameter must be > 1", new byte[0], new byte[0], 1, 1, 1, 1);
-        checkOK("Cost parameter 65536 OK for r == 1", new byte[0], new byte[0], 65536, 1, 1, 1);
-        checkIllegal("Cost parameter must <= 65536 for r == 1", new byte[0], new byte[0], 65537, 1, 1, 1);
+        checkOK("Cost parameter 32768 OK for r == 1", new byte[0], new byte[0], 32768, 1, 1, 1);
+        checkIllegal("Cost parameter must < 65536 for r == 1", new byte[0], new byte[0], 65536, 1, 1, 1);
         checkIllegal("Block size must be >= 1", new byte[0], new byte[0], 2, 0, 2, 1);
         checkIllegal("Parallelisation parameter must be >= 1", new byte[0], new byte[0], 2, 1, 0, 1);
         // checkOK("Parallelisation parameter 65535 OK for r = 4", new byte[0], new byte[0], 2, 32,
@@ -38,7 +40,7 @@
         checkIllegal("Parallelisation parameter must be < 65535 for r = 4", new byte[0], new byte[0], 2, 32, 65536, 1);
 
         checkIllegal("Len parameter must be > 1", new byte[0], new byte[0], 2, 1, 1, 0);
-       }
+    }
 
     private void checkOK(String msg, byte[] pass, byte[] salt, int N, int r, int p, int len)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2EngineTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2EngineTest.java
index 82fbefe..a9cbad0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2EngineTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2EngineTest.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.Arrays;
@@ -34,10 +35,11 @@
         BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
         BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
         BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+        BigInteger SM2_ECC_H = ECConstants.ONE;
         BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
         BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
 
-        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
@@ -111,11 +113,11 @@
         BigInteger SM2_ECC_A = new BigInteger("00", 16);
         BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
         BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
         BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
         BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
-        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
 
-        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B);
+        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N, SM2_ECC_H);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2KeyExchangeTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2KeyExchangeTest.java
index f6f694d..c29ec72 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2KeyExchangeTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2KeyExchangeTest.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.crypto.params.ParametersWithID;
 import org.bouncycastle.crypto.params.SM2KeyExchangePrivateParameters;
 import org.bouncycastle.crypto.params.SM2KeyExchangePublicParameters;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.Arrays;
@@ -35,10 +36,11 @@
         BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
         BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
         BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+        BigInteger SM2_ECC_H = ECConstants.ONE;
         BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
         BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
 
-        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
@@ -125,11 +127,11 @@
         BigInteger SM2_ECC_A = new BigInteger("00", 16);
         BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
         BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
         BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
         BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
-        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
 
-        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B);
+        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N, SM2_ECC_H);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2SignerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2SignerTest.java
index 1df48b6..51c9bcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2SignerTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM2SignerTest.java
@@ -1,7 +1,12 @@
 package org.bouncycastle.crypto.test;
 
+import java.io.IOException;
 import java.math.BigInteger;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.params.ECDomainParameters;
@@ -11,6 +16,7 @@
 import org.bouncycastle.crypto.params.ParametersWithID;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.Strings;
@@ -32,10 +38,11 @@
         BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
         BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
         BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+        BigInteger SM2_ECC_H = ECConstants.ONE;
         BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
         BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
-        
-        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+
+        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
@@ -56,14 +63,24 @@
                     new TestRandomBigInteger("6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F", 16)),
                 Strings.toByteArray("ALICE123@YAHOO.COM")));
 
-        BigInteger[] rs = signer.generateSignature(Strings.toByteArray("message digest"));
+        byte[] msg = Strings.toByteArray("message digest");
+
+        signer.update(msg, 0, msg.length);
+
+        byte[] sig = signer.generateSignature();
+
+        BigInteger[] rs = decode(sig);
 
         isTrue("r wrong", rs[0].equals(new BigInteger("40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D1", 16)));
         isTrue("s wrong", rs[1].equals(new BigInteger("6FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7", 16)));
 
+        signer = new SM2Signer();
+
         signer.init(false, new ParametersWithID(ecPub, Strings.toByteArray("ALICE123@YAHOO.COM")));
 
-        isTrue("verification failed", signer.verifySignature(Strings.toByteArray("message digest"), rs[0], rs[1]));
+        signer.update(msg, 0, msg.length);
+
+        isTrue("verification failed", signer.verifySignature(sig));
     }
 
     private void doSignerTestF2m()
@@ -72,10 +89,11 @@
         BigInteger SM2_ECC_A = new BigInteger("00", 16);
         BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
         BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
         BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
         BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
 
-        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B);
+        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
 
         ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
         ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
@@ -96,14 +114,62 @@
                     new TestRandomBigInteger("36CD79FC8E24B7357A8A7B4A46D454C397703D6498158C605399B341ADA186D6", 16)),
                 Strings.toByteArray("ALICE123@YAHOO.COM")));
 
-        BigInteger[] rs = signer.generateSignature(Strings.toByteArray("message digest"));
+        byte[] msg = Strings.toByteArray("message digest");
+
+        signer.update(msg, 0, msg.length);
+
+        byte[] sig = signer.generateSignature();
+
+        BigInteger[] rs = decode(sig);
 
         isTrue("F2m r wrong", rs[0].equals(new BigInteger("6D3FBA26EAB2A1054F5D198332E335817C8AC453ED26D3391CD4439D825BF25B", 16)));
         isTrue("F2m s wrong", rs[1].equals(new BigInteger("3124C5688D95F0A10252A9BED033BEC84439DA384621B6D6FAD77F94B74A9556", 16)));
 
         signer.init(false, new ParametersWithID(ecPub, Strings.toByteArray("ALICE123@YAHOO.COM")));
 
-        isTrue("F2m verification failed", signer.verifySignature(Strings.toByteArray("message digest"), rs[0], rs[1]));
+        signer.update(msg, 0, msg.length);
+
+        isTrue("verification failed", signer.verifySignature(sig));
+    }
+
+    private void doVerifyBoundsCheck()
+        throws IOException
+    {
+        BigInteger SM2_ECC_A = new BigInteger("00", 16);
+        BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
+        BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+        BigInteger SM2_ECC_H = BigInteger.valueOf(4);
+        BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
+        BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
+
+        ECCurve curve = new ECCurve.F2m(257, 12, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
+
+        ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
+        ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+        ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("771EF3DBFF5F1CDC32B9C572930476191998B2BF7CB981D7F5B39202645F0931", 16));
+        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+        keyPairGenerator.init(keyGenerationParams);
+        AsymmetricCipherKeyPair kp = keyPairGenerator.generateKeyPair();
+
+        ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.getPublic();
+
+        SM2Signer signer = new SM2Signer();
+
+        signer.init(false, ecPub);
+
+        signer.update(new byte[20], 0, 20);
+        isTrue(!signer.verifySignature(encode(ECConstants.ZERO, ECConstants.EIGHT)));
+
+        signer.update(new byte[20], 0, 20);
+        isTrue(!signer.verifySignature(encode(ECConstants.EIGHT, ECConstants.ZERO)));
+
+        signer.update(new byte[20], 0, 20);
+        isTrue(!signer.verifySignature(encode(SM2_ECC_N, ECConstants.EIGHT)));
+
+        signer.update(new byte[20], 0, 20);
+        isTrue(!signer.verifySignature(encode(ECConstants.EIGHT, SM2_ECC_N)));
     }
 
     public void performTest()
@@ -111,6 +177,21 @@
     {
         doSignerTestFp();
         doSignerTestF2m();
+        doVerifyBoundsCheck();
+    }
+
+    private static BigInteger[] decode(byte[] sig)
+    {
+        ASN1Sequence s = ASN1Sequence.getInstance(sig);
+
+        return new BigInteger[] { ASN1Integer.getInstance(s.getObjectAt(0)).getValue(),
+            ASN1Integer.getInstance(s.getObjectAt(1)).getValue() };
+    }
+
+    private static byte[] encode(BigInteger r, BigInteger s)
+        throws IOException
+    {
+        return new DERSequence(new ASN1Encodable[] { new ASN1Integer(r), new ASN1Integer(s)}).getEncoded();
     }
 
     public static void main(String[] args)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/X25519Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/X25519Test.java
new file mode 100644
index 0000000..cdf6aaa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/X25519Test.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.agreement.X25519Agreement;
+import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class X25519Test
+    extends SimpleTest
+{
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+    public String getName()
+    {
+        return "X25519";
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new X25519Test());
+    }
+
+    public void performTest()
+    {
+        for (int i = 0; i < 10; ++i)
+        {
+            testAgreement();
+        }
+    }
+
+    private void testAgreement()
+    {
+        AsymmetricCipherKeyPairGenerator kpGen = new X25519KeyPairGenerator();
+        kpGen.init(new X25519KeyGenerationParameters(RANDOM));
+
+        AsymmetricCipherKeyPair kpA = kpGen.generateKeyPair();
+        AsymmetricCipherKeyPair kpB = kpGen.generateKeyPair();
+
+        X25519Agreement agreeA = new X25519Agreement();
+        agreeA.init(kpA.getPrivate());
+        byte[] secretA = new byte[agreeA.getAgreementSize()];
+        agreeA.calculateAgreement(kpB.getPublic(), secretA, 0);
+
+        X25519Agreement agreeB = new X25519Agreement();
+        agreeB.init(kpB.getPrivate());
+        byte[] secretB = new byte[agreeB.getAgreementSize()];
+        agreeB.calculateAgreement(kpA.getPublic(), secretB, 0);
+
+        if (!areEqual(secretA, secretB))
+        {
+            fail("X25519 agreement failed");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/X448Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/X448Test.java
new file mode 100644
index 0000000..476a62f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/X448Test.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.agreement.X448Agreement;
+import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
+import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class X448Test
+    extends SimpleTest
+{
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+    public String getName()
+    {
+        return "X448";
+    }
+
+    public static void main(String[] args)
+    {
+        runTest(new X448Test());
+    }
+
+    public void performTest()
+    {
+        for (int i = 0; i < 10; ++i)
+        {
+            testAgreement();
+        }
+    }
+
+    private void testAgreement()
+    {
+        AsymmetricCipherKeyPairGenerator kpGen = new X448KeyPairGenerator();
+        kpGen.init(new X448KeyGenerationParameters(RANDOM));
+
+        AsymmetricCipherKeyPair kpA = kpGen.generateKeyPair();
+        AsymmetricCipherKeyPair kpB = kpGen.generateKeyPair();
+
+        X448Agreement agreeA = new X448Agreement();
+        agreeA.init(kpA.getPrivate());
+        byte[] secretA = new byte[agreeA.getAgreementSize()];
+        agreeA.calculateAgreement(kpB.getPublic(), secretA, 0);
+
+        X448Agreement agreeB = new X448Agreement();
+        agreeB.init(kpB.getPrivate());
+        byte[] secretB = new byte[agreeB.getAgreementSize()];
+        agreeB.calculateAgreement(kpA.getPublic(), secretB, 0);
+
+        if (!areEqual(secretA, secretB))
+        {
+            fail("X448 agreement failed");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
index 2ad3259..49a0b03 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
@@ -42,6 +42,16 @@
              */
             TlsECCUtils.readSupportedEllipticCurvesExtension(extensionData);
             return true;
+
+        case ExtensionType.ec_point_formats:
+            /*
+             * Exception added based on field reports that some servers send this even when they
+             * didn't negotiate an ECC cipher suite. If present, we still require that it is a valid
+             * ECPointFormatList.
+             */
+            TlsECCUtils.readSupportedPointFormatsExtension(extensionData);
+            return true;
+
         default:
             return false;
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
index efe3ec0..31ee181 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
@@ -98,21 +98,23 @@
 
     public byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length)
     {
-        /*
-         * TODO[session-hash]
-         * 
-         * draft-ietf-tls-session-hash-04 5.4. If a client or server chooses to continue with a full
-         * handshake without the extended master secret extension, [..] the client or server MUST
-         * NOT export any key material based on the new master secret for any subsequent
-         * application-level authentication. In particular, it MUST disable [RFC5705] [..].
-         */
-
         if (context_value != null && !TlsUtils.isValidUint16(context_value.length))
         {
             throw new IllegalArgumentException("'context_value' must have length less than 2^16 (or be null)");
         }
 
         SecurityParameters sp = getSecurityParameters();
+        if (!sp.isExtendedMasterSecret())
+        {
+            /*
+             * RFC 7627 5.4. If a client or server chooses to continue with a full handshake without
+             * the extended master secret extension, [..] the client or server MUST NOT export any
+             * key material based on the new master secret for any subsequent application-level
+             * authentication. In particular, it MUST disable [RFC5705] [..].
+             */
+            throw new IllegalStateException("cannot export keying material without extended_master_secret");
+        }
+
         byte[] cr = sp.getClientRandom(), sr = sp.getServerRandom();
 
         int seedLength = cr.length + sr.length;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
index a4c9c05..aa3fd69 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
@@ -5,6 +5,11 @@
 public abstract class AbstractTlsPeer
     implements TlsPeer
 {
+    public boolean requiresExtendedMasterSecret()
+    {
+        return false;
+    }
+
     public boolean shouldUseGMTUnixTime()
     {
         /*
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
index aa3b36a..0624ddf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java
@@ -3,8 +3,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 
-import org.bouncycastle.crypto.tls.ByteQueue;
-
 public class ByteQueueOutputStream
     extends OutputStream
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
index 38f7d5b..e88de6a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
@@ -118,8 +118,8 @@
         Vector certificate_list = new Vector();
         while (buf.available() > 0)
         {
-            byte[] derEncoding = TlsUtils.readOpaque24(buf);
-            ASN1Primitive asn1Cert = TlsUtils.readDERObject(derEncoding);
+            byte[] berEncoding = TlsUtils.readOpaque24(buf);
+            ASN1Primitive asn1Cert = TlsUtils.readASN1Object(berEncoding);
             certificate_list.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1Cert));
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
index 34a0284..7db6ee3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
@@ -99,7 +99,7 @@
         case CertificateStatusType.ocsp:
             return response instanceof OCSPResponse;
         default:
-            throw new IllegalArgumentException("'statusType' is an unsupported value");
+            throw new IllegalArgumentException("'statusType' is an unsupported CertificateStatusType");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
index b947c48..929348b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
@@ -92,7 +92,7 @@
         case CertificateStatusType.ocsp:
             return request instanceof OCSPStatusRequest;
         default:
-            throw new IllegalArgumentException("'statusType' is an unsupported value");
+            throw new IllegalArgumentException("'statusType' is an unsupported CertificateStatusType");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
index 4f7f1ed..a12c0d1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
@@ -48,7 +48,7 @@
         if (sessionToResume != null && sessionToResume.isResumable())
         {
             SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
-            if (sessionParameters != null)
+            if (sessionParameters != null && sessionParameters.isExtendedMasterSecret())
             {
                 state.tlsSession = sessionToResume;
                 state.sessionParameters = sessionParameters;
@@ -367,6 +367,7 @@
             state.sessionParameters = new SessionParameters.Builder()
                 .setCipherSuite(securityParameters.getCipherSuite())
                 .setCompressionAlgorithm(securityParameters.getCompressionAlgorithm())
+                .setExtendedMasterSecret(securityParameters.isExtendedMasterSecret())
                 .setMasterSecret(securityParameters.getMasterSecret())
                 .setPeerCertificate(serverCertificate)
                 .setPSKIdentity(securityParameters.getPSKIdentity())
@@ -396,8 +397,6 @@
     protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client)
         throws IOException
     {
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-
         ProtocolVersion client_version = client.getClientVersion();
         if (!client_version.isDTLS())
         {
@@ -407,10 +406,8 @@
         TlsClientContextImpl context = state.clientContext;
 
         context.setClientVersion(client_version);
-        TlsUtils.writeVersion(client_version, buf);
 
         SecurityParameters securityParameters = context.getSecurityParameters();
-        buf.write(securityParameters.getClientRandom());
 
         // Session ID
         byte[] session_id = TlsUtils.EMPTY_BYTES;
@@ -422,21 +419,36 @@
                 session_id = TlsUtils.EMPTY_BYTES;
             }
         }
+
+        boolean fallback = client.isFallback();
+
+        state.offeredCipherSuites = client.getCipherSuites();
+
+        if (session_id.length > 0 && state.sessionParameters != null)
+        {
+            if (!state.sessionParameters.isExtendedMasterSecret()
+                || !Arrays.contains(state.offeredCipherSuites, state.sessionParameters.getCipherSuite())
+                || CompressionMethod._null != state.sessionParameters.getCompressionAlgorithm())
+            {
+                session_id = TlsUtils.EMPTY_BYTES;
+            }
+        }
+
+        state.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(client.getClientExtensions());
+
+        TlsExtensionsUtils.addExtendedMasterSecretExtension(state.clientExtensions);
+
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+        TlsUtils.writeVersion(client_version, buf);
+
+        buf.write(securityParameters.getClientRandom());
+
         TlsUtils.writeOpaque8(session_id, buf);
 
         // Cookie
         TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
 
-        boolean fallback = client.isFallback();
-
-        /*
-         * Cipher suites
-         */
-        state.offeredCipherSuites = client.getCipherSuites();
-
-        // Integer -> byte[]
-        state.clientExtensions = client.getClientExtensions();
-
         // Cipher Suites (and SCSV)
         {
             /*
@@ -470,18 +482,10 @@
             TlsUtils.writeUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
         }
 
-        // TODO Add support for compression
-        // Compression methods
-        // state.offeredCompressionMethods = client.getCompressionMethods();
-        state.offeredCompressionMethods = new short[]{ CompressionMethod._null };
-
-        TlsUtils.writeUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);
+        TlsUtils.writeUint8ArrayWithUint8Length(new short[]{ CompressionMethod._null }, buf);
 
         // Extensions
-        if (state.clientExtensions != null)
-        {
-            TlsProtocol.writeExtensions(buf, state.clientExtensions);
-        }
+        TlsProtocol.writeExtensions(buf, state.clientExtensions);
 
         return buf.toByteArray();
     }
@@ -644,7 +648,7 @@
         state.client.notifySelectedCipherSuite(selectedCipherSuite);
 
         short selectedCompressionMethod = TlsUtils.readUint8(buf);
-        if (!Arrays.contains(state.offeredCompressionMethods, selectedCompressionMethod))
+        if (CompressionMethod._null != selectedCompressionMethod)
         {
             throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
@@ -669,6 +673,18 @@
         state.serverExtensions = TlsProtocol.readExtensions(buf);
 
         /*
+         * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+         * master secret [..]. (and see 5.2, 5.3)
+         */
+        securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(state.serverExtensions);
+
+        if (!securityParameters.isExtendedMasterSecret()
+            && (state.resumedSession || state.client.requiresExtendedMasterSecret()))
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
+        /*
          * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
          * extended client hello message. However, see RFC 5746 exception below. We always include
          * the SCSV, so an Extended Server Hello is always allowed.
@@ -765,7 +781,7 @@
         securityParameters.cipherSuite = selectedCipherSuite;
         securityParameters.compressionAlgorithm = selectedCompressionMethod;
 
-        if (sessionServerExtensions != null)
+        if (sessionServerExtensions != null && !sessionServerExtensions.isEmpty())
         {
             {
                 /*
@@ -782,8 +798,6 @@
                 securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
             }
 
-            securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions);
-
             securityParameters.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.resumedSession,
                 sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
 
@@ -802,13 +816,6 @@
                     AlertDescription.illegal_parameter);
         }
 
-        /*
-         * TODO[session-hash]
-         * 
-         * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-         * that do not use the extended master secret [..]. (and see 5.2, 5.3)
-         */
-
         if (sessionClientExtensions != null)
         {
             state.client.processServerExtensions(sessionServerExtensions);
@@ -886,7 +893,6 @@
         SessionParameters sessionParameters = null;
         SessionParameters.Builder sessionParametersBuilder = null;
         int[] offeredCipherSuites = null;
-        short[] offeredCompressionMethods = null;
         Hashtable clientExtensions = null;
         Hashtable serverExtensions = null;
         byte[] selectedSessionID = null;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
index bfec777..eea254b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
@@ -49,6 +49,11 @@
         this.plaintextLimit = plaintextLimit;
     }
 
+    int getReadEpoch()
+    {
+        return readEpoch.getEpoch();
+    }
+
     ProtocolVersion getReadVersion()
     {
         return readVersion;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
index 9c0b8c1..1dfb4db 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
@@ -10,7 +10,8 @@
 
 class DTLSReliableHandshake
 {
-    private final static int MAX_RECEIVE_AHEAD = 10;
+    private final static int MAX_RECEIVE_AHEAD = 16;
+    private static final int MESSAGE_HEADER_LENGTH = 12;
 
     /*
      * No 'final' modifiers so that it works in earlier JDKs
@@ -88,21 +89,7 @@
         if (sending)
         {
             sending = false;
-            prepareInboundFlight();
-        }
-
-        // Check if we already have the next message waiting
-        {
-            DTLSReassembler next = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(next_receive_seq));
-            if (next != null)
-            {
-                byte[] body = next.getBodyIfComplete();
-                if (body != null)
-                {
-                    previousInboundFlight = null;
-                    return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getMsgType(), body));
-                }
-            }
+            prepareInboundFlight(new Hashtable());
         }
 
         byte[] buf = null;
@@ -112,96 +99,32 @@
 
         for (;;)
         {
-            int receiveLimit = recordLayer.getReceiveLimit();
-            if (buf == null || buf.length < receiveLimit)
-            {
-                buf = new byte[receiveLimit];
-            }
-
-            // TODO Handle records containing multiple handshake messages
-
             try
             {
-                for (; ; )
+                for (;;)
                 {
+                    Message pending = getPendingMessage();
+                    if (pending != null)
+                    {
+                        return pending;
+                    }
+
+                    int receiveLimit = recordLayer.getReceiveLimit();
+                    if (buf == null || buf.length < receiveLimit)
+                    {
+                        buf = new byte[receiveLimit];
+                    }
+
                     int received = recordLayer.receive(buf, 0, receiveLimit, readTimeoutMillis);
                     if (received < 0)
                     {
                         break;
                     }
-                    if (received < 12)
-                    {
-                        continue;
-                    }
-                    int fragment_length = TlsUtils.readUint24(buf, 9);
-                    if (received != (fragment_length + 12))
-                    {
-                        continue;
-                    }
-                    int seq = TlsUtils.readUint16(buf, 4);
-                    if (seq > (next_receive_seq + MAX_RECEIVE_AHEAD))
-                    {
-                        continue;
-                    }
-                    short msg_type = TlsUtils.readUint8(buf, 0);
-                    int length = TlsUtils.readUint24(buf, 1);
-                    int fragment_offset = TlsUtils.readUint24(buf, 6);
-                    if (fragment_offset + fragment_length > length)
-                    {
-                        continue;
-                    }
 
-                    if (seq < next_receive_seq)
+                    boolean resentOutbound = processRecord(MAX_RECEIVE_AHEAD, recordLayer.getReadEpoch(), buf, 0, received);
+                    if (resentOutbound)
                     {
-                        /*
-                         * NOTE: If we receive the previous flight of incoming messages in full
-                         * again, retransmit our last flight
-                         */
-                        if (previousInboundFlight != null)
-                        {
-                            DTLSReassembler reassembler = (DTLSReassembler)previousInboundFlight.get(Integers
-                                .valueOf(seq));
-                            if (reassembler != null)
-                            {
-                                reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset,
-                                    fragment_length);
-
-                                if (checkAll(previousInboundFlight))
-                                {
-                                    resendOutboundFlight();
-
-                                    /*
-                                     * TODO[DTLS] implementations SHOULD back off handshake packet
-                                     * size during the retransmit backoff.
-                                     */
-                                    readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000);
-
-                                    resetAll(previousInboundFlight);
-                                }
-                            }
-                        }
-                    }
-                    else
-                    {
-                        DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq));
-                        if (reassembler == null)
-                        {
-                            reassembler = new DTLSReassembler(msg_type, length);
-                            currentInboundFlight.put(Integers.valueOf(seq), reassembler);
-                        }
-
-                        reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset, fragment_length);
-
-                        if (seq == next_receive_seq)
-                        {
-                            byte[] body = reassembler.getBodyIfComplete();
-                            if (body != null)
-                            {
-                                previousInboundFlight = null;
-                                return updateHandshakeMessagesDigest(new Message(next_receive_seq++,
-                                    reassembler.getMsgType(), body));
-                            }
-                        }
+                        readTimeoutMillis = backOff(readTimeoutMillis);
                     }
                 }
             }
@@ -211,12 +134,7 @@
             }
 
             resendOutboundFlight();
-
-            /*
-             * TODO[DTLS] implementations SHOULD back off handshake packet size during the
-             * retransmit backoff.
-             */
-            readTimeoutMillis = Math.min(readTimeoutMillis * 2, 60000);
+            readTimeoutMillis = backOff(readTimeoutMillis);
         }
     }
 
@@ -227,67 +145,27 @@
         {
             checkInboundFlight();
         }
-        else if (currentInboundFlight != null)
+        else
         {
-            /*
-             * RFC 6347 4.2.4. In addition, for at least twice the default MSL defined for [TCP],
-             * when in the FINISHED state, the node that transmits the last flight (the server in an
-             * ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit
-             * of the peer's last flight with a retransmit of the last flight.
-             */
-            retransmit = new DTLSHandshakeRetransmit()
+            prepareInboundFlight(null);
+
+            if (previousInboundFlight != null)
             {
-                public void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len)
-                    throws IOException
+                /*
+                 * RFC 6347 4.2.4. In addition, for at least twice the default MSL defined for [TCP],
+                 * when in the FINISHED state, the node that transmits the last flight (the server in an
+                 * ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit
+                 * of the peer's last flight with a retransmit of the last flight.
+                 */
+                retransmit = new DTLSHandshakeRetransmit()
                 {
-                    /*
-                     * TODO Need to handle the case where the previous inbound flight contains
-                     * messages from two epochs.
-                     */
-                    if (len < 12)
+                    public void receivedHandshakeRecord(int epoch, byte[] buf, int off, int len)
+                        throws IOException
                     {
-                        return;
+                        processRecord(0, epoch, buf, off, len);
                     }
-                    int fragment_length = TlsUtils.readUint24(buf, off + 9);
-                    if (len != (fragment_length + 12))
-                    {
-                        return;
-                    }
-                    int seq = TlsUtils.readUint16(buf, off + 4);
-                    if (seq >= next_receive_seq)
-                    {
-                        return;
-                    }
-
-                    short msg_type = TlsUtils.readUint8(buf, off);
-
-                    // TODO This is a hack that only works until we try to support renegotiation
-                    int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0;
-                    if (epoch != expectedEpoch)
-                    {
-                        return;
-                    }
-
-                    int length = TlsUtils.readUint24(buf, off + 1);
-                    int fragment_offset = TlsUtils.readUint24(buf, off + 6);
-                    if (fragment_offset + fragment_length > length)
-                    {
-                        return;
-                    }
-
-                    DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq));
-                    if (reassembler != null)
-                    {
-                        reassembler.contributeFragment(msg_type, length, buf, off + 12, fragment_offset,
-                            fragment_length);
-                        if (checkAll(currentInboundFlight))
-                        {
-                            resendOutboundFlight();
-                            resetAll(currentInboundFlight);
-                        }
-                    }
-                }
-            };
+                };
+            }
         }
 
         recordLayer.handshakeSuccessful(retransmit);
@@ -298,6 +176,15 @@
         handshakeHash.reset();
     }
 
+    private int backOff(int timeoutMillis)
+    {
+        /*
+         * TODO[DTLS] implementations SHOULD back off handshake packet size during the
+         * retransmit backoff.
+         */
+        return Math.min(timeoutMillis * 2, 60000);
+    }
+
     /**
      * Check that there are no "extra" messages left in the current inbound flight
      */
@@ -314,11 +201,105 @@
         }
     }
 
-    private void prepareInboundFlight()
+    private Message getPendingMessage() throws IOException
+    {
+        DTLSReassembler next = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(next_receive_seq));
+        if (next != null)
+        {
+            byte[] body = next.getBodyIfComplete();
+            if (body != null)
+            {
+                previousInboundFlight = null;
+                return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getMsgType(), body));
+            }
+        }
+        return null;
+    }
+
+    private void prepareInboundFlight(Hashtable nextFlight)
     {
         resetAll(currentInboundFlight);
         previousInboundFlight = currentInboundFlight;
-        currentInboundFlight = new Hashtable();
+        currentInboundFlight = nextFlight;
+    }
+
+    private boolean processRecord(int windowSize, int epoch, byte[] buf, int off, int len) throws IOException
+    {
+        boolean checkPreviousFlight = false;
+
+        while (len >= MESSAGE_HEADER_LENGTH)
+        {
+            int fragment_length = TlsUtils.readUint24(buf, off + 9);
+            int message_length = fragment_length + MESSAGE_HEADER_LENGTH;
+            if (len < message_length)
+            {
+                // NOTE: Truncated message - ignore it
+                break;
+            }
+
+            int length = TlsUtils.readUint24(buf, off + 1);
+            int fragment_offset = TlsUtils.readUint24(buf, off + 6);
+            if (fragment_offset + fragment_length > length)
+            {
+                // NOTE: Malformed fragment - ignore it and the rest of the record
+                break;
+            }
+
+            /*
+             * NOTE: This very simple epoch check will only work until we want to support
+             * renegotiation (and we're not likely to do that anyway).
+             */
+            short msg_type = TlsUtils.readUint8(buf, off + 0);
+            int expectedEpoch = msg_type == HandshakeType.finished ? 1 : 0;
+            if (epoch != expectedEpoch)
+            {
+                break;
+            }
+
+            int message_seq = TlsUtils.readUint16(buf, off + 4);
+            if (message_seq >= (next_receive_seq + windowSize))
+            {
+                // NOTE: Too far ahead - ignore
+            }
+            else if (message_seq >= next_receive_seq)
+            {
+                DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(message_seq));
+                if (reassembler == null)
+                {
+                    reassembler = new DTLSReassembler(msg_type, length);
+                    currentInboundFlight.put(Integers.valueOf(message_seq), reassembler);
+                }
+
+                reassembler.contributeFragment(msg_type, length, buf, off + MESSAGE_HEADER_LENGTH, fragment_offset,
+                    fragment_length);
+            }
+            else if (previousInboundFlight != null)
+            {
+                /*
+                 * NOTE: If we receive the previous flight of incoming messages in full again,
+                 * retransmit our last flight
+                 */
+
+                DTLSReassembler reassembler = (DTLSReassembler)previousInboundFlight.get(Integers.valueOf(message_seq));
+                if (reassembler != null)
+                {
+                    reassembler.contributeFragment(msg_type, length, buf, off + MESSAGE_HEADER_LENGTH, fragment_offset,
+                        fragment_length);
+                    checkPreviousFlight = true;
+                }
+            }
+
+            off += message_length;
+            len -= message_length;
+        }
+
+        boolean result = checkPreviousFlight && checkAll(previousInboundFlight);
+        if (result)
+        {
+            resendOutboundFlight();
+            resetAll(previousInboundFlight);
+        }
+        return result;
     }
 
     private void resendOutboundFlight()
@@ -337,7 +318,7 @@
         if (message.getType() != HandshakeType.hello_request)
         {
             byte[] body = message.getBody();
-            byte[] buf = new byte[12];
+            byte[] buf = new byte[MESSAGE_HEADER_LENGTH];
             TlsUtils.writeUint8(message.getType(), buf, 0);
             TlsUtils.writeUint24(body.length, buf, 1);
             TlsUtils.writeUint16(message.getSeq(), buf, 4);
@@ -353,7 +334,7 @@
         throws IOException
     {
         int sendLimit = recordLayer.getSendLimit();
-        int fragmentLimit = sendLimit - 12;
+        int fragmentLimit = sendLimit - MESSAGE_HEADER_LENGTH;
 
         // TODO Support a higher minimum fragment size?
         if (fragmentLimit < 1)
@@ -378,7 +359,7 @@
     private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length)
         throws IOException
     {
-        RecordLayerBuffer fragment = new RecordLayerBuffer(12 + fragment_length);
+        RecordLayerBuffer fragment = new RecordLayerBuffer(MESSAGE_HEADER_LENGTH + fragment_length);
         TlsUtils.writeUint8(message.getType(), fragment);
         TlsUtils.writeUint24(message.getBody().length, fragment);
         TlsUtils.writeUint16(message.getSeq(), fragment);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
index a61bf1f..6e3f79e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
@@ -283,6 +283,24 @@
 
         handshake.finish();
 
+//        {
+//            state.sessionParameters = new SessionParameters.Builder()
+//                .setCipherSuite(securityParameters.getCipherSuite())
+//                .setCompressionAlgorithm(securityParameters.getCompressionAlgorithm())
+//                .setExtendedMasterSecret(securityParameters.isExtendedMasterSecret())
+//                .setMasterSecret(securityParameters.getMasterSecret())
+//                .setPeerCertificate(state.clientCertificate)
+//                .setPSKIdentity(securityParameters.getPSKIdentity())
+//                .setSRPIdentity(securityParameters.getSRPIdentity())
+//                // TODO Consider filtering extensions that aren't relevant to resumed sessions
+//                .setServerExtensions(state.serverExtensions)
+//                .build();
+//
+//            state.tlsSession = TlsUtils.importSession(state.tlsSession.getSessionID(), state.sessionParameters);
+//
+//            state.serverContext.setResumableSession(state.tlsSession);
+//        }
+
         state.server.notifyHandshakeComplete();
 
         return new DTLSTransport(recordLayer);
@@ -364,7 +382,7 @@
         TlsUtils.writeUint16(selectedCipherSuite, buf);
         TlsUtils.writeUint8(selectedCompressionMethod, buf);
 
-        state.serverExtensions = state.server.getServerExtensions();
+        state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.server.getServerExtensions());
 
         /*
          * RFC 5746 3.6. Server Behavior: Initial Handshake
@@ -388,15 +406,13 @@
                  * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                  * "renegotiation_info" extension in the ServerHello message.
                  */
-                state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
                 state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo,
                     TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
             }
         }
 
-        if (securityParameters.extendedMasterSecret)
+        if (securityParameters.isExtendedMasterSecret())
         {
-            state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
             TlsExtensionsUtils.addExtendedMasterSecretExtension(state.serverExtensions);
         }
 
@@ -406,7 +422,7 @@
          * extensions.
          */
 
-        if (state.serverExtensions != null)
+        if (!state.serverExtensions.isEmpty())
         {
             securityParameters.encryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(state.serverExtensions);
 
@@ -623,12 +639,18 @@
         SecurityParameters securityParameters = context.getSecurityParameters();
 
         /*
-         * TODO[session-hash]
-         * 
-         * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-         * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+         * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
+         */
+
+        /*
+         * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+         * master secret [..]. (and see 5.2, 5.3)
          */
         securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(state.clientExtensions);
+        if (!securityParameters.isExtendedMasterSecret() && state.server.requiresExtendedMasterSecret())
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
 
         context.setClientVersion(client_version);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
index a5b44a6..110531c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
@@ -5,14 +5,23 @@
 public abstract class DefaultTlsClient
     extends AbstractTlsClient
 {
+    protected TlsDHVerifier dhVerifier;
+
     public DefaultTlsClient()
     {
-        super();
+        this(new DefaultTlsCipherFactory());
     }
 
     public DefaultTlsClient(TlsCipherFactory cipherFactory)
     {
+        this(cipherFactory, new DefaultTlsDHVerifier());
+    }
+
+    public DefaultTlsClient(TlsCipherFactory cipherFactory, TlsDHVerifier dhVerifier)
+    {
         super(cipherFactory);
+
+        this.dhVerifier = dhVerifier;
     }
 
     public int[] getCipherSuites()
@@ -25,12 +34,6 @@
             CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
             CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
             CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
-            CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
-            CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
-            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
             CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
             CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
             CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
@@ -77,12 +80,12 @@
 
     protected TlsKeyExchange createDHKeyExchange(int keyExchange)
     {
-        return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, null);
+        return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, dhVerifier, null);
     }
 
     protected TlsKeyExchange createDHEKeyExchange(int keyExchange)
     {
-        return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, null);
+        return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, dhVerifier, null);
     }
 
     protected TlsKeyExchange createECDHKeyExchange(int keyExchange)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsDHVerifier.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsDHVerifier.java
new file mode 100644
index 0000000..5994f4a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsDHVerifier.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.crypto.tls;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.agreement.DHStandardGroups;
+import org.bouncycastle.crypto.params.DHParameters;
+
+public class DefaultTlsDHVerifier
+    implements TlsDHVerifier
+{
+    public static final int DEFAULT_MINIMUM_PRIME_BITS = 2048;
+
+    protected static final Vector DEFAULT_GROUPS = new Vector();
+
+    private static void addDefaultGroup(DHParameters dhParameters)
+    {
+        DEFAULT_GROUPS.addElement(dhParameters);
+    }
+
+    static
+    {
+        addDefaultGroup(DHStandardGroups.rfc7919_ffdhe2048);
+        addDefaultGroup(DHStandardGroups.rfc7919_ffdhe3072);
+        addDefaultGroup(DHStandardGroups.rfc7919_ffdhe4096);
+        addDefaultGroup(DHStandardGroups.rfc7919_ffdhe6144);
+        addDefaultGroup(DHStandardGroups.rfc7919_ffdhe8192);
+
+        addDefaultGroup(DHStandardGroups.rfc3526_1536);
+        addDefaultGroup(DHStandardGroups.rfc3526_2048);
+        addDefaultGroup(DHStandardGroups.rfc3526_3072);
+        addDefaultGroup(DHStandardGroups.rfc3526_4096);
+        addDefaultGroup(DHStandardGroups.rfc3526_6144);
+        addDefaultGroup(DHStandardGroups.rfc3526_8192);
+    }
+
+    // Vector is (DHParameters)
+    protected Vector groups;
+    protected int minimumPrimeBits;
+
+    /**
+     * Accept various standard DH groups with 'P' at least {@link #DEFAULT_MINIMUM_PRIME_BITS} bits.
+     */
+    public DefaultTlsDHVerifier()
+    {
+        this(DEFAULT_MINIMUM_PRIME_BITS);
+    }
+
+    /**
+     * Accept various standard DH groups with 'P' at least the specified number of bits.
+     */
+    public DefaultTlsDHVerifier(int minimumPrimeBits)
+    {
+        this(DEFAULT_GROUPS, minimumPrimeBits);
+    }
+
+    /**
+     * Accept a custom set of group parameters, subject to a minimum bitlength for 'P'.
+     * 
+     * @param groups a {@link Vector} of acceptable {@link DHParameters}.
+     */
+    public DefaultTlsDHVerifier(Vector groups, int minimumPrimeBits)
+    {
+        this.groups = groups;
+        this.minimumPrimeBits = minimumPrimeBits;
+    }
+
+    public boolean accept(DHParameters dhParameters)
+    {
+        return checkMinimumPrimeBits(dhParameters) && checkGroup(dhParameters);
+    }
+
+    public int getMinimumPrimeBits()
+    {
+        return minimumPrimeBits;
+    }
+
+    protected boolean areGroupsEqual(DHParameters a, DHParameters b)
+    {
+        return a == b || (areParametersEqual(a.getP(), b.getP()) && areParametersEqual(a.getG(), b.getG()));
+    }
+
+    protected boolean areParametersEqual(BigInteger a, BigInteger b)
+    {
+        return a == b || a.equals(b);
+    }
+
+    protected boolean checkGroup(DHParameters dhParameters)
+    {
+        for (int i = 0; i < groups.size(); ++i)
+        {
+            if (areGroupsEqual(dhParameters, (DHParameters)groups.elementAt(i)))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected boolean checkMinimumPrimeBits(DHParameters dhParameters)
+    {
+        return dhParameters.getP().bitLength() >= getMinimumPrimeBits();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
index 2adfab4..e9f76a2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
@@ -44,7 +44,7 @@
 
     protected DHParameters getDHParameters()
     {
-        return DHStandardGroups.rfc3526_2048;
+        return DHStandardGroups.rfc7919_ffdhe2048;
     }
 
     protected int[] getCipherSuites()
@@ -142,12 +142,12 @@
 
     protected TlsKeyExchange createDHKeyExchange(int keyExchange)
     {
-        return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters());
+        return new TlsDHKeyExchange(keyExchange, supportedSignatureAlgorithms, null, getDHParameters());
     }
 
     protected TlsKeyExchange createDHEKeyExchange(int keyExchange)
     {
-        return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, getDHParameters());
+        return new TlsDHEKeyExchange(keyExchange, supportedSignatureAlgorithms, null, getDHParameters());
     }
 
     protected TlsKeyExchange createECDHKeyExchange(int keyExchange)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
index a45c3b9..a8df994 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
@@ -55,10 +55,9 @@
     public static final int CAMELLIA_256_GCM = 20;
 
     /*
-     * draft-ietf-tls-chacha20-poly1305-04
+     * RFC 7905
      */
-    public static final int CHACHA20_POLY1305 = 102;
-    /** @deprecated */ public static final int AEAD_CHACHA20_POLY1305 = CHACHA20_POLY1305;
+    public static final int CHACHA20_POLY1305 = 21;
 
     /*
      * draft-zauner-tls-aes-ocb-04
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
index 6937efe..a3d7d55 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
@@ -30,7 +30,7 @@
     public static final String dtls_srtp = "EXTRACTOR-dtls_srtp";
 
     /*
-     * draft-ietf-tls-session-hash-04
+     * RFC 7627
      */
     public static final String extended_master_secret = "extended master secret";
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
index 5bc9eee..fdf79eb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
@@ -97,6 +97,16 @@
     public static final int extended_master_secret = 23;
 
     /*
+     * draft-ietf-tokbind-negotiation-08
+     */
+    public static final int DRAFT_token_binding = 24;
+
+    /*
+     * RFC 7924
+     */
+    public static final int cached_info = 25;
+
+    /*
      * RFC 5077 7.
      */
     public static final int session_ticket = 35;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
index f68e3c7..ac1ae40 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
@@ -43,6 +43,22 @@
 
     public static boolean isPrivate(short hashAlgorithm)
     {
-        return 224 <= hashAlgorithm && hashAlgorithm <= 255; 
+        return 224 <= hashAlgorithm && hashAlgorithm <= 255;
+    }
+
+    public static boolean isRecognized(short hashAlgorithm)
+    {
+        switch (hashAlgorithm)
+        {
+        case md5:
+        case sha1:
+        case sha224:
+        case sha256:
+        case sha384:
+        case sha512:
+            return true;
+        default:
+            return false;
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
index e77bde0..a38035e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
@@ -5,6 +5,7 @@
 public class PSKTlsClient
     extends AbstractTlsClient
 {
+    protected TlsDHVerifier dhVerifier;
     protected TlsPSKIdentity pskIdentity;
 
     public PSKTlsClient(TlsPSKIdentity pskIdentity)
@@ -14,7 +15,14 @@
 
     public PSKTlsClient(TlsCipherFactory cipherFactory, TlsPSKIdentity pskIdentity)
     {
+        this(cipherFactory, new DefaultTlsDHVerifier(), pskIdentity);
+    }
+
+    public PSKTlsClient(TlsCipherFactory cipherFactory, TlsDHVerifier dhVerifier, TlsPSKIdentity pskIdentity)
+    {
         super(cipherFactory);
+
+        this.dhVerifier = dhVerifier;
         this.pskIdentity = pskIdentity;
     }
 
@@ -24,8 +32,6 @@
         {
             CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
             CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
-            CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
         };
     }
 
@@ -62,7 +68,7 @@
 
     protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
     {
-        return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, null, namedCurves,
-            clientECPointFormats, serverECPointFormats);
+        return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, dhVerifier, null,
+            namedCurves, clientECPointFormats, serverECPointFormats);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java
index 181e982..091e49d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java
@@ -28,7 +28,7 @@
 
     protected DHParameters getDHParameters()
     {
-        return DHStandardGroups.rfc3526_2048;
+        return DHStandardGroups.rfc7919_ffdhe2048;
     }
 
     protected int[] getCipherSuites()
@@ -87,6 +87,6 @@
     protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
     {
         return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, null, pskIdentityManager,
-            getDHParameters(), namedCurves, clientECPointFormats, serverECPointFormats);
+            null, getDHParameters(), namedCurves, clientECPointFormats, serverECPointFormats);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
index 8a942bb..0250dcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
@@ -23,7 +23,7 @@
     private OutputStream output;
     private TlsCompression pendingCompression = null, readCompression = null, writeCompression = null;
     private TlsCipher pendingCipher = null, readCipher = null, writeCipher = null;
-    private long readSeqNo = 0, writeSeqNo = 0;
+    private SequenceNumber readSeqNo = new SequenceNumber(), writeSeqNo = new SequenceNumber();
     private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 
     private TlsHandshakeHash handshakeHash = null;
@@ -113,7 +113,7 @@
         }
         this.writeCompression = this.pendingCompression;
         this.writeCipher = this.pendingCipher;
-        this.writeSeqNo = 0;
+        this.writeSeqNo = new SequenceNumber();
     }
 
     void receivedReadCipherSpec()
@@ -125,7 +125,7 @@
         }
         this.readCompression = this.pendingCompression;
         this.readCipher = this.pendingCipher;
-        this.readSeqNo = 0;
+        this.readSeqNo = new SequenceNumber();
     }
 
     void finaliseHandshake()
@@ -227,7 +227,9 @@
         throws IOException
     {
         byte[] buf = TlsUtils.readFully(len, input);
-        byte[] decoded = readCipher.decodeCiphertext(readSeqNo++, type, buf, 0, buf.length);
+
+        long seqNo = readSeqNo.nextValue(AlertDescription.unexpected_message);
+        byte[] decoded = readCipher.decodeCiphertext(seqNo, type, buf, 0, buf.length);
 
         checkLength(decoded.length, compressedLimit, AlertDescription.record_overflow);
 
@@ -293,10 +295,12 @@
 
         OutputStream cOut = writeCompression.compress(buffer);
 
+        long seqNo = writeSeqNo.nextValue(AlertDescription.internal_error);
+
         byte[] ciphertext;
         if (cOut == buffer)
         {
-            ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, plaintext, plaintextOffset, plaintextLength);
+            ciphertext = writeCipher.encodePlaintext(seqNo, type, plaintext, plaintextOffset, plaintextLength);
         }
         else
         {
@@ -310,7 +314,7 @@
              */
             checkLength(compressed.length, plaintextLength + 1024, AlertDescription.internal_error);
 
-            ciphertext = writeCipher.encodePlaintext(writeSeqNo++, type, compressed, 0, compressed.length);
+            ciphertext = writeCipher.encodePlaintext(seqNo, type, compressed, 0, compressed.length);
         }
 
         /*
@@ -405,4 +409,24 @@
             throw new TlsFatalAlert(alertDescription);
         }
     }
+
+    private static class SequenceNumber
+    {
+        private long value = 0L;
+        private boolean exhausted = false;
+
+        synchronized long nextValue(short alertDescription) throws TlsFatalAlert
+        {
+            if (exhausted)
+            {
+                throw new TlsFatalAlert(alertDescription);
+            }
+            long result = value;
+            if (++value == 0)
+            {
+                exhausted = true;
+            }
+            return result;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
index 84cc3cc..b523c95 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
@@ -105,4 +105,9 @@
     {
         return srpIdentity;
     }
+
+    public boolean isExtendedMasterSecret()
+    {
+        return extendedMasterSecret;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
deleted file mode 100644
index 911338e..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-
-import org.bouncycastle.crypto.params.DHParameters;
-import org.bouncycastle.crypto.params.DHPublicKeyParameters;
-
-public class ServerDHParams
-{
-    protected DHPublicKeyParameters publicKey;
-
-    public ServerDHParams(DHPublicKeyParameters publicKey)
-    {
-        if (publicKey == null)
-        {
-            throw new IllegalArgumentException("'publicKey' cannot be null");
-        }
-
-        this.publicKey = publicKey;
-    }
-
-    public DHPublicKeyParameters getPublicKey()
-    {
-        return publicKey;
-    }
-
-    /**
-     * Encode this {@link ServerDHParams} to an {@link OutputStream}.
-     * 
-     * @param output
-     *            the {@link OutputStream} to encode to.
-     * @throws IOException
-     */
-    public void encode(OutputStream output) throws IOException
-    {
-        DHParameters dhParameters = publicKey.getParameters();
-        BigInteger Ys = publicKey.getY();
-
-        TlsDHUtils.writeDHParameter(dhParameters.getP(), output);
-        TlsDHUtils.writeDHParameter(dhParameters.getG(), output);
-        TlsDHUtils.writeDHParameter(Ys, output);
-    }
-
-    /**
-     * Parse a {@link ServerDHParams} from an {@link InputStream}.
-     * 
-     * @param input
-     *            the {@link InputStream} to parse from.
-     * @return a {@link ServerDHParams} object.
-     * @throws IOException
-     */
-    public static ServerDHParams parse(InputStream input) throws IOException
-    {
-        BigInteger p = TlsDHUtils.readDHParameter(input);
-        BigInteger g = TlsDHUtils.readDHParameter(input);
-        BigInteger Ys = TlsDHUtils.readDHParameter(input);
-
-        return new ServerDHParams(TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g))));
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
index 68015d7..656214c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
@@ -104,7 +104,7 @@
         case NameType.host_name:
             return name instanceof String;
         default:
-            throw new IllegalArgumentException("'name' is an unsupported value");
+            throw new IllegalArgumentException("'nameType' is an unsupported NameType");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
index 983df60..4ed5993 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
@@ -18,6 +18,7 @@
         private byte[] pskIdentity = null;
         private byte[] srpIdentity = null;
         private byte[] encodedServerExtensions = null;
+        private boolean extendedMasterSecret = false;
 
         public Builder()
         {
@@ -29,7 +30,7 @@
             validate(this.compressionAlgorithm >= 0, "compressionAlgorithm");
             validate(this.masterSecret != null, "masterSecret");
             return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, pskIdentity,
-                srpIdentity, encodedServerExtensions);
+                srpIdentity, encodedServerExtensions, extendedMasterSecret);
         }
 
         public Builder setCipherSuite(int cipherSuite)
@@ -44,6 +45,12 @@
             return this;
         }
 
+        public Builder setExtendedMasterSecret(boolean extendedMasterSecret)
+        {
+            this.extendedMasterSecret = extendedMasterSecret;
+            return this;
+        }
+
         public Builder setMasterSecret(byte[] masterSecret)
         {
             this.masterSecret = masterSecret;
@@ -108,9 +115,11 @@
     private byte[] pskIdentity = null;
     private byte[] srpIdentity = null;
     private byte[] encodedServerExtensions;
+    private boolean extendedMasterSecret;
 
     private SessionParameters(int cipherSuite, short compressionAlgorithm, byte[] masterSecret,
-        Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions)
+        Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions,
+        boolean extendedMasterSecret)
     {
         this.cipherSuite = cipherSuite;
         this.compressionAlgorithm = compressionAlgorithm;
@@ -119,6 +128,7 @@
         this.pskIdentity = Arrays.clone(pskIdentity);
         this.srpIdentity = Arrays.clone(srpIdentity);
         this.encodedServerExtensions = encodedServerExtensions;
+        this.extendedMasterSecret = extendedMasterSecret;
     }
 
     public void clear()
@@ -132,7 +142,7 @@
     public SessionParameters copy()
     {
         return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, pskIdentity,
-            srpIdentity, encodedServerExtensions);
+            srpIdentity, encodedServerExtensions, extendedMasterSecret);
     }
 
     public int getCipherSuite()
@@ -173,6 +183,11 @@
         return srpIdentity;
     }
 
+    public boolean isExtendedMasterSecret()
+    {
+        return extendedMasterSecret;
+    }
+
     public Hashtable readServerExtensions() throws IOException
     {
         if (encodedServerExtensions == null)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
index c2c591c..918b683 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
@@ -162,13 +162,22 @@
 
         int padding_length = blockSize - 1 - (enc_input_length % blockSize);
 
-        // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
-        if (!version.isDTLS() && !version.isSSL())
+        /*
+         * Don't use variable-length padding with truncated MACs.
+         * 
+         * See "Tag Size Does Matter: Attacks and Proofs for the TLS Record Protocol", Paterson,
+         * Ristenpart, Shrimpton.
+         */
+        if (encryptThenMAC || !context.getSecurityParameters().truncatedHMac)
         {
-            // Add a random number of extra blocks worth of padding
-            int maxExtraPadBlocks = (255 - padding_length) / blockSize;
-            int actualExtraPadBlocks = chooseExtraPadBlocks(context.getSecureRandom(), maxExtraPadBlocks);
-            padding_length += actualExtraPadBlocks * blockSize;
+            // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
+            if (!version.isDTLS() && !version.isSSL())
+            {
+                // Add a random number of extra blocks worth of padding
+                int maxExtraPadBlocks = (255 - padding_length) / blockSize;
+                int actualExtraPadBlocks = chooseExtraPadBlocks(context.getSecureRandom(), maxExtraPadBlocks);
+                padding_length += actualExtraPadBlocks * blockSize;
+            }
         }
 
         int totalSize = len + macSize + padding_length + 1;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
index 6bce001..b96845b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
@@ -23,7 +23,7 @@
     TlsSession getSessionToResume();
 
     /**
-     * Return the {@link ProtocolVersion} to use for the <c>TLSPlaintext.version</c> field prior to
+     * Return the {@link ProtocolVersion} to use for the <code>TLSPlaintext.version</code> field prior to
      * receiving the server version. NOTE: This method is <b>not</b> called for DTLS.
      *
      * <p>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
index 45a62b8..7680a8d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
@@ -39,15 +39,13 @@
     /**
      * Constructor for non-blocking mode.<br>
      * <br>
-     * When data is received, use {@link #offerInput(java.nio.ByteBuffer)} to
-     * provide the received ciphertext, then use
-     * {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br>
+     * When data is received, use {@link #offerInput(byte[])} to provide the received ciphertext,
+     * then use {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br>
      * <br>
-     * Similarly, when data needs to be sent, use
-     * {@link #offerOutput(byte[], int, int)} to provide the cleartext, then use
-     * {@link #readOutput(byte[], int, int)} to get the corresponding
+     * Similarly, when data needs to be sent, use {@link #offerOutput(byte[], int, int)} to provide
+     * the cleartext, then use {@link #readOutput(byte[], int, int)} to get the corresponding
      * ciphertext.
-     * 
+     *
      * @param secureRandom
      *            Random number generator for various cryptographic functions
      */
@@ -94,7 +92,7 @@
         if (sessionToResume != null && sessionToResume.isResumable())
         {
             SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
-            if (sessionParameters != null)
+            if (sessionParameters != null && sessionParameters.isExtendedMasterSecret())
             {
                 this.tlsSession = sessionToResume;
                 this.sessionParameters = sessionParameters;
@@ -146,6 +144,7 @@
             processFinishedMessage(buf);
             this.connection_state = CS_SERVER_FINISHED;
 
+            sendChangeCipherSpecMessage();
             sendFinishedMessage();
             this.connection_state = CS_CLIENT_FINISHED;
 
@@ -269,8 +268,6 @@
                 {
                     this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret());
                     this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
-
-                    sendChangeCipherSpecMessage();
                 }
                 else
                 {
@@ -655,7 +652,7 @@
         this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod);
 
         /*
-         * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+         * RFC 3546 2.2 The extended server hello message format MAY be sent in place of the server
          * hello message when the client has requested extended functionality via the extended
          * client hello message specified in Section 2.1. ... Note that the extended server hello
          * message is only sent in response to an extended client hello message. This prevents the
@@ -665,6 +662,19 @@
         this.serverExtensions = readExtensions(buf);
 
         /*
+         * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+         * master secret [..]. (and see 5.2, 5.3)
+         */
+        this.securityParameters.extendedMasterSecret = !TlsUtils.isSSL(tlsClientContext)
+            && TlsExtensionsUtils.hasExtendedMasterSecretExtension(serverExtensions);
+
+        if (!securityParameters.isExtendedMasterSecret()
+            && (resumedSession || tlsClient.requiresExtendedMasterSecret()))
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
+        /*
          * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
          * extended client hello message.
          * 
@@ -762,7 +772,7 @@
         this.securityParameters.cipherSuite = selectedCipherSuite;
         this.securityParameters.compressionAlgorithm = selectedCompressionMethod;
 
-        if (sessionServerExtensions != null)
+        if (sessionServerExtensions != null && !sessionServerExtensions.isEmpty())
         {
             {
                 /*
@@ -779,8 +789,6 @@
                 this.securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
             }
 
-            this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions);
-
             this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions,
                 sessionServerExtensions, AlertDescription.illegal_parameter);
 
@@ -799,13 +807,6 @@
                     AlertDescription.illegal_parameter);
         }
 
-        /*
-         * TODO[session-hash]
-         * 
-         * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-         * that do not use the extended master secret [..]. (and see 5.2, 5.3)
-         */
-        
         if (sessionClientExtensions != null)
         {
             this.tlsClient.processServerExtensions(sessionServerExtensions);
@@ -867,14 +868,20 @@
 
         if (session_id.length > 0 && this.sessionParameters != null)
         {
-            if (!Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite())
+            if (!sessionParameters.isExtendedMasterSecret()
+                || !Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite())
                 || !Arrays.contains(this.offeredCompressionMethods, sessionParameters.getCompressionAlgorithm()))
             {
                 session_id = TlsUtils.EMPTY_BYTES;
             }
         }
 
-        this.clientExtensions = this.tlsClient.getClientExtensions();
+        this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.tlsClient.getClientExtensions());
+
+        if (!client_version.isSSL())
+        {
+            TlsExtensionsUtils.addExtendedMasterSecretExtension(this.clientExtensions);
+        }
 
         HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
 
@@ -899,8 +906,6 @@
             if (noRenegExt && noRenegSCSV)
             {
                 // TODO Consider whether to default to a client extension instead
-//                this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions);
-//                this.clientExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
                 this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
             }
 
@@ -921,10 +926,7 @@
 
         TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message);
 
-        if (clientExtensions != null)
-        {
-            writeExtensions(message, clientExtensions);
-        }
+        writeExtensions(message, clientExtensions);
 
         message.writeToRecordStream();
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
index f431230..6650cc4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
 import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import org.bouncycastle.util.io.TeeInputStream;
 
 public class TlsDHEKeyExchange
@@ -14,9 +15,17 @@
 {
     protected TlsSignerCredentials serverCredentials = null;
 
+    /**
+     * @deprecated Use constructor that takes a TlsDHVerifier
+     */
     public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
     {
-        super(keyExchange, supportedSignatureAlgorithms, dhParameters);
+        this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsDHVerifier(), dhParameters);
+    }
+
+    public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsDHVerifier dhVerifier, DHParameters dhParameters)
+    {
+        super(keyExchange, supportedSignatureAlgorithms, dhVerifier, dhParameters);
     }
 
     public void processServerCredentials(TlsCredentials serverCredentials)
@@ -77,7 +86,8 @@
         SignerInputBuffer buf = new SignerInputBuffer();
         InputStream teeIn = new TeeInputStream(input, buf);
 
-        ServerDHParams dhParams = ServerDHParams.parse(teeIn);
+        this.dhParameters = TlsDHUtils.receiveDHParameters(dhVerifier, teeIn);
+        this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(teeIn), dhParameters);
 
         DigitallySigned signed_params = parseSignature(input);
 
@@ -87,9 +97,6 @@
         {
             throw new TlsFatalAlert(AlertDescription.decrypt_error);
         }
-
-        this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(dhParams.getPublicKey());
-        this.dhParameters = validateDHParameters(dhAgreePublicKey.getParameters());
     }
 
     protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
index 3a2ac7b..52c5e17 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
@@ -4,7 +4,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.math.BigInteger;
 import java.util.Vector;
 
 import org.bouncycastle.asn1.x509.KeyUsage;
@@ -22,6 +21,7 @@
     extends AbstractTlsKeyExchange
 {
     protected TlsSigner tlsSigner;
+    protected TlsDHVerifier dhVerifier;
     protected DHParameters dhParameters;
 
     protected AsymmetricKeyParameter serverPublicKey;
@@ -30,8 +30,16 @@
     protected DHPrivateKeyParameters dhAgreePrivateKey;
     protected DHPublicKeyParameters dhAgreePublicKey;
 
+    /**
+     * @deprecated Use constructor that takes a TlsDHVerifier
+     */
     public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
     {
+        this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsDHVerifier(), dhParameters);
+    }
+
+    public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsDHVerifier dhVerifier, DHParameters dhParameters)
+    {
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
@@ -51,6 +59,7 @@
             throw new IllegalArgumentException("unsupported key exchange algorithm");
         }
 
+        this.dhVerifier = dhVerifier;
         this.dhParameters = dhParameters;
     }
 
@@ -101,8 +110,8 @@
         {
             try
             {
-                this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
-                this.dhParameters = validateDHParameters(dhAgreePublicKey.getParameters());
+                this.dhAgreePublicKey = (DHPublicKeyParameters)this.serverPublicKey;
+                this.dhParameters = dhAgreePublicKey.getParameters();
             }
             catch (ClassCastException e)
             {
@@ -161,15 +170,18 @@
 
         // DH_anon is handled here, DHE_* in a subclass
 
-        ServerDHParams dhParams = ServerDHParams.parse(input);
-
-        this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(dhParams.getPublicKey());
-        this.dhParameters = validateDHParameters(dhAgreePublicKey.getParameters());
+        this.dhParameters = TlsDHUtils.receiveDHParameters(dhVerifier, input);
+        this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
     }
 
     public void validateCertificateRequest(CertificateRequest certificateRequest)
         throws IOException
     {
+        if (keyExchange == KeyExchangeAlgorithm.DH_anon)
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
         short[] types = certificateRequest.getCertificateTypes();
         for (int i = 0; i < types.length; ++i)
         {
@@ -245,9 +257,7 @@
             return;
         }
 
-        BigInteger Yc = TlsDHUtils.readDHParameter(input);
-        
-        this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Yc, dhParameters));
+        this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
     }
 
     public byte[] generatePremasterSecret()
@@ -265,19 +275,4 @@
 
         throw new TlsFatalAlert(AlertDescription.internal_error);
     }
-
-    protected int getMinimumPrimeBits()
-    {
-        return 1024;
-    }
-
-    protected DHParameters validateDHParameters(DHParameters params) throws IOException
-    {
-        if (params.getP().bitLength() < getMinimumPrimeBits())
-        {
-            throw new TlsFatalAlert(AlertDescription.insufficient_security);
-        }
-
-        return TlsDHUtils.validateDHParameters(params);
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
index 2c6a692..4523c32 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
@@ -409,6 +409,25 @@
         case CipherSuite.DRAFT_TLS_DHE_PSK_WITH_AES_128_OCB:
         case CipherSuite.DRAFT_TLS_DHE_PSK_WITH_AES_256_OCB:
 
+        /*
+         * DH_anon cipher suites are consider ephemeral DH 
+         */
+        case CipherSuite.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+        case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
+        case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+        case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5:
+        case CipherSuite.TLS_DH_anon_WITH_SEED_CBC_SHA:
+
             return true;
 
         default:
@@ -459,50 +478,43 @@
         AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams);
 
         DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.getPublic();
-        new ServerDHParams(dhPublic).encode(output);
+        writeDHParameters(dhParams, output);
+        writeDHParameter(dhPublic.getY(), output);
 
         return (DHPrivateKeyParameters)kp.getPrivate();
     }
 
-    public static DHParameters validateDHParameters(DHParameters params) throws IOException
-    {
-        BigInteger p = params.getP();
-        BigInteger g = params.getG();
-
-        if (!p.isProbablePrime(2))
-        {
-            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-        }
-        if (g.compareTo(TWO) < 0 || g.compareTo(p.subtract(TWO)) > 0)
-        {
-            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-        }
-
-        return params;
-    }
-
-    public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) throws IOException
-    {
-        DHParameters params = validateDHParameters(key.getParameters());
-
-        BigInteger Y = key.getY();
-        if (Y.compareTo(TWO) < 0 || Y.compareTo(params.getP().subtract(TWO)) > 0)
-        {
-            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-        }
-
-        // TODO See RFC 2631 for more discussion of Diffie-Hellman validation
-
-        return key;
-    }
-
     public static BigInteger readDHParameter(InputStream input) throws IOException
     {
         return new BigInteger(1, TlsUtils.readOpaque16(input));
     }
 
+    public static DHParameters readDHParameters(InputStream input) throws IOException
+    {
+        BigInteger p = readDHParameter(input);
+        BigInteger g = readDHParameter(input);
+
+        return new DHParameters(p, g);
+    }
+
+    public static DHParameters receiveDHParameters(TlsDHVerifier dhVerifier, InputStream input) throws IOException
+    {
+        DHParameters dhParameters = readDHParameters(input);
+        if (!dhVerifier.accept(dhParameters))
+        {
+            throw new TlsFatalAlert(AlertDescription.insufficient_security);
+        }
+        return dhParameters;
+    }
+
     public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException
     {
         TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
     }
+
+    public static void writeDHParameters(DHParameters dhParameters, OutputStream output) throws IOException
+    {
+        writeDHParameter(dhParameters.getP(), output);
+        writeDHParameter(dhParameters.getG(), output);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHVerifier.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHVerifier.java
new file mode 100644
index 0000000..2140c0e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHVerifier.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.params.DHParameters;
+
+/**
+ * Interface a class for verifying Diffie-Hellman parameters needs to conform to.
+ */
+public interface TlsDHVerifier
+{
+    /**
+     * Check whether the given DH parameters are acceptable for use.
+     * 
+     * @param dhParameters the {@link DHParameters} to check
+     * @return true if (and only if) the specified parameters are acceptable
+     */
+    boolean accept(DHParameters dhParameters);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
index b2f3136..50bf1c3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
@@ -172,6 +172,11 @@
 
     public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
     {
+        if (keyExchange == KeyExchangeAlgorithm.ECDH_anon)
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
         /*
          * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
          * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsException.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsException.java
new file mode 100644
index 0000000..97b2831
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsException.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+public class TlsException
+    extends IOException
+{
+    // TODO Some day we might be able to just pass this down to IOException (1.6+)
+    protected Throwable cause;
+
+    public TlsException(String message, Throwable cause)
+    {
+        super(message);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
index dfbb09e..2aaed2b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
@@ -1,17 +1,10 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.IOException;
-
 public class TlsFatalAlert
-    extends IOException
+    extends TlsException
 {
-    private static final long serialVersionUID = 3584313123679111168L;
-
     protected short alertDescription;
 
-    // TODO Some day we might be able to just pass this down to IOException (1.6+)
-    protected Throwable alertCause;
-
     public TlsFatalAlert(short alertDescription)
     {
         this(alertDescription, null);
@@ -19,19 +12,13 @@
 
     public TlsFatalAlert(short alertDescription, Throwable alertCause)
     {
-        super(AlertDescription.getText(alertDescription));
+        super(AlertDescription.getText(alertDescription), alertCause);
 
         this.alertDescription = alertDescription;
-        this.alertCause = alertCause;
     }
 
     public short getAlertDescription()
     {
         return alertDescription;
     }
-
-    public Throwable getCause()
-    {
-        return alertCause;
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlertReceived.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlertReceived.java
new file mode 100644
index 0000000..964e0ea
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlertReceived.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.crypto.tls;
+
+public class TlsFatalAlertReceived
+    extends TlsException
+{
+    protected short alertDescription;
+
+    public TlsFatalAlertReceived(short alertDescription)
+    {
+        super(AlertDescription.getText(alertDescription), null);
+
+        this.alertDescription = alertDescription;
+    }
+
+    public short getAlertDescription()
+    {
+        return alertDescription;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java
index a5c583b..997d990 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java
@@ -12,4 +12,8 @@
 public class TlsNoCloseNotifyException
     extends EOFException
 {
+    public TlsNoCloseNotifyException()
+    {
+        super("No close_notify alert received before connection closed");
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
index 43c651d..c2c3982 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
@@ -30,6 +30,7 @@
     protected TlsPSKIdentity pskIdentity;
     protected TlsPSKIdentityManager pskIdentityManager;
 
+    protected TlsDHVerifier dhVerifier;
     protected DHParameters dhParameters;
     protected int[] namedCurves;
     protected short[] clientECPointFormats, serverECPointFormats;
@@ -48,10 +49,21 @@
     protected TlsEncryptionCredentials serverCredentials = null;
     protected byte[] premasterSecret;
 
+    /**
+     * @deprecated Use constructor that takes a TlsDHVerifier
+     */
     public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity,
         TlsPSKIdentityManager pskIdentityManager, DHParameters dhParameters, int[] namedCurves,
         short[] clientECPointFormats, short[] serverECPointFormats)
     {
+        this(keyExchange, supportedSignatureAlgorithms, pskIdentity, pskIdentityManager, new DefaultTlsDHVerifier(),
+            dhParameters, namedCurves, clientECPointFormats, serverECPointFormats);
+    }
+
+    public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity,
+        TlsPSKIdentityManager pskIdentityManager, TlsDHVerifier dhVerifier, DHParameters dhParameters, int[] namedCurves,
+        short[] clientECPointFormats, short[] serverECPointFormats)
+    {
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
@@ -67,6 +79,7 @@
 
         this.pskIdentity = pskIdentity;
         this.pskIdentityManager = pskIdentityManager;
+        this.dhVerifier = dhVerifier;
         this.dhParameters = dhParameters;
         this.namedCurves = namedCurves;
         this.clientECPointFormats = clientECPointFormats;
@@ -186,10 +199,8 @@
 
         if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
         {
-            ServerDHParams serverDHParams = ServerDHParams.parse(input);
-
-            this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey());
-            this.dhParameters = dhAgreePublicKey.getParameters();
+            this.dhParameters = TlsDHUtils.receiveDHParameters(dhVerifier, input);
+            this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
         }
         else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
         {
@@ -270,9 +281,7 @@
 
         if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
         {
-            BigInteger Yc = TlsDHUtils.readDHParameter(input);
-
-            this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Yc, dhParameters));
+            this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
         }
         else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
index aa0dcf8..0cddb2b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
@@ -5,6 +5,19 @@
 public interface TlsPeer
 {
     /**
+     * This implementation supports RFC 7627 and will always negotiate the extended_master_secret
+     * extension where possible. When connecting to a peer that does not offer/accept this
+     * extension, it is recommended to abort the handshake. This option is provided for
+     * interoperability with legacy peers, although some TLS features will be disabled in that case
+     * (see RFC 7627 5.4).
+     * 
+     * @return <code>true</code> if the handshake should be aborted when the peer does not negotiate
+     *         the extended_master_secret extension, or <code>false</code> to support legacy
+     *         interoperability.
+     */
+    boolean requiresExtendedMasterSecret();
+
+    /**
      * draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on
      * gmt_unix_time containing the current time, we recommend that implementors MAY provide the
      * ability to set gmt_unix_time as an option only, off by default."
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
index 9c274ea..bc65b27 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
@@ -21,8 +21,6 @@
     protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
     protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket);
 
-    private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
-
     /*
      * Our Connection states
      */
@@ -118,18 +116,101 @@
 
     protected abstract TlsPeer getPeer();
 
+    protected void handleAlertMessage(short alertLevel, short alertDescription)
+        throws IOException
+    {
+        getPeer().notifyAlertReceived(alertLevel, alertDescription);
+
+        if (alertLevel == AlertLevel.warning)
+        {
+            handleAlertWarningMessage(alertDescription);
+        }
+        else
+        {
+            handleFailure();
+
+            throw new TlsFatalAlertReceived(alertDescription);
+        }
+    }
+
+    protected void handleAlertWarningMessage(short alertDescription)
+        throws IOException
+    {
+        /*
+         * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
+         * and close down the connection immediately, discarding any pending writes.
+         */
+        if (alertDescription == AlertDescription.close_notify)
+        {
+            if (!appDataReady)
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+            handleClose(false);
+        }
+    }
+
     protected void handleChangeCipherSpecMessage() throws IOException
     {
     }
 
-    protected abstract void handleHandshakeMessage(short type, ByteArrayInputStream buf)
-        throws IOException;
-
-    protected void handleWarningMessage(short description)
+    protected void handleClose(boolean user_canceled)
         throws IOException
     {
+        if (!closed)
+        {
+            this.closed = true;
+
+            if (user_canceled && !appDataReady)
+            {
+                raiseAlertWarning(AlertDescription.user_canceled, "User canceled handshake");
+            }
+
+            raiseAlertWarning(AlertDescription.close_notify, "Connection closed");
+
+            recordStream.safeClose();
+
+            if (!appDataReady)
+            {
+                cleanupHandshake();
+            }
+        }
     }
 
+    protected void handleException(short alertDescription, String message, Throwable cause)
+        throws IOException
+    {
+        if (!closed)
+        {
+            raiseAlertFatal(alertDescription, message, cause);
+
+            handleFailure();
+        }
+    }
+
+    protected void handleFailure()
+    {
+        this.closed = true;
+        this.failedWithError = true;
+
+        /*
+         * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+         * without proper close_notify messages with level equal to warning.
+         */
+        // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
+        invalidateSession();
+
+        recordStream.safeClose();
+
+        if (!appDataReady)
+        {
+            cleanupHandshake();
+        }
+    }
+
+    protected abstract void handleHandshakeMessage(short type, ByteArrayInputStream buf)
+        throws IOException;
+
     protected void applyMaxFragmentLengthExtension()
         throws IOException
     {
@@ -229,6 +310,7 @@
                     this.sessionParameters = new SessionParameters.Builder()
                         .setCipherSuite(this.securityParameters.getCipherSuite())
                         .setCompressionAlgorithm(this.securityParameters.getCompressionAlgorithm())
+                        .setExtendedMasterSecret(securityParameters.isExtendedMasterSecret())
                         .setMasterSecret(this.securityParameters.getMasterSecret())
                         .setPeerCertificate(this.peerCertificate)
                         .setPSKIdentity(this.securityParameters.getPSKIdentity())
@@ -338,31 +420,30 @@
                 break;
             }
 
-            checkReceivedChangeCipherSpec(connection_state == CS_END || type == HandshakeType.finished);
-
             /*
              * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
              * starting at client hello up to, but not including, this finished message.
              * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
              */
-            switch (type)
+            if (HandshakeType.hello_request != type)
             {
-            case HandshakeType.hello_request:
-                break;
-            case HandshakeType.finished:
-            {
-                TlsContext ctx = getContext();
-                if (this.expected_verify_data == null
-                    && ctx.getSecurityParameters().getMasterSecret() != null)
+                if (HandshakeType.finished == type)
                 {
-                    this.expected_verify_data = createVerifyData(!ctx.isServer());
+                    checkReceivedChangeCipherSpec(true);
+
+                    TlsContext ctx = getContext();
+                    if (this.expected_verify_data == null
+                        && ctx.getSecurityParameters().getMasterSecret() != null)
+                    {
+                        this.expected_verify_data = createVerifyData(!ctx.isServer());
+                    }
+                }
+                else
+                {
+                    checkReceivedChangeCipherSpec(connection_state == CS_END);
                 }
 
-                // NB: Fall through to next case label
-            }
-            default:
                 queue.copyTo(recordStream.getHandshakeHashUpdater(), totalLength);
-                break;
             }
 
             queue.removeData(4);
@@ -393,47 +474,11 @@
             /*
              * An alert is always 2 bytes. Read the alert.
              */
-            byte[] tmp = alertQueue.removeData(2, 0);
-            short level = tmp[0];
-            short description = tmp[1];
+            byte[] alert = alertQueue.removeData(2, 0);
+            short alertLevel = alert[0];
+            short alertDescription = alert[1];
 
-            getPeer().notifyAlertReceived(level, description);
-
-            if (level == AlertLevel.fatal)
-            {
-                /*
-                 * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
-                 * without proper close_notify messages with level equal to warning.
-                 */
-                invalidateSession();
-
-                this.failedWithError = true;
-                this.closed = true;
-
-                recordStream.safeClose();
-
-                throw new IOException(TLS_ERROR_MESSAGE);
-            }
-            else
-            {
-                /*
-                 * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
-                 * and close down the connection immediately, discarding any pending writes.
-                 */
-                if (description == AlertDescription.close_notify)
-                {
-                    if (!appDataReady)
-                    {
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                    }
-                    handleClose(false);
-                }
-
-                /*
-                 * If it is just a warning, we continue.
-                 */
-                handleWarningMessage(description);
-            }
+            handleAlertMessage(alertLevel, alertDescription);
         }
     }
 
@@ -495,22 +540,17 @@
 
         while (applicationDataQueue.available() == 0)
         {
-            /*
-             * We need to read some data.
-             */
             if (this.closed)
             {
                 if (this.failedWithError)
                 {
-                    /*
-                     * Something went terribly wrong, we should throw an IOException
-                     */
-                    throw new IOException(TLS_ERROR_MESSAGE);
+                    throw new IOException("Cannot read application data on failed TLS connection");
+                }
+                if (!appDataReady)
+                {
+                    throw new IllegalStateException("Cannot read application data until initial handshake completed.");
                 }
 
-                /*
-                 * Connection has been closed, there is no more data to read.
-                 */
                 return -1;
             }
 
@@ -531,18 +571,18 @@
         }
         catch (TlsFatalAlert e)
         {
-            this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e);
+            handleException(e.getAlertDescription(), "Failed to read record", e);
             throw e;
         }
         catch (IOException e)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
+            handleException(AlertDescription.internal_error, "Failed to read record", e);
             throw e;
         }
         catch (RuntimeException e)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
-            throw e;
+            handleException(AlertDescription.internal_error, "Failed to read record", e);
+            throw new TlsFatalAlert(AlertDescription.internal_error, e);
         }
     }
 
@@ -551,39 +591,40 @@
     {
         try
         {
-            if (!recordStream.readRecord())
+            if (recordStream.readRecord())
             {
-                if (!appDataReady)
-                {
-                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                }
-                throw new TlsNoCloseNotifyException();
+                return;
             }
+
+            if (!appDataReady)
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+        }
+        catch (TlsFatalAlertReceived e)
+        {
+            // Connection failure already handled at source
+            throw e;
         }
         catch (TlsFatalAlert e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e);
-            }
+            handleException(e.getAlertDescription(), "Failed to read record", e);
             throw e;
         }
         catch (IOException e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
-            }
+            handleException(AlertDescription.internal_error, "Failed to read record", e);
             throw e;
         }
         catch (RuntimeException e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
-            }
-            throw e;
+            handleException(AlertDescription.internal_error, "Failed to read record", e);
+            throw new TlsFatalAlert(AlertDescription.internal_error, e);
         }
+
+        handleFailure();
+
+        throw new TlsNoCloseNotifyException();
     }
 
     protected void safeWriteRecord(short type, byte[] buf, int offset, int len)
@@ -595,27 +636,18 @@
         }
         catch (TlsFatalAlert e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e);
-            }
+            handleException(e.getAlertDescription(), "Failed to write record", e);
             throw e;
         }
         catch (IOException e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
-            }
+            handleException(AlertDescription.internal_error, "Failed to write record", e);
             throw e;
         }
         catch (RuntimeException e)
         {
-            if (!closed)
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
-            }
-            throw e;
+            handleException(AlertDescription.internal_error, "Failed to write record", e);
+            throw new TlsFatalAlert(AlertDescription.internal_error, e);
         }
     }
 
@@ -634,12 +666,7 @@
     {
         if (this.closed)
         {
-            if (this.failedWithError)
-            {
-                throw new IOException(TLS_ERROR_MESSAGE);
-            }
-
-            throw new IOException("Sorry, connection has been closed, you cannot write more data");
+            throw new IOException("Cannot write application data on closed/failed TLS connection");
         }
 
         while (len > 0)
@@ -950,51 +977,6 @@
         return bytesToRead;
     }
 
-    /**
-     * Terminate this connection with an alert. Can be used for normal closure too.
-     * 
-     * @param alertLevel
-     *            See {@link AlertLevel} for values.
-     * @param alertDescription
-     *            See {@link AlertDescription} for values.
-     * @throws IOException
-     *             If alert was fatal.
-     */
-    protected void failWithError(short alertLevel, short alertDescription, String message, Throwable cause)
-        throws IOException
-    {
-        /*
-         * Check if the connection is still open.
-         */
-        if (!closed)
-        {
-            /*
-             * Prepare the message
-             */
-            this.closed = true;
-
-            if (alertLevel == AlertLevel.fatal)
-            {
-                /*
-                 * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
-                 * without proper close_notify messages with level equal to warning.
-                 */
-                // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
-                invalidateSession();
-
-                this.failedWithError = true;
-            }
-            raiseAlert(alertLevel, alertDescription, message, cause);
-            recordStream.safeClose();
-            if (alertLevel != AlertLevel.fatal)
-            {
-                return;
-            }
-        }
-
-        throw new IOException(TLS_ERROR_MESSAGE);
-    }
-
     protected void invalidateSession()
     {
         if (this.sessionParameters != null)
@@ -1034,22 +1016,31 @@
         }
     }
 
-    protected void raiseAlert(short alertLevel, short alertDescription, String message, Throwable cause)
+    protected void raiseAlertFatal(short alertDescription, String message, Throwable cause)
         throws IOException
     {
-        getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause);
+        getPeer().notifyAlertRaised(AlertLevel.fatal, alertDescription, message, cause);
 
-        byte[] error = new byte[2];
-        error[0] = (byte)alertLevel;
-        error[1] = (byte)alertDescription;
+        byte[] alert = new byte[]{ (byte)AlertLevel.fatal, (byte)alertDescription };
 
-        safeWriteRecord(ContentType.alert, error, 0, 2);
+        try
+        {
+            recordStream.writeRecord(ContentType.alert, alert, 0, 2);
+        }
+        catch (Exception e)
+        {
+            // We are already processing an exception, so just ignore this
+        }
     }
 
-    protected void raiseWarning(short alertDescription, String message)
+    protected void raiseAlertWarning(short alertDescription, String message)
         throws IOException
     {
-        raiseAlert(AlertLevel.warning, alertDescription, message, null);
+        getPeer().notifyAlertRaised(AlertLevel.warning, alertDescription, message, null);
+
+        byte[] alert = new byte[]{ (byte)AlertLevel.warning, (byte)alertDescription };
+
+        safeWriteRecord(ContentType.alert, alert, 0, 2);
     }
 
     protected void sendCertificateMessage(Certificate certificate)
@@ -1069,7 +1060,7 @@
                 if (serverVersion.isSSL())
                 {
                     String errorMessage = serverVersion.toString() + " client didn't provide credentials";
-                    raiseWarning(AlertDescription.no_certificate, errorMessage);
+                    raiseAlertWarning(AlertDescription.no_certificate, errorMessage);
                     return;
                 }
             }
@@ -1132,19 +1123,6 @@
         handleClose(true);
     }
 
-    protected void handleClose(boolean user_canceled)
-        throws IOException
-    {
-        if (!closed)
-        {
-            if (user_canceled && !appDataReady)
-            {
-                raiseWarning(AlertDescription.user_canceled, "User canceled handshake");
-            }
-            this.failWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
-        }
-    }
-
     protected void flush()
         throws IOException
     {
@@ -1184,7 +1162,7 @@
             throw new TlsFatalAlert(AlertDescription.handshake_failure);
         }
 
-        raiseWarning(AlertDescription.no_renegotiation, "Renegotiation not supported");
+        raiseAlertWarning(AlertDescription.no_renegotiation, "Renegotiation not supported");
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
index d9aecfe..ed98708 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
@@ -39,15 +39,13 @@
     /**
      * Constructor for non-blocking mode.<br>
      * <br>
-     * When data is received, use {@link #offerInput(java.nio.ByteBuffer)} to
-     * provide the received ciphertext, then use
-     * {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br>
+     * When data is received, use {@link #offerInput(byte[])} to provide the received ciphertext,
+     * then use {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br>
      * <br>
-     * Similarly, when data needs to be sent, use
-     * {@link #offerOutput(byte[], int, int)} to provide the cleartext, then use
-     * {@link #readOutput(byte[], int, int)} to get the corresponding
+     * Similarly, when data needs to be sent, use {@link #offerOutput(byte[], int, int)} to provide
+     * the cleartext, then use {@link #readOutput(byte[], int, int)} to get the corresponding
      * ciphertext.
-     * 
+     *
      * @param secureRandom
      *            Random number generator for various cryptographic functions
      */
@@ -365,10 +363,10 @@
                 if (this.expectSessionTicket)
                 {
                     sendNewSessionTicketMessage(tlsServer.getNewSessionTicket());
-                    sendChangeCipherSpecMessage();
                 }
                 this.connection_state = CS_SERVER_SESSION_TICKET;
 
+                sendChangeCipherSpecMessage();
                 sendFinishedMessage();
                 this.connection_state = CS_SERVER_FINISHED;
 
@@ -392,10 +390,12 @@
         }
     }
 
-    protected void handleWarningMessage(short description)
+    protected void handleAlertWarningMessage(short alertDescription)
         throws IOException
     {
-        switch (description)
+        super.handleAlertWarningMessage(alertDescription);
+
+        switch (alertDescription)
         {
         case AlertDescription.no_certificate:
         {
@@ -403,16 +403,24 @@
              * SSL 3.0 If the server has sent a certificate request Message, the client must send
              * either the certificate message or a no_certificate alert.
              */
-            if (TlsUtils.isSSL(getContext()) && certificateRequest != null)
+            if (TlsUtils.isSSL(getContext()) && this.certificateRequest != null)
             {
-                notifyClientCertificate(Certificate.EMPTY_CHAIN);
+                switch (this.connection_state)
+                {
+                case CS_SERVER_HELLO_DONE:
+                {
+                    tlsServer.processClientSupplementalData(null);
+                    // NB: Fall through to next case label
+                }
+                case CS_CLIENT_SUPPLEMENTAL_DATA:
+                {
+                    notifyClientCertificate(Certificate.EMPTY_CHAIN);
+                    this.connection_state = CS_CLIENT_CERTIFICATE;
+                    return;
+                }
+                }
             }
-            break;
-        }
-        default:
-        {
-            super.handleWarningMessage(description);
-            break;
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
         }
     }
@@ -575,12 +583,18 @@
         this.clientExtensions = readExtensions(buf);
 
         /*
-         * TODO[session-hash]
-         * 
-         * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-         * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+         * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
+         */
+
+        /*
+         * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+         * master secret [..]. (and see 5.2, 5.3)
          */
         this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(clientExtensions);
+        if (!securityParameters.isExtendedMasterSecret() && tlsServer.requiresExtendedMasterSecret())
+        {
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
 
         getContextAdmin().setClientVersion(client_version);
 
@@ -665,11 +679,6 @@
         }
 
         recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
-
-        if (!expectSessionTicket)
-        {
-            sendChangeCipherSpecMessage();
-        }
     }
 
     protected void sendCertificateRequestMessage(CertificateRequest certificateRequest)
@@ -755,7 +764,7 @@
         TlsUtils.writeUint16(selectedCipherSuite, message);
         TlsUtils.writeUint8(selectedCompressionMethod, message);
 
-        this.serverExtensions = tlsServer.getServerExtensions();
+        this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(tlsServer.getServerExtensions());
 
         /*
          * RFC 5746 3.6. Server Behavior: Initial Handshake
@@ -779,14 +788,16 @@
                  * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                  * "renegotiation_info" extension in the ServerHello message.
                  */
-                this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(serverExtensions);
                 this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
             }
         }
 
-        if (securityParameters.extendedMasterSecret)
+        if (TlsUtils.isSSL(tlsServerContext))
         {
-            this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(serverExtensions);
+            securityParameters.extendedMasterSecret = false;
+        }
+        else if (securityParameters.isExtendedMasterSecret())
+        {
             TlsExtensionsUtils.addExtendedMasterSecretExtension(serverExtensions);
         }
 
@@ -796,7 +807,7 @@
          * extensions.
          */
 
-        if (this.serverExtensions != null)
+        if (!this.serverExtensions.isEmpty())
         {
             this.securityParameters.encryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(serverExtensions);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
index 615c442..2d07289 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
@@ -5,7 +5,8 @@
 class TlsSessionImpl implements TlsSession
 {
     final byte[] sessionID;
-    SessionParameters sessionParameters;
+    final SessionParameters sessionParameters;
+    boolean resumable;
 
     TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
     {
@@ -13,13 +14,16 @@
         {
             throw new IllegalArgumentException("'sessionID' cannot be null");
         }
-        if (sessionID.length < 1 || sessionID.length > 32)
+        if (sessionID.length > 32)
         {
-            throw new IllegalArgumentException("'sessionID' must have length between 1 and 32 bytes, inclusive");
+            throw new IllegalArgumentException("'sessionID' cannot be longer than 32 bytes");
         }
 
         this.sessionID = Arrays.clone(sessionID);
         this.sessionParameters = sessionParameters;
+        this.resumable = sessionID.length > 0
+            && null != sessionParameters
+            && sessionParameters.isExtendedMasterSecret();
     }
 
     public synchronized SessionParameters exportSessionParameters()
@@ -34,15 +38,11 @@
 
     public synchronized void invalidate()
     {
-        if (this.sessionParameters != null)
-        {
-            this.sessionParameters.clear();
-            this.sessionParameters = null;
-        }
+        this.resumable = false;
     }
 
     public synchronized boolean isResumable()
     {
-        return this.sessionParameters != null;
+        return resumable;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
index 446060d..b803243 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
@@ -1100,7 +1100,7 @@
         SecurityParameters securityParameters = context.getSecurityParameters();
 
         byte[] seed;
-        if (securityParameters.extendedMasterSecret)
+        if (securityParameters.isExtendedMasterSecret())
         {
             seed = securityParameters.getSessionHash();
         }
@@ -1114,7 +1114,7 @@
             return calculateMasterSecret_SSL(pre_master_secret, seed);
         }
 
-        String asciiLabel = securityParameters.extendedMasterSecret
+        String asciiLabel = securityParameters.isExtendedMasterSecret()
             ?   ExporterLabel.extended_master_secret
             :   ExporterLabel.master_secret;
 
@@ -1352,11 +1352,14 @@
                     supportedSignatureAlgorithms.elementAt(i);
                 short hashAlgorithm = signatureAndHashAlgorithm.getHash();
 
-                // TODO Support values in the "Reserved for Private Use" range
-                if (!HashAlgorithm.isPrivate(hashAlgorithm))
+                if (HashAlgorithm.isRecognized(hashAlgorithm))
                 {
                     handshakeHash.trackHashAlgorithm(hashAlgorithm);
                 }
+                else //if (HashAlgorithm.isPrivate(hashAlgorithm))
+                {
+                    // TODO Support values in the "Reserved for Private Use" range
+                }
             }
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java
index 2a6b250..ba6f2a8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java
@@ -80,7 +80,6 @@
     {
         Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
         TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
-        TlsExtensionsUtils.addExtendedMasterSecretExtension(clientExtensions);
         {
             /*
              * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java
index dc10410..e2bd5c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java
@@ -71,7 +71,6 @@
     {
         Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
         TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
-        TlsExtensionsUtils.addExtendedMasterSecretExtension(clientExtensions);
         {
             /*
              * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/AlgorithmIdentifierFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/AlgorithmIdentifierFactory.java
new file mode 100644
index 0000000..6dc572f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/AlgorithmIdentifierFactory.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.crypto.util;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.misc.CAST5CBCParameters;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Factory methods for common AlgorithmIdentifiers.
+ */
+public class AlgorithmIdentifierFactory
+{
+    private AlgorithmIdentifierFactory()
+    {
+
+    }
+
+    static final ASN1ObjectIdentifier  IDEA_CBC        = new ASN1ObjectIdentifier("1.3.6.1.4.1.188.7.1.1.2").intern();
+    static final ASN1ObjectIdentifier  CAST5_CBC       = new ASN1ObjectIdentifier("1.2.840.113533.7.66.10").intern();
+
+    private static final short[] rc2Table = {
+        0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
+        0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
+        0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
+        0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
+        0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
+        0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
+        0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
+        0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
+        0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
+        0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
+        0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
+        0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
+        0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
+        0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
+        0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
+        0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
+    };
+
+    /**
+     * Create an AlgorithmIdentifier for the passed in encryption algorithm.
+     *
+     * @param encryptionOID OID for the encryption algorithm
+     * @param keySize key size in bits (-1 if unknown)
+     * @param random SecureRandom to use for parameter generation.
+     * @return a full AlgorithmIdentifier including parameters
+     * @throws IllegalArgumentException if encryptionOID cannot be matched
+     */
+    public static AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
+        throws IllegalArgumentException
+    {
+        if (encryptionOID.equals(NISTObjectIdentifiers.id_aes128_CBC)
+                || encryptionOID.equals(NISTObjectIdentifiers.id_aes192_CBC)
+                || encryptionOID.equals(NISTObjectIdentifiers.id_aes256_CBC)
+                || encryptionOID.equals(NTTObjectIdentifiers.id_camellia128_cbc)
+                || encryptionOID.equals(NTTObjectIdentifiers.id_camellia192_cbc)
+                || encryptionOID.equals(NTTObjectIdentifiers.id_camellia256_cbc)
+                || encryptionOID.equals(KISAObjectIdentifiers.id_seedCBC))
+        {
+            byte[] iv = new byte[16];
+
+            random.nextBytes(iv);
+
+            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
+        }
+        else if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC)
+                || encryptionOID.equals(IDEA_CBC)
+                || encryptionOID.equals(OIWObjectIdentifiers.desCBC))
+        {
+            byte[] iv = new byte[8];
+
+            random.nextBytes(iv);
+
+            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
+        }
+        else if (encryptionOID.equals(CAST5_CBC))
+        {
+            byte[] iv = new byte[8];
+
+            random.nextBytes(iv);
+
+            CAST5CBCParameters cbcParams = new CAST5CBCParameters(iv, keySize);
+
+            return new AlgorithmIdentifier(encryptionOID, cbcParams);
+        }
+        else if (encryptionOID.equals(PKCSObjectIdentifiers.rc4))
+        {
+            return new AlgorithmIdentifier(encryptionOID, DERNull.INSTANCE);
+        }
+        else if (encryptionOID.equals(PKCSObjectIdentifiers.RC2_CBC))
+        {
+            byte[] iv = new byte[8];
+
+            random.nextBytes(iv);
+
+            RC2CBCParameter cbcParams = new RC2CBCParameter(rc2Table[128], iv);
+
+            return new AlgorithmIdentifier(encryptionOID, cbcParams);
+        }
+        else
+        {
+            throw new IllegalArgumentException("unable to match algorithm");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherFactory.java
new file mode 100644
index 0000000..613b9c1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherFactory.java
@@ -0,0 +1,199 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.misc.CAST5CBCParameters;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.CAST5Engine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.crypto.io.CipherOutputStream;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.RC2Parameters;
+
+/**
+ * Factory methods for creating Cipher objects and CipherOutputStreams.
+ */
+public class CipherFactory
+{
+
+    private static final short[] rc2Ekb = {
+        0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
+        0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
+        0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
+        0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
+        0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
+        0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
+        0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
+        0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
+        0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
+        0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
+        0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
+        0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
+        0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
+        0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
+        0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
+        0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd
+    };
+
+    /**
+     * Create a content cipher for encrypting bulk data.
+     *
+     * @param forEncryption true if the cipher is for encryption, false otherwise.
+     * @param encKey the basic key to use.
+     * @param encryptionAlgID identifying algorithm OID and parameters to use.
+     * @return a StreamCipher or a BufferedBlockCipher depending on the algorithm.
+     * @throws IllegalArgumentException
+     */
+    public static Object createContentCipher(boolean forEncryption, CipherParameters encKey, AlgorithmIdentifier encryptionAlgID)
+        throws IllegalArgumentException
+    {
+        ASN1ObjectIdentifier encAlg = encryptionAlgID.getAlgorithm();
+
+        if (encAlg.equals(PKCSObjectIdentifiers.rc4))
+        {
+            StreamCipher cipher = new RC4Engine();
+
+            cipher.init(forEncryption, encKey);
+
+            return cipher;
+        }
+        else
+        {
+            BufferedBlockCipher cipher = createCipher(encryptionAlgID.getAlgorithm());
+            ASN1Primitive sParams = encryptionAlgID.getParameters().toASN1Primitive();
+
+            if (sParams != null && !(sParams instanceof ASN1Null))
+            {
+                if (encAlg.equals(PKCSObjectIdentifiers.des_EDE3_CBC)
+                    || encAlg.equals(AlgorithmIdentifierFactory.IDEA_CBC)
+                    || encAlg.equals(NISTObjectIdentifiers.id_aes128_CBC)
+                    || encAlg.equals(NISTObjectIdentifiers.id_aes192_CBC)
+                    || encAlg.equals(NISTObjectIdentifiers.id_aes256_CBC)
+                    || encAlg.equals(NTTObjectIdentifiers.id_camellia128_cbc)
+                    || encAlg.equals(NTTObjectIdentifiers.id_camellia192_cbc)
+                    || encAlg.equals(NTTObjectIdentifiers.id_camellia256_cbc)
+                    || encAlg.equals(KISAObjectIdentifiers.id_seedCBC)
+                    || encAlg.equals(OIWObjectIdentifiers.desCBC))
+                {
+                    cipher.init(forEncryption, new ParametersWithIV(encKey,
+                        ASN1OctetString.getInstance(sParams).getOctets()));
+                }
+                else if (encAlg.equals(AlgorithmIdentifierFactory.CAST5_CBC))
+                {
+                    CAST5CBCParameters cbcParams = CAST5CBCParameters.getInstance(sParams);
+
+                    cipher.init(forEncryption, new ParametersWithIV(encKey, cbcParams.getIV()));
+                }
+                else if (encAlg.equals(PKCSObjectIdentifiers.RC2_CBC))
+                {
+                    RC2CBCParameter cbcParams = RC2CBCParameter.getInstance(sParams);
+
+                    cipher.init(forEncryption, new ParametersWithIV(new RC2Parameters(((KeyParameter)encKey).getKey(), rc2Ekb[cbcParams.getRC2ParameterVersion().intValue()]), cbcParams.getIV()));
+                }
+                else
+                {
+                    throw new IllegalArgumentException("cannot match parameters");
+                }
+            }
+            else
+            {
+                if (encAlg.equals(PKCSObjectIdentifiers.des_EDE3_CBC)
+                    || encAlg.equals(AlgorithmIdentifierFactory.IDEA_CBC)
+                    || encAlg.equals(AlgorithmIdentifierFactory.CAST5_CBC))
+                {
+                    cipher.init(forEncryption, new ParametersWithIV(encKey, new byte[8]));
+                }
+                else
+                {
+                    cipher.init(forEncryption, encKey);
+                }
+            }
+
+            return cipher;
+        }
+    }
+
+    private static BufferedBlockCipher createCipher(ASN1ObjectIdentifier algorithm)
+        throws IllegalArgumentException
+    {
+        BlockCipher cipher;
+
+        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm)
+            || NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm)
+            || NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
+        {
+            cipher = new CBCBlockCipher(new AESEngine());
+        }
+        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
+        {
+            cipher = new CBCBlockCipher(new DESedeEngine());
+        }
+        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
+        {
+            cipher = new CBCBlockCipher(new DESEngine());
+        }
+        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
+        {
+            cipher = new CBCBlockCipher(new RC2Engine());
+        }
+        else if (MiscObjectIdentifiers.cast5CBC.equals(algorithm))
+        {
+            cipher = new CBCBlockCipher(new CAST5Engine());
+        }
+        else
+        {
+            throw new IllegalArgumentException("cannot recognise cipher: " + algorithm);
+        }
+
+        return new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
+    }
+
+    /**
+     * Return a new CipherOutputStream based on the passed in cipher.
+     *
+     * @param dOut the output stream to write the processed data to.
+     * @param cipher the cipher to use.
+     * @return a BC CipherOutputStream using the cipher and writing to dOut.
+     */
+    public static CipherOutputStream createOutputStream(OutputStream dOut, Object cipher)
+    {
+        if (cipher instanceof BufferedBlockCipher)
+        {
+            return new CipherOutputStream(dOut, (BufferedBlockCipher)cipher);
+        }
+        if (cipher instanceof StreamCipher)
+        {
+            return new CipherOutputStream(dOut, (StreamCipher)cipher);
+        }
+        if (cipher instanceof AEADBlockCipher)
+        {
+            return new CipherOutputStream(dOut, (AEADBlockCipher)cipher);
+        }
+        throw new IllegalArgumentException("unknown cipher object: " + cipher);
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherKeyGeneratorFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherKeyGeneratorFactory.java
new file mode 100644
index 0000000..14d1785
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/CipherKeyGeneratorFactory.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.crypto.util;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.generators.DESKeyGenerator;
+import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
+
+/**
+ * Factory methods for generating secret key generators for symmetric ciphers.
+ */
+public class CipherKeyGeneratorFactory
+{
+    private CipherKeyGeneratorFactory()
+    {
+    }
+
+    /**
+     * Create a key generator for the passed in Object Identifier.
+     *
+     * @param algorithm the Object Identifier indicating the algorithn the generator is for.
+     * @param random a source of random to initialise the generator with.
+     * @return an initialised CipherKeyGenerator.
+     * @throws IllegalArgumentException if the algorithm cannot be identified.
+     */
+    public static CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRandom random)
+        throws IllegalArgumentException
+    {
+        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else if (NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 192);
+        }
+        else if (NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 256);
+        }
+        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
+        {
+            DESedeKeyGenerator keyGen = new DESedeKeyGenerator();
+
+            keyGen.init(new KeyGenerationParameters(random, 192));
+
+            return keyGen;
+        }
+        else if (NTTObjectIdentifiers.id_camellia128_cbc.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else if (NTTObjectIdentifiers.id_camellia192_cbc.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 192);
+        }
+        else if (NTTObjectIdentifiers.id_camellia256_cbc.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 256);
+        }
+        else if (KISAObjectIdentifiers.id_seedCBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else if (AlgorithmIdentifierFactory.CAST5_CBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
+        {
+            DESKeyGenerator keyGen = new DESKeyGenerator();
+
+            keyGen.init(new KeyGenerationParameters(random, 64));
+
+            return keyGen;
+        }
+        else if (PKCSObjectIdentifiers.rc4.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
+        {
+            return createCipherKeyGenerator(random, 128);
+        }
+        else
+        {
+            throw new IllegalArgumentException("cannot recognise cipher: " + algorithm);
+        }
+    }
+
+    private static CipherKeyGenerator createCipherKeyGenerator(SecureRandom random, int keySize)
+    {
+        CipherKeyGenerator keyGen = new CipherKeyGenerator();
+
+        keyGen.init(new KeyGenerationParameters(random, keySize));
+
+        return keyGen;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/JournalingSecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/JournalingSecureRandom.java
new file mode 100644
index 0000000..a5cf185
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/JournalingSecureRandom.java
@@ -0,0 +1,122 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A SecureRandom that maintains a journal of its output.
+ */
+public class JournalingSecureRandom
+    extends SecureRandom
+{
+    private static byte[] EMPTY_TRANSCRIPT = new byte[0];
+
+    private final SecureRandom base;
+    private final byte[] transcript;
+
+    private TranscriptStream tOut = new TranscriptStream();
+    private int index = 0;
+
+    /**
+     * Base constructor - no prior transcript.
+     *
+     * @param random source of randomness we will be journaling.
+     */
+    public JournalingSecureRandom(SecureRandom random)
+    {
+        this.base = random;
+        this.transcript = EMPTY_TRANSCRIPT;
+    }
+
+    /**
+     * Constructor with a prior transcript. Both the transcript used and
+     * any new randomness are journaled.
+     *
+     * @param transcript initial transcript of randomness.
+     * @param random source of randomness we will be journaling when the transcript runs out.
+     */
+    public JournalingSecureRandom(byte[] transcript, SecureRandom random)
+    {
+         this.base = random;
+         this.transcript = Arrays.clone(transcript);
+    }
+
+    /**
+     * Fill bytes with random data, journaling the random data before returning.
+     *
+     * @param bytes a block of bytes to be filled with random data.
+     */
+    public final void nextBytes(byte[] bytes)
+    {
+        if (index >= transcript.length)
+        {
+            base.nextBytes(bytes);
+        }
+        else
+        {
+            int i = 0;
+
+            while (i != bytes.length)
+            {
+                if (index < transcript.length)
+                {
+                    bytes[i] = transcript[index++];
+                }
+                else
+                {
+                    break;
+                }
+                i++;
+            }
+
+            if (i != bytes.length)
+            {
+                byte[] extra = new byte[bytes.length - i];
+
+                base.nextBytes(extra);
+
+                System.arraycopy(extra, 0, bytes, i, extra.length);
+            }
+        }
+
+        try
+        {
+            tOut.write(bytes);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("unable to record transcript: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Clear the internals
+     */
+    public void clear()
+    {
+        Arrays.fill(transcript, (byte)0);
+        tOut.clear();
+    }
+
+    /**
+     * Return the transcript so far,
+     *
+     * @return a copy of the randomness produced so far.
+     */
+    public byte[] getTranscript()
+    {
+        return tOut.toByteArray();
+    }
+
+    private class TranscriptStream
+        extends ByteArrayOutputStream
+    {
+        public void clear()
+        {
+            Arrays.fill(buf, (byte)0);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPrivateKeyUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPrivateKeyUtil.java
new file mode 100644
index 0000000..f047af0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPrivateKeyUtil.java
@@ -0,0 +1,282 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.sec.ECPrivateKey;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Strings;
+
+
+/**
+ * A collection of utility methods for parsing OpenSSH private keys.
+ */
+public class OpenSSHPrivateKeyUtil
+{
+    private OpenSSHPrivateKeyUtil()
+    {
+
+    }
+
+    /**
+     * Magic value for propriety OpenSSH private key.
+     **/
+    static final byte[] AUTH_MAGIC = Strings.toByteArray("openssh-key-v1\0"); // C string so null terminated
+
+    /**
+     * Encode a cipher parameters into an OpenSSH private key.
+     * This does not add headers like ----BEGIN RSA PRIVATE KEY----
+     *
+     * @param params the cipher parameters.
+     * @return a byte array
+     */
+    public static byte[] encodePrivateKey(AsymmetricKeyParameter params)
+        throws IOException
+    {
+        if (params == null)
+        {
+            throw new IllegalArgumentException("param is null");
+        }
+
+        if (params instanceof RSAPrivateCrtKeyParameters)
+        {
+            PrivateKeyInfo pInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(params);
+
+            return pInfo.parsePrivateKey().toASN1Primitive().getEncoded();
+        }
+        else if (params instanceof ECPrivateKeyParameters)
+        {
+            PrivateKeyInfo pInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(params);
+
+            return pInfo.parsePrivateKey().toASN1Primitive().getEncoded();
+        }
+        else if (params instanceof DSAPrivateKeyParameters)
+        {
+            ASN1EncodableVector vec = new ASN1EncodableVector();
+            vec.add(new ASN1Integer(0));
+            vec.add(new ASN1Integer(((DSAPrivateKeyParameters)params).getParameters().getP()));
+            vec.add(new ASN1Integer(((DSAPrivateKeyParameters)params).getParameters().getQ()));
+            vec.add(new ASN1Integer(((DSAPrivateKeyParameters)params).getParameters().getG()));
+
+            // public key = g.modPow(x, p);
+
+            BigInteger pubKey = ((DSAPrivateKeyParameters)params).getParameters().getG().modPow(
+                ((DSAPrivateKeyParameters)params).getX(),
+                ((DSAPrivateKeyParameters)params).getParameters().getP());
+            vec.add(new ASN1Integer(pubKey));
+
+            vec.add(new ASN1Integer(((DSAPrivateKeyParameters)params).getX()));
+            try
+            {
+                return new DERSequence(vec).getEncoded();
+            }
+            catch (Exception ex)
+            {
+                throw new IllegalStateException("unable to encode DSAPrivateKeyParameters " + ex.getMessage());
+            }
+        }
+        else if (params instanceof Ed25519PrivateKeyParameters)
+        {
+            SSHBuilder builder = new SSHBuilder();
+
+            builder.write(AUTH_MAGIC);
+            builder.writeString("none");
+            builder.writeString("none");
+            builder.u32(0); // Zero length of the KDF
+
+            builder.u32(1);
+
+            Ed25519PublicKeyParameters publicKeyParameters = ((Ed25519PrivateKeyParameters)params).generatePublicKey();
+
+            byte[] pkEncoded = OpenSSHPublicKeyUtil.encodePublicKey(publicKeyParameters);
+            builder.rawArray(pkEncoded);
+
+            SSHBuilder pkBuild = new SSHBuilder();
+
+            pkBuild.u32(0x00ff00ff);
+            pkBuild.u32(0x00ff00ff);
+
+            pkBuild.writeString("ssh-ed25519");
+
+            byte[] pubKeyEncoded = ((Ed25519PrivateKeyParameters)params).generatePublicKey().getEncoded();
+
+            pkBuild.rawArray(pubKeyEncoded); // Public key written as length defined item.
+
+            // The private key in SSH is 64 bytes long and is the concatenation of the private and the public keys
+            pkBuild.rawArray(Arrays.concatenate(((Ed25519PrivateKeyParameters)params).getEncoded(), pubKeyEncoded));
+            pkBuild.u32(0); // No comment.
+            builder.rawArray(pkBuild.getBytes());
+
+            return builder.getBytes();
+        }
+
+        throw new IllegalArgumentException("unable to convert " + params.getClass().getName() + " to openssh private key");
+
+    }
+
+    /**
+     * Parse a private key.
+     * <p>
+     * This method accepts the body of the OpenSSH private key.
+     * The easiest way to extract the body is to use PemReader, for example:
+     * <p>
+     * byte[] blob = new PemReader([reader]).readPemObject().getContent();
+     * CipherParameters params = parsePrivateKeyBlob(blob);
+     *
+     * @param blob The key.
+     * @return A cipher parameters instance.
+     */
+    public static AsymmetricKeyParameter parsePrivateKeyBlob(byte[] blob)
+    {
+        AsymmetricKeyParameter result = null;
+
+        if  (blob[0] == 0x30)
+        {
+            ASN1Sequence sequence = ASN1Sequence.getInstance(blob);
+
+            if (sequence.size() == 6)
+            {
+                if (allIntegers(sequence) && ((ASN1Integer)sequence.getObjectAt(0)).getPositiveValue().equals(BigIntegers.ZERO))
+                {
+                    // length of 6 and all Integers -- DSA
+                    result = new DSAPrivateKeyParameters(
+                        ((ASN1Integer)sequence.getObjectAt(5)).getPositiveValue(),
+                        new DSAParameters(
+                            ((ASN1Integer)sequence.getObjectAt(1)).getPositiveValue(),
+                            ((ASN1Integer)sequence.getObjectAt(2)).getPositiveValue(),
+                            ((ASN1Integer)sequence.getObjectAt(3)).getPositiveValue())
+                    );
+                }
+            }
+            else if (sequence.size() == 9)
+            {
+                if (allIntegers(sequence) && ((ASN1Integer)sequence.getObjectAt(0)).getPositiveValue().equals(BigIntegers.ZERO))
+                {
+                    // length of 8 and all Integers -- RSA
+                    RSAPrivateKey rsaPrivateKey = RSAPrivateKey.getInstance(sequence);
+
+                    result = new RSAPrivateCrtKeyParameters(
+                        rsaPrivateKey.getModulus(),
+                        rsaPrivateKey.getPublicExponent(),
+                        rsaPrivateKey.getPrivateExponent(),
+                        rsaPrivateKey.getPrime1(),
+                        rsaPrivateKey.getPrime2(),
+                        rsaPrivateKey.getExponent1(),
+                        rsaPrivateKey.getExponent2(),
+                        rsaPrivateKey.getCoefficient());
+                }
+            }
+            else if (sequence.size() == 4)
+            {
+                if (sequence.getObjectAt(3) instanceof DERTaggedObject && sequence.getObjectAt(2) instanceof DERTaggedObject)
+                {
+                    ECPrivateKey ecPrivateKey = ECPrivateKey.getInstance(sequence);
+                    ASN1ObjectIdentifier curveOID = (ASN1ObjectIdentifier)ecPrivateKey.getParameters();
+                    X9ECParameters x9Params = ECNamedCurveTable.getByOID(curveOID);
+                    result = new ECPrivateKeyParameters(
+                        ecPrivateKey.getKey(),
+                        new ECNamedDomainParameters(
+                            curveOID,
+                            x9Params.getCurve(),
+                            x9Params.getG(),
+                            x9Params.getN(),
+                            x9Params.getH(),
+                            x9Params.getSeed()));
+                }
+            }
+        }
+        else
+        {
+            SSHBuffer kIn = new SSHBuffer(AUTH_MAGIC, blob);
+            // Cipher name.
+            String cipherName = Strings.fromByteArray(kIn.readString());
+
+            if (!"none".equals(cipherName))
+            {
+                throw new IllegalStateException("encrypted keys not supported");
+            }
+
+            // KDF name
+            kIn.readString();
+
+            // KDF options
+            kIn.readString();
+
+            long publicKeyCount = kIn.readU32();
+
+            for (int l = 0; l != publicKeyCount; l++)
+            {
+                // Burn off public keys.
+                OpenSSHPublicKeyUtil.parsePublicKey(kIn.readString());
+            }
+
+            SSHBuffer pkIn = new SSHBuffer(kIn.readPaddedString());
+            int check1 = pkIn.readU32();
+            int check2 = pkIn.readU32();
+
+            if (check1 != check2)
+            {
+                throw new IllegalStateException("private key check values are not the same");
+            }
+
+            String keyType = Strings.fromByteArray(pkIn.readString());
+
+            if ("ssh-ed25519".equals(keyType))
+            {
+                //
+                // Skip public key
+                //
+                pkIn.readString();
+                byte[] edPrivateKey = pkIn.readString();
+
+                result = new Ed25519PrivateKeyParameters(edPrivateKey, 0);
+            }
+            else
+            {
+                throw new IllegalStateException("can not parse private key of type " + keyType);
+            }
+        }
+
+        if (result == null)
+        {
+            throw new IllegalArgumentException("unable to parse key");
+        }
+
+        return result;
+    }
+
+    /**
+     * allIntegers returns true if the sequence holds only ASN1Integer types.
+     **/
+    private static boolean allIntegers(ASN1Sequence sequence)
+    {
+        for (int t = 0; t < sequence.size(); t++)
+        {
+            if (!(sequence.getObjectAt(t) instanceof ASN1Integer))
+            {
+                return false;
+
+            }
+        }
+        return true;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPublicKeyUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPublicKeyUtil.java
new file mode 100644
index 0000000..a0809f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/OpenSSHPublicKeyUtil.java
@@ -0,0 +1,201 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
+import org.bouncycastle.util.Strings;
+
+
+/**
+ * OpenSSHPublicKeyUtil utility classes for parsing OpenSSH public keys.
+ */
+public class OpenSSHPublicKeyUtil
+{
+    private OpenSSHPublicKeyUtil()
+    {
+
+    }
+
+    private static final String RSA = "ssh-rsa";
+    private static final String ECDSA = "ecdsa";
+    private static final String ED_25519 = "ssh-ed25519";
+    private static final String DSS = "ssh-dss";
+
+    /**
+     * Parse a public key.
+     * <p>
+     * This method accepts the bytes that are Base64 encoded in an OpenSSH public key file.
+     *
+     * @param encoded The key.
+     * @return An AsymmetricKeyParameter instance.
+     */
+    public static AsymmetricKeyParameter parsePublicKey(byte[] encoded)
+    {
+        SSHBuffer buffer = new SSHBuffer(encoded);
+        return parsePublicKey(buffer);
+    }
+
+    /**
+     * Encode a public key from an AsymmetricKeyParameter instance.
+     *
+     * @param cipherParameters The key to encode.
+     * @return the key OpenSSH encoded.
+     * @throws IOException
+     */
+    public static byte[] encodePublicKey(AsymmetricKeyParameter cipherParameters)
+        throws IOException
+    {
+        BigInteger e;
+        BigInteger n;
+
+        if (cipherParameters == null)
+        {
+            throw new IllegalArgumentException("cipherParameters was null.");
+        }
+
+        if (cipherParameters instanceof RSAKeyParameters)
+        {
+            if (cipherParameters.isPrivate())
+            {
+                throw new IllegalArgumentException("RSAKeyParamaters was for encryption");
+            }
+
+            e = ((RSAKeyParameters)cipherParameters).getExponent();
+            n = ((RSAKeyParameters)cipherParameters).getModulus();
+
+            SSHBuilder builder = new SSHBuilder();
+            builder.writeString(RSA);
+            builder.rawArray(e.toByteArray());
+            builder.rawArray(n.toByteArray());
+
+            return builder.getBytes();
+
+        }
+        else if (cipherParameters instanceof ECPublicKeyParameters)
+        {
+            SSHBuilder builder = new SSHBuilder();
+
+            String name = null;
+            if (((ECPublicKeyParameters)cipherParameters).getParameters().getCurve() instanceof SecP256R1Curve)
+            {
+                name = "nistp256";
+            }
+            else
+            {
+                throw new IllegalArgumentException("unable to derive ssh curve name for " + ((ECPublicKeyParameters)cipherParameters).getParameters().getCurve().getClass().getName());
+            }
+
+            builder.writeString(ECDSA + "-sha2-" + name); // Magic
+            builder.writeString(name);
+            builder.rawArray(((ECPublicKeyParameters)cipherParameters).getQ().getEncoded(false)); //Uncompressed
+            return builder.getBytes();
+        }
+        else if (cipherParameters instanceof DSAPublicKeyParameters)
+        {
+            SSHBuilder builder = new SSHBuilder();
+            builder.writeString(DSS);
+            builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getP().toByteArray());
+            builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getQ().toByteArray());
+            builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getG().toByteArray());
+            builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getY().toByteArray());
+            return builder.getBytes();
+        }
+        else if (cipherParameters instanceof Ed25519PublicKeyParameters)
+        {
+            SSHBuilder builder = new SSHBuilder();
+            builder.writeString(ED_25519);
+            builder.rawArray(((Ed25519PublicKeyParameters)cipherParameters).getEncoded());
+            return builder.getBytes();
+        }
+
+        throw new IllegalArgumentException("unable to convert " + cipherParameters.getClass().getName() + " to private key");
+    }
+
+    /**
+     * Parse a public key from an SSHBuffer instance.
+     *
+     * @param buffer containing the SSH public key.
+     * @return A CipherParameters instance.
+     */
+    public static AsymmetricKeyParameter parsePublicKey(SSHBuffer buffer)
+    {
+        AsymmetricKeyParameter result = null;
+
+        String magic = Strings.fromByteArray(buffer.readString());
+        if (RSA.equals(magic))
+        {
+            BigInteger e = buffer.positiveBigNum();
+            BigInteger n = buffer.positiveBigNum();
+            result = new RSAKeyParameters(false, n, e);
+        }
+        else if (DSS.equals(magic))
+        {
+            BigInteger p = buffer.positiveBigNum();
+            BigInteger q = buffer.positiveBigNum();
+            BigInteger g = buffer.positiveBigNum();
+            BigInteger pubKey = buffer.positiveBigNum();
+
+            result = new DSAPublicKeyParameters(pubKey, new DSAParameters(p, q, g));
+        }
+        else if (magic.startsWith(ECDSA))
+        {
+            String curveName = Strings.fromByteArray(buffer.readString());
+            String nameToFind = curveName;
+
+            if (curveName.startsWith("nist"))
+            {
+                //
+                // NIST names like P-256 are encoded in SSH as nistp256
+                //
+
+                nameToFind = curveName.substring(4);
+                nameToFind = nameToFind.substring(0, 1) + "-" + nameToFind.substring(1);
+            }
+
+            X9ECParameters x9ECParameters = ECNamedCurveTable.getByName(nameToFind);
+
+            if (x9ECParameters == null)
+            {
+                throw new IllegalStateException("unable to find curve for " + magic + " using curve name " + nameToFind);
+            }
+
+            //
+            // Extract name of digest from magic string value;
+            //
+            //String digest = magic.split("-")[1];
+
+            ECCurve curve = x9ECParameters.getCurve();
+
+            byte[] pointRaw = buffer.readString();
+
+            result = new ECPublicKeyParameters(curve.decodePoint(pointRaw), new ECDomainParameters(curve, x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed()));
+        }
+        else if (magic.startsWith(ED_25519))
+        {
+            result = new Ed25519PublicKeyParameters(buffer.readString(), 0);
+        }
+
+        if (result == null)
+        {
+            throw new IllegalArgumentException("unable to parse key");
+        }
+
+        if (buffer.hasRemaining())
+        {
+            throw new IllegalArgumentException("uncoded key has trailing data");
+        }
+
+        return result;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDF2Config.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDF2Config.java
new file mode 100644
index 0000000..f5639b3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDF2Config.java
@@ -0,0 +1,171 @@
+package org.bouncycastle.crypto.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Integers;
+
+/**
+ * Configuration class for a PBKDF using PKCS#5 Scheme 2.
+ */
+public class PBKDF2Config
+    extends PBKDFConfig
+{
+    /**
+     * AlgorithmIdentifier for a PRF using HMac with SHA-1
+     */
+    public static final AlgorithmIdentifier PRF_SHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
+
+    /**
+     * AlgorithmIdentifier for a PRF using HMac with SHA-256
+     */
+    public static final AlgorithmIdentifier PRF_SHA256 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE);
+
+    /**
+     * AlgorithmIdentifier for a PRF using HMac with SHA-512
+     */
+    public static final AlgorithmIdentifier PRF_SHA512 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+
+    /**
+     * AlgorithmIdentifier for a PRF using HMac with SHA3-256
+     */
+    public static final AlgorithmIdentifier PRF_SHA3_256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256, DERNull.INSTANCE);
+
+    /**
+     * AlgorithmIdentifier for a PRF using SHA3-512
+     */
+    public static final AlgorithmIdentifier PRF_SHA3_512 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+
+    private static final Map PRFS_SALT = new HashMap();
+
+    static
+    {
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
+        PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
+        PRFS_SALT.put(RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_256, Integers.valueOf(32));
+        PRFS_SALT.put(RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_512, Integers.valueOf(64));
+        PRFS_SALT.put(GMObjectIdentifiers.hmac_sm3, Integers.valueOf(32));
+    }
+
+    static int getSaltSize(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS_SALT.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no salt size for algorithm: " + algorithm);
+        }
+
+        return ((Integer)PRFS_SALT.get(algorithm)).intValue();
+    }
+
+    public static class Builder
+    {
+        private int iterationCount = 1024;
+        private int saltLength = -1;
+        private AlgorithmIdentifier prf = PRF_SHA1;
+
+        /**
+         * Base constructor.
+         *
+         * This configures the builder to use an iteration count of 1024, and the HMacSHA1 PRF.
+         */
+        public Builder()
+        {
+        }
+
+        /**
+         * Set the iteration count for the PBE calculation.
+         *
+         * @param iterationCount the iteration count to apply to the key creation.
+         * @return the current builder.
+         */
+        public Builder withIterationCount(int iterationCount)
+        {
+            this.iterationCount = iterationCount;
+
+            return this;
+        }
+
+        /**
+         * Set the PRF to use for key generation. By default this is HmacSHA1.
+         *
+         * @param prf algorithm id for PRF.
+         * @return the current builder.
+         */
+        public Builder withPRF(AlgorithmIdentifier prf)
+        {
+            this.prf = prf;
+
+            return this;
+        }
+
+        /**
+         * Set the length of the salt to use.
+         *
+         * @param saltLength the length of the salt (in octets) to use.
+         * @return the current builder.
+         */
+        public Builder withSaltLength(int saltLength)
+        {
+            this.saltLength = saltLength;
+
+            return this;
+        }
+
+        public PBKDF2Config build()
+        {
+            return new PBKDF2Config(this);
+        }
+    }
+
+    private final int iterationCount;
+    private final int saltLength;
+    private final AlgorithmIdentifier prf;
+
+    private PBKDF2Config(Builder builder)
+    {
+        super(PKCSObjectIdentifiers.id_PBKDF2);
+
+        this.iterationCount = builder.iterationCount;
+        this.prf = builder.prf;
+
+        if (builder.saltLength < 0)
+        {
+            this.saltLength = getSaltSize(prf.getAlgorithm());
+        }
+        else
+        {
+            this.saltLength = builder.saltLength;
+        }
+    }
+
+    public int getIterationCount()
+    {
+        return iterationCount;
+    }
+
+    public AlgorithmIdentifier getPRF()
+    {
+        return prf;
+    }
+
+    public int getSaltLength()
+    {
+        return saltLength;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDFConfig.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDFConfig.java
new file mode 100644
index 0000000..33582df
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PBKDFConfig.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.crypto.util;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Base class for PBKDF configs.
+ */
+public abstract class PBKDFConfig
+{
+    private final ASN1ObjectIdentifier algorithm;
+
+    protected PBKDFConfig(ASN1ObjectIdentifier algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    public ASN1ObjectIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index 92684ec..22d5ad4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -8,16 +8,24 @@
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.ElGamalParameter;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.ECPrivateKey;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -29,11 +37,16 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
 import org.bouncycastle.crypto.params.ElGamalParameters;
 import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
 
 /**
  * Factory for creating private key objects from PKCS8 PrivateKeyInfo objects.
@@ -42,12 +55,13 @@
 {
     /**
      * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding.
-     * 
+     *
      * @param privateKeyInfoData the PrivateKeyInfo encoding
      * @return a suitable private key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) throws IOException
+    public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData)
+        throws IOException
     {
         return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
     }
@@ -55,28 +69,33 @@
     /**
      * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding read from a
      * stream.
-     * 
+     *
      * @param inStr the stream to read the PrivateKeyInfo encoding from
      * @return a suitable private key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException
+    public static AsymmetricKeyParameter createKey(InputStream inStr)
+        throws IOException
     {
         return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
     }
 
     /**
      * Create a private key parameter from the passed in PKCS8 PrivateKeyInfo object.
-     * 
+     *
      * @param keyInfo the PrivateKeyInfo object containing the key material
      * @return a suitable private key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) throws IOException
+    public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo)
+        throws IOException
     {
         AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
+        ASN1ObjectIdentifier algOID = algId.getAlgorithm();
 
-        if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
+        if (algOID.equals(PKCSObjectIdentifiers.rsaEncryption)
+            || algOID.equals(PKCSObjectIdentifiers.id_RSASSA_PSS)
+            || algOID.equals(X509ObjectIdentifiers.id_ea_rsa))
         {
             RSAPrivateKey keyStructure = RSAPrivateKey.getInstance(keyInfo.parsePrivateKey());
 
@@ -86,8 +105,8 @@
                 keyStructure.getExponent2(), keyStructure.getCoefficient());
         }
         // TODO?
-//      else if (algId.getObjectId().equals(X9ObjectIdentifiers.dhpublicnumber))
-        else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement))
+//      else if (algOID.equals(X9ObjectIdentifiers.dhpublicnumber))
+        else if (algOID.equals(PKCSObjectIdentifiers.dhKeyAgreement))
         {
             DHParameter params = DHParameter.getInstance(algId.getParameters());
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
@@ -98,7 +117,7 @@
 
             return new DHPrivateKeyParameters(derX.getValue(), dhParams);
         }
-        else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+        else if (algOID.equals(OIWObjectIdentifiers.elGamalAlgorithm))
         {
             ElGamalParameter params = ElGamalParameter.getInstance(algId.getParameters());
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
@@ -106,7 +125,7 @@
             return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(
                 params.getP(), params.getG()));
         }
-        else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa))
+        else if (algOID.equals(X9ObjectIdentifiers.id_dsa))
         {
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
             ASN1Encodable de = algId.getParameters();
@@ -120,7 +139,7 @@
 
             return new DSAPrivateKeyParameters(derX.getValue(), parameters);
         }
-        else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
+        else if (algOID.equals(X9ObjectIdentifiers.id_ecPublicKey))
         {
             X962Parameters params = new X962Parameters((ASN1Primitive)algId.getParameters());
 
@@ -151,9 +170,147 @@
 
             return new ECPrivateKeyParameters(d, dParams);
         }
+        else if (algOID.equals(EdECObjectIdentifiers.id_X25519))
+        {
+            return new X25519PrivateKeyParameters(getRawKey(keyInfo, X25519PrivateKeyParameters.KEY_SIZE), 0);
+        }
+        else if (algOID.equals(EdECObjectIdentifiers.id_X448))
+        {
+            return new X448PrivateKeyParameters(getRawKey(keyInfo, X448PrivateKeyParameters.KEY_SIZE), 0);
+        }
+        else if (algOID.equals(EdECObjectIdentifiers.id_Ed25519))
+        {
+            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo, Ed25519PrivateKeyParameters.KEY_SIZE), 0);
+        }
+        else if (algOID.equals(EdECObjectIdentifiers.id_Ed448))
+        {
+            return new Ed448PrivateKeyParameters(getRawKey(keyInfo, Ed448PrivateKeyParameters.KEY_SIZE), 0);
+        }
+        else if (
+            algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) ||
+                algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) ||
+                algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256))
+        {
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ECGOST3410Parameters ecSpec = null;
+            BigInteger d = null;
+            ASN1Primitive p = keyInfo.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+            if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
+            {
+
+                ECDomainParameters ecP = ECGOST3410NamedCurves.getByOID(gostParams.getPublicKeyParamSet());
+
+                ecSpec = new ECGOST3410Parameters(
+                    new ECNamedDomainParameters(
+                        gostParams.getPublicKeyParamSet(), ecP),
+                    gostParams.getPublicKeyParamSet(),
+                    gostParams.getDigestParamSet(),
+                    gostParams.getEncryptionParamSet());
+                ASN1Encodable privKey = keyInfo.parsePrivateKey();
+                if (privKey instanceof ASN1Integer)
+                {
+                    d = ASN1Integer.getInstance(privKey).getPositiveValue();
+                }
+                else
+                {
+                    byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets();
+                    byte[] dVal = new byte[encVal.length];
+
+                    for (int i = 0; i != encVal.length; i++)
+                    {
+                        dVal[i] = encVal[encVal.length - 1 - i];
+                    }
+
+                    d = new BigInteger(1, dVal);
+                }
+
+
+            }
+            else
+            {
+                X962Parameters params = X962Parameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+
+                if (params.isNamedCurve())
+                {
+                    ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
+                    X9ECParameters ecP = ECNamedCurveTable.getByOID(oid);
+                    if (ecP == null)
+                    {
+                        ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
+                        ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters(
+                            oid,
+                            gParam.getCurve(),
+                            gParam.getG(),
+                            gParam.getN(),
+                            gParam.getH(),
+                            gParam.getSeed()), gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet());
+                    }
+                    else
+                    {
+                        ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters(
+                            oid,
+                            ecP.getCurve(),
+                            ecP.getG(),
+                            ecP.getN(),
+                            ecP.getH(),
+                            ecP.getSeed()), gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet());
+                    }
+                }
+                else if (params.isImplicitlyCA())
+                {
+                    ecSpec = null;
+                }
+                else
+                {
+                    X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+                    ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters(
+                        algOID,
+                        ecP.getCurve(),
+                        ecP.getG(),
+                        ecP.getN(),
+                        ecP.getH(),
+                        ecP.getSeed()), gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet());
+                }
+
+                ASN1Encodable privKey = keyInfo.parsePrivateKey();
+                if (privKey instanceof ASN1Integer)
+                {
+                    ASN1Integer derD = ASN1Integer.getInstance(privKey);
+
+                    d = derD.getValue();
+                }
+                else
+                {
+                    org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
+
+                    d = ec.getKey();
+                }
+
+            }
+
+            return new ECPrivateKeyParameters(
+                d,
+                new ECGOST3410Parameters(
+                    ecSpec,
+                    gostParams.getPublicKeyParamSet(),
+                    gostParams.getDigestParamSet(),
+                    gostParams.getEncryptionParamSet()));
+
+        }
         else
         {
-            throw new RuntimeException("algorithm identifier in key not recognised");
+            throw new RuntimeException("algorithm identifier in private key not recognised");
         }
     }
+
+    private static byte[] getRawKey(PrivateKeyInfo keyInfo, int expectedSize)
+        throws IOException
+    {
+        byte[] result = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
+        if (expectedSize != result.length)
+        {
+            throw new RuntimeException("private key encoding has incorrect length");
+        }
+        return result;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
index be4f5e4..76a6413 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
@@ -1,13 +1,24 @@
 package org.bouncycastle.crypto.util;
 
 import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.ECPrivateKey;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
@@ -18,16 +29,32 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
 
 /**
  * Factory to create ASN.1 private key info objects from lightweight private keys.
  */
 public class PrivateKeyInfoFactory
 {
+    private static Set cryptoProOids = new HashSet(5);
+
+    static
+    {
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB);
+    }
+
     private PrivateKeyInfoFactory()
     {
 
@@ -37,23 +64,42 @@
      * Create a PrivateKeyInfo representation of a private key.
      *
      * @param privateKey the key to be encoded into the info object.
-     * @return the appropriate key parameter
+     * @return the appropriate PrivateKeyInfo
      * @throws java.io.IOException on an error encoding the key
      */
-    public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException
+    public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey)
+        throws IOException
+    {
+        return createPrivateKeyInfo(privateKey, null);
+    }
+
+    /**
+     * Create a PrivateKeyInfo representation of a private key with attributes.
+     *
+     * @param privateKey the key to be encoded into the info object.
+     * @param attributes the set of attributes to be included.
+     * @return the appropriate PrivateKeyInfo
+     * @throws java.io.IOException on an error encoding the key
+     */
+    public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes)
+        throws IOException
     {
         if (privateKey instanceof RSAKeyParameters)
         {
             RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters)privateKey;
 
-            return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv()));
+            return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE),
+                new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv()),
+                attributes);
         }
         else if (privateKey instanceof DSAPrivateKeyParameters)
         {
             DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)privateKey;
             DSAParameters params = priv.getParameters();
 
-            return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()));
+            return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
+                new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()),
+                attributes);
         }
         else if (privateKey instanceof ECPrivateKeyParameters)
         {
@@ -67,6 +113,37 @@
                 params = new X962Parameters(DERNull.INSTANCE);      // Implicitly CA
                 orderBitLength = priv.getD().bitLength();   // TODO: this is as good as currently available, must be a better way...
             }
+            else if (domainParams instanceof ECGOST3410Parameters)
+            {
+                GOST3410PublicKeyAlgParameters gostParams = new GOST3410PublicKeyAlgParameters(
+                    ((ECGOST3410Parameters)domainParams).getPublicKeyParamSet(),
+                    ((ECGOST3410Parameters)domainParams).getDigestParamSet(),
+                    ((ECGOST3410Parameters)domainParams).getEncryptionParamSet());
+
+
+                int size;
+                ASN1ObjectIdentifier identifier;
+
+                if (cryptoProOids.contains(gostParams.getPublicKeyParamSet()))
+                {
+                    size = 32;
+                    identifier = CryptoProObjectIdentifiers.gostR3410_2001;
+                }
+                else
+                {
+
+                    boolean is512 = priv.getD().bitLength() > 256;
+                    identifier = (is512) ?
+                        RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512 :
+                        RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+                    size = (is512) ? 64 : 32;
+                }
+                byte[] encKey = new byte[size];
+
+                extractBytes(encKey, size, 0, priv.getD());
+
+                return new PrivateKeyInfo(new AlgorithmIdentifier(identifier, gostParams), new DEROctetString(encKey));
+            }
             else if (domainParams instanceof ECNamedDomainParameters)
             {
                 params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName());
@@ -85,11 +162,59 @@
                 orderBitLength = domainParams.getN().bitLength();
             }
 
-            return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(orderBitLength, priv.getD(), params));
+            return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params),
+                new ECPrivateKey(orderBitLength, priv.getD(),
+                    new DERBitString(domainParams.getG().multiply(priv.getD()).getEncoded(false)), params),
+                attributes);
+        }
+        else if (privateKey instanceof X448PrivateKeyParameters)
+        {
+            X448PrivateKeyParameters key = (X448PrivateKeyParameters)privateKey;
+
+            return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448),
+                new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded());
+        }
+        else if (privateKey instanceof X25519PrivateKeyParameters)
+        {
+            X25519PrivateKeyParameters key = (X25519PrivateKeyParameters)privateKey;
+
+            return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519),
+                new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded());
+        }
+        else if (privateKey instanceof Ed448PrivateKeyParameters)
+        {
+            Ed448PrivateKeyParameters key = (Ed448PrivateKeyParameters)privateKey;
+
+            return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448),
+                new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded());
+        }
+        else if (privateKey instanceof Ed25519PrivateKeyParameters)
+        {
+            Ed25519PrivateKeyParameters key = (Ed25519PrivateKeyParameters)privateKey;
+
+            return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519),
+                new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded());
         }
         else
         {
-            throw new IOException("key parameters not recognised.");
+            throw new IOException("key parameters not recognized");
+        }
+    }
+
+
+    private static void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI)
+    {
+        byte[] val = bI.toByteArray();
+        if (val.length < size)
+        {
+            byte[] tmp = new byte[size];
+            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+            val = tmp;
+        }
+
+        for (int i = 0; i != size; i++)
+        {
+            encKey[offSet + i] = val[val.length - 1 - i];
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
index c751061..b2ab535 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -3,6 +3,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1InputStream;
@@ -10,12 +12,24 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.ElGamalParameter;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.ua.DSTU4145BinaryField;
+import org.bouncycastle.asn1.ua.DSTU4145ECBinary;
+import org.bouncycastle.asn1.ua.DSTU4145NamedCurves;
+import org.bouncycastle.asn1.ua.DSTU4145Params;
+import org.bouncycastle.asn1.ua.DSTU4145PointEncoder;
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -27,6 +41,7 @@
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.ec.CustomNamedCurves;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
@@ -36,11 +51,17 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
 import org.bouncycastle.crypto.params.ElGamalParameters;
 import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+import org.bouncycastle.math.ec.ECCurve;
 
 /**
  * Factory to create asymmetric public key parameters for asymmetric ciphers from range of
@@ -48,55 +69,122 @@
  */
 public class PublicKeyFactory
 {
+    private static Map converters = new HashMap();
+
+    static
+    {
+        converters.put(PKCSObjectIdentifiers.rsaEncryption, new RSAConverter());
+        converters.put(PKCSObjectIdentifiers.id_RSASSA_PSS, new RSAConverter());
+        converters.put(X509ObjectIdentifiers.id_ea_rsa, new RSAConverter());
+        converters.put(X9ObjectIdentifiers.dhpublicnumber, new DHPublicNumberConverter());
+        converters.put(PKCSObjectIdentifiers.dhKeyAgreement, new DHAgreementConverter());
+        converters.put(X9ObjectIdentifiers.id_dsa, new DSAConverter());
+        converters.put(OIWObjectIdentifiers.dsaWithSHA1, new DSAConverter());
+        converters.put(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalConverter());
+        converters.put(X9ObjectIdentifiers.id_ecPublicKey, new ECConverter());
+        converters.put(CryptoProObjectIdentifiers.gostR3410_2001, new GOST3410_2001Converter());
+        converters.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, new GOST3410_2012Converter());
+        converters.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, new GOST3410_2012Converter());
+        converters.put(UAObjectIdentifiers.dstu4145be, new DSTUConverter());
+        converters.put(UAObjectIdentifiers.dstu4145le, new DSTUConverter());
+        converters.put(EdECObjectIdentifiers.id_X25519, new X25519Converter());
+        converters.put(EdECObjectIdentifiers.id_X448, new X448Converter());
+        converters.put(EdECObjectIdentifiers.id_Ed25519, new Ed25519Converter());
+        converters.put(EdECObjectIdentifiers.id_Ed448, new Ed448Converter());
+    }
+
     /**
      * Create a public key from a SubjectPublicKeyInfo encoding
-     * 
+     *
      * @param keyInfoData the SubjectPublicKeyInfo encoding
      * @return the appropriate key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(byte[] keyInfoData) throws IOException
+    public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
+        throws IOException
     {
         return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
     }
 
     /**
      * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
-     * 
+     *
      * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
      * @return the appropriate key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException
+    public static AsymmetricKeyParameter createKey(InputStream inStr)
+        throws IOException
     {
         return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
     }
 
     /**
      * Create a public key from the passed in SubjectPublicKeyInfo
-     * 
+     *
      * @param keyInfo the SubjectPublicKeyInfo containing the key data
      * @return the appropriate key parameter
      * @throws IOException on an error decoding the key
      */
-    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) throws IOException
+    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        return createKey(keyInfo, null);
+    }
+
+    /**
+     * Create a public key from the passed in SubjectPublicKeyInfo
+     *
+     * @param keyInfo       the SubjectPublicKeyInfo containing the key data
+     * @param defaultParams default parameters that might be needed.
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        throws IOException
     {
         AlgorithmIdentifier algId = keyInfo.getAlgorithm();
+        SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algId.getAlgorithm());
 
-        if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption)
-            || algId.getAlgorithm().equals(X509ObjectIdentifiers.id_ea_rsa))
+        if (converter != null)
+        {
+            return converter.getPublicKeyParameters(keyInfo, defaultParams);
+        }
+        else
+        {
+            throw new IOException("algorithm identifier in public key not recognised: " + algId.getAlgorithm());
+        }
+    }
+
+    private static abstract class SubjectPublicKeyInfoConverter
+    {
+        abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException;
+    }
+
+    private static class RSAConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
         {
             RSAPublicKey pubKey = RSAPublicKey.getInstance(keyInfo.parsePublicKey());
 
             return new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent());
         }
-        else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.dhpublicnumber))
+    }
+
+    private static class DHPublicNumberConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
         {
             DHPublicKey dhPublicKey = DHPublicKey.getInstance(keyInfo.parsePublicKey());
 
             BigInteger y = dhPublicKey.getY();
 
-            DomainParameters dhParams = DomainParameters.getInstance(algId.getParameters());
+            DomainParameters dhParams = DomainParameters.getInstance(keyInfo.getAlgorithm().getParameters());
 
             BigInteger p = dhParams.getP();
             BigInteger g = dhParams.getG();
@@ -122,9 +210,15 @@
 
             return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation));
         }
-        else if (algId.getAlgorithm().equals(PKCSObjectIdentifiers.dhKeyAgreement))
+    }
+
+    private static class DHAgreementConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
         {
-            DHParameter params = DHParameter.getInstance(algId.getParameters());
+            DHParameter params = DHParameter.getInstance(keyInfo.getAlgorithm().getParameters());
             ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
 
             BigInteger lVal = params.getL();
@@ -133,19 +227,30 @@
 
             return new DHPublicKeyParameters(derY.getValue(), dhParams);
         }
-        else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+    }
+
+    private static class ElGamalConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
         {
-            ElGamalParameter params = ElGamalParameter.getInstance(algId.getParameters());
+            ElGamalParameter params = ElGamalParameter.getInstance(keyInfo.getAlgorithm().getParameters());
             ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
 
             return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters(
                 params.getP(), params.getG()));
         }
-        else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_dsa)
-            || algId.getAlgorithm().equals(OIWObjectIdentifiers.dsaWithSHA1))
+    }
+
+    private static class DSAConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
         {
             ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
-            ASN1Encodable de = algId.getParameters();
+            ASN1Encodable de = keyInfo.getAlgorithm().getParameters();
 
             DSAParameters parameters = null;
             if (de != null)
@@ -156,40 +261,278 @@
 
             return new DSAPublicKeyParameters(derY.getValue(), parameters);
         }
-        else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
-        {
-            X962Parameters params = X962Parameters.getInstance(algId.getParameters());
+    }
 
-            X9ECParameters x9;
+    private static class ECConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            X962Parameters params = X962Parameters.getInstance(keyInfo.getAlgorithm().getParameters());
             ECDomainParameters dParams;
 
             if (params.isNamedCurve())
             {
                 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
 
-                x9 = CustomNamedCurves.getByOID(oid);
+                X9ECParameters x9 = CustomNamedCurves.getByOID(oid);
                 if (x9 == null)
                 {
                     x9 = ECNamedCurveTable.getByOID(oid);
                 }
                 dParams = new ECNamedDomainParameters(
-                         oid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+                    oid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+            }
+            else if (params.isImplicitlyCA())
+            {
+                dParams = (ECDomainParameters)defaultParams;
             }
             else
             {
-                x9 = X9ECParameters.getInstance(params.getParameters());
+                X9ECParameters x9 = X9ECParameters.getInstance(params.getParameters());
                 dParams = new ECDomainParameters(
-                         x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+                    x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
             }
 
-            ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes());
-            X9ECPoint derQ = new X9ECPoint(x9.getCurve(), key);
+            DERBitString bits = keyInfo.getPublicKeyData();
+            byte[] data = bits.getBytes();
+            ASN1OctetString key = new DEROctetString(data);
+
+            //
+            // extra octet string - the old extra embedded octet string
+            //
+            if (data[0] == 0x04 && data[1] == data.length - 2
+                && (data[2] == 0x02 || data[2] == 0x03))
+            {
+                int qLength = new X9IntegerConverter().getByteLength(dParams.getCurve());
+
+                if (qLength >= data.length - 3)
+                {
+                    try
+                    {
+                        key = (ASN1OctetString)ASN1Primitive.fromByteArray(data);
+                    }
+                    catch (IOException ex)
+                    {
+                        throw new IllegalArgumentException("error recovering public key");
+                    }
+                }
+            }
+
+            X9ECPoint derQ = new X9ECPoint(dParams.getCurve(), key);
 
             return new ECPublicKeyParameters(derQ.getPoint(), dParams);
         }
-        else
+    }
+
+    private static class GOST3410_2001Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            throw new RuntimeException("algorithm identifier in key not recognised");
+            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1OctetString key;
+
+            try
+            {
+                key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
+            }
+            catch (IOException ex)
+            {
+                throw new IllegalArgumentException("error recovering public key");
+            }
+
+            byte[] keyEnc = key.getOctets();
+
+            byte[] x9Encoding = new byte[65];
+            x9Encoding[0] = 0x04;
+            for (int i = 1; i <= 32; ++i)
+            {
+                x9Encoding[i] = keyEnc[32 - i];
+                x9Encoding[i + 32] = keyEnc[64 - i];
+            }
+
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getAlgorithm().getParameters());
+
+            ECGOST3410Parameters ecDomainParameters =
+                new ECGOST3410Parameters(
+                    new ECNamedDomainParameters(gostParams.getPublicKeyParamSet(), ECGOST3410NamedCurves.getByOID(gostParams.getPublicKeyParamSet())),
+                    gostParams.getPublicKeyParamSet(),
+                    gostParams.getDigestParamSet(),
+                    gostParams.getEncryptionParamSet());
+
+
+            return new ECPublicKeyParameters(ecDomainParameters.getCurve().decodePoint(x9Encoding), ecDomainParameters);
+
         }
     }
+
+    private static class GOST3410_2012Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm();
+            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1OctetString key;
+
+            try
+            {
+                key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
+            }
+            catch (IOException ex)
+            {
+                throw new IllegalArgumentException("error recovering public key");
+            }
+
+            byte[] keyEnc = key.getOctets();
+
+            int fieldSize = 32;
+            if (algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512))
+            {
+                fieldSize = 64;
+            }
+
+            int keySize = 2 * fieldSize;
+
+            byte[] x9Encoding = new byte[1 + keySize];
+            x9Encoding[0] = 0x04;
+            for (int i = 1; i <= fieldSize; ++i)
+            {
+                x9Encoding[i] = keyEnc[fieldSize - i];
+                x9Encoding[i + fieldSize] = keyEnc[keySize - i];
+            }
+
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getAlgorithm().getParameters());
+
+            ECGOST3410Parameters ecDomainParameters =
+                new ECGOST3410Parameters(
+                    new ECNamedDomainParameters(gostParams.getPublicKeyParamSet(), ECGOST3410NamedCurves.getByOID(gostParams.getPublicKeyParamSet())),
+                    gostParams.getPublicKeyParamSet(),
+                    gostParams.getDigestParamSet(),
+                    gostParams.getEncryptionParamSet());
+
+
+            return new ECPublicKeyParameters(ecDomainParameters.getCurve().decodePoint(x9Encoding), ecDomainParameters);
+        }
+    }
+
+    private static class DSTUConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1OctetString key;
+
+            try
+            {
+                key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
+            }
+            catch (IOException ex)
+            {
+                throw new IllegalArgumentException("error recovering public key");
+            }
+
+            byte[] keyEnc = key.getOctets();
+
+            if (keyInfo.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+            {
+                reverseBytes(keyEnc);
+            }
+
+            DSTU4145Params dstuParams = DSTU4145Params.getInstance(keyInfo.getAlgorithm().getParameters());
+
+            ECDomainParameters ecDomain;
+            if (dstuParams.isNamedCurve())
+            {
+                ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
+
+                ecDomain = DSTU4145NamedCurves.getByOID(curveOid);
+            }
+            else
+            {
+                DSTU4145ECBinary binary = dstuParams.getECBinary();
+                byte[] b_bytes = binary.getB();
+                if (keyInfo.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                {
+                    reverseBytes(b_bytes);
+                }
+                DSTU4145BinaryField field = binary.getField();
+                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
+                byte[] g_bytes = binary.getG();
+                if (keyInfo.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                {
+                    reverseBytes(g_bytes);
+                }
+                ecDomain = new ECDomainParameters(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
+            }
+
+            return new ECPublicKeyParameters(DSTU4145PointEncoder.decodePoint(ecDomain.getCurve(), keyEnc), ecDomain);
+        }
+
+        private void reverseBytes(byte[] bytes)
+        {
+            byte tmp;
+
+            for (int i = 0; i < bytes.length / 2; i++)
+            {
+                tmp = bytes[i];
+                bytes[i] = bytes[bytes.length - 1 - i];
+                bytes[bytes.length - 1 - i] = tmp;
+            }
+        }
+    }
+
+    private static class X25519Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, X25519PublicKeyParameters.KEY_SIZE), 0);
+        }
+    }
+
+    private static class X448Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams, X448PublicKeyParameters.KEY_SIZE), 0);
+        }
+    }
+
+    private static class Ed25519Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed25519PublicKeyParameters.KEY_SIZE), 0);
+        }
+    }
+
+    private static class Ed448Converter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        {
+            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed448PublicKeyParameters.KEY_SIZE), 0);
+        }
+    }
+
+    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams, int expectedSize)
+    {
+        /*
+         * TODO[RFC 8422]
+         * - Require defaultParams == null?
+         * - Require keyInfo.getAlgorithm().getParameters() == null?
+         */
+        byte[] result = keyInfo.getPublicKeyData().getOctets();
+        if (expectedSize != result.length)
+        {
+            throw new RuntimeException("public key encoding has incorrect length");
+        }
+        return result;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuffer.java
new file mode 100644
index 0000000..f177d22
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuffer.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.util;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A Buffer for dealing with SSH key products.
+ */
+class SSHBuffer
+{
+    private final byte[] buffer;
+    private int pos = 0;
+
+    public SSHBuffer(byte[] magic, byte[] buffer)
+    {
+        this.buffer = buffer;
+        for (int i = 0; i != magic.length; i++)
+        {
+            if (magic[i] != buffer[i])
+            {
+                throw new IllegalArgumentException("magic-number incorrect");
+            }
+        }
+
+        pos += magic.length;
+    }
+
+    public SSHBuffer(byte[] buffer)
+    {
+        this.buffer = buffer;
+    }
+
+    public int readU32()
+    {
+        if (pos + 4 > buffer.length)
+        {
+            throw new IllegalArgumentException("4 bytes for U32 exceeds buffer.");
+        }
+
+        int i = (buffer[pos++] & 0xFF) << 24;
+        i |= (buffer[pos++] & 0xFF) << 16;
+        i |= (buffer[pos++] & 0xFF) << 8;
+        i |= (buffer[pos++] & 0xFF);
+
+        return i;
+    }
+
+    public byte[] readString()
+    {
+        int len = readU32();
+        if (len == 0)
+        {
+            return new byte[0];
+        }
+
+        if (pos + len > buffer.length)
+        {
+            throw new IllegalArgumentException("not enough data for string");
+        }
+
+        return Arrays.copyOfRange(buffer, pos, pos += len);
+    }
+
+    public byte[] readPaddedString()
+    {
+        int len = readU32();
+        if (len == 0)
+        {
+            return new byte[0];
+        }
+
+        if (pos + len > buffer.length)
+        {
+            throw new IllegalArgumentException("not enough data for string");
+        }
+
+        return Arrays.copyOfRange(buffer, pos, pos += (len - (buffer[pos + len - 1] & 0xff)));
+    }
+
+
+    public BigInteger positiveBigNum()
+    {
+        int len = readU32();
+        if (pos + len > buffer.length)
+        {
+            throw new IllegalArgumentException("not enough data for big num");
+        }
+
+        byte[] d = new byte[len];
+        System.arraycopy(buffer, pos, d, 0, d.length);
+        pos += len;
+        return new BigInteger(1, d);
+    }
+
+    public byte[] getBuffer()
+    {
+        return Arrays.clone(buffer);
+    }
+
+    public boolean hasRemaining()
+    {
+        return pos < buffer.length;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuilder.java
new file mode 100644
index 0000000..28f9a32
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHBuilder.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.crypto.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.bouncycastle.util.Strings;
+
+class SSHBuilder
+{
+    private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    public void u32(long value)
+    {
+        bos.write((int)((value >>> 24) & 0xFF));
+        bos.write((int)((value >>> 16) & 0xFF));
+        bos.write((int)((value >>> 8) & 0xFF));
+        bos.write((int)(value & 0xFF));
+    }
+
+    public void rawArray(byte[] value)
+    {
+        u32(value.length);
+        try
+        {
+            bos.write(value);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    public void write(byte[] value)
+    {
+        try
+        {
+            bos.write(value);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    public void writeString(String str)
+    {
+        rawArray(Strings.toByteArray(str));
+    }
+
+    public byte[] getBytes()
+    {
+        return bos.toByteArray();
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/ScryptConfig.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/ScryptConfig.java
new file mode 100644
index 0000000..bb02683
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/ScryptConfig.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.util;
+
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+
+/**
+ * Configuration class for a PBKDF based around scrypt.
+ */
+public class ScryptConfig
+    extends PBKDFConfig
+{
+    public static class Builder
+    {
+        private final int costParameter;
+        private final int blockSize;
+        private final int parallelizationParameter;
+
+        private int saltLength = 16;
+
+        /**
+         * Base constructor.
+         *
+         * @param costParameter cost parameter (must be a power of 2)
+         * @param blockSize block size
+         * @param parallelizationParameter parallelization parameter
+         */
+        public Builder(int costParameter, int blockSize, int parallelizationParameter)
+        {
+            if (costParameter <= 1 || !isPowerOf2(costParameter))
+            {
+                throw new IllegalArgumentException("Cost parameter N must be > 1 and a power of 2");
+            }
+
+            this.costParameter = costParameter;
+            this.blockSize = blockSize;
+            this.parallelizationParameter = parallelizationParameter;
+        }
+
+        /**
+         * Set the length of the salt to use.
+         *
+         * @param saltLength the length of the salt (in octets) to use.
+         * @return the current builder.
+         */
+        public Builder withSaltLength(int saltLength)
+        {
+            this.saltLength = saltLength;
+
+            return this;
+        }
+
+        public ScryptConfig build()
+        {
+            return new ScryptConfig(this);
+        }
+
+        // note: we know X is non-zero
+        private static boolean isPowerOf2(int x)
+        {
+            return ((x & (x - 1)) == 0);
+        }
+    }
+
+    private final int costParameter;
+    private final int blockSize;
+    private final int parallelizationParameter;
+    private final int saltLength;
+
+    private ScryptConfig(Builder builder)
+    {
+        super(MiscObjectIdentifiers.id_scrypt);
+
+        this.costParameter = builder.costParameter;
+        this.blockSize = builder.blockSize;
+        this.parallelizationParameter = builder.parallelizationParameter;
+        this.saltLength = builder.saltLength;
+    }
+
+    public int getCostParameter()
+    {
+        return costParameter;
+    }
+
+    public int getBlockSize()
+    {
+        return blockSize;
+    }
+
+    public int getParallelizationParameter()
+    {
+        return parallelizationParameter;
+    }
+
+    public int getSaltLength()
+    {
+        return saltLength;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
index 3e07106..ffe1efa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
@@ -1,13 +1,22 @@
 package org.bouncycastle.crypto.util;
 
 import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -19,15 +28,31 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
 
 /**
  * Factory to create ASN.1 subject public key info objects from lightweight public keys.
  */
 public class SubjectPublicKeyInfoFactory
 {
+    private static Set cryptoProOids = new HashSet(5);
+
+    static
+    {
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA);
+        cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB);
+    }
+
     private SubjectPublicKeyInfoFactory()
     {
 
@@ -40,7 +65,8 @@
      * @return a SubjectPublicKeyInfo representing the key.
      * @throws java.io.IOException on an error encoding the key
      */
-    public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) throws IOException
+    public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey)
+        throws IOException
     {
         if (publicKey instanceof RSAKeyParameters)
         {
@@ -65,12 +91,64 @@
         {
             ECPublicKeyParameters pub = (ECPublicKeyParameters)publicKey;
             ECDomainParameters domainParams = pub.getParameters();
-            ASN1Encodable      params;
+            ASN1Encodable params;
 
             if (domainParams == null)
             {
                 params = new X962Parameters(DERNull.INSTANCE);      // Implicitly CA
             }
+            else if (domainParams instanceof ECGOST3410Parameters)
+            {
+                ECGOST3410Parameters gostParams = (ECGOST3410Parameters)domainParams;
+
+                BigInteger bX = pub.getQ().getAffineXCoord().toBigInteger();
+                BigInteger bY = pub.getQ().getAffineYCoord().toBigInteger();
+
+                params = new GOST3410PublicKeyAlgParameters(gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet());
+
+                int encKeySize;
+                int offset;
+                ASN1ObjectIdentifier algIdentifier;
+
+
+                if (cryptoProOids.contains(gostParams.getPublicKeyParamSet()))
+                {
+                    encKeySize = 64;
+                    offset = 32;
+                    algIdentifier = CryptoProObjectIdentifiers.gostR3410_2001;
+                }
+                else
+                {
+                    boolean is512 = (bX.bitLength() > 256);
+                    if (is512)
+                    {
+                        encKeySize = 128;
+                        offset = 64;
+                        algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512;
+                    }
+                    else
+                    {
+                        encKeySize = 64;
+                        offset = 32;
+                        algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+                    }
+                }
+
+                byte[] encKey = new byte[encKeySize];
+
+
+                extractBytes(encKey, encKeySize / 2, 0, bX);
+                extractBytes(encKey, encKeySize / 2, offset, bY);
+
+                try
+                {
+                    return new SubjectPublicKeyInfo(new AlgorithmIdentifier(algIdentifier, params), new DEROctetString(encKey));
+                }
+                catch (IOException e)
+                {
+                    return null;
+                }
+            }
             else if (domainParams instanceof ECNamedDomainParameters)
             {
                 params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName());
@@ -91,9 +169,49 @@
 
             return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
         }
+        else if (publicKey instanceof X448PublicKeyParameters)
+        {
+            X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey;
+
+            return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.getEncoded());
+        }
+        else if (publicKey instanceof X25519PublicKeyParameters)
+        {
+            X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey;
+
+            return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.getEncoded());
+        }
+        else if (publicKey instanceof Ed448PublicKeyParameters)
+        {
+            Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey;
+
+            return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.getEncoded());
+        }
+        else if (publicKey instanceof Ed25519PublicKeyParameters)
+        {
+            Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey;
+
+            return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.getEncoded());
+        }
         else
         {
-            throw new IOException("key parameters not recognised.");
+            throw new IOException("key parameters not recognized");
+        }
+    }
+
+    private static void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI)
+    {
+        byte[] val = bI.toByteArray();
+        if (val.length < size)
+        {
+            byte[] tmp = new byte[size];
+            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+            val = tmp;
+        }
+
+        for (int i = 0; i != size; i++)
+        {
+            encKey[offSet + i] = val[val.length - 1 - i];
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSLoadStoreParameter.java b/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSLoadStoreParameter.java
new file mode 100644
index 0000000..3781f8a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSLoadStoreParameter.java
@@ -0,0 +1,325 @@
+package org.bouncycastle.jcajce;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.crypto.util.PBKDF2Config;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+
+/**
+ * LoadStoreParameter to allow configuring of the PBKDF used to generate encryption keys for
+ * use in the keystore.
+ */
+public class BCFKSLoadStoreParameter
+    extends BCLoadStoreParameter
+{
+    public enum EncryptionAlgorithm
+    {
+        AES256_CCM,
+        AES256_KWP
+    }
+
+    public enum MacAlgorithm
+    {
+        HmacSHA512,
+        HmacSHA3_512
+    }
+
+    public enum SignatureAlgorithm
+    {
+        SHA512withDSA,
+        SHA3_512withDSA,
+        SHA512withECDSA,
+        SHA3_512withECDSA,
+        SHA512withRSA,
+        SHA3_512withRSA
+    }
+
+    public interface CertChainValidator
+    {
+        /**
+         * Return true if the passed in chain is valid, false otherwise.
+         *
+         * @param chain the certChain we know about, the end-entity is at position 0.
+         * @return true if valid, false otherwise.
+         */
+         boolean isValid(X509Certificate[] chain);
+    }
+
+    public static class Builder
+    {
+        private final OutputStream out;
+        private final InputStream in;
+        private final KeyStore.ProtectionParameter protectionParameter;
+        private final Key sigKey;
+
+        private PBKDFConfig storeConfig = new PBKDF2Config.Builder()
+                                                .withIterationCount(16384)
+                                                .withSaltLength(64).withPRF(PBKDF2Config.PRF_SHA512).build();
+        private EncryptionAlgorithm encAlg = EncryptionAlgorithm.AES256_CCM;
+        private MacAlgorithm macAlg = MacAlgorithm.HmacSHA512;
+        private SignatureAlgorithm sigAlg = SignatureAlgorithm.SHA512withECDSA;
+        private X509Certificate[] certs = null;
+        private CertChainValidator validator;
+
+
+        /**
+         * Base constructor for creating a LoadStoreParameter for initializing a key store.
+         */
+        public Builder()
+        {
+            this((OutputStream)null, (KeyStore.ProtectionParameter)null);
+        }
+
+        /**
+         * Base constructor for storing to an OutputStream using a password.
+         *
+         * @param out OutputStream to write KeyStore to.
+         * @param password the password to use to protect the KeyStore.
+         */
+        public Builder(OutputStream out, char[] password)
+        {
+            this(out, new KeyStore.PasswordProtection(password));
+        }
+
+        /**
+         * Base constructor for storing to an OutputStream using a protection parameter.
+         *
+         * @param out OutputStream to write KeyStore to.
+         * @param protectionParameter the protection parameter to use to protect the KeyStore.
+         */
+        public Builder(OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+        {
+            this.in = null;
+            this.out = out;
+            this.protectionParameter = protectionParameter;
+            this.sigKey = null;
+        }
+
+        /**
+         * Base constructor for storing to an OutputStream using a protection parameter.
+         *
+         * @param out OutputStream to write KeyStore to.
+         * @param sigKey the key used to protect the integrity of the key store.
+         */
+        public Builder(OutputStream out, PrivateKey sigKey)
+        {
+            this.in = null;
+            this.out = out;
+            this.protectionParameter = null;
+            this.sigKey = sigKey;
+        }
+
+        /**
+         * Base constructor for reading a KeyStore from an InputStream using a public key for validation.
+         *
+         * @param in InputStream to load KeyStore to.
+         * @param sigKey the public key parameter to used to verify the KeyStore.
+         */
+        public Builder(InputStream in, PublicKey sigKey)
+        {
+            this.in = in;
+            this.out = null;
+            this.protectionParameter = null;
+            this.sigKey = sigKey;
+        }
+
+        /**
+         * Base constructor for reading a KeyStore from an InputStream using validation based on
+         * encapsulated certificates in the KeyStore data.
+         *
+         * @param in InputStream to load KeyStore to.
+         * @param validator the certificate chain validator to check the signing certificates.
+         */
+        public Builder(InputStream in, CertChainValidator validator)
+        {
+            this.in = in;
+            this.out = null;
+            this.protectionParameter = null;
+            this.validator = validator;
+            this.sigKey = null;
+        }
+
+        /**
+         * Base constructor for reading a KeyStore from an InputStream using a password.
+         *
+         * @param in InputStream to read the KeyStore from.
+         * @param password the password used to protect the KeyStore.
+         */
+        public Builder(InputStream in, char[] password)
+        {
+            this(in, new KeyStore.PasswordProtection(password));
+        }
+
+        /**
+         * Base constructor for reading a KeyStore from an InputStream using a password.
+         *
+         * @param in InputStream to read the KeyStore from.
+         * @param protectionParameter  the protection parameter used to protect the KeyStore.
+         */
+        public Builder(InputStream in, KeyStore.ProtectionParameter protectionParameter)
+        {
+            this.in = in;
+            this.out = null;
+            this.protectionParameter = protectionParameter;
+            this.sigKey = null;
+        }
+
+        /**
+         * Configure the PBKDF to use for protecting the KeyStore.
+         *
+         * @param storeConfig the PBKDF config to use for protecting the KeyStore.
+         * @return the current Builder instance.
+         */
+        public Builder withStorePBKDFConfig(PBKDFConfig storeConfig)
+        {
+            this.storeConfig = storeConfig;
+            return this;
+        }
+
+        /**
+         * Configure the encryption algorithm to use for protecting the KeyStore and its keys.
+         *
+         * @param encAlg the PBKDF config to use for protecting the KeyStore and its keys.
+         * @return the current Builder instance.
+         */
+        public Builder withStoreEncryptionAlgorithm(EncryptionAlgorithm encAlg)
+        {
+            this.encAlg = encAlg;
+            return this;
+        }
+
+        /**
+         * Configure the MAC algorithm to use for protecting the KeyStore.
+         *
+         * @param macAlg the PBKDF config to use for protecting the KeyStore.
+         * @return the current Builder instance.
+         */
+        public Builder withStoreMacAlgorithm(MacAlgorithm macAlg)
+        {
+            this.macAlg = macAlg;
+            return this;
+        }
+
+
+        /**
+         * Add a valid certificate chain where certs[0] is the end-entity matching the
+         * private key we are using to sign the key store.
+         *
+         * @param certs an array of X509 certificates.
+         * @return the current Builder instance.
+         */
+        public Builder withCertificates(X509Certificate[] certs)
+        {
+            X509Certificate[] tmp = new X509Certificate[certs.length];
+            System.arraycopy(certs, 0, tmp, 0, tmp.length);
+            this.certs = tmp;
+
+            return this;
+        }
+
+        /**
+         * Configure the signature algorithm to use for protecting the KeyStore.
+         *
+         * @param sigAlg the signature config to use for protecting the KeyStore.
+         * @return the current Builder instance.
+         */
+        public Builder withStoreSignatureAlgorithm(SignatureAlgorithm sigAlg)
+        {
+            this.sigAlg = sigAlg;
+
+            return this;
+        }
+
+        /**
+         * Build and return a BCFKSLoadStoreParameter.
+         *
+         * @return a new BCFKSLoadStoreParameter.
+         */
+        public BCFKSLoadStoreParameter build()
+        {
+            return new BCFKSLoadStoreParameter(this);
+        }
+    }
+
+    private final PBKDFConfig storeConfig;
+    private final EncryptionAlgorithm encAlg;
+    private final MacAlgorithm macAlg;
+    private final SignatureAlgorithm sigAlg;
+    private final Key sigKey;
+    private final X509Certificate[] certificates;
+    private final CertChainValidator validator;
+
+    private BCFKSLoadStoreParameter(Builder bldr)
+    {
+        super(bldr.in, bldr.out, bldr.protectionParameter);
+
+        this.storeConfig = bldr.storeConfig;
+        this.encAlg = bldr.encAlg;
+        this.macAlg = bldr.macAlg;
+        this.sigAlg = bldr.sigAlg;
+        this.sigKey = bldr.sigKey;
+        this.certificates = bldr.certs;
+        this.validator = bldr.validator;
+    }
+
+    /**
+     * Return the PBKDF used for generating the HMAC and store encryption keys.
+     *
+     * @return the PBKDF to use for deriving HMAC and store encryption keys.
+     */
+    public PBKDFConfig getStorePBKDFConfig()
+    {
+        return storeConfig;
+    }
+
+    /**
+     * Return encryption algorithm used to secure the store and its entries.
+     *
+     * @return the encryption algorithm to use.
+     */
+    public EncryptionAlgorithm getStoreEncryptionAlgorithm()
+    {
+        return encAlg;
+    }
+
+    /**
+     * Return mac algorithm used to protect the integrity of the store and its contents.
+     *
+     * @return the mac algorithm to use.
+     */
+    public MacAlgorithm getStoreMacAlgorithm()
+    {
+        return macAlg;
+    }
+
+    /**
+     * Return signature algorithm used to protect the integrity of the store and its contents.
+     *
+     * @return the signature algorithm to use.
+     */
+    public SignatureAlgorithm getStoreSignatureAlgorithm()
+    {
+        return sigAlg;
+    }
+
+    public Key getStoreSignatureKey()
+    {
+        return sigKey;
+    }
+
+    public X509Certificate[] getStoreCertificates()
+    {
+        return certificates;
+    }
+
+    public CertChainValidator getCertChainValidator()
+    {
+        return validator;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSStoreParameter.java b/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSStoreParameter.java
new file mode 100644
index 0000000..6b52831
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/BCFKSStoreParameter.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.jcajce;
+
+import java.io.OutputStream;
+import java.security.KeyStore;
+
+import org.bouncycastle.crypto.util.PBKDFConfig;
+
+/**
+ * LoadStoreParameter to allow configuring of the PBKDF used to generate encryption keys for
+ * use in the keystore.
+ * @deprecated This class does not support configuration on creation, use BCFKSLoadStoreParameter for best results.
+ */
+public class BCFKSStoreParameter
+    implements KeyStore.LoadStoreParameter
+{
+    private final KeyStore.ProtectionParameter protectionParameter;
+    private final PBKDFConfig storeConfig;
+
+    private OutputStream out;
+
+    public BCFKSStoreParameter(OutputStream out, PBKDFConfig storeConfig, char[] password)
+    {
+        this(out, storeConfig, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCFKSStoreParameter(OutputStream out, PBKDFConfig storeConfig, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this.out = out;
+        this.storeConfig = storeConfig;
+        this.protectionParameter = protectionParameter;
+    }
+
+    public KeyStore.ProtectionParameter getProtectionParameter()
+    {
+        return protectionParameter;
+    }
+
+    public OutputStream getOutputStream()
+    {
+        return out;
+    }
+
+    /**
+     * Return the PBKDF used for generating the HMAC and store encryption keys.
+     *
+     * @return the PBKDF to use for deriving HMAC and store encryption keys.
+     */
+    public PBKDFConfig getStorePBKDFConfig()
+    {
+        return storeConfig;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java b/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java
new file mode 100644
index 0000000..0a299cb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.jcajce;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+
+public class BCLoadStoreParameter
+    implements KeyStore.LoadStoreParameter
+{
+    private final InputStream in;
+    private final OutputStream out;
+    private final KeyStore.ProtectionParameter protectionParameter;
+
+    /**
+     * Base constructor for
+     *
+     * @param out
+     * @param password
+     */
+    public BCLoadStoreParameter(OutputStream out, char[] password)
+    {
+        this(out, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, char[] password)
+    {
+        this(in, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(in, null, protectionParameter);
+    }
+
+    public BCLoadStoreParameter(OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(null, out, protectionParameter);
+    }
+
+    BCLoadStoreParameter(InputStream in, OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this.in = in;
+        this.out = out;
+        this.protectionParameter = protectionParameter;
+    }
+
+    public KeyStore.ProtectionParameter getProtectionParameter()
+    {
+        return protectionParameter;
+    }
+
+    public OutputStream getOutputStream()
+    {
+        if (out == null)
+        {
+            throw new UnsupportedOperationException("parameter not configured for storage - no OutputStream");
+        }
+
+        return out;
+    }
+
+    public InputStream getInputStream()
+    {
+        if (out != null)
+        {
+            throw new UnsupportedOperationException("parameter configured for storage OutputStream present");
+        }
+
+        return in;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PKIXExtendedParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PKIXExtendedParameters.java
index cd24f06..34ac2b0 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/PKIXExtendedParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PKIXExtendedParameters.java
@@ -41,7 +41,7 @@
      * when the end certificate was signed. The CA (or Root CA) certificate must
      * have been valid, when the CA certificate was signed and so on. So the
      * {@link PKIXParameters#setDate(Date)} method sets the time, when
-     * the <em>end certificate</em> must have been valid. <p/> It is used e.g.
+     * the <em>end certificate</em> must have been valid. It is used e.g.
      * in the German signature law.
      */
     public static final int CHAIN_VALIDITY_MODEL = 1;
@@ -337,4 +337,8 @@
         return revocationEnabled;
     }
 
+    public boolean getPolicyQualifiersRejected()
+    {
+        return baseParameters.getPolicyQualifiersRejected();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/EdDSAKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/EdDSAKey.java
new file mode 100644
index 0000000..9ae3805
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/EdDSAKey.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.jcajce.interfaces;
+
+import java.security.Key;
+
+public interface EdDSAKey
+    extends Key
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/XDHKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/XDHKey.java
new file mode 100644
index 0000000..cd3d9e8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/interfaces/XDHKey.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.jcajce.interfaces;
+
+import java.security.Key;
+
+public interface XDHKey
+    extends Key
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/DigestUpdatingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/DigestUpdatingOutputStream.java
new file mode 100644
index 0000000..3e3542d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/DigestUpdatingOutputStream.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+
+class DigestUpdatingOutputStream
+    extends OutputStream
+{
+    private MessageDigest digest;
+
+    DigestUpdatingOutputStream(MessageDigest digest)
+    {
+        this.digest = digest;
+    }
+
+    public void write(byte[] bytes, int off, int len)
+        throws IOException
+    {
+        digest.update(bytes, off, len);
+    }
+
+    public void write(byte[] bytes)
+        throws IOException
+    {
+        digest.update(bytes);
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        digest.update((byte)b);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/MacUpdatingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/MacUpdatingOutputStream.java
new file mode 100644
index 0000000..88496e8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/MacUpdatingOutputStream.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.crypto.Mac;
+
+class MacUpdatingOutputStream
+    extends OutputStream
+{
+    private Mac mac;
+
+    MacUpdatingOutputStream(Mac mac)
+    {
+        this.mac = mac;
+    }
+
+    public void write(byte[] bytes, int off, int len)
+        throws IOException
+    {
+        mac.update(bytes, off, len);
+    }
+
+    public void write(byte[] bytes)
+        throws IOException
+    {
+        mac.update(bytes);
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        mac.update((byte)b);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/OutputStreamFactory.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/OutputStreamFactory.java
new file mode 100644
index 0000000..9276328
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/OutputStreamFactory.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.Signature;
+
+import javax.crypto.Mac;
+
+/**
+ * Utility class for creating OutputStreams from different JCA/JCE operators.
+ */
+public class OutputStreamFactory
+{
+    /**
+     * Create an OutputStream that wraps a signature.
+     *
+     * @param signature the signature to be updated as the stream is written to.
+     * @return an OutputStream.
+     */
+    public static OutputStream createStream(Signature signature)
+    {
+        return new SignatureUpdatingOutputStream(signature);
+    }
+
+    /**
+     * Create an OutputStream that wraps a digest.
+     *
+     * @param digest the digest to be updated as the stream is written to.
+     * @return an OutputStream.
+     */
+    public static OutputStream createStream(MessageDigest digest)
+    {
+        return new DigestUpdatingOutputStream(digest);
+    }
+
+    /**
+     * Create an OutputStream that wraps a mac.
+     *
+     * @param mac the signature to be updated as the stream is written to.
+     * @return an OutputStream.
+     */
+    public static OutputStream createStream(Mac mac)
+    {
+        return new MacUpdatingOutputStream(mac);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
new file mode 100644
index 0000000..f092df1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.Signature;
+import java.security.SignatureException;
+
+class SignatureUpdatingOutputStream
+    extends OutputStream
+{
+    private Signature sig;
+
+    SignatureUpdatingOutputStream(Signature sig)
+    {
+        this.sig = sig;
+    }
+
+    public void write(byte[] bytes, int off, int len)
+        throws IOException
+    {
+        try
+        {
+            sig.update(bytes, off, len);
+        }
+        catch (SignatureException e)
+        {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    public void write(byte[] bytes)
+        throws IOException
+    {
+        try
+        {
+            sig.update(bytes);
+        }
+        catch (SignatureException e)
+        {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        try
+        {
+            sig.update((byte)b);
+        }
+        catch (SignatureException e)
+        {
+            throw new IOException(e.getMessage());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
index 8512395..6c663e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
@@ -59,6 +59,36 @@
             provider.addAlgorithm("Cipher.DHIESWITHAES-CBC", PREFIX + "IESCipher$IESwithAESCBC");
             provider.addAlgorithm("Cipher.DHIESWITHDESEDE-CBC", PREFIX + "IESCipher$IESwithDESedeCBC");
 
+            provider.addAlgorithm("KeyAgreement.DHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.DHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement.DHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.DHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement.DHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF");
+            provider.addAlgorithm("KeyAgreement.DHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF");
+
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF");
+            provider.addAlgorithm("KeyAgreement.MQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF");
+
             registerOid(provider, PKCSObjectIdentifiers.dhKeyAgreement, "DH", new KeyFactorySpi());
             registerOid(provider, X9ObjectIdentifiers.dhpublicnumber, "DH", new KeyFactorySpi());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
index 2164cb6..08949fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
@@ -70,8 +70,6 @@
             provider.addAlgorithm("Alg.Alias.Signature.SHA1WithDSA", "DSA");
             provider.addAlgorithm("Alg.Alias.Signature.DSAWithSHA1", "DSA");
 
-            provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10040.4.3", "DSA");
-
             AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi();
 
             for (int i = 0; i != DSAUtil.dsaOids.length; i++)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
index 174d9c8..9dd00b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
@@ -44,28 +44,71 @@
             provider.addAttributes("KeyAgreement.ECCDH", generalEcAttributes);
             provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC");
 
-            provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAttributes("KeyAgreement.ECCDHU", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC");
 
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
 
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
 
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
 
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
 
             provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF");
             provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF");
             provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF");
             provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF");
 
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF");
+
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
+
             registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC", new KeyFactorySpi.EC());
 
             registerOid(provider, X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, "EC", new KeyFactorySpi.EC());
@@ -110,6 +153,12 @@
                 provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF");
                 provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF");
 
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF");
+
                 provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo");
                 provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo");
                 provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ECGOST.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ECGOST.java
index d33126b..67aef3f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ECGOST.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ECGOST.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.jcajce.provider.asymmetric;
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.asymmetric.ecgost.KeyFactorySpi;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -8,6 +9,7 @@
 public class ECGOST
 {
     private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric" + ".ecgost.";
+    private static final String PREFIX_GOST_2012 = "org.bouncycastle.jcajce.provider.asymmetric" + ".ecgost12.";
 
     public static class Mappings
         extends AsymmetricAlgorithmProvider
@@ -18,12 +20,18 @@
         
         public void configure(ConfigurableProvider provider)
         {
+
+            // ========= GOST34.10 2001
             provider.addAlgorithm("KeyFactory.ECGOST3410", PREFIX + "KeyFactorySpi");
             provider.addAlgorithm("Alg.Alias.KeyFactory.GOST-3410-2001", "ECGOST3410");
             provider.addAlgorithm("Alg.Alias.KeyFactory.ECGOST-3410", "ECGOST3410");
 
-            registerOid(provider, CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410", new KeyFactorySpi());
-            registerOidAlgorithmParameters(provider, CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+            registerOid(provider, CryptoProObjectIdentifiers.gostR3410_2001,
+                    "ECGOST3410", new KeyFactorySpi());
+            registerOid(provider, CryptoProObjectIdentifiers.gostR3410_2001DH,
+                    "ECGOST3410", new KeyFactorySpi());
+            registerOidAlgorithmParameters(provider, CryptoProObjectIdentifiers.gostR3410_2001,
+                    "ECGOST3410");
 
             provider.addAlgorithm("KeyPairGenerator.ECGOST3410", PREFIX + "KeyPairGeneratorSpi");
             provider.addAlgorithm("Alg.Alias.KeyPairGenerator.ECGOST-3410", "ECGOST3410");
@@ -33,7 +41,85 @@
             provider.addAlgorithm("Alg.Alias.Signature.ECGOST-3410", "ECGOST3410");
             provider.addAlgorithm("Alg.Alias.Signature.GOST-3410-2001", "ECGOST3410");
 
-            addSignatureAlgorithm(provider, "GOST3411", "ECGOST3410", PREFIX + "SignatureSpi", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+            provider.addAlgorithm("KeyAgreement.ECGOST3410", PREFIX + "KeyAgreementSpi$ECVKO");
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+            provider.addAlgorithm("Alg.Alias.KeyAgreement.GOST-3410-2001", "ECGOST3410");
+
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH, "ECGOST3410");
+            
+            provider.addAlgorithm("AlgorithmParameters.ECGOST3410", PREFIX + "AlgorithmParametersSpi");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.GOST-3410-2001", "ECGOST3410");
+
+            addSignatureAlgorithm(provider, "GOST3411",
+                    "ECGOST3410", PREFIX + "SignatureSpi",
+                    CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+
+            // ========= GOST34.10 2012 256|512
+
+            provider.addAlgorithm("KeyFactory.ECGOST3410-2012", PREFIX_GOST_2012 + "KeyFactorySpi");
+            provider.addAlgorithm("Alg.Alias.KeyFactory.GOST-3410-2012", "ECGOST3410-2012");
+            provider.addAlgorithm("Alg.Alias.KeyFactory.ECGOST-3410-2012", "ECGOST3410-2012");
+
+            registerOid(provider, RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256,
+                    "ECGOST3410-2012",
+                    new org.bouncycastle.jcajce.provider.asymmetric.ecgost12.KeyFactorySpi());
+            registerOid(provider, RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256,
+                             "ECGOST3410-2012",
+                             new org.bouncycastle.jcajce.provider.asymmetric.ecgost12.KeyFactorySpi());
+            registerOidAlgorithmParameters(provider,
+                    RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012");
+
+            registerOid(provider, RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512,
+                    "ECGOST3410-2012",
+                    new org.bouncycastle.jcajce.provider.asymmetric.ecgost12.KeyFactorySpi());
+            registerOid(provider, RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512,
+                    "ECGOST3410-2012",
+                    new org.bouncycastle.jcajce.provider.asymmetric.ecgost12.KeyFactorySpi());
+            registerOidAlgorithmParameters(provider,
+                    RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012");
+
+            provider.addAlgorithm("KeyPairGenerator.ECGOST3410-2012",
+                    PREFIX_GOST_2012 + "KeyPairGeneratorSpi");
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator.ECGOST3410-2012",
+                    "ECGOST3410-2012");
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator.GOST-3410-2012",
+                    "ECGOST3410-2012");
+
+            // 256 signature
+
+            provider.addAlgorithm("Signature.ECGOST3410-2012-256",
+                    PREFIX_GOST_2012 + "ECGOST2012SignatureSpi256");
+            provider.addAlgorithm("Alg.Alias.Signature.ECGOST3410-2012-256",
+                    "ECGOST3410-2012-256");
+            provider.addAlgorithm("Alg.Alias.Signature.GOST-3410-2012-256",
+                    "ECGOST3410-2012-256");
+
+
+            addSignatureAlgorithm(provider, "GOST3411-2012-256", "ECGOST3410-2012-256",
+                    PREFIX_GOST_2012 + "ECGOST2012SignatureSpi256",
+                    RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+
+            // 512 signature
+
+
+            provider.addAlgorithm("Signature.ECGOST3410-2012-512",
+                    PREFIX_GOST_2012 + "ECGOST2012SignatureSpi512");
+            provider.addAlgorithm("Alg.Alias.Signature.ECGOST3410-2012-512",
+                    "ECGOST3410-2012-512");
+            provider.addAlgorithm("Alg.Alias.Signature.GOST-3410-2012-512",
+                    "ECGOST3410-2012-512");
+
+            addSignatureAlgorithm(provider, "GOST3411-2012-512", "ECGOST3410-2012-512",
+                    PREFIX_GOST_2012 + "ECGOST2012SignatureSpi512",
+                    RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+
+            provider.addAlgorithm("KeyAgreement.ECGOST3410-2012-256", PREFIX_GOST_2012 + "KeyAgreementSpi$ECVKO256");
+            provider.addAlgorithm("KeyAgreement.ECGOST3410-2012-512", PREFIX_GOST_2012 + "KeyAgreementSpi$ECVKO512");
+
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256, "ECGOST3410-2012-256");
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512, "ECGOST3410-2012-512");
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012-256");
+            provider.addAlgorithm("Alg.Alias.KeyAgreement." + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012-512");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EdEC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EdEC.java
new file mode 100644
index 0000000..60f8ea6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EdEC.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.jcajce.provider.asymmetric.edec.KeyFactorySpi;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+
+public class EdEC
+{
+    private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric" + ".edec.";
+
+    private static final Map<String, String> edxAttributes = new HashMap<String, String>();
+
+    static
+    {
+        edxAttributes.put("SupportedKeyClasses", "java.security.interfaces.ECPublicKey|java.security.interfaces.ECPrivateKey");
+        edxAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.XDH", PREFIX + "KeyFactorySpi$XDH");
+            provider.addAlgorithm("KeyFactory.X448", PREFIX + "KeyFactorySpi$X448");
+            provider.addAlgorithm("KeyFactory.X25519", PREFIX + "KeyFactorySpi$X25519");
+
+            provider.addAlgorithm("KeyFactory.EDDSA", PREFIX + "KeyFactorySpi$EDDSA");
+            provider.addAlgorithm("KeyFactory.ED448", PREFIX + "KeyFactorySpi$ED448");
+            provider.addAlgorithm("KeyFactory.ED25519", PREFIX + "KeyFactorySpi$ED25519");
+
+            provider.addAlgorithm("Signature.EDDSA", PREFIX + "SignatureSpi$EdDSA");
+            provider.addAlgorithm("Signature.ED448", PREFIX + "SignatureSpi$Ed448");
+            provider.addAlgorithm("Signature.ED25519", PREFIX + "SignatureSpi$Ed25519");
+            provider.addAlgorithm("Signature", EdECObjectIdentifiers.id_Ed448, PREFIX + "SignatureSpi$Ed448");
+            provider.addAlgorithm("Signature", EdECObjectIdentifiers.id_Ed25519, PREFIX + "SignatureSpi$Ed25519");
+
+            provider.addAlgorithm("KeyPairGenerator.EDDSA", PREFIX + "KeyPairGeneratorSpi$EdDSA");
+            provider.addAlgorithm("KeyPairGenerator.ED448", PREFIX + "KeyPairGeneratorSpi$Ed448");
+            provider.addAlgorithm("KeyPairGenerator.ED25519", PREFIX + "KeyPairGeneratorSpi$Ed25519");
+            provider.addAlgorithm("KeyPairGenerator", EdECObjectIdentifiers.id_Ed448, PREFIX + "KeyPairGeneratorSpi$Ed448");
+            provider.addAlgorithm("KeyPairGenerator", EdECObjectIdentifiers.id_Ed25519, PREFIX + "KeyPairGeneratorSpi$Ed25519");
+
+            provider.addAlgorithm("KeyAgreement.XDH", PREFIX + "KeyAgreementSpi$XDH");
+            provider.addAlgorithm("KeyAgreement.X448", PREFIX + "KeyAgreementSpi$X448");
+            provider.addAlgorithm("KeyAgreement.X25519", PREFIX + "KeyAgreementSpi$X25519");
+            provider.addAlgorithm("KeyAgreement", EdECObjectIdentifiers.id_X448, PREFIX + "KeyAgreementSpi$X448");
+            provider.addAlgorithm("KeyAgreement", EdECObjectIdentifiers.id_X25519, PREFIX + "KeyAgreementSpi$X25519");
+
+            provider.addAlgorithm("KeyAgreement.X25519WITHSHA256CKDF", PREFIX + "KeyAgreementSpi$X25519withSHA256CKDF");
+            provider.addAlgorithm("KeyAgreement.X25519WITHSHA384CKDF", PREFIX + "KeyAgreementSpi$X25519withSHA384CKDF");
+            provider.addAlgorithm("KeyAgreement.X25519WITHSHA512CKDF", PREFIX + "KeyAgreementSpi$X25519withSHA512CKDF");
+
+            provider.addAlgorithm("KeyAgreement.X448WITHSHA256CKDF", PREFIX + "KeyAgreementSpi$X448withSHA256CKDF");
+            provider.addAlgorithm("KeyAgreement.X448WITHSHA384CKDF", PREFIX + "KeyAgreementSpi$X448withSHA384CKDF");
+            provider.addAlgorithm("KeyAgreement.X448WITHSHA512CKDF", PREFIX + "KeyAgreementSpi$X448withSHA512CKDF");
+
+            provider.addAlgorithm("KeyAgreement.X25519WITHSHA256KDF", PREFIX + "KeyAgreementSpi$X25519withSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.X448WITHSHA512KDF", PREFIX + "KeyAgreementSpi$X448withSHA512KDF");
+
+            provider.addAlgorithm("KeyAgreement.X25519UWITHSHA256KDF", PREFIX + "KeyAgreementSpi$X25519UwithSHA256KDF");
+            provider.addAlgorithm("KeyAgreement.X448UWITHSHA512KDF", PREFIX + "KeyAgreementSpi$X448UwithSHA512KDF");
+
+            provider.addAlgorithm("KeyPairGenerator.XDH", PREFIX + "KeyPairGeneratorSpi$XDH");
+            provider.addAlgorithm("KeyPairGenerator.X448", PREFIX + "KeyPairGeneratorSpi$X448");
+            provider.addAlgorithm("KeyPairGenerator.X25519", PREFIX + "KeyPairGeneratorSpi$X25519");
+            provider.addAlgorithm("KeyPairGenerator", EdECObjectIdentifiers.id_X448, PREFIX + "KeyPairGeneratorSpiSpi$X448");
+            provider.addAlgorithm("KeyPairGenerator", EdECObjectIdentifiers.id_X25519, PREFIX + "KeyPairGeneratorSpiSpi$X25519");
+
+            registerOid(provider, EdECObjectIdentifiers.id_X448, "XDH", new KeyFactorySpi.X448());
+            registerOid(provider, EdECObjectIdentifiers.id_X25519, "XDH", new KeyFactorySpi.X25519());
+            registerOid(provider, EdECObjectIdentifiers.id_Ed448, "EDDSA", new KeyFactorySpi.ED448());
+            registerOid(provider, EdECObjectIdentifiers.id_Ed25519, "EDDSA", new KeyFactorySpi.ED25519());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/GM.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/GM.java
index 3192904..744200c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/GM.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/GM.java
@@ -30,6 +30,30 @@
         {
             provider.addAlgorithm("Signature.SM3WITHSM2", PREFIX + "GMSignatureSpi$sm3WithSM2");
             provider.addAlgorithm("Alg.Alias.Signature." + GMObjectIdentifiers.sm2sign_with_sm3, "SM3WITHSM2");
+
+            provider.addAlgorithm("Cipher.SM2", PREFIX + "GMCipherSpi$SM2");
+            provider.addAlgorithm("Alg.Alias.Cipher.SM2WITHSM3", "SM2");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sm3, "SM2");
+            provider.addAlgorithm("Cipher.SM2WITHBLAKE2B", PREFIX + "GMCipherSpi$SM2withBlake2b");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_blake2b512, "SM2WITHBLAKE2B");
+            provider.addAlgorithm("Cipher.SM2WITHBLAKE2S", PREFIX + "GMCipherSpi$SM2withBlake2s");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_blake2s256, "SM2WITHBLAKE2S");
+            provider.addAlgorithm("Cipher.SM2WITHWHIRLPOOL", PREFIX + "GMCipherSpi$SM2withWhirlpool");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_whirlpool, "SM2WITHWHIRLPOOL");
+            provider.addAlgorithm("Cipher.SM2WITHMD5", PREFIX + "GMCipherSpi$SM2withMD5");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_md5, "SM2WITHMD5");
+            provider.addAlgorithm("Cipher.SM2WITHRIPEMD160", PREFIX + "GMCipherSpi$SM2withRMD");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_rmd160, "SM2WITHRIPEMD160");
+            provider.addAlgorithm("Cipher.SM2WITHSHA1", PREFIX + "GMCipherSpi$SM2withSha1");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sha1, "SM2WITHSHA1");
+            provider.addAlgorithm("Cipher.SM2WITHSHA224", PREFIX + "GMCipherSpi$SM2withSha224");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sha224, "SM2WITHSHA224");
+            provider.addAlgorithm("Cipher.SM2WITHSHA256", PREFIX + "GMCipherSpi$SM2withSha256");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sha256, "SM2WITHSHA256");
+            provider.addAlgorithm("Cipher.SM2WITHSHA384", PREFIX + "GMCipherSpi$SM2withSha384");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sha384, "SM2WITHSHA384");
+            provider.addAlgorithm("Cipher.SM2WITHSHA512", PREFIX + "GMCipherSpi$SM2withSha512");
+            provider.addAlgorithm("Alg.Alias.Cipher." + GMObjectIdentifiers.sm2encrypt_with_sha512, "SM2WITHSHA512");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
index bf6bfe7..b1ef7e5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
@@ -8,6 +8,7 @@
 import javax.crypto.spec.DHGenParameterSpec;
 import javax.crypto.spec.DHParameterSpec;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.DHParametersGenerator;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAlgorithmParameterGeneratorSpi;
@@ -57,7 +58,7 @@
         }
         else
         {
-            pGen.init(strength, certainty, new SecureRandom());
+            pGen.init(strength, certainty, CryptoServicesRegistrar.getSecureRandom());
         }
 
         DHParameters p = pGen.generateParameters();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java
index 6fb54b6..21182ef 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java
@@ -19,11 +19,14 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x9.DHDomainParameters;
 import org.bouncycastle.asn1.x9.DomainParameters;
+import org.bouncycastle.asn1.x9.ValidationParams;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHValidationParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 
 
@@ -36,6 +39,7 @@
 
     private transient DHParameterSpec dhSpec;
     private transient PrivateKeyInfo  info;
+    private transient DHPrivateKeyParameters dhPrivateKey;
 
     private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
@@ -75,29 +79,37 @@
             if (params.getL() != null)
             {
                 this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
+                this.dhPrivateKey = new DHPrivateKeyParameters(x,
+                          new DHParameters(params.getP(), params.getG(), null, params.getL().intValue()));
             }
             else
             {
                 this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+                this.dhPrivateKey = new DHPrivateKeyParameters(x,
+                          new DHParameters(params.getP(), params.getG()));
             }
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
             DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+            this.dhSpec = new DHDomainParameterSpec(params.getP(), params.getQ(), params.getG(), params.getJ(), 0);
+            this.dhPrivateKey = new DHPrivateKeyParameters(x,
+                new DHParameters(params.getP(), params.getG(), params.getQ(), params.getJ(), null));
         }
         else
         {
             throw new IllegalArgumentException("unknown algorithm type: " + id);
         }
+
+
     }
 
     BCDHPrivateKey(
         DHPrivateKeyParameters params)
     {
         this.x = params.getX();
-        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+        this.dhSpec = new DHDomainParameterSpec(params.getParameters());
     }
 
     public String getAlgorithm()
@@ -130,16 +142,35 @@
                 return info.getEncoded(ASN1Encoding.DER);
             }
 
-            PrivateKeyInfo          info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(getX()));
-
+            PrivateKeyInfo          info;
+            if (dhSpec instanceof DHDomainParameterSpec && ((DHDomainParameterSpec)dhSpec).getQ() != null)
+            {
+                DHParameters params = ((DHDomainParameterSpec)dhSpec).getDomainParameters();
+                DHValidationParameters validationParameters = params.getValidationParameters();
+                ValidationParams vParams = null;
+                if (validationParameters != null)
+                {
+                    vParams = new ValidationParams(validationParameters.getSeed(), validationParameters.getCounter());
+                }
+                info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.dhpublicnumber, new DomainParameters(params.getP(), params.getG(), params.getQ(), params.getJ(), vParams).toASN1Primitive()), new ASN1Integer(getX()));
+            }
+            else
+            {
+                info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(getX()));
+            }
             return info.getEncoded(ASN1Encoding.DER);
         }
         catch (Exception e)
-        {
+        {              
             return null;
         }
     }
 
+    public String toString()
+    {
+        return DHUtil.privateKeyToString("DH", x, new DHParameters(dhSpec.getP(), dhSpec.getG()));
+    }
+
     public DHParameterSpec getParams()
     {
         return dhSpec;
@@ -150,6 +181,20 @@
         return x;
     }
 
+    DHPrivateKeyParameters engineGetKeyParameters()
+    {
+        if (dhPrivateKey != null)
+        {
+            return dhPrivateKey;
+        }
+
+        if (dhSpec instanceof DHDomainParameterSpec)
+        {
+            return new DHPrivateKeyParameters(x, ((DHDomainParameterSpec)dhSpec).getDomainParameters());
+        }
+        return new DHPrivateKeyParameters(x, new DHParameters(dhSpec.getP(), dhSpec.getG(), null, dhSpec.getL()));
+    }
+
     public boolean equals(
         Object o)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java
index 1462a38..039b8d3 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java
@@ -23,6 +23,7 @@
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import org.bouncycastle.crypto.params.DHValidationParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
 
 public class BCDHPublicKey
     implements DHPublicKey
@@ -55,7 +56,7 @@
         DHPublicKeyParameters params)
     {
         this.y = params.getY();
-        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+        this.dhSpec = new DHDomainParameterSpec(params.getParameters());
         this.dhPublicKey = params;
     }
 
@@ -65,7 +66,15 @@
     {
         this.y = y;
         this.dhSpec = dhSpec;
-        this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
+
+        if (dhSpec instanceof DHDomainParameterSpec)
+        {
+            this.dhPublicKey = new DHPublicKeyParameters(y, ((DHDomainParameterSpec)dhSpec).getDomainParameters());
+        }
+        else
+        {
+            this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
+        }
     }
 
     public BCDHPublicKey(
@@ -107,7 +116,6 @@
         {
             DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
             ValidationParams validationParams = params.getValidationParams();
             if (validationParams != null)
             {
@@ -118,6 +126,7 @@
             {
                 this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(params.getP(), params.getG(), params.getQ(), params.getJ(), null));
             }
+            this.dhSpec = new DHDomainParameterSpec(dhPublicKey.getParameters());
         }
         else
         {
@@ -142,9 +151,25 @@
             return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
         }
 
+        if (dhSpec instanceof DHDomainParameterSpec && ((DHDomainParameterSpec)dhSpec).getQ() != null)
+        {
+            DHParameters params = ((DHDomainParameterSpec)dhSpec).getDomainParameters();
+            DHValidationParameters validationParameters = params.getValidationParameters();
+            ValidationParams vParams = null;
+            if (validationParameters != null)
+            {
+                vParams = new ValidationParams(validationParameters.getSeed(), validationParameters.getCounter());
+            }
+            return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.dhpublicnumber, new DomainParameters(params.getP(), params.getG(), params.getQ(), params.getJ(), vParams).toASN1Primitive()), new ASN1Integer(y));
+        }
         return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(y));
     }
 
+    public String toString()
+    {
+        return DHUtil.publicKeyToString("DH", y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
+    }
+
     public DHParameterSpec getParams()
     {
         return dhSpec;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/DHUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/DHUtil.java
new file mode 100644
index 0000000..f26b83b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/DHUtil.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.jcajce.provider.asymmetric.dh;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Fingerprint;
+import org.bouncycastle.util.Strings;
+
+class DHUtil
+{
+    static String privateKeyToString(String algorithm, BigInteger x, DHParameters dhParams)
+    {
+        StringBuffer buf = new StringBuffer();
+        String       nl = Strings.lineSeparator();
+
+        BigInteger y = dhParams.getG().modPow(x, dhParams.getP());
+
+        buf.append(algorithm);
+        buf.append(" Private Key [").append(generateKeyFingerprint(y, dhParams)).append("]").append(nl);
+        buf.append("              Y: ").append(y.toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    static String publicKeyToString(String algorithm, BigInteger y, DHParameters dhParams)
+    {
+        StringBuffer buf = new StringBuffer();
+        String       nl = Strings.lineSeparator();
+
+        buf.append(algorithm);
+        buf.append(" Public Key [").append(generateKeyFingerprint(y, dhParams)).append("]").append(nl);
+        buf.append("             Y: ").append(y.toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    private static String generateKeyFingerprint(BigInteger y, DHParameters dhParams)
+    {
+            return new Fingerprint(
+                Arrays.concatenate(
+                    y.toByteArray(),
+                    dhParams.getP().toByteArray(), dhParams.getG().toByteArray())).toString();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
index 801a04a..18e3597 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -5,6 +5,8 @@
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
@@ -15,10 +17,25 @@
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.agreement.DHUnifiedAgreement;
+import org.bouncycastle.crypto.agreement.MQVBasicAgreement;
+import org.bouncycastle.crypto.agreement.kdf.ConcatenationKDFGenerator;
 import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.params.DHMQVPrivateParameters;
+import org.bouncycastle.crypto.params.DHMQVPublicParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DHUPrivateParameters;
+import org.bouncycastle.crypto.params.DHUPublicParameters;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
+import org.bouncycastle.jcajce.spec.DHUParameterSpec;
+import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 
 /**
@@ -32,15 +49,21 @@
     private static final BigInteger ONE = BigInteger.valueOf(1);
     private static final BigInteger TWO = BigInteger.valueOf(2);
 
+    private final DHUnifiedAgreement unifiedAgreement;
+    private final BasicAgreement mqvAgreement;
+
+    private DHUParameterSpec dheParameters;
+    private MQVParameterSpec mqvParameters;
+
     private BigInteger      x;
     private BigInteger      p;
     private BigInteger      g;
 
-    private BigInteger     result;
+    private byte[]          result;
 
     public KeyAgreementSpi()
     {
-        super("Diffie-Hellman", null);
+        this("Diffie-Hellman", null);
     }
 
     public KeyAgreementSpi(
@@ -48,6 +71,28 @@
         DerivationFunction kdf)
     {
         super(kaAlgorithm, kdf);
+        this.unifiedAgreement = null;
+        this.mqvAgreement = null;
+    }
+
+    public KeyAgreementSpi(
+        String kaAlgorithm,
+        DHUnifiedAgreement unifiedAgreement,
+        DerivationFunction kdf)
+    {
+        super(kaAlgorithm, kdf);
+        this.unifiedAgreement = unifiedAgreement;
+        this.mqvAgreement = null;
+    }
+
+    public KeyAgreementSpi(
+        String kaAlgorithm,
+        BasicAgreement mqvAgreement,
+        DerivationFunction kdf)
+    {
+        super(kaAlgorithm, kdf);
+        this.unifiedAgreement = null;
+        this.mqvAgreement = mqvAgreement;
     }
 
     protected byte[] bigIntToBytes(
@@ -111,18 +156,55 @@
             throw new InvalidKeyException("Invalid DH PublicKey");
         }
 
-        result = peerY.modPow(x, p);
-        if (result.compareTo(ONE) == 0)
+        if (unifiedAgreement != null)
         {
-            throw new InvalidKeyException("Shared key can't be 1");
-        }
+            if (!lastPhase)
+            {
+                throw new IllegalStateException("unified Diffie-Hellman can use only two key pairs");
+            }
 
-        if (lastPhase)
-        {
+            DHPublicKeyParameters staticKey = generatePublicKeyParameter((PublicKey)key);
+            DHPublicKeyParameters ephemKey = generatePublicKeyParameter(dheParameters.getOtherPartyEphemeralKey());
+
+            DHUPublicParameters pKey = new DHUPublicParameters(staticKey, ephemKey);
+
+            result = unifiedAgreement.calculateAgreement(pKey);
+
             return null;
         }
+        else if (mqvAgreement != null)
+        {
+            if (!lastPhase)
+            {
+                throw new IllegalStateException("MQV Diffie-Hellman can use only two key pairs");
+            }
 
-        return new BCDHPublicKey(result, pubKey.getParams());
+            DHPublicKeyParameters staticKey = generatePublicKeyParameter((PublicKey)key);
+            DHPublicKeyParameters ephemKey = generatePublicKeyParameter(mqvParameters.getOtherPartyEphemeralKey());
+
+            DHMQVPublicParameters pKey = new DHMQVPublicParameters(staticKey, ephemKey);
+
+            result = bigIntToBytes(mqvAgreement.calculateAgreement(pKey));
+
+            return null;
+        }
+        else
+        {
+            BigInteger res = peerY.modPow(x, p);
+            if (res.compareTo(ONE) == 0)
+            {
+                throw new InvalidKeyException("Shared key can't be 1");
+            }
+
+            result = bigIntToBytes(res);
+
+            if (lastPhase)
+            {
+                return null;
+            }
+
+            return new BCDHPublicKey(res, pubKey.getParams());
+        }
     }
 
     protected byte[] engineGenerateSecret() 
@@ -158,12 +240,10 @@
             throw new IllegalStateException("Diffie-Hellman not initialised.");
         }
 
-        byte[] res = bigIntToBytes(result);
-
         // for JSSE compatibility
         if (algorithm.equals("TlsPremasterSecret"))
         {
-            return new SecretKeySpec(trimZeroes(res), algorithm);
+            return new SecretKeySpec(trimZeroes(result), algorithm);
         }
 
         return super.engineGenerateSecret(algorithm);
@@ -189,11 +269,64 @@
 
                 this.p = p.getP();
                 this.g = p.getG();
+                this.dheParameters = null;
+                this.ukmParameters = null;
+            }
+            else if (params instanceof DHUParameterSpec)
+            {
+                if (unifiedAgreement == null)
+                {
+                    throw new InvalidAlgorithmParameterException("agreement algorithm not DHU based");
+                }
+                this.p = privKey.getParams().getP();
+                this.g = privKey.getParams().getG();
+                this.dheParameters = (DHUParameterSpec)params;
+                this.ukmParameters = ((DHUParameterSpec)params).getUserKeyingMaterial();
+
+                if (dheParameters.getEphemeralPublicKey() != null)
+                {
+                    unifiedAgreement.init(new DHUPrivateParameters(generatePrivateKeyParameter(privKey),
+                        generatePrivateKeyParameter(dheParameters.getEphemeralPrivateKey()),
+                        generatePublicKeyParameter(dheParameters.getEphemeralPublicKey())));
+                }
+                else
+                {
+                    unifiedAgreement.init(new DHUPrivateParameters(generatePrivateKeyParameter(privKey),
+                            generatePrivateKeyParameter(dheParameters.getEphemeralPrivateKey())));
+                }
+            }
+            else if (params instanceof MQVParameterSpec)
+            {
+                if (mqvAgreement == null)
+                {
+                    throw new InvalidAlgorithmParameterException("agreement algorithm not MQV based");
+                }
+                this.p = privKey.getParams().getP();
+                this.g = privKey.getParams().getG();
+                this.mqvParameters = (MQVParameterSpec)params;
+                this.ukmParameters = ((MQVParameterSpec)params).getUserKeyingMaterial();
+
+                if (mqvParameters.getEphemeralPublicKey() != null)
+                {
+                    mqvAgreement.init(new DHMQVPrivateParameters(generatePrivateKeyParameter(privKey),
+                        generatePrivateKeyParameter(mqvParameters.getEphemeralPrivateKey()),
+                        generatePublicKeyParameter(mqvParameters.getEphemeralPublicKey())));
+                }
+                else
+                {
+                    mqvAgreement.init(new DHMQVPrivateParameters(generatePrivateKeyParameter(privKey),
+                            generatePrivateKeyParameter(mqvParameters.getEphemeralPrivateKey())));
+                }
             }
             else if (params instanceof UserKeyingMaterialSpec)
             {
+                if (kdf == null)
+                {
+                    throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
+                }
                 this.p = privKey.getParams().getP();
                 this.g = privKey.getParams().getG();
+                this.dheParameters = null;
                 this.ukmParameters = ((UserKeyingMaterialSpec)params).getUserKeyingMaterial();
             }
             else
@@ -207,7 +340,8 @@
             this.g = privKey.getParams().getG();
         }
 
-        this.x = this.result = privKey.getX();
+        this.x = privKey.getX();
+        this.result = bigIntToBytes(x);
     }
 
     protected void engineInit(
@@ -224,12 +358,66 @@
 
         this.p = privKey.getParams().getP();
         this.g = privKey.getParams().getG();
-        this.x = this.result = privKey.getX();
+        this.x = privKey.getX();
+        this.result = bigIntToBytes(x);
     }
 
     protected byte[] calcSecret()
     {
-        return bigIntToBytes(result);
+        return result;
+    }
+
+    private DHPrivateKeyParameters generatePrivateKeyParameter(PrivateKey privKey)
+        throws InvalidKeyException
+    {
+        if (privKey instanceof DHPrivateKey)
+        {
+            if (privKey instanceof BCDHPrivateKey)
+            {
+                return ((BCDHPrivateKey)privKey).engineGetKeyParameters();
+            }
+            else
+            {
+                DHPrivateKey pub = (DHPrivateKey)privKey;
+
+                DHParameterSpec params = pub.getParams();
+                return new DHPrivateKeyParameters(pub.getX(),
+                            new DHParameters(params.getP(), params.getG(), null, params.getL()));
+            }
+        }
+        else
+        {
+            throw new InvalidKeyException("private key not a DHPrivateKey");
+        }
+    }
+
+    private DHPublicKeyParameters generatePublicKeyParameter(PublicKey pubKey)
+        throws InvalidKeyException
+    {
+        if (pubKey instanceof DHPublicKey)
+        {
+            if (pubKey instanceof BCDHPublicKey)
+            {
+                return ((BCDHPublicKey)pubKey).engineGetKeyParameters();
+            }
+            else
+            {
+                DHPublicKey pub = (DHPublicKey)pubKey;
+
+                DHParameterSpec params = pub.getParams();
+
+                if (params instanceof DHDomainParameterSpec)
+                {
+                    return new DHPublicKeyParameters(pub.getY(), ((DHDomainParameterSpec)params).getDomainParameters());
+                }
+                return new DHPublicKeyParameters(pub.getY(),
+                            new DHParameters(params.getP(), params.getG(), null, params.getL()));
+            }
+        }
+        else
+        {
+            throw new InvalidKeyException("public key not a DHPublicKey");
+        }
     }
 
     public static class DHwithRFC2631KDF
@@ -240,4 +428,274 @@
             super("DHwithRFC2631KDF", new DHKEKGenerator(DigestFactory.createSHA1()));
         }
     }
+
+    public static class DHwithSHA1KDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA1KDF()
+        {
+            super("DHwithSHA1CKDF", new KDF2BytesGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHwithSHA224KDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA224KDF()
+        {
+            super("DHwithSHA224CKDF", new KDF2BytesGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA256KDF()
+        {
+            super("DHwithSHA256CKDF", new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHwithSHA384KDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA384KDF()
+        {
+            super("DHwithSHA384KDF", new KDF2BytesGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA512KDF()
+        {
+            super("DHwithSHA512KDF", new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class DHwithSHA1CKDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA1CKDF()
+        {
+            super("DHwithSHA1CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHwithSHA224CKDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA224CKDF()
+        {
+            super("DHwithSHA224CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHwithSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA256CKDF()
+        {
+            super("DHwithSHA256CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHwithSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA384CKDF()
+        {
+            super("DHwithSHA384CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHwithSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA512CKDF()
+        {
+            super("DHwithSHA512CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class DHUwithSHA1KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA1KDF()
+        {
+            super("DHUwithSHA1KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHUwithSHA224KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA224KDF()
+        {
+            super("DHUwithSHA224KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHUwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA256KDF()
+        {
+            super("DHUwithSHA256KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHUwithSHA384KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA384KDF()
+        {
+            super("DHUwithSHA384KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHUwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA512KDF()
+        {
+            super("DHUwithSHA512KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class DHUwithSHA1CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA1CKDF()
+        {
+            super("DHUwithSHA1CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHUwithSHA224CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA224CKDF()
+        {
+            super("DHUwithSHA224CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHUwithSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA256CKDF()
+        {
+            super("DHUwithSHA256CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHUwithSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA384CKDF()
+        {
+            super("DHUwithSHA384CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHUwithSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA512CKDF()
+        {
+            super("DHUwithSHA512CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class MQVwithSHA1KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA1KDF()
+        {
+            super("MQVwithSHA1KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class MQVwithSHA224KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA224KDF()
+        {
+            super("MQVwithSHA224KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class MQVwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA256KDF()
+        {
+            super("MQVwithSHA256KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class MQVwithSHA384KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA384KDF()
+        {
+            super("MQVwithSHA384KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class MQVwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA512KDF()
+        {
+            super("MQVwithSHA512KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class MQVwithSHA1CKDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA1CKDF()
+        {
+            super("MQVwithSHA1CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class MQVwithSHA224CKDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA224CKDF()
+        {
+            super("MQVwithSHA224CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class MQVwithSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA256CKDF()
+        {
+            super("MQVwithSHA256CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class MQVwithSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA384CKDF()
+        {
+            super("MQVwithSHA384CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class MQVwithSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA512CKDF()
+        {
+            super("MQVwithSHA512CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
index 864bf56..1e426b1 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
@@ -9,6 +9,7 @@
 import javax.crypto.spec.DHParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
 import org.bouncycastle.crypto.generators.DHParametersGenerator;
 import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
@@ -16,6 +17,7 @@
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Integers;
 
@@ -28,7 +30,7 @@
     DHKeyGenerationParameters param;
     DHBasicKeyPairGenerator engine = new DHBasicKeyPairGenerator();
     int strength = 2048;
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public KeyPairGeneratorSpi()
@@ -56,12 +58,28 @@
         }
         DHParameterSpec dhParams = (DHParameterSpec)params;
 
-        param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
-
+        try
+        {
+            param = convertParams(random, dhParams);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new InvalidAlgorithmParameterException(e.getMessage(), e);
+        }
+        
         engine.init(param);
         initialised = true;
     }
 
+    private DHKeyGenerationParameters convertParams(SecureRandom random, DHParameterSpec dhParams)
+    {
+        if (dhParams instanceof DHDomainParameterSpec)
+        {
+            return new DHKeyGenerationParameters(random, ((DHDomainParameterSpec)dhParams).getDomainParameters());
+        }
+        return new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
+    }
+
     public KeyPair generateKeyPair()
     {
         if (!initialised)
@@ -77,8 +95,8 @@
                 DHParameterSpec dhParams = BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(strength);
 
                 if (dhParams != null)
-                {
-                    param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
+                {   
+                    param = convertParams(random, dhParams);
                 }
                 else
                 {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
index 9a79659..f7db9be 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
@@ -7,6 +7,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.DSAParameterSpec;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.generators.DSAParametersGenerator;
 import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
@@ -67,7 +68,7 @@
 
         if (random == null)
         {
-            random = new SecureRandom();
+            random = CryptoServicesRegistrar.getSecureRandom();
         }
 
         int certainty = PrimeCertaintyCalculator.getDefaultCertainty(strength);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java
index 0fb4bd9..d19c90e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java
@@ -21,6 +21,7 @@
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Strings;
 
 public class BCDSAPrivateKey
     implements DSAPrivateKey, PKCS12BagAttributeCarrier
@@ -164,4 +165,17 @@
         out.writeObject(dsaSpec.getQ());
         out.writeObject(dsaSpec.getG());
     }
+
+    public String toString()
+    {
+        StringBuffer    buf = new StringBuffer();
+        String          nl = Strings.lineSeparator();
+
+        BigInteger y = getParams().getG().modPow(x, getParams().getP());
+
+        buf.append("DSA Private Key [").append(DSAUtil.generateKeyFingerprint(y, getParams())).append("]").append(nl);
+        buf.append("            Y: ").append(y.toString(16)).append(nl);
+
+        return buf.toString();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java
index 13223cb..601fddd 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java
@@ -137,8 +137,8 @@
         StringBuffer    buf = new StringBuffer();
         String          nl = Strings.lineSeparator();
 
-        buf.append("DSA Public Key").append(nl);
-        buf.append("            y: ").append(this.getY().toString(16)).append(nl);
+        buf.append("DSA Public Key [").append(DSAUtil.generateKeyFingerprint(y, getParams())).append("]").append(nl);
+        buf.append("            Y: ").append(this.getY().toString(16)).append(nl);
 
         return buf.toString();
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
index 0237978..d0e3674 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.jcajce.provider.asymmetric.dsa;
 
-import java.io.IOException;
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
@@ -10,33 +9,30 @@
 import java.security.SignatureSpi;
 import java.security.spec.AlgorithmParameterSpec;
 
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.NullDigest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.DSAEncoding;
 import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
+import org.bouncycastle.crypto.signers.StandardDSAEncoding;
 import org.bouncycastle.crypto.util.DigestFactory;
-import org.bouncycastle.util.Arrays;
 
 public class DSASigner
     extends SignatureSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
     private Digest                  digest;
-    private DSA                     signer;
+    private DSAExt                  signer;
+    private DSAEncoding             encoding = StandardDSAEncoding.INSTANCE;
     private SecureRandom            random;
 
     protected DSASigner(
         Digest digest,
-        DSA signer)
+        DSAExt signer)
     {
         this.digest = digest;
         this.signer = signer;
@@ -101,9 +97,9 @@
 
         try
         {
-            BigInteger[]    sig = signer.generateSignature(hash);
+            BigInteger[] sig = signer.generateSignature(hash);
 
-            return derEncode(sig[0], sig[1]);
+            return encoding.encode(signer.getOrder(), sig[0], sig[1]);
         }
         catch (Exception e)
         {
@@ -119,11 +115,11 @@
 
         digest.doFinal(hash, 0);
 
-        BigInteger[]    sig;
+        BigInteger[] sig;
 
         try
         {
-            sig = derDecode(sigBytes);
+            sig = encoding.decode(signer.getOrder(), sigBytes);
         }
         catch (Exception e)
         {
@@ -140,7 +136,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(
         String  param,
@@ -155,36 +151,7 @@
     protected Object engineGetParameter(
         String      param)
     {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
-    }
-
-    private byte[] derEncode(
-        BigInteger  r,
-        BigInteger  s)
-        throws IOException
-    {
-        ASN1Integer[] rs = new ASN1Integer[]{ new ASN1Integer(r), new ASN1Integer(s) };
-        return new DERSequence(rs).getEncoded(ASN1Encoding.DER);
-    }
-
-    private BigInteger[] derDecode(
-        byte[]  encoding)
-        throws IOException
-    {
-        ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
-        if (s.size() != 2)
-        {
-            throw new IOException("malformed signature");
-        }
-        if (!Arrays.areEqual(encoding, s.getEncoded(ASN1Encoding.DER)))
-        {
-            throw new IOException("malformed signature");
-        }
-
-        return new BigInteger[]{
-            ((ASN1Integer)s.getObjectAt(0)).getValue(),
-            ((ASN1Integer)s.getObjectAt(1)).getValue()
-        };
+        throw new UnsupportedOperationException("engineGetParameter unsupported");
     }
 
     static public class stdDSA
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java
index 8f83bf8..f7ebf95 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java
@@ -1,12 +1,12 @@
 package org.bouncycastle.jcajce.provider.asymmetric.dsa;
 
+import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.interfaces.DSAParams;
 import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAParameterSpec;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -15,7 +15,8 @@
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
-import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Fingerprint;
 
 /**
  * utility class for converting jce/jca DSA objects
@@ -26,9 +27,16 @@
     public static final ASN1ObjectIdentifier[] dsaOids =
     {
         X9ObjectIdentifiers.id_dsa,
-        OIWObjectIdentifiers.dsaWithSHA1
+        OIWObjectIdentifiers.dsaWithSHA1,
+        X9ObjectIdentifiers.id_dsa_with_sha1
     };
 
+    /**
+     * Return true if the passed in OID could be associated with a DSA key.
+     *
+     * @param algOid algorithm OID from a key.
+     * @return true if it's for a DSA key, false otherwise.
+     */
     public static boolean isDsaOid(
         ASN1ObjectIdentifier algOid)
     {
@@ -95,4 +103,9 @@
                         
         throw new InvalidKeyException("can't identify DSA private key.");
     }
+
+    static String generateKeyFingerprint(BigInteger y, DSAParams params)
+    {
+        return new Fingerprint(Arrays.concatenate(y.toByteArray(), params.getP().toByteArray(), params.getQ().toByteArray(), params.getG().toByteArray())).toString();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java
index 7816320..3b33c32 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java
@@ -15,7 +15,15 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
+import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPublicKeySpec;
 
 public class KeyFactorySpi
     extends BaseKeyFactorySpi
@@ -41,6 +49,30 @@
 
             return new DSAPrivateKeySpec(k.getX(), k.getParams().getP(), k.getParams().getQ(), k.getParams().getG());
         }
+        else if (spec.isAssignableFrom(OpenSSHPublicKeySpec.class) && key instanceof java.security.interfaces.DSAPublicKey)
+        {
+            DSAPublicKey k = (DSAPublicKey)key;
+            try
+            {
+                return new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(new DSAPublicKeyParameters(k.getY(), new DSAParameters(k.getParams().getP(), k.getParams().getQ(), k.getParams().getG()))));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("unable to produce encoding: " + e.getMessage());
+            }
+        }
+        else if (spec.isAssignableFrom(OpenSSHPrivateKeySpec.class) && key instanceof java.security.interfaces.DSAPrivateKey)
+        {
+            DSAPrivateKey k = (DSAPrivateKey)key;
+            try
+            {
+                return new OpenSSHPrivateKeySpec(OpenSSHPrivateKeyUtil.encodePrivateKey(new DSAPrivateKeyParameters(k.getX(), new DSAParameters(k.getParams().getP(), k.getParams().getQ(), k.getParams().getG()))));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("unable to produce encoding: " + e.getMessage());
+            }
+        }
 
         return super.engineGetKeySpec(key, spec);
     }
@@ -99,6 +131,24 @@
         {
             return new BCDSAPrivateKey((DSAPrivateKeySpec)keySpec);
         }
+        else if (keySpec instanceof OpenSSHPrivateKeySpec)
+        {
+            CipherParameters params = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(((OpenSSHPrivateKeySpec)keySpec).getEncoded());
+            if (params instanceof DSAPrivateKeyParameters)
+            {
+                return engineGeneratePrivate(
+                    new DSAPrivateKeySpec(
+                        ((DSAPrivateKeyParameters)params).getX(),
+                        ((DSAPrivateKeyParameters)params).getParameters().getP(),
+                        ((DSAPrivateKeyParameters)params).getParameters().getQ(),
+                        ((DSAPrivateKeyParameters)params).getParameters().getG()));
+            }
+            else
+            {
+                throw new IllegalArgumentException("openssh private key is not dsa privare key");
+            }
+
+        }
 
         return super.engineGeneratePrivate(keySpec);
     }
@@ -118,12 +168,28 @@
                 throw new InvalidKeySpecException("invalid KeySpec: " + e.getMessage())
                 {
                     public Throwable getCause()
-                                {
-                                    return e;
-                                }
+                    {
+                        return e;
+                    }
                 };
             }
         }
+        else if (keySpec instanceof OpenSSHPublicKeySpec)
+        {
+            CipherParameters parameters = OpenSSHPublicKeyUtil.parsePublicKey(((OpenSSHPublicKeySpec)keySpec).getEncoded());
+
+            if (parameters instanceof DSAPublicKeyParameters)
+            {
+                return engineGeneratePublic(
+                    new DSAPublicKeySpec(((DSAPublicKeyParameters)parameters).getY(),
+                        ((DSAPublicKeyParameters)parameters).getParameters().getP(),
+                        ((DSAPublicKeyParameters)parameters).getParameters().getQ(),
+                        ((DSAPublicKeyParameters)parameters).getParameters().getG()));
+            }
+
+            throw new IllegalArgumentException("openssh public key is not dsa public key");
+
+        }
 
         return super.engineGeneratePublic(keySpec);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
index 0c019e7..091caa2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
@@ -9,6 +9,7 @@
 import java.util.Hashtable;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
 import org.bouncycastle.crypto.generators.DSAParametersGenerator;
@@ -18,6 +19,7 @@
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Properties;
 
@@ -30,7 +32,7 @@
     DSAKeyGenerationParameters param;
     DSAKeyPairGenerator engine = new DSAKeyPairGenerator();
     int strength = 2048;
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public KeyPairGeneratorSpi()
@@ -47,9 +49,21 @@
             throw new InvalidParameterException("strength must be from 512 - 4096 and a multiple of 1024 above 1024");
         }
 
-        this.strength = strength;
-        this.random = random;
-        this.initialised = false;
+        DSAParameterSpec spec = BouncyCastleProvider.CONFIGURATION.getDSADefaultParameters(strength);
+
+        if (spec != null)
+        {
+            param = new DSAKeyGenerationParameters(random, new DSAParameters(spec.getP(), spec.getQ(), spec.getG()));
+
+            engine.init(param);
+            this.initialised = true;
+        }
+        else
+        {
+            this.strength = strength;
+            this.random = random;
+            this.initialised = false;
+        }
     }
 
     public void initialize(
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
index bdd9920..8481e3a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
@@ -6,7 +6,6 @@
 import java.math.BigInteger;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
@@ -16,10 +15,15 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.ua.DSTU4145BinaryField;
+import org.bouncycastle.asn1.ua.DSTU4145ECBinary;
 import org.bouncycastle.asn1.ua.DSTU4145NamedCurves;
+import org.bouncycastle.asn1.ua.DSTU4145Params;
+import org.bouncycastle.asn1.ua.DSTU4145PointEncoder;
 import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -34,9 +38,9 @@
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Strings;
 
 public class BCDSTU4145PrivateKey
     implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
@@ -118,9 +122,7 @@
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    dp.getG().getAffineXCoord().toBigInteger(),
-                    dp.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(dp.getG()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -149,9 +151,7 @@
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    dp.getG().getAffineXCoord().toBigInteger(),
-                    dp.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(dp.getG()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -161,9 +161,7 @@
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(spec.getG()),
                 spec.getN(),
                 spec.getH().intValue());
         }
@@ -190,7 +188,7 @@
     private void populateFromPrivKeyInfo(PrivateKeyInfo info)
         throws IOException
     {
-        X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters());
+        X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
 
         if (params.isNamedCurve())
         {
@@ -205,9 +203,7 @@
                 ecSpec = new ECNamedCurveSpec(
                     oid.getId(),
                     ellipticCurve,
-                    new ECPoint(
-                        gParam.getG().getAffineXCoord().toBigInteger(),
-                        gParam.getG().getAffineYCoord().toBigInteger()),
+                    EC5Util.convertPoint(gParam.getG()),
                     gParam.getN(),
                     gParam.getH());
             }
@@ -218,9 +214,7 @@
                 ecSpec = new ECNamedCurveSpec(
                     ECUtil.getCurveName(oid),
                     ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getAffineXCoord().toBigInteger(),
-                        ecP.getG().getAffineYCoord().toBigInteger()),
+                    EC5Util.convertPoint(ecP.getG()),
                     ecP.getN(),
                     ecP.getH());
             }
@@ -231,16 +225,56 @@
         }
         else
         {
-            X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
-            EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+            ASN1Sequence seq = ASN1Sequence.getInstance(params.getParameters());
 
-            this.ecSpec = new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                    ecP.getG().getAffineXCoord().toBigInteger(),
-                    ecP.getG().getAffineYCoord().toBigInteger()),
-                ecP.getN(),
-                ecP.getH().intValue());
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+                this.ecSpec = new ECParameterSpec(
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH().intValue());
+            }
+            else
+            {
+                DSTU4145Params dstuParams = DSTU4145Params.getInstance(seq);
+                org.bouncycastle.jce.spec.ECParameterSpec spec;
+                if (dstuParams.isNamedCurve())
+                {
+                    ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
+                    ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid);
+
+                    spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
+                }
+                else
+                {
+                    DSTU4145ECBinary binary = dstuParams.getECBinary();
+                    byte[] b_bytes = binary.getB();
+                    if (info.getPrivateKeyAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                    {
+                        reverseBytes(b_bytes);
+                    }
+                    DSTU4145BinaryField field = binary.getField();
+                    ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
+                    byte[] g_bytes = binary.getG();
+                    if (info.getPrivateKeyAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                    {
+                        reverseBytes(g_bytes);
+                    }
+                    spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
+                }
+
+                EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
+
+                this.ecSpec = new ECParameterSpec(
+                    ellipticCurve,
+                    EC5Util.convertPoint(spec.getG()),
+                    spec.getN(),
+                    spec.getH().intValue());
+            }
         }
 
         ASN1Encodable privKey = info.parsePrivateKey();
@@ -259,6 +293,18 @@
         }
     }
 
+    private void reverseBytes(byte[] bytes)
+    {
+        byte tmp;
+
+        for (int i = 0; i < bytes.length / 2; i++)
+        {
+            tmp = bytes[i];
+            bytes[i] = bytes[bytes.length - 1 - i];
+            bytes[bytes.length - 1 - i] = tmp;
+        }
+    }
+
     public String getAlgorithm()
     {
         return algorithm;
@@ -424,14 +470,7 @@
 
     public String toString()
     {
-        StringBuffer buf = new StringBuffer();
-        String nl = Strings.lineSeparator();
-
-        buf.append("EC Private Key").append(nl);
-        buf.append("             S: ").append(this.d.toString(16)).append(nl);
-
-        return buf.toString();
-
+        return ECUtil.privateKeyToString(algorithm, d, engineGetSpec());
     }
 
     private DERBitString getPublicKeyDetails(BCDSTU4145PublicKey pub)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
index 2c9fdab..b1fd2f2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
@@ -11,6 +11,7 @@
 import java.security.spec.EllipticCurve;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -38,9 +39,6 @@
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.math.ec.custom.sec.SecP256K1Point;
-import org.bouncycastle.math.ec.custom.sec.SecP256R1Point;
-import org.bouncycastle.util.Strings;
 
 public class BCDSTU4145PublicKey
     implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
@@ -156,9 +154,7 @@
     {
         return new ECParameterSpec(
             ellipticCurve,
-            new ECPoint(
-                dp.getG().getAffineXCoord().toBigInteger(),
-                dp.getG().getAffineYCoord().toBigInteger()),
+            EC5Util.convertPoint(dp.getG()),
             dp.getN(),
             dp.getH().intValue());
     }
@@ -203,56 +199,71 @@
             reverseBytes(keyEnc);
         }
 
-        dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters());
-
-        //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
         org.bouncycastle.jce.spec.ECParameterSpec spec = null;
-        if (dstuParams.isNamedCurve())
-        {
-            ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
-            ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid);
+        X9ECParameters x9Params = null;
 
-            spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
+        if (seq.getObjectAt(0) instanceof ASN1Integer)
+        {
+            x9Params = X9ECParameters.getInstance(seq);
+            spec = new  org.bouncycastle.jce.spec.ECParameterSpec(x9Params.getCurve(), x9Params.getG(), x9Params.getN(), x9Params.getH(), x9Params.getSeed());
         }
         else
         {
-            DSTU4145ECBinary binary = dstuParams.getECBinary();
-            byte[] b_bytes = binary.getB();
-            if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+            dstuParams = DSTU4145Params.getInstance(seq);
+
+            if (dstuParams.isNamedCurve())
             {
-                reverseBytes(b_bytes);
+                ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
+                ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid);
+
+                spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
             }
-            DSTU4145BinaryField field = binary.getField();
-            ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
-            byte[] g_bytes = binary.getG();
-            if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+            else
             {
-                reverseBytes(g_bytes);
+                DSTU4145ECBinary binary = dstuParams.getECBinary();
+                byte[] b_bytes = binary.getB();
+                if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                {
+                    reverseBytes(b_bytes);
+                }
+                DSTU4145BinaryField field = binary.getField();
+                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
+                byte[] g_bytes = binary.getG();
+                if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+                {
+                    reverseBytes(g_bytes);
+                }
+                spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
             }
-            spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
         }
 
         ECCurve curve = spec.getCurve();
         EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
 
-        if (dstuParams.isNamedCurve())
+        if (dstuParams != null)
         {
-            ecSpec = new ECNamedCurveSpec(
-                dstuParams.getNamedCurve().getId(),
-                ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
-                spec.getN(), spec.getH());
+            if (dstuParams.isNamedCurve())
+            {
+                ecSpec = new ECNamedCurveSpec(
+                    dstuParams.getNamedCurve().getId(),
+                    ellipticCurve,
+                    EC5Util.convertPoint(spec.getG()),
+                    spec.getN(),
+                    spec.getH());
+            }
+            else
+            {
+                ecSpec = new ECParameterSpec(
+                    ellipticCurve,
+                    EC5Util.convertPoint(spec.getG()),
+                    spec.getN(),
+                    spec.getH().intValue());
+            }
         }
         else
         {
-            ecSpec = new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
-                spec.getN(), spec.getH().intValue());
+            ecSpec = EC5Util.convertToSpec(x9Params);
         }
 
         //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
@@ -342,9 +353,7 @@
 
     public ECPoint getW()
     {
-        org.bouncycastle.math.ec.ECPoint q = ecPublicKey.getQ();
-
-        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
+        return EC5Util.convertPoint(ecPublicKey.getQ());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -360,9 +369,9 @@
     }
 
     ECPublicKeyParameters engineGetKeyParameters()
-       {
-           return ecPublicKey;
-       }
+    {
+        return ecPublicKey;
+    }
 
     org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
     {
@@ -376,14 +385,7 @@
 
     public String toString()
     {
-        StringBuffer buf = new StringBuffer();
-        String nl = Strings.lineSeparator();
-
-        buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(getQ().getAffineXCoord().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(getQ().getAffineYCoord().toBigInteger().toString(16)).append(nl);
-
-        return buf.toString();
+        return ECUtil.publicKeyToString(algorithm, ecPublicKey.getQ(), engineGetSpec());
     }
 
     public void setPointFormat(String style)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java
index 2dfb20f..3429d39 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java
@@ -13,11 +13,13 @@
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.generators.DSTU4145KeyPairGenerator;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.DSTU4145Parameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.spec.DSTU4145ParameterSpec;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
@@ -88,8 +90,17 @@
             ECCurve curve = EC5Util.convertCurve(p.getCurve());
             ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false);
 
-            param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
+            if (p instanceof DSTU4145ParameterSpec)
+            {
+                DSTU4145ParameterSpec dstuSpec = (DSTU4145ParameterSpec)p;
 
+                param = new ECKeyGenerationParameters(new DSTU4145Parameters(
+                    new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), dstuSpec.getDKE()), random);
+            }
+            else
+            {
+                param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
+            }
             engine.init(param);
             initialised = true;
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java
index 7a5f009..52a2da4 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java
@@ -10,41 +10,26 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.ua.DSTU4145Params;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.GOST3411Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.DSTU4145Signer;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 import org.bouncycastle.jce.interfaces.ECKey;
-import org.bouncycastle.jce.interfaces.ECPublicKey;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
 public class SignatureSpi
     extends java.security.SignatureSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
     private Digest digest;
-    private DSA signer;
-
-    private static byte[] DEFAULT_SBOX = {
-        0xa, 0x9, 0xd, 0x6, 0xe, 0xb, 0x4, 0x5, 0xf, 0x1, 0x3, 0xc, 0x7, 0x0, 0x8, 0x2,
-        0x8, 0x0, 0xc, 0x4, 0x9, 0x6, 0x7, 0xb, 0x2, 0x3, 0x1, 0xf, 0x5, 0xe, 0xa, 0xd,
-        0xf, 0x6, 0x5, 0x8, 0xe, 0xb, 0xa, 0x4, 0xc, 0x0, 0x3, 0x7, 0x2, 0x9, 0x1, 0xd,
-        0x3, 0x8, 0xd, 0x9, 0x6, 0xb, 0xf, 0x0, 0x2, 0x5, 0xc, 0xa, 0x4, 0xe, 0x1, 0x7,
-        0xf, 0x8, 0xe, 0x9, 0x7, 0x2, 0x0, 0xd, 0xc, 0x6, 0x1, 0x5, 0xb, 0x4, 0x3, 0xa,
-        0x2, 0x8, 0x9, 0x7, 0x5, 0xf, 0x0, 0xb, 0xc, 0x1, 0xd, 0xe, 0xa, 0x3, 0x6, 0x4,
-        0x3, 0x8, 0xb, 0x5, 0x6, 0x4, 0xe, 0xa, 0x2, 0xc, 0x1, 0x7, 0x9, 0xf, 0xd, 0x0,
-        0x1, 0x2, 0x3, 0xe, 0x6, 0xd, 0xb, 0x8, 0xf, 0xa, 0xc, 0x5, 0x7, 0x9, 0x0, 0x4
-    };
+    private DSAExt signer;
 
     public SignatureSpi()
     {
-        //TODO: Add default ua s-box
-        //this.digest = new GOST3411Digest(DEFAULT_SBOX);
         this.signer = new DSTU4145Signer();
     }
 
@@ -57,13 +42,14 @@
         if (publicKey instanceof BCDSTU4145PublicKey)
         {
             param = ((BCDSTU4145PublicKey)publicKey).engineGetKeyParameters();
+            digest = new GOST3411Digest(expandSbox(((BCDSTU4145PublicKey)publicKey).getSbox()));
         }
         else
         {
             param = ECUtil.generatePublicKeyParameter(publicKey);
+            digest = new GOST3411Digest(expandSbox(DSTU4145Params.getDefaultDKE()));
         }
 
-        digest = new GOST3411Digest(expandSbox(((BCDSTU4145PublicKey)publicKey).getSbox()));
         signer.init(false, param);
     }
 
@@ -85,13 +71,18 @@
     {
         CipherParameters param = null;
 
-        if (privateKey instanceof ECKey)
+        if (privateKey instanceof BCDSTU4145PrivateKey)
+        {
+            // TODO: add parameters support.
+            param = ECUtil.generatePrivateKeyParameter(privateKey);
+            digest = new GOST3411Digest(expandSbox(DSTU4145Params.getDefaultDKE()));
+        }
+        else if (privateKey instanceof ECKey)
         {
             param = ECUtil.generatePrivateKeyParameter(privateKey);
+            digest = new GOST3411Digest(expandSbox(DSTU4145Params.getDefaultDKE()));
         }
 
-        digest = new GOST3411Digest(DEFAULT_SBOX);
-
         if (appRandom != null)
         {
             signer.init(true, new ParametersWithRandom(param, appRandom));
@@ -183,7 +174,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(
         String param,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
index 9f56a55..7851b25 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.jcajce.provider.asymmetric.ec;
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
@@ -42,7 +43,9 @@
                 throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName());
             }
             curveName = ecGenParameterSpec.getName();
-            ecParameterSpec = EC5Util.convertToSpec(params);
+            ECParameterSpec baseSpec = EC5Util.convertToSpec(params);
+            ecParameterSpec = new ECNamedCurveSpec(curveName,
+                baseSpec.getCurve(), baseSpec.getGenerator(), baseSpec.getOrder(), BigInteger.valueOf(baseSpec.getCofactor()));
         }
         else if (algorithmParameterSpec instanceof ECParameterSpec)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
index 815bcac..714f910 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
@@ -32,7 +32,6 @@
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Strings;
 
 public class BCECPrivateKey
     implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
@@ -120,30 +119,27 @@
         ECParameterSpec spec,
         ProviderConfiguration configuration)
     {
-        ECDomainParameters      dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getAffineXCoord().toBigInteger(),
-                                    dp.getG().getAffineYCoord().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
             this.ecSpec = spec;
         }
 
-        publicKey = getPublicKeyDetails(pubKey);
+        this.publicKey = getPublicKeyDetails(pubKey);
     }
 
     public BCECPrivateKey(
@@ -153,23 +149,20 @@
         org.bouncycastle.jce.spec.ECParameterSpec spec,
         ProviderConfiguration configuration)
     {
-        ECDomainParameters      dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getAffineXCoord().toBigInteger(),
-                                    dp.getG().getAffineYCoord().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
@@ -180,11 +173,11 @@
 
         try
         {
-            publicKey = getPublicKeyDetails(pubKey);
+            this.publicKey = getPublicKeyDetails(pubKey);
         }
         catch (Exception e)
         {
-            publicKey = null; // not all curves are encodable
+            this.publicKey = null; // not all curves are encodable
         }
     }
 
@@ -370,14 +363,12 @@
 
     public String toString()
     {
-        StringBuffer    buf = new StringBuffer();
-        String          nl = Strings.lineSeparator();
+        return ECUtil.privateKeyToString("EC", d, engineGetSpec());
+    }
 
-        buf.append("EC Private Key").append(nl);
-        buf.append("             S: ").append(this.d.toString(16)).append(nl);
-
-        return buf.toString();
-
+    private org.bouncycastle.math.ec.ECPoint calculateQ(org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        return spec.getG().multiply(d).normalize();
     }
 
     private DERBitString getPublicKeyDetails(BCECPublicKey pub)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
index 443c5f6..9236787 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
@@ -29,7 +29,6 @@
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Strings;
 
 public class BCECPublicKey
     implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
@@ -181,12 +180,10 @@
     private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
     {
         return new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                        dp.getG().getAffineXCoord().toBigInteger(),
-                        dp.getG().getAffineYCoord().toBigInteger()),
-                        dp.getN(),
-                        dp.getH().intValue());
+            ellipticCurve,
+            EC5Util.convertPoint(dp.getG()),
+            dp.getN(),
+            dp.getH().intValue());
     }
 
     private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
@@ -263,9 +260,7 @@
 
     public ECPoint getW()
     {
-        org.bouncycastle.math.ec.ECPoint q = ecPublicKey.getQ();
-
-        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
+        return EC5Util.convertPoint(ecPublicKey.getQ());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -297,16 +292,7 @@
 
     public String toString()
     {
-        StringBuffer    buf = new StringBuffer();
-        String          nl = Strings.lineSeparator();
-        org.bouncycastle.math.ec.ECPoint q = ecPublicKey.getQ();
-
-        buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
-
-        return buf.toString();
-
+        return ECUtil.publicKeyToString("EC", ecPublicKey.getQ(), engineGetSpec());
     }
     
     public void setPointFormat(String style)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMCipherSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMCipherSpi.java
new file mode 100644
index 0000000..fca555a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMCipherSpi.java
@@ -0,0 +1,424 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+import org.bouncycastle.crypto.digests.Blake2sDigest;
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.WhirlpoolDigest;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.util.BadBlockException;
+import org.bouncycastle.jcajce.util.BCJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.jce.interfaces.ECKey;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+
+public class GMCipherSpi
+    extends CipherSpi
+{
+    private final JcaJceHelper helper = new BCJcaJceHelper();
+
+    private SM2Engine engine;
+    private int state = -1;
+    private ErasableOutputStream buffer = new ErasableOutputStream();
+    private AsymmetricKeyParameter key;
+    private SecureRandom random;
+
+    public GMCipherSpi(SM2Engine engine)
+    {
+        this.engine = engine;
+    }
+
+    public int engineGetBlockSize()
+    {
+        return 0;
+    }
+
+    public int engineGetKeySize(Key key)
+    {
+        if (key instanceof ECKey)
+        {
+            return ((ECKey)key).getParameters().getCurve().getFieldSize();
+        }
+        else
+        {
+            throw new IllegalArgumentException("not an EC key");
+        }
+    }
+
+
+    public byte[] engineGetIV()
+    {
+        return null;
+    }
+
+    public AlgorithmParameters engineGetParameters()
+    {
+        return null;
+    }
+
+    public void engineSetMode(String mode)
+        throws NoSuchAlgorithmException
+    {
+        String modeName = Strings.toUpperCase(mode);
+
+        if (!modeName.equals("NONE"))
+        {
+            throw new IllegalArgumentException("can't support mode " + mode);
+        }
+    }
+
+    public int engineGetOutputSize(int inputLen)
+    {
+        if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+        {
+            return engine.getOutputSize(inputLen);
+        }
+        else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
+        {
+            return engine.getOutputSize(inputLen);
+        }
+        else
+        {
+            throw new IllegalStateException("cipher not initialised");
+        }
+    }
+
+    public void engineSetPadding(String padding)
+        throws NoSuchPaddingException
+    {
+        String paddingName = Strings.toUpperCase(padding);
+
+        // TDOD: make this meaningful...
+        if (!paddingName.equals("NOPADDING"))
+        {
+            throw new NoSuchPaddingException("padding not available with IESCipher");
+        }
+    }
+
+
+    // Initialisation methods
+
+    public void engineInit(
+        int opmode,
+        Key key,
+        AlgorithmParameters params,
+        SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        AlgorithmParameterSpec paramSpec = null;
+
+        if (params != null)
+        {
+            throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + params.getClass().getName());
+        }
+
+        engineInit(opmode, key, paramSpec, random);
+    }
+
+    public void engineInit(
+        int opmode,
+        Key key,
+        AlgorithmParameterSpec engineSpec,
+        SecureRandom random)
+        throws InvalidAlgorithmParameterException, InvalidKeyException
+    {
+        // Parse the recipient's key
+        if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)
+        {
+            if (key instanceof PublicKey)
+            {
+                this.key = ECUtils.generatePublicKeyParameter((PublicKey)key);
+            }
+            else
+            {
+                throw new InvalidKeyException("must be passed public EC key for encryption");
+            }
+        }
+        else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE)
+        {
+            if (key instanceof PrivateKey)
+            {
+                this.key = ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            }
+            else
+            {
+                throw new InvalidKeyException("must be passed private EC key for decryption");
+            }
+        }
+        else
+        {
+            throw new InvalidKeyException("must be passed EC key");
+        }
+
+
+        if (random != null)
+        {
+            this.random = random;
+        }
+        else
+        {
+            this.random = CryptoServicesRegistrar.getSecureRandom();
+        }
+
+        this.state = opmode;
+        buffer.reset();
+    }
+
+    public void engineInit(
+        int opmode,
+        Key key,
+        SecureRandom random)
+        throws InvalidKeyException
+    {
+        try
+        {
+            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            throw new IllegalArgumentException("cannot handle supplied parameter spec: " + e.getMessage());
+        }
+    }
+
+
+    // Update methods - buffer the input
+
+    public byte[] engineUpdate(
+        byte[] input,
+        int inputOffset,
+        int inputLen)
+    {
+        buffer.write(input, inputOffset, inputLen);
+        return null;
+    }
+
+
+    public int engineUpdate(
+        byte[] input,
+        int inputOffset,
+        int inputLen,
+        byte[] output,
+        int outputOffset)
+    {
+        buffer.write(input, inputOffset, inputLen);
+        return 0;
+    }
+
+
+    // Finalisation methods
+
+    public byte[] engineDoFinal(
+        byte[] input,
+        int inputOffset,
+        int inputLen)
+        throws IllegalBlockSizeException, BadPaddingException
+    {
+        if (inputLen != 0)
+        {
+            buffer.write(input, inputOffset, inputLen);
+        }
+
+        try
+        {
+            if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+            {
+                // Encrypt the buffer
+                try
+                {
+                    engine.init(true, new ParametersWithRandom(key, random));
+
+                    return engine.processBlock(buffer.getBuf(), 0, buffer.size());
+                }
+                catch (final Exception e)
+                {
+                    throw new BadBlockException("unable to process block", e);
+                }
+            }
+            else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
+            {
+                // Decrypt the buffer
+                try
+                {
+                    engine.init(false, key);
+
+                    return engine.processBlock(buffer.getBuf(), 0, buffer.size());
+                }
+                catch (final Exception e)
+                {
+                    throw new BadBlockException("unable to process block", e);
+                }
+            }
+            else
+            {
+                throw new IllegalStateException("cipher not initialised");
+            }
+        }
+        finally
+        {
+            buffer.erase();
+        }
+    }
+
+    public int engineDoFinal(
+        byte[] input,
+        int inputOffset,
+        int inputLength,
+        byte[] output,
+        int outputOffset)
+        throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
+    {
+        byte[] buf = engineDoFinal(input, inputOffset, inputLength);
+        System.arraycopy(buf, 0, output, outputOffset, buf.length);
+        return buf.length;
+    }
+
+    /**
+     * Classes that inherit from us
+     */
+    static public class SM2
+        extends GMCipherSpi
+    {
+        public SM2()
+        {
+            super(new SM2Engine());
+        }
+    }
+
+    static public class SM2withBlake2b
+        extends GMCipherSpi
+    {
+        public SM2withBlake2b()
+        {
+            super(new SM2Engine(new Blake2bDigest(512)));
+        }
+    }
+
+    static public class SM2withBlake2s
+        extends GMCipherSpi
+    {
+        public SM2withBlake2s()
+        {
+            super(new SM2Engine(new Blake2sDigest(256)));
+        }
+    }
+
+    static public class SM2withWhirlpool
+        extends GMCipherSpi
+    {
+        public SM2withWhirlpool()
+        {
+            super(new SM2Engine(new WhirlpoolDigest()));
+        }
+    }
+
+    static public class SM2withMD5
+        extends GMCipherSpi
+    {
+        public SM2withMD5()
+        {
+            super(new SM2Engine(new MD5Digest()));
+        }
+    }
+
+    static public class SM2withRMD
+        extends GMCipherSpi
+    {
+        public SM2withRMD()
+        {
+            super(new SM2Engine(new RIPEMD160Digest()));
+        }
+    }
+
+    static public class SM2withSha1
+        extends GMCipherSpi
+    {
+        public SM2withSha1()
+        {
+            super(new SM2Engine(new SHA1Digest()));
+        }
+    }
+
+    static public class SM2withSha224
+        extends GMCipherSpi
+    {
+        public SM2withSha224()
+        {
+            super(new SM2Engine(new SHA224Digest()));
+        }
+    }
+
+    static public class SM2withSha256
+        extends GMCipherSpi
+    {
+        public SM2withSha256()
+        {
+            super(new SM2Engine(new SHA256Digest()));
+        }
+    }
+
+    static public class SM2withSha384
+        extends GMCipherSpi
+    {
+        public SM2withSha384()
+        {
+            super(new SM2Engine(new SHA384Digest()));
+        }
+    }
+
+    static public class SM2withSha512
+        extends GMCipherSpi
+    {
+        public SM2withSha512()
+        {
+            super(new SM2Engine(new SHA512Digest()));
+        }
+    }
+
+    protected static final class ErasableOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ErasableOutputStream()
+        {
+        }
+
+        public byte[] getBuf()
+        {
+            return buf;
+        }
+
+        public void erase()
+        {
+            Arrays.fill(this.buf, (byte)0);
+            reset();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMSignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMSignatureSpi.java
index 3500de3..bc7abd4 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMSignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMSignatureSpi.java
@@ -1,34 +1,36 @@
 package org.bouncycastle.jcajce.provider.asymmetric.ec;
 
-import java.io.IOException;
-import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
 
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.params.ParametersWithID;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.SM2Signer;
-import org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
-import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
-import org.bouncycastle.util.Arrays;
+import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
+import org.bouncycastle.jcajce.util.BCJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
 
 public class GMSignatureSpi
-    extends DSABase
+    extends java.security.SignatureSpi
 {
-    GMSignatureSpi(Digest digest, DSA signer, DSAEncoder encoder)
+    private final JcaJceHelper helper = new BCJcaJceHelper();
+
+    private AlgorithmParameters engineParams;
+    private SM2ParameterSpec paramSpec;
+
+    private final SM2Signer signer;
+
+    GMSignatureSpi(SM2Signer signer)
     {
-        super(digest, signer, encoder);
+        this.signer = signer;
     }
 
     protected void engineInitVerify(PublicKey publicKey)
@@ -36,7 +38,11 @@
     {
         CipherParameters param = ECUtils.generatePublicKeyParameter(publicKey);
 
-        digest.reset();
+        if (paramSpec != null)
+        {
+            param = new ParametersWithID(param, paramSpec.getID());
+        }
+
         signer.init(false, param);
     }
 
@@ -46,11 +52,14 @@
     {
         CipherParameters param = ECUtil.generatePrivateKeyParameter(privateKey);
 
-        digest.reset();
-
         if (appRandom != null)
         {
-            signer.init(true, new ParametersWithRandom(param, appRandom));
+            param = new ParametersWithRandom(param, appRandom);
+        }
+
+        if (paramSpec != null)
+        {
+            signer.init(true, new ParametersWithID(param, paramSpec.getID()));
         }
         else
         {
@@ -58,115 +67,91 @@
         }
     }
 
+    protected void engineUpdate(byte b)
+        throws SignatureException
+    {
+        signer.update(b);
+    }
+
+    protected void engineUpdate(byte[] bytes, int off, int length)
+        throws SignatureException
+    {
+        signer.update(bytes, off, length);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        try
+        {
+            return signer.generateSignature();
+        }
+        catch (CryptoException e)
+        {
+            throw new SignatureException("unable to create signature: " + e.getMessage());
+        }
+    }
+
+    protected boolean engineVerify(byte[] bytes)
+        throws SignatureException
+    {
+        return signer.verifySignature(bytes);
+    }
+
+    protected void engineSetParameter(
+        AlgorithmParameterSpec params)
+        throws InvalidAlgorithmParameterException
+    {
+        if (params instanceof SM2ParameterSpec)
+        {
+            paramSpec = (SM2ParameterSpec)params;
+        }
+        else
+        {
+            throw new InvalidAlgorithmParameterException("only SM2ParameterSpec supported");
+        }
+    }
+
+    protected AlgorithmParameters engineGetParameters()
+    {
+        if (engineParams == null)
+        {
+            if (paramSpec != null)
+            {
+                try
+                {
+                    engineParams = helper.createAlgorithmParameters("PSS");
+                    engineParams.init(paramSpec);
+                }
+                catch (Exception e)
+                {
+                    throw new RuntimeException(e.toString());
+                }
+            }
+        }
+
+        return engineParams;
+    }
+
+    protected void engineSetParameter(
+        String param,
+        Object value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    protected Object engineGetParameter(
+        String param)
+    {
+        throw new UnsupportedOperationException("engineGetParameter unsupported");
+    }
+
     static public class sm3WithSM2
         extends GMSignatureSpi
     {
         public sm3WithSM2()
         {
-            super(new SM3Digest(), new SM2Signer(), new StdDSAEncoder());
-        }
-    }
-    
-    private static class StdDSAEncoder
-        implements DSAEncoder
-    {
-        public byte[] encode(
-            BigInteger r,
-            BigInteger s)
-            throws IOException
-        {
-            ASN1EncodableVector v = new ASN1EncodableVector();
-
-            v.add(new ASN1Integer(r));
-            v.add(new ASN1Integer(s));
-
-            return new DERSequence(v).getEncoded(ASN1Encoding.DER);
-        }
-
-        public BigInteger[] decode(
-            byte[] encoding)
-            throws IOException
-        {
-            ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
-            if (s.size() != 2)
-            {
-                throw new IOException("malformed signature");
-            }
-            if (!Arrays.areEqual(encoding, s.getEncoded(ASN1Encoding.DER)))
-            {
-                throw new IOException("malformed signature");
-            }
-
-            BigInteger[] sig = new BigInteger[2];
-
-
-            sig[0] = ASN1Integer.getInstance(s.getObjectAt(0)).getValue();
-            sig[1] = ASN1Integer.getInstance(s.getObjectAt(1)).getValue();
-
-            return sig;
-        }
-    }
-
-    private static class PlainDSAEncoder
-        implements DSAEncoder
-    {
-        public byte[] encode(
-            BigInteger r,
-            BigInteger s)
-            throws IOException
-        {
-            byte[] first = makeUnsigned(r);
-            byte[] second = makeUnsigned(s);
-            byte[] res;
-
-            if (first.length > second.length)
-            {
-                res = new byte[first.length * 2];
-            }
-            else
-            {
-                res = new byte[second.length * 2];
-            }
-
-            System.arraycopy(first, 0, res, res.length / 2 - first.length, first.length);
-            System.arraycopy(second, 0, res, res.length - second.length, second.length);
-
-            return res;
-        }
-
-
-        private byte[] makeUnsigned(BigInteger val)
-        {
-            byte[] res = val.toByteArray();
-
-            if (res[0] == 0)
-            {
-                byte[] tmp = new byte[res.length - 1];
-
-                System.arraycopy(res, 1, tmp, 0, tmp.length);
-
-                return tmp;
-            }
-
-            return res;
-        }
-
-        public BigInteger[] decode(
-            byte[] encoding)
-            throws IOException
-        {
-            BigInteger[] sig = new BigInteger[2];
-
-            byte[] first = new byte[encoding.length / 2];
-            byte[] second = new byte[encoding.length / 2];
-
-            System.arraycopy(encoding, 0, first, 0, first.length);
-            System.arraycopy(encoding, first.length, second, 0, second.length);
-
-            sig[0] = new BigInteger(1, first);
-            sig[1] = new BigInteger(1, second);
-
-            return sig;
+            super(new SM2Signer());
         }
     }
 }
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/IESCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/IESCipher.java
index 6fdebc7..e937f5c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/IESCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/IESCipher.java
@@ -49,6 +49,7 @@
 import org.bouncycastle.jce.interfaces.ECKey;
 import org.bouncycastle.jce.interfaces.IESKey;
 import org.bouncycastle.jce.spec.IESParameterSpec;
+import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.Strings;
 
 
@@ -167,7 +168,9 @@
 
         if (otherKeyParameter == null)
         {
-            len2 = 2 * (((ECKeyParameters)key).getParameters().getCurve().getFieldSize() + 7) / 8;
+            ECCurve c = ((ECKeyParameters)key).getParameters().getCurve();
+            int feSize = (c.getFieldSize() + 7) / 8; 
+            len2 = 2 * feSize;
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
index 29d227c..d82481e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
@@ -15,9 +15,13 @@
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
 import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.bouncycastle.crypto.agreement.ECDHCUnifiedAgreement;
 import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement;
 import org.bouncycastle.crypto.agreement.kdf.ConcatenationKDFGenerator;
+import org.bouncycastle.crypto.digests.RIPEMD160Digest;
 import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.params.ECDHUPrivateParameters;
+import org.bouncycastle.crypto.params.ECDHUPublicParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -26,17 +30,19 @@
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.spec.DHUParameterSpec;
 import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.jce.interfaces.ECPrivateKey;
 import org.bouncycastle.jce.interfaces.ECPublicKey;
 import org.bouncycastle.jce.interfaces.MQVPrivateKey;
 import org.bouncycastle.jce.interfaces.MQVPublicKey;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363
  * both the simple one, and the simple one with cofactors are supported.
- *
+ * <p>
  * Also, MQV key agreement per SEC-1
  */
 public class KeyAgreementSpi
@@ -44,13 +50,14 @@
 {
     private static final X9IntegerConverter converter = new X9IntegerConverter();
 
-    private String                 kaAlgorithm;
+    private String kaAlgorithm;
 
-    private ECDomainParameters     parameters;
-    private BasicAgreement         agreement;
+    private ECDomainParameters parameters;
+    private Object agreement;
 
-    private MQVParameterSpec       mqvParameters;
-    private BigInteger             result;
+    private MQVParameterSpec mqvParameters;
+    private DHUParameterSpec dheParameters;
+    private byte[] result;
 
     protected KeyAgreementSpi(
         String kaAlgorithm,
@@ -63,15 +70,26 @@
         this.agreement = agreement;
     }
 
+    protected KeyAgreementSpi(
+        String kaAlgorithm,
+        ECDHCUnifiedAgreement agreement,
+        DerivationFunction kdf)
+    {
+        super(kaAlgorithm, kdf);
+
+        this.kaAlgorithm = kaAlgorithm;
+        this.agreement = agreement;
+    }
+
     protected byte[] bigIntToBytes(
-        BigInteger    r)
+        BigInteger r)
     {
         return converter.integerToBytes(r, converter.getByteLength(parameters.getCurve()));
     }
 
     protected Key engineDoPhase(
-        Key     key,
-        boolean lastPhase) 
+        Key key,
+        boolean lastPhase)
         throws InvalidKeyException, IllegalStateException
     {
         if (parameters == null)
@@ -84,7 +102,7 @@
             throw new IllegalStateException(kaAlgorithm + " can only be between two parties.");
         }
 
-        CipherParameters pubKey;        
+        CipherParameters pubKey;
         if (agreement instanceof ECMQVBasicAgreement)
         {
             if (!(key instanceof MQVPublicKey))
@@ -107,6 +125,15 @@
                 pubKey = new MQVPublicParameters(staticKey, ephemKey);
             }
         }
+        else if (agreement instanceof ECDHCUnifiedAgreement)
+        {
+            ECPublicKeyParameters staticKey = (ECPublicKeyParameters)
+                ECUtils.generatePublicKeyParameter((PublicKey)key);
+            ECPublicKeyParameters ephemKey = (ECPublicKeyParameters)
+                ECUtils.generatePublicKeyParameter(dheParameters.getOtherPartyEphemeralKey());
+
+            pubKey = new ECDHUPublicParameters(staticKey, ephemKey);
+        }
         else
         {
             if (!(key instanceof PublicKey))
@@ -120,16 +147,23 @@
 
         try
         {
-            result = agreement.calculateAgreement(pubKey);
+            if (agreement instanceof BasicAgreement)
+            {
+                result = bigIntToBytes(((BasicAgreement)agreement).calculateAgreement(pubKey));
+            }
+            else
+            {
+                result = ((ECDHCUnifiedAgreement)agreement).calculateAgreement(pubKey);
+            }
         }
         catch (final Exception e)
         {
             throw new InvalidKeyException("calculation failed: " + e.getMessage())
             {
                 public Throwable getCause()
-                            {
-                                return e;
-                            }
+                {
+                    return e;
+                }
             };
         }
 
@@ -137,12 +171,13 @@
     }
 
     protected void engineInit(
-        Key                     key,
-        AlgorithmParameterSpec  params,
-        SecureRandom            random) 
+        Key key,
+        AlgorithmParameterSpec params,
+        SecureRandom random)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
-        if (params != null && !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec))
+        if (params != null &&
+            !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
         {
             throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
         }
@@ -151,22 +186,30 @@
     }
 
     protected void engineInit(
-        Key             key,
-        SecureRandom    random) 
+        Key key,
+        SecureRandom random)
         throws InvalidKeyException
     {
-        initFromKey(key, null);
+        try
+        {
+            initFromKey(key, null);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            // this should never occur.
+            throw new InvalidKeyException(e.getMessage());
+        }
     }
 
     private void initFromKey(Key key, AlgorithmParameterSpec parameterSpec)
-        throws InvalidKeyException
+        throws InvalidKeyException, InvalidAlgorithmParameterException
     {
         if (agreement instanceof ECMQVBasicAgreement)
         {
             mqvParameters = null;
             if (!(key instanceof MQVPrivateKey) && !(parameterSpec instanceof MQVParameterSpec))
             {
-                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
+                throw new InvalidAlgorithmParameterException(kaAlgorithm + " key agreement requires "
                     + getSimpleName(MQVParameterSpec.class) + " for initialisation");
             }
 
@@ -212,7 +255,38 @@
 
             // TODO Validate that all the keys are using the same parameters?
 
-            agreement.init(localParams);
+            ((ECMQVBasicAgreement)agreement).init(localParams);
+        }
+        else if (parameterSpec instanceof DHUParameterSpec)
+        {
+            if (!(agreement instanceof ECDHCUnifiedAgreement))
+            {
+                throw new InvalidAlgorithmParameterException(kaAlgorithm + " key agreement cannot be used with "
+                    + getSimpleName(DHUParameterSpec.class));
+            }
+            DHUParameterSpec dheParameterSpec = (DHUParameterSpec)parameterSpec;
+            ECPrivateKeyParameters staticPrivKey;
+            ECPrivateKeyParameters ephemPrivKey;
+            ECPublicKeyParameters ephemPubKey;
+
+            staticPrivKey = (ECPrivateKeyParameters)
+                ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            ephemPrivKey = (ECPrivateKeyParameters)
+                ECUtil.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
+
+            ephemPubKey = null;
+            if (dheParameterSpec.getEphemeralPublicKey() != null)
+            {
+                ephemPubKey = (ECPublicKeyParameters)
+                    ECUtils.generatePublicKeyParameter(dheParameterSpec.getEphemeralPublicKey());
+            }
+            dheParameters = dheParameterSpec;
+            ukmParameters = dheParameterSpec.getUserKeyingMaterial();
+
+            ECDHUPrivateParameters localParams = new ECDHUPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey);
+            this.parameters = staticPrivKey.getParameters();
+
+            ((ECDHCUnifiedAgreement)agreement).init(localParams);
         }
         else
         {
@@ -221,11 +295,14 @@
                 throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
                     + getSimpleName(ECPrivateKey.class) + " for initialisation");
             }
-
+            if (kdf == null && parameterSpec instanceof UserKeyingMaterialSpec)
+            {
+                throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
+            }
             ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
             this.parameters = privKey.getParameters();
             ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
-            agreement.init(privKey);
+            ((BasicAgreement)agreement).init(privKey);
         }
     }
 
@@ -235,11 +312,10 @@
 
         return fullName.substring(fullName.lastIndexOf('.') + 1);
     }
-
-
+    
     protected byte[] calcSecret()
     {
-        return bigIntToBytes(result);
+        return Arrays.clone(result);
     }
 
     public static class DH
@@ -269,6 +345,15 @@
         }
     }
 
+    public static class DHUC
+        extends KeyAgreementSpi
+    {
+        public DHUC()
+        {
+            super("ECCDHU", new ECDHCUnifiedAgreement(), null);
+        }
+    }
+
     public static class DHwithSHA1KDF
         extends KeyAgreementSpi
     {
@@ -351,22 +436,22 @@
     }
 
     public static class DHwithSHA512KDFAndSharedInfo
-         extends KeyAgreementSpi
-     {
-         public DHwithSHA512KDFAndSharedInfo()
-         {
-             super("ECDHwithSHA512KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
-         }
-     }
+        extends KeyAgreementSpi
+    {
+        public DHwithSHA512KDFAndSharedInfo()
+        {
+            super("ECDHwithSHA512KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
 
-     public static class CDHwithSHA512KDFAndSharedInfo
-         extends KeyAgreementSpi
-     {
-         public CDHwithSHA512KDFAndSharedInfo()
-         {
-             super("ECCDHwithSHA512KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
-         }
-     }
+    public static class CDHwithSHA512KDFAndSharedInfo
+        extends KeyAgreementSpi
+    {
+        public CDHwithSHA512KDFAndSharedInfo()
+        {
+            super("ECCDHwithSHA512KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
 
     public static class MQVwithSHA1KDFAndSharedInfo
         extends KeyAgreementSpi
@@ -493,4 +578,217 @@
             super("ECMQVwithSHA512CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
         }
     }
+
+    public static class MQVwithSHA1KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA1KDF()
+        {
+            super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class MQVwithSHA224KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA224KDF()
+        {
+            super("ECMQVwithSHA224KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class MQVwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA256KDF()
+        {
+            super("ECMQVwithSHA256KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class MQVwithSHA384KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA384KDF()
+        {
+            super("ECMQVwithSHA384KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class MQVwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public MQVwithSHA512KDF()
+        {
+            super("ECMQVwithSHA512KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class DHUwithSHA1CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA1CKDF()
+        {
+            super("ECCDHUwithSHA1CKDF", new ECDHCUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHUwithSHA224CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA224CKDF()
+        {
+            super("ECCDHUwithSHA224CKDF", new ECDHCUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHUwithSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA256CKDF()
+        {
+            super("ECCDHUwithSHA256CKDF", new ECDHCUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHUwithSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA384CKDF()
+        {
+            super("ECCDHUwithSHA384CKDF", new ECDHCUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHUwithSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA512CKDF()
+        {
+            super("ECCDHUwithSHA512CKDF", new ECDHCUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class DHUwithSHA1KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA1KDF()
+        {
+            super("ECCDHUwithSHA1KDF", new ECDHCUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
+        }
+    }
+
+    public static class DHUwithSHA224KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA224KDF()
+        {
+            super("ECCDHUwithSHA224KDF", new ECDHCUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
+        }
+    }
+
+    public static class DHUwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA256KDF()
+        {
+            super("ECCDHUwithSHA256KDF", new ECDHCUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class DHUwithSHA384KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA384KDF()
+        {
+            super("ECCDHUwithSHA384KDF", new ECDHCUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class DHUwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public DHUwithSHA512KDF()
+        {
+            super("ECCDHUwithSHA512KDF", new ECDHCUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    /**
+   	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+   	 */
+   	public static class ECKAEGwithSHA1KDF
+   			extends KeyAgreementSpi
+   	{
+   		public ECKAEGwithSHA1KDF()
+   		{
+   			super("ECKAEGwithSHA1KDF", new ECDHBasicAgreement(),
+                   new KDF2BytesGenerator(DigestFactory.createSHA1()));
+   		}
+   	}
+
+    /**
+   	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+   	 */
+   	public static class ECKAEGwithRIPEMD160KDF
+   			extends KeyAgreementSpi
+   	{
+   		public ECKAEGwithRIPEMD160KDF()
+   		{
+   			super("ECKAEGwithRIPEMD160KDF", new ECDHBasicAgreement(),
+                   new KDF2BytesGenerator(new RIPEMD160Digest()));
+   		}
+   	}
+
+    /**
+   	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+   	 */
+   	public static class ECKAEGwithSHA224KDF
+   			extends KeyAgreementSpi
+   	{
+   		public ECKAEGwithSHA224KDF()
+   		{
+   			super("ECKAEGwithSHA224KDF", new ECDHBasicAgreement(),
+                   new KDF2BytesGenerator(DigestFactory.createSHA224()));
+   		}
+   	}
+
+	/**
+	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+	 */
+	public static class ECKAEGwithSHA256KDF
+			extends KeyAgreementSpi
+	{
+		public ECKAEGwithSHA256KDF()
+		{
+			super("ECKAEGwithSHA256KDF", new ECDHBasicAgreement(),
+                new KDF2BytesGenerator(DigestFactory.createSHA256()));
+		}
+	}
+
+	/**
+	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+	 */
+	public static class ECKAEGwithSHA384KDF
+			extends KeyAgreementSpi
+	{
+		public ECKAEGwithSHA384KDF()
+		{
+			super("ECKAEGwithSHA384KDF", new ECDHBasicAgreement(),
+                new KDF2BytesGenerator(DigestFactory.createSHA384()));
+		}
+	}
+
+	/**
+	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
+	 */
+	public static class ECKAEGwithSHA512KDF
+			extends KeyAgreementSpi
+	{
+		public ECKAEGwithSHA512KDF()
+		{
+			super("ECKAEGwithSHA512KDF", new ECDHBasicAgreement(),
+                new KDF2BytesGenerator(DigestFactory.createSHA512()));
+		}
+	}
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
index 243ba51..ea82bc8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
@@ -12,8 +12,13 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
@@ -22,6 +27,8 @@
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
 import org.bouncycastle.jce.spec.ECPublicKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPublicKeySpec;
 
 public class KeyFactorySpi
     extends BaseKeyFactorySpi
@@ -39,7 +46,7 @@
     }
 
     protected Key engineTranslateKey(
-        Key    key)
+        Key key)
         throws InvalidKeyException
     {
         if (key instanceof ECPublicKey)
@@ -55,70 +62,111 @@
     }
 
     protected KeySpec engineGetKeySpec(
-        Key    key,
-        Class    spec)
-    throws InvalidKeySpecException
+        Key key,
+        Class spec)
+        throws InvalidKeySpecException
     {
-       if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey)
-       {
-           ECPublicKey k = (ECPublicKey)key;
-           if (k.getParams() != null)
-           {
-               return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams());
-           }
-           else
-           {
-               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+        if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey)
+        {
+            ECPublicKey k = (ECPublicKey)key;
+            if (k.getParams() != null)
+            {
+                return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams());
+            }
+            else
+            {
+                ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-               return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec));
-           }
-       }
-       else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
-       {
-           ECPrivateKey k = (ECPrivateKey)key;
+                return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec));
+            }
+        }
+        else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
+        {
+            ECPrivateKey k = (ECPrivateKey)key;
 
-           if (k.getParams() != null)
-           {
-               return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams());
-           }
-           else
-           {
-               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+            if (k.getParams() != null)
+            {
+                return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams());
+            }
+            else
+            {
+                ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-               return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); 
-           }
-       }
-       else if (spec.isAssignableFrom(org.bouncycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey)
-       {
-           ECPublicKey k = (ECPublicKey)key;
-           if (k.getParams() != null)
-           {
-               return new org.bouncycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false));
-           }
-           else
-           {
-               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+                return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec));
+            }
+        }
+        else if (spec.isAssignableFrom(org.bouncycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey)
+        {
+            ECPublicKey k = (ECPublicKey)key;
+            if (k.getParams() != null)
+            {
+                return new org.bouncycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false));
+            }
+            else
+            {
+                ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-               return new org.bouncycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec);
-           }
-       }
-       else if (spec.isAssignableFrom(org.bouncycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
-       {
-           ECPrivateKey k = (ECPrivateKey)key;
+                return new org.bouncycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec);
+            }
+        }
+        else if (spec.isAssignableFrom(org.bouncycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
+        {
+            ECPrivateKey k = (ECPrivateKey)key;
 
-           if (k.getParams() != null)
-           {
-               return new org.bouncycastle.jce.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false));
-           }
-           else
-           {
-               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+            if (k.getParams() != null)
+            {
+                return new org.bouncycastle.jce.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false));
+            }
+            else
+            {
+                ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-               return new org.bouncycastle.jce.spec.ECPrivateKeySpec(k.getS(), implicitSpec);
-           }
-       }
+                return new org.bouncycastle.jce.spec.ECPrivateKeySpec(k.getS(), implicitSpec);
+            }
+        }
+        else if (spec.isAssignableFrom(OpenSSHPublicKeySpec.class) && key instanceof ECPublicKey)
+        {
+            if (key instanceof BCECPublicKey)
+            {
+                BCECPublicKey bcPk = (BCECPublicKey)key;
+                ECParameterSpec sc = bcPk.getParameters();
+                try
+                {
+                    return new OpenSSHPublicKeySpec(
+                        OpenSSHPublicKeyUtil.encodePublicKey(
+                            new ECPublicKeyParameters(bcPk.getQ(), new ECDomainParameters(sc.getCurve(), sc.getG(), sc.getN(), sc.getH(), sc.getSeed()))));
+                }
+                catch (IOException e)
+                {
+                    throw new IllegalArgumentException("unable to produce encoding: " + e.getMessage());
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("invalid key type: " + key.getClass().getName());
+            }
+        }
+        else if (spec.isAssignableFrom(OpenSSHPrivateKeySpec.class) && key instanceof ECPrivateKey)
+        {
+            if (key instanceof BCECPrivateKey)
+            {
+                try
+                {
+                    return new OpenSSHPrivateKeySpec(PrivateKeyInfo.getInstance(key.getEncoded()).parsePrivateKey().toASN1Primitive().getEncoded());
+                }
+                catch (IOException e)
+                {
+                    throw new IllegalArgumentException("cannot encoded key: " + e.getMessage());
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("invalid key type: " + key.getClass().getName());
+            }
 
-       return super.engineGetKeySpec(key, spec);
+        }
+
+        return super.engineGetKeySpec(key, spec);
     }
 
     protected PrivateKey engineGeneratePrivate(
@@ -133,6 +181,19 @@
         {
             return new BCECPrivateKey(algorithm, (java.security.spec.ECPrivateKeySpec)keySpec, configuration);
         }
+        else if (keySpec instanceof OpenSSHPrivateKeySpec)
+        {
+            org.bouncycastle.asn1.sec.ECPrivateKey ecKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(((OpenSSHPrivateKeySpec)keySpec).getEncoded());
+
+            try
+            {
+                return new BCECPrivateKey(algorithm, new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParameters()), ecKey), configuration);
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeySpecException("bad encoding: " + e.getMessage());
+            }
+        }
 
         return super.engineGeneratePrivate(keySpec);
     }
@@ -151,6 +212,22 @@
             {
                 return new BCECPublicKey(algorithm, (java.security.spec.ECPublicKeySpec)keySpec, configuration);
             }
+            else if (keySpec instanceof OpenSSHPublicKeySpec)
+            {
+                CipherParameters params = OpenSSHPublicKeyUtil.parsePublicKey(((OpenSSHPublicKeySpec)keySpec).getEncoded());
+                if (params instanceof ECPublicKeyParameters)
+                {
+                    ECDomainParameters parameters = ((ECPublicKeyParameters)params).getParameters();
+                    return engineGeneratePublic(
+                        new ECPublicKeySpec(((ECPublicKeyParameters)params).getQ(),
+                            new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH(), parameters.getSeed())
+                        ));
+                }
+                else
+                {
+                    throw new IllegalArgumentException("openssh key is not ec public key");
+                }
+            }
         }
         catch (Exception e)
         {
@@ -217,6 +294,15 @@
         }
     }
 
+    public static class ECGOST3410_2012
+        extends KeyFactorySpi
+    {
+        public ECGOST3410_2012()
+        {
+            super("ECGOST3410-2012", BouncyCastleProvider.CONFIGURATION);
+        }
+    }
+
     public static class ECDH
         extends KeyFactorySpi
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
index 62ec75a..72fcca9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
@@ -14,6 +14,7 @@
 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
@@ -44,8 +45,7 @@
         ECKeyPairGenerator          engine = new ECKeyPairGenerator();
         Object                      ecParams = null;
         int                         strength = 239;
-        int                         certainty = 50;
-        SecureRandom                random = new SecureRandom();
+        SecureRandom                random = CryptoServicesRegistrar.getSecureRandom();
         boolean                     initialised = false;
         String                      algorithm;
         ProviderConfiguration       configuration;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
index b8ce0d8..73d2ae5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
@@ -1,38 +1,31 @@
 package org.bouncycastle.jcajce.provider.asymmetric.ec;
 
-import java.io.IOException;
-import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.NullDigest;
 import org.bouncycastle.crypto.digests.RIPEMD160Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.DSAEncoding;
 import org.bouncycastle.crypto.signers.ECDSASigner;
 import org.bouncycastle.crypto.signers.ECNRSigner;
 import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
+import org.bouncycastle.crypto.signers.PlainDSAEncoding;
+import org.bouncycastle.crypto.signers.StandardDSAEncoding;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
-import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
-import org.bouncycastle.util.Arrays;
 
 public class SignatureSpi
     extends DSABase
 {
-    SignatureSpi(Digest digest, DSA signer, DSAEncoder encoder)
+    SignatureSpi(Digest digest, DSAExt signer, DSAEncoding encoding)
     {
-        super(digest, signer, encoder);
+        super(digest, signer, encoding);
     }
 
     protected void engineInitVerify(PublicKey publicKey)
@@ -67,7 +60,7 @@
     {
         public ecDSA()
         {
-            super(DigestFactory.createSHA1(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA1(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -76,7 +69,7 @@
     {
         public ecDetDSA()
         {
-            super(DigestFactory.createSHA1(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA1())), new StdDSAEncoder());
+            super(DigestFactory.createSHA1(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA1())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -85,7 +78,7 @@
     {
         public ecDSAnone()
         {
-            super(new NullDigest(), new ECDSASigner(), new StdDSAEncoder());
+            super(new NullDigest(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -94,7 +87,7 @@
     {
         public ecDSA224()
         {
-            super(DigestFactory.createSHA224(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA224(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -103,7 +96,7 @@
     {
         public ecDetDSA224()
         {
-            super(DigestFactory.createSHA224(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA224())), new StdDSAEncoder());
+            super(DigestFactory.createSHA224(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA224())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -112,7 +105,7 @@
     {
         public ecDSA256()
         {
-            super(DigestFactory.createSHA256(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA256(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -121,7 +114,7 @@
     {
         public ecDetDSA256()
         {
-            super(DigestFactory.createSHA256(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA256())), new StdDSAEncoder());
+            super(DigestFactory.createSHA256(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA256())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -130,7 +123,7 @@
     {
         public ecDSA384()
         {
-            super(DigestFactory.createSHA384(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA384(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -139,7 +132,7 @@
     {
         public ecDetDSA384()
         {
-            super(DigestFactory.createSHA384(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA384())), new StdDSAEncoder());
+            super(DigestFactory.createSHA384(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA384())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -148,7 +141,7 @@
     {
         public ecDSA512()
         {
-            super(DigestFactory.createSHA512(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA512(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -157,7 +150,7 @@
     {
         public ecDetDSA512()
         {
-            super(DigestFactory.createSHA512(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA512())), new StdDSAEncoder());
+            super(DigestFactory.createSHA512(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA512())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -166,7 +159,7 @@
     {
         public ecDSASha3_224()
         {
-            super(DigestFactory.createSHA3_224(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_224(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -175,7 +168,7 @@
     {
         public ecDetDSASha3_224()
         {
-            super(DigestFactory.createSHA3_224(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_224())), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_224(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_224())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -184,7 +177,7 @@
     {
         public ecDSASha3_256()
         {
-            super(DigestFactory.createSHA3_256(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_256(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -193,7 +186,7 @@
     {
         public ecDetDSASha3_256()
         {
-            super(DigestFactory.createSHA3_256(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_256())), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_256(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_256())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -202,7 +195,7 @@
     {
         public ecDSASha3_384()
         {
-            super(DigestFactory.createSHA3_384(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_384(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -211,7 +204,7 @@
     {
         public ecDetDSASha3_384()
         {
-            super(DigestFactory.createSHA3_384(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_384())), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_384(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_384())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -220,7 +213,7 @@
     {
         public ecDSASha3_512()
         {
-            super(DigestFactory.createSHA3_512(), new ECDSASigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_512(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -229,7 +222,7 @@
     {
         public ecDetDSASha3_512()
         {
-            super(DigestFactory.createSHA3_512(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_512())), new StdDSAEncoder());
+            super(DigestFactory.createSHA3_512(), new ECDSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_512())), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -238,7 +231,7 @@
     {
         public ecDSARipeMD160()
         {
-            super(new RIPEMD160Digest(), new ECDSASigner(), new StdDSAEncoder());
+            super(new RIPEMD160Digest(), new ECDSASigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -247,7 +240,7 @@
     {
         public ecNR()
         {
-            super(DigestFactory.createSHA1(), new ECNRSigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA1(), new ECNRSigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -256,7 +249,7 @@
     {
         public ecNR224()
         {
-            super(DigestFactory.createSHA224(), new ECNRSigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA224(), new ECNRSigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -265,7 +258,7 @@
     {
         public ecNR256()
         {
-            super(DigestFactory.createSHA256(), new ECNRSigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA256(), new ECNRSigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -274,7 +267,7 @@
     {
         public ecNR384()
         {
-            super(DigestFactory.createSHA384(), new ECNRSigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA384(), new ECNRSigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -283,7 +276,7 @@
     {
         public ecNR512()
         {
-            super(DigestFactory.createSHA512(), new ECNRSigner(), new StdDSAEncoder());
+            super(DigestFactory.createSHA512(), new ECNRSigner(), StandardDSAEncoding.INSTANCE);
         }
     }
 
@@ -292,7 +285,7 @@
     {
         public ecCVCDSA()
         {
-            super(DigestFactory.createSHA1(), new ECDSASigner(), new PlainDSAEncoder());
+            super(DigestFactory.createSHA1(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
 
@@ -301,7 +294,7 @@
     {
         public ecCVCDSA224()
         {
-            super(DigestFactory.createSHA224(), new ECDSASigner(), new PlainDSAEncoder());
+            super(DigestFactory.createSHA224(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
 
@@ -310,7 +303,7 @@
     {
         public ecCVCDSA256()
         {
-            super(DigestFactory.createSHA256(), new ECDSASigner(), new PlainDSAEncoder());
+            super(DigestFactory.createSHA256(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
 
@@ -319,7 +312,7 @@
     {
         public ecCVCDSA384()
         {
-            super(DigestFactory.createSHA384(), new ECDSASigner(), new PlainDSAEncoder());
+            super(DigestFactory.createSHA384(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
 
@@ -328,7 +321,7 @@
     {
         public ecCVCDSA512()
         {
-            super(DigestFactory.createSHA512(), new ECDSASigner(), new PlainDSAEncoder());
+            super(DigestFactory.createSHA512(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
 
@@ -337,109 +330,7 @@
     {
         public ecPlainDSARP160()
         {
-            super(new RIPEMD160Digest(), new ECDSASigner(), new PlainDSAEncoder());
+            super(new RIPEMD160Digest(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
-
-    private static class StdDSAEncoder
-        implements DSAEncoder
-    {
-        public byte[] encode(
-            BigInteger r,
-            BigInteger s)
-            throws IOException
-        {
-            ASN1EncodableVector v = new ASN1EncodableVector();
-
-            v.add(new ASN1Integer(r));
-            v.add(new ASN1Integer(s));
-
-            return new DERSequence(v).getEncoded(ASN1Encoding.DER);
-        }
-
-        public BigInteger[] decode(
-            byte[] encoding)
-            throws IOException
-        {
-            ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
-            if (s.size() != 2)
-            {
-                throw new IOException("malformed signature");
-            }
-            if (!Arrays.areEqual(encoding, s.getEncoded(ASN1Encoding.DER)))
-            {
-                throw new IOException("malformed signature");
-            }
-
-            BigInteger[] sig = new BigInteger[2];
-
-            sig[0] = ASN1Integer.getInstance(s.getObjectAt(0)).getValue();
-            sig[1] = ASN1Integer.getInstance(s.getObjectAt(1)).getValue();
-
-            return sig;
-        }
-    }
-
-    private static class PlainDSAEncoder
-        implements DSAEncoder
-    {
-        public byte[] encode(
-            BigInteger r,
-            BigInteger s)
-            throws IOException
-        {
-            byte[] first = makeUnsigned(r);
-            byte[] second = makeUnsigned(s);
-            byte[] res;
-
-            if (first.length > second.length)
-            {
-                res = new byte[first.length * 2];
-            }
-            else
-            {
-                res = new byte[second.length * 2];
-            }
-
-            System.arraycopy(first, 0, res, res.length / 2 - first.length, first.length);
-            System.arraycopy(second, 0, res, res.length - second.length, second.length);
-
-            return res;
-        }
-
-
-        private byte[] makeUnsigned(BigInteger val)
-        {
-            byte[] res = val.toByteArray();
-
-            if (res[0] == 0)
-            {
-                byte[] tmp = new byte[res.length - 1];
-
-                System.arraycopy(res, 1, tmp, 0, tmp.length);
-
-                return tmp;
-            }
-
-            return res;
-        }
-
-        public BigInteger[] decode(
-            byte[] encoding)
-            throws IOException
-        {
-            BigInteger[] sig = new BigInteger[2];
-
-            byte[] first = new byte[encoding.length / 2];
-            byte[] second = new byte[encoding.length / 2];
-
-            System.arraycopy(encoding, 0, first, 0, first.length);
-            System.arraycopy(encoding, first.length, second, 0, second.length);
-
-            sig[0] = new BigInteger(1, first);
-            sig[1] = new BigInteger(1, second);
-
-            return sig;
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
index c9fa135..4ad33a8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
@@ -6,7 +6,6 @@
 import java.math.BigInteger;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
@@ -25,7 +24,6 @@
 import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
 import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.util.ASN1Dump;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X962Parameters;
@@ -42,7 +40,6 @@
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Strings;
 
 public class BCECGOST3410PrivateKey
     implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
@@ -52,7 +49,7 @@
     private String algorithm = "ECGOST3410";
     private boolean withCompression;
 
-    private transient GOST3410PublicKeyAlgParameters gostParams;
+    private transient ASN1Encodable gostParams;
     private transient BigInteger d;
     private transient ECParameterSpec ecSpec;
     private transient DERBitString publicKey;
@@ -115,20 +112,17 @@
         BCECGOST3410PublicKey pubKey,
         ECParameterSpec spec)
     {
-        ECDomainParameters dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    dp.getG().getAffineXCoord().toBigInteger(),
-                    dp.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(dp.getG()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -148,20 +142,17 @@
         BCECGOST3410PublicKey pubKey,
         org.bouncycastle.jce.spec.ECParameterSpec spec)
     {
-        ECDomainParameters dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    dp.getG().getAffineXCoord().toBigInteger(),
-                    dp.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(dp.getG()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -171,9 +162,7 @@
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(spec.getG()),
                 spec.getN(),
                 spec.getH().intValue());
         }
@@ -202,23 +191,24 @@
     private void populateFromPrivKeyInfo(PrivateKeyInfo info)
         throws IOException
     {
-        ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+        AlgorithmIdentifier pkAlg = info.getPrivateKeyAlgorithm(); 
+        ASN1Encodable pkParams = pkAlg.getParameters();
+        ASN1Primitive p = pkParams.toASN1Primitive();
 
         if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
         {
-            gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+            GOST3410PublicKeyAlgParameters gParams = GOST3410PublicKeyAlgParameters.getInstance(pkParams);
+            gostParams = gParams;
 
-            ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+            ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gParams.getPublicKeyParamSet()));
 
             ECCurve curve = spec.getCurve();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
 
             ecSpec = new ECNamedCurveSpec(
-                ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+                ECGOST3410NamedCurves.getName(gParams.getPublicKeyParamSet()),
                 ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(spec.getG()),
                 spec.getN(), spec.getH());
 
             ASN1Encodable privKey = info.parsePrivateKey();
@@ -243,40 +233,34 @@
         else
         {
             // for backwards compatibility
-            X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+            X962Parameters params = X962Parameters.getInstance(pkParams);
 
             if (params.isNamedCurve())
             {
                 ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
                 X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
 
+                String curveName;
                 if (ecP == null) // GOST Curve
                 {
                     ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
-                    EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
+                    ecP = new X9ECParameters(gParam.getCurve(), gParam.getG(), gParam.getN(), gParam.getH(), gParam.getSeed());
 
-                    ecSpec = new ECNamedCurveSpec(
-                        ECGOST3410NamedCurves.getName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                            gParam.getG().getAffineXCoord().toBigInteger(),
-                            gParam.getG().getAffineYCoord().toBigInteger()),
-                        gParam.getN(),
-                        gParam.getH());
+                    curveName = ECGOST3410NamedCurves.getName(oid);
                 }
                 else
                 {
-                    EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
-
-                    ecSpec = new ECNamedCurveSpec(
-                        ECUtil.getCurveName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                            ecP.getG().getAffineXCoord().toBigInteger(),
-                            ecP.getG().getAffineYCoord().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH());
+                    curveName = ECUtil.getCurveName(oid);
                 }
+
+                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+                ecSpec = new ECNamedCurveSpec(
+                    curveName,
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH());
             }
             else if (params.isImplicitlyCA())
             {
@@ -289,9 +273,7 @@
 
                 this.ecSpec = new ECParameterSpec(
                     ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getAffineXCoord().toBigInteger(),
-                        ecP.getG().getAffineYCoord().toBigInteger()),
+                    EC5Util.convertPoint(ecP.getG()),
                     ecP.getN(),
                     ecP.getH().intValue());
             }
@@ -506,14 +488,7 @@
 
     public String toString()
     {
-        StringBuffer buf = new StringBuffer();
-        String nl = Strings.lineSeparator();
-
-        buf.append("EC Private Key").append(nl);
-        buf.append("             S: ").append(this.d.toString(16)).append(nl);
-
-        return buf.toString();
-
+        return ECUtil.privateKeyToString(algorithm, d, engineGetSpec());
     }
 
     private DERBitString getPublicKeyDetails(BCECGOST3410PublicKey pub)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
index a2f7081..cb21e96 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
@@ -11,6 +11,7 @@
 import java.security.spec.EllipticCurve;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERBitString;
@@ -23,6 +24,7 @@
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
@@ -34,7 +36,6 @@
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Strings;
 
 public class BCECGOST3410PublicKey
     implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
@@ -46,7 +47,7 @@
 
     private transient ECPublicKeyParameters   ecPublicKey;
     private transient ECParameterSpec ecSpec;
-    private transient GOST3410PublicKeyAlgParameters gostParams;
+    private transient ASN1Encodable gostParams;
 
     public BCECGOST3410PublicKey(
         BCECGOST3410PublicKey key)
@@ -94,6 +95,14 @@
     {
         ECDomainParameters      dp = params.getParameters();
 
+        if (dp instanceof ECGOST3410Parameters)
+        {
+            ECGOST3410Parameters p = (ECGOST3410Parameters)dp;
+
+            this.gostParams = new GOST3410PublicKeyAlgParameters(p.getPublicKeyParamSet(),
+                                                p.getDigestParamSet(), p.getEncryptionParamSet());
+        }
+
         this.algorithm = algorithm;
         this.ecPublicKey = params;
 
@@ -149,9 +158,7 @@
     {
         return new ECParameterSpec(
             ellipticCurve,
-            new ECPoint(
-                dp.getG().getAffineXCoord().toBigInteger(),
-                dp.getG().getAffineYCoord().toBigInteger()),
+            EC5Util.convertPoint(dp.getG()),
             dp.getN(),
             dp.getH().intValue());
     }
@@ -186,34 +193,41 @@
         }
 
         byte[] keyEnc = key.getOctets();
-        byte[] x = new byte[32];
-        byte[] y = new byte[32];
 
-        for (int i = 0; i != x.length; i++)
+        byte[] x9Encoding = new byte[65];
+        x9Encoding[0] = 0x04;
+        for (int i = 1; i <= 32; ++i)
         {
-            x[i] = keyEnc[32 - 1 - i];
+            x9Encoding[i     ] = keyEnc[32 - i];
+            x9Encoding[i + 32] = keyEnc[64 - i];
         }
 
-        for (int i = 0; i != y.length; i++)
+        ASN1ObjectIdentifier paramOID;
+
+        if (info.getAlgorithm().getParameters() instanceof ASN1ObjectIdentifier)
         {
-            y[i] = keyEnc[64 - 1 - i];
+            paramOID = ASN1ObjectIdentifier.getInstance(info.getAlgorithm().getParameters());
+            gostParams = paramOID;
+        }
+        else
+        {
+            GOST3410PublicKeyAlgParameters params = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters());
+            gostParams = params;
+            paramOID = params.getPublicKeyParamSet();
+
         }
 
-        gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters());
-
-        ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+        ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(paramOID));
 
         ECCurve curve = spec.getCurve();
         EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
 
-        this.ecPublicKey = new ECPublicKeyParameters(curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)), ECUtil.getDomainParameters(null, spec));
+        this.ecPublicKey = new ECPublicKeyParameters(curve.decodePoint(x9Encoding), ECUtil.getDomainParameters(null, spec));
 
-        ecSpec = new ECNamedCurveSpec(
-            ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+        this.ecSpec = new ECNamedCurveSpec(
+            ECGOST3410NamedCurves.getName(paramOID),
             ellipticCurve,
-            new ECPoint(
-                spec.getG().getAffineXCoord().toBigInteger(),
-                spec.getG().getAffineYCoord().toBigInteger()),
+            EC5Util.convertPoint(spec.getG()),
             spec.getN(), spec.getH());
     }
 
@@ -232,11 +246,9 @@
         ASN1Encodable params;
         SubjectPublicKeyInfo info;
 
-        if (gostParams != null)
-        {
-            params = gostParams;
-        }
-        else
+        params = getGostParams();
+
+        if (params == null)
         {
             if (ecSpec instanceof ECNamedCurveSpec)
             {
@@ -311,7 +323,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(ecPublicKey.getQ().getAffineXCoord().toBigInteger(), ecPublicKey.getQ().getAffineYCoord().toBigInteger());
+        return EC5Util.convertPoint(ecPublicKey.getQ());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -341,15 +353,7 @@
 
     public String toString()
     {
-        StringBuffer buf = new StringBuffer();
-        String nl = Strings.lineSeparator();
-        org.bouncycastle.math.ec.ECPoint q = ecPublicKey.getQ();
-
-        buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
-
-        return buf.toString();
+        return ECUtil.publicKeyToString(algorithm, ecPublicKey.getQ(), engineGetSpec());
     }
 
     public void setPointFormat(String style)
@@ -394,8 +398,15 @@
         out.writeObject(this.getEncoded());
     }
 
-    public GOST3410PublicKeyAlgParameters getGostParams()
+    ASN1Encodable getGostParams()
     {
+        if (gostParams == null && ecSpec instanceof ECNamedCurveSpec)
+        {
+            this.gostParams = new GOST3410PublicKeyAlgParameters(
+                ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet);
+        }
+
         return gostParams;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyAgreementSpi.java
new file mode 100644
index 0000000..3296c24
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyAgreementSpi.java
@@ -0,0 +1,160 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.agreement.ECVKOAgreement;
+import org.bouncycastle.crypto.digests.GOST3411Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+
+public class KeyAgreementSpi
+    extends BaseAgreementSpi
+{
+    private static final X9IntegerConverter converter = new X9IntegerConverter();
+
+    private String                 kaAlgorithm;
+
+    private ECDomainParameters     parameters;
+    private ECVKOAgreement agreement;
+
+    private byte[]             result;
+
+    protected KeyAgreementSpi(
+        String kaAlgorithm,
+        ECVKOAgreement agreement,
+        DerivationFunction kdf)
+    {
+        super(kaAlgorithm, kdf);
+
+        this.kaAlgorithm = kaAlgorithm;
+        this.agreement = agreement;
+    }
+
+    protected Key engineDoPhase(
+        Key     key,
+        boolean lastPhase) 
+        throws InvalidKeyException, IllegalStateException
+    {
+        if (parameters == null)
+        {
+            throw new IllegalStateException(kaAlgorithm + " not initialised.");
+        }
+
+        if (!lastPhase)
+        {
+            throw new IllegalStateException(kaAlgorithm + " can only be between two parties.");
+        }
+
+        CipherParameters pubKey;
+        {
+            if (!(key instanceof PublicKey))
+            {
+                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
+                    + getSimpleName(ECPublicKey.class) + " for doPhase");
+            }
+
+            pubKey = generatePublicKeyParameter((PublicKey)key);
+        }
+
+        try
+        {
+            result = agreement.calculateAgreement(pubKey);
+        }
+        catch (final Exception e)
+        {
+            throw new InvalidKeyException("calculation failed: " + e.getMessage())
+            {
+                public Throwable getCause()
+                            {
+                                return e;
+                            }
+            };
+        }
+
+        return null;
+    }
+
+    protected void engineInit(
+        Key                     key,
+        AlgorithmParameterSpec  params,
+        SecureRandom            random) 
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        if (params != null && !(params instanceof UserKeyingMaterialSpec))
+        {
+            throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
+        }
+
+        initFromKey(key, params);
+    }
+
+    protected void engineInit(
+        Key             key,
+        SecureRandom    random) 
+        throws InvalidKeyException
+    {
+        initFromKey(key, null);
+    }
+
+    private void initFromKey(Key key, AlgorithmParameterSpec parameterSpec)
+        throws InvalidKeyException
+    {
+        {
+            if (!(key instanceof PrivateKey))
+            {
+                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
+                    + getSimpleName(ECPrivateKey.class) + " for initialisation");
+            }
+
+            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            this.parameters = privKey.getParameters();
+            ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
+            agreement.init(new ParametersWithUKM(privKey, ukmParameters));
+        }
+    }
+
+    private static String getSimpleName(Class clazz)
+    {
+        String fullName = clazz.getName();
+
+        return fullName.substring(fullName.lastIndexOf('.') + 1);
+    }
+
+    protected byte[] calcSecret()
+    {
+        return result;
+    }
+
+    static AsymmetricKeyParameter generatePublicKeyParameter(
+            PublicKey key)
+        throws InvalidKeyException
+    {
+        return (key instanceof BCECPublicKey) ? ((BCECGOST3410PublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
+    }
+
+    public static class ECVKO
+        extends KeyAgreementSpi
+    {
+        public ECVKO()
+        {
+            super("ECGOST3410", new ECVKOAgreement(new GOST3411Digest()), null);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java
index 3159cba..09a232a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java
@@ -14,8 +14,8 @@
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
@@ -143,6 +143,14 @@
         {
             return new BCECGOST3410PrivateKey(keyInfo);
         }
+        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001DH))
+        {
+            return new BCECGOST3410PrivateKey(keyInfo);
+        }
+        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH))
+        {
+            return new BCECGOST3410PrivateKey(keyInfo);
+        }
         else
         {
             throw new IOException("algorithm identifier " + algOid + " in key not recognised");
@@ -158,6 +166,14 @@
         {
             return new BCECGOST3410PublicKey(keyInfo);
         }
+        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001DH))
+        {
+            return new BCECGOST3410PublicKey(keyInfo);
+        }
+        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH))
+        {
+            return new BCECGOST3410PublicKey(keyInfo);
+        }
         else
         {
             throw new IOException("algorithm identifier " + algOid + " in key not recognised");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java
index e8e9677..07e3419 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java
@@ -12,10 +12,13 @@
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.spec.GOST3410ParameterSpec;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
@@ -69,7 +72,13 @@
         SecureRandom random)
         throws InvalidAlgorithmParameterException
     {
-        if (params instanceof ECParameterSpec)
+        if (params instanceof GOST3410ParameterSpec)
+        {
+            GOST3410ParameterSpec gostParams = (GOST3410ParameterSpec)params;
+
+            init(gostParams, random);
+        }
+        else if (params instanceof ECParameterSpec)
         {
             ECParameterSpec p = (ECParameterSpec)params;
             this.ecParams = params;
@@ -105,29 +114,7 @@
                 curveName = ((ECNamedCurveGenParameterSpec)params).getName();
             }
 
-            ECDomainParameters ecP = ECGOST3410NamedCurves.getByName(curveName);
-            if (ecP == null)
-            {
-                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
-            }
-
-            this.ecParams = new ECNamedCurveSpec(
-                curveName,
-                ecP.getCurve(),
-                ecP.getG(),
-                ecP.getN(),
-                ecP.getH(),
-                ecP.getSeed());
-
-            java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
-
-            ECCurve curve = EC5Util.convertCurve(p.getCurve());
-            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false);
-
-            param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
-
-            engine.init(param);
-            initialised = true;
+            init(new GOST3410ParameterSpec(curveName), random);
         }
         else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() != null)
         {
@@ -149,6 +136,32 @@
         }
     }
 
+    private void init(GOST3410ParameterSpec gostParams, SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        ECDomainParameters ecP = ECGOST3410NamedCurves.getByOID(gostParams.getPublicKeyParamSet());
+        if (ecP == null)
+        {
+            throw new InvalidAlgorithmParameterException("unknown curve: " + gostParams.getPublicKeyParamSet());
+        }
+
+        this.ecParams = new ECNamedCurveSpec(
+            ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+            ecP.getCurve(),
+            ecP.getG(),
+            ecP.getN(),
+            ecP.getH(),
+            ecP.getSeed());
+        
+        param = new ECKeyGenerationParameters(
+            new ECGOST3410Parameters(
+                new ECNamedDomainParameters(gostParams.getPublicKeyParamSet(), ecP),
+                gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet()), random);
+
+        engine.init(param);
+        initialised = true;
+    }
+    
     public KeyPair generateKeyPair()
     {
         if (!initialised)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java
index 8c3d911..3bda40f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java
@@ -11,7 +11,7 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.GOST3411Digest;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
@@ -29,7 +29,7 @@
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
     private Digest                  digest;
-    private DSA                     signer;
+    private DSAExt                  signer;
 
     public SignatureSpi()
     {
@@ -192,7 +192,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(
         String  param,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PrivateKey.java
new file mode 100644
index 0000000..5919257
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PrivateKey.java
@@ -0,0 +1,532 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.EllipticCurve;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
+import org.bouncycastle.jce.interfaces.ECPointEncoder;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.math.ec.ECCurve;
+
+/**
+ * Represent two kind of GOST34.10 2012 PrivateKeys: with 256 and 512 size
+ */
+public class BCECGOST3410_2012PrivateKey
+    implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
+{
+    static final long serialVersionUID = 7245981689601667138L;
+
+    private String algorithm = "ECGOST3410-2012";
+    private boolean withCompression;
+
+    private transient GOST3410PublicKeyAlgParameters gostParams;
+    private transient BigInteger d;
+    private transient ECParameterSpec ecSpec;
+    private transient DERBitString publicKey;
+    private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+    protected BCECGOST3410_2012PrivateKey()
+    {
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        ECPrivateKey key)
+    {
+        this.d = key.getS();
+        this.algorithm = key.getAlgorithm();
+        this.ecSpec = key.getParams();
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        org.bouncycastle.jce.spec.ECPrivateKeySpec spec)
+    {
+        this.d = spec.getD();
+
+        if (spec.getParams() != null) // can be null if implicitlyCA
+        {
+            ECCurve curve = spec.getParams().getCurve();
+            EllipticCurve ellipticCurve;
+
+            ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
+
+            this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
+        }
+        else
+        {
+            this.ecSpec = null;
+        }
+    }
+
+
+    public BCECGOST3410_2012PrivateKey(
+        ECPrivateKeySpec spec)
+    {
+        this.d = spec.getS();
+        this.ecSpec = spec.getParams();
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        BCECGOST3410_2012PrivateKey key)
+    {
+        this.d = key.d;
+        this.ecSpec = key.ecSpec;
+        this.withCompression = key.withCompression;
+        this.attrCarrier = key.attrCarrier;
+        this.publicKey = key.publicKey;
+        this.gostParams = key.gostParams;
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        String algorithm,
+        ECPrivateKeyParameters params,
+        BCECGOST3410_2012PublicKey pubKey,
+        ECParameterSpec spec)
+    {
+        ECDomainParameters dp = params.getParameters();
+
+        this.algorithm = algorithm;
+        this.d = params.getD();
+
+        if (spec == null)
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+            this.ecSpec = new ECParameterSpec(
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
+        }
+        else
+        {
+            this.ecSpec = spec;
+        }
+
+        this.gostParams = pubKey.getGostParams();
+
+        publicKey = getPublicKeyDetails(pubKey);
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        String algorithm,
+        ECPrivateKeyParameters params,
+        BCECGOST3410_2012PublicKey pubKey,
+        org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        ECDomainParameters dp = params.getParameters();
+
+        this.algorithm = algorithm;
+        this.d = params.getD();
+
+        if (spec == null)
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+            this.ecSpec = new ECParameterSpec(
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
+        }
+        else
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
+
+            this.ecSpec = new ECParameterSpec(
+                ellipticCurve,
+                EC5Util.convertPoint(spec.getG()),
+                spec.getN(),
+                spec.getH().intValue());
+        }
+
+        this.gostParams = pubKey.getGostParams();
+
+        publicKey = getPublicKeyDetails(pubKey);
+    }
+
+    public BCECGOST3410_2012PrivateKey(
+        String algorithm,
+        ECPrivateKeyParameters params)
+    {
+        this.algorithm = algorithm;
+        this.d = params.getD();
+        this.ecSpec = null;
+    }
+
+    BCECGOST3410_2012PrivateKey(
+        PrivateKeyInfo info)
+        throws IOException
+    {
+        populateFromPrivKeyInfo(info);
+    }
+
+    private void populateFromPrivKeyInfo(PrivateKeyInfo info)
+        throws IOException
+    {
+        ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+
+        if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
+        {
+            gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+
+            ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+
+            ECCurve curve = spec.getCurve();
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
+
+            ecSpec = new ECNamedCurveSpec(
+                ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+                ellipticCurve,
+                EC5Util.convertPoint(spec.getG()),
+                spec.getN(), spec.getH());
+
+            ASN1Encodable privKey = info.parsePrivateKey();
+
+            if (privKey instanceof ASN1Integer)
+            {
+                this.d = ASN1Integer.getInstance(privKey).getPositiveValue();
+            }
+            else
+            {
+                byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets();
+                byte[] dVal = new byte[encVal.length];
+
+                for (int i = 0; i != encVal.length; i++)
+                {
+                    dVal[i] = encVal[encVal.length - 1 - i];
+                }
+
+                this.d = new BigInteger(1, dVal);
+            }
+        }
+        else
+        {
+            // for backwards compatibility
+            X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+
+            if (params.isNamedCurve())
+            {
+                ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
+                X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+
+                if (ecP == null) // GOST Curve
+                {
+                    ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
+                    EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
+
+                    ecSpec = new ECNamedCurveSpec(
+                        ECGOST3410NamedCurves.getName(oid),
+                        ellipticCurve,
+                        EC5Util.convertPoint(gParam.getG()),
+                        gParam.getN(),
+                        gParam.getH());
+                }
+                else
+                {
+                    EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+                    ecSpec = new ECNamedCurveSpec(
+                        ECUtil.getCurveName(oid),
+                        ellipticCurve,
+                        EC5Util.convertPoint(ecP.getG()),
+                        ecP.getN(),
+                        ecP.getH());
+                }
+            }
+            else if (params.isImplicitlyCA())
+            {
+                ecSpec = null;
+            }
+            else
+            {
+                X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+                this.ecSpec = new ECParameterSpec(
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH().intValue());
+            }
+
+            ASN1Encodable privKey = info.parsePrivateKey();
+            if (privKey instanceof ASN1Integer)
+            {
+                ASN1Integer derD = ASN1Integer.getInstance(privKey);
+
+                this.d = derD.getValue();
+            }
+            else
+            {
+                org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
+
+                this.d = ec.getKey();
+                this.publicKey = ec.getPublicKey();
+            }
+        }
+    }
+
+    public String getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    /**
+     * return the encoding format we produce in getEncoded().
+     *
+     * @return the string "PKCS#8"
+     */
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    /**
+     * Return a PKCS8 representation of the key. The sequence returned
+     * represents a full PrivateKeyInfo object.
+     *
+     * @return a PKCS8 representation of the key.
+     */
+    public byte[] getEncoded()
+    {
+        boolean is512 = d.bitLength() > 256;
+        ASN1ObjectIdentifier identifier = (is512)?
+                RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512 :
+                RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+        int size = (is512) ? 64 : 32;
+
+        if (gostParams != null)
+        {
+            byte[] encKey = new byte[size];
+
+            extractBytes(encKey, size,0, this.getS());
+
+            try
+            {
+                PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(identifier, gostParams), new DEROctetString(encKey));
+
+                return info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
+        }
+        else
+        {
+            X962Parameters params;
+            int orderBitLength;
+
+            if (ecSpec instanceof ECNamedCurveSpec)
+            {
+                ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+                if (curveOid == null)  // guess it's the OID
+                {
+                    curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+                }
+                params = new X962Parameters(curveOid);
+                orderBitLength = ECUtil.getOrderBitLength(BouncyCastleProvider.CONFIGURATION, ecSpec.getOrder(), this.getS());
+            }
+            else if (ecSpec == null)
+            {
+                params = new X962Parameters(DERNull.INSTANCE);
+                orderBitLength = ECUtil.getOrderBitLength(BouncyCastleProvider.CONFIGURATION, null, this.getS());
+            }
+            else
+            {
+                ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+
+                X9ECParameters ecP = new X9ECParameters(
+                    curve,
+                    EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+                    ecSpec.getOrder(),
+                    BigInteger.valueOf(ecSpec.getCofactor()),
+                    ecSpec.getCurve().getSeed());
+
+                params = new X962Parameters(ecP);
+                orderBitLength = ECUtil.getOrderBitLength(BouncyCastleProvider.CONFIGURATION, ecSpec.getOrder(), this.getS());
+            }
+
+            PrivateKeyInfo info;
+            org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
+
+            if (publicKey != null)
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+            }
+            else
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            }
+
+            try
+            {
+                info = new PrivateKeyInfo(new AlgorithmIdentifier(identifier, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+
+                return info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
+        }
+    }
+
+    private void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI)
+    {
+        byte[] val = bI.toByteArray();
+        if (val.length < size)
+        {
+            byte[] tmp = new byte[size];
+            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+            val = tmp;
+        }
+
+        for (int i = 0; i != size; i++)
+        {
+            encKey[offSet + i] = val[val.length - 1 - i];
+        }
+    }
+
+    public ECParameterSpec getParams()
+    {
+        return ecSpec;
+    }
+
+    public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
+    {
+        if (ecSpec == null)
+        {
+            return null;
+        }
+
+        return EC5Util.convertSpec(ecSpec, withCompression);
+    }
+
+    org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
+    {
+        if (ecSpec != null)
+        {
+            return EC5Util.convertSpec(ecSpec, withCompression);
+        }
+
+        return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+    }
+
+    public BigInteger getS()
+    {
+        return d;
+    }
+
+    public BigInteger getD()
+    {
+        return d;
+    }
+
+    public void setBagAttribute(
+        ASN1ObjectIdentifier oid,
+        ASN1Encodable attribute)
+    {
+        attrCarrier.setBagAttribute(oid, attribute);
+    }
+
+    public ASN1Encodable getBagAttribute(
+        ASN1ObjectIdentifier oid)
+    {
+        return attrCarrier.getBagAttribute(oid);
+    }
+
+    public Enumeration getBagAttributeKeys()
+    {
+        return attrCarrier.getBagAttributeKeys();
+    }
+
+    public void setPointFormat(String style)
+    {
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+    }
+
+    public boolean equals(Object o)
+    {
+        if (!(o instanceof BCECGOST3410_2012PrivateKey))
+        {
+            return false;
+        }
+
+        BCECGOST3410_2012PrivateKey other = (BCECGOST3410_2012PrivateKey)o;
+
+        return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
+    }
+
+    public int hashCode()
+    {
+        return getD().hashCode() ^ engineGetSpec().hashCode();
+    }
+
+    public String toString()
+    {
+        return ECUtil.privateKeyToString(algorithm, d, engineGetSpec());
+    }
+
+    private DERBitString getPublicKeyDetails(BCECGOST3410_2012PublicKey pub)
+    {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(pub.getEncoded());
+
+        return info.getPublicKeyData();
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
+
+        this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PublicKey.java
new file mode 100644
index 0000000..af89a5d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/BCECGOST3410_2012PublicKey.java
@@ -0,0 +1,456 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
+import org.bouncycastle.jce.interfaces.ECPointEncoder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.math.ec.ECCurve;
+
+/**
+ * Represent two kind of GOST34.10 2012 PublicKeys: with 256 and 512 size
+ */
+public class BCECGOST3410_2012PublicKey
+    implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
+{
+    static final long serialVersionUID = 7026240464295649314L;
+
+    private String algorithm = "ECGOST3410-2012";
+    private boolean withCompression;
+
+    private transient ECPublicKeyParameters ecPublicKey;
+    private transient ECParameterSpec ecSpec;
+    private transient GOST3410PublicKeyAlgParameters gostParams;
+
+    public BCECGOST3410_2012PublicKey(
+        BCECGOST3410_2012PublicKey key)
+    {
+        this.ecPublicKey = key.ecPublicKey;
+        this.ecSpec = key.ecSpec;
+        this.withCompression = key.withCompression;
+        this.gostParams = key.gostParams;
+    }
+
+    public BCECGOST3410_2012PublicKey(
+        ECPublicKeySpec spec)
+    {
+        this.ecSpec = spec.getParams();
+        this.ecPublicKey = new ECPublicKeyParameters(EC5Util.convertPoint(ecSpec, spec.getW(), false), EC5Util.getDomainParameters(null, spec.getParams()));
+    }
+
+    public BCECGOST3410_2012PublicKey(
+        org.bouncycastle.jce.spec.ECPublicKeySpec spec,
+        ProviderConfiguration configuration)
+    {
+        if (spec.getParams() != null) // can be null if implictlyCa
+        {
+            ECCurve curve = spec.getParams().getCurve();
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
+
+            // this may seem a little long-winded but it's how we pick up the custom curve.
+            this.ecPublicKey = new ECPublicKeyParameters(
+                spec.getQ(), ECUtil.getDomainParameters(configuration, spec.getParams()));
+            this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
+        }
+        else
+        {
+            org.bouncycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa();
+
+            this.ecPublicKey = new ECPublicKeyParameters(s.getCurve().createPoint(spec.getQ().getAffineXCoord().toBigInteger(), spec.getQ().getAffineYCoord().toBigInteger()), EC5Util.getDomainParameters(configuration, (ECParameterSpec)null));
+            this.ecSpec = null;
+        }
+    }
+
+    public BCECGOST3410_2012PublicKey(
+        String algorithm,
+        ECPublicKeyParameters params,
+        ECParameterSpec spec)
+    {
+        ECDomainParameters dp = params.getParameters();
+
+        this.algorithm = algorithm;
+        this.ecPublicKey = params;
+
+        if (dp instanceof ECGOST3410Parameters)
+        {
+            ECGOST3410Parameters p = (ECGOST3410Parameters)dp;
+
+            this.gostParams = new GOST3410PublicKeyAlgParameters(p.getPublicKeyParamSet(),
+                                                p.getDigestParamSet(), p.getEncryptionParamSet());
+        }
+
+        if (spec == null)
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+            this.ecSpec = createSpec(ellipticCurve, dp);
+        }
+        else
+        {
+            this.ecSpec = spec;
+        }
+
+
+    }
+
+    public BCECGOST3410_2012PublicKey(
+        String algorithm,
+        ECPublicKeyParameters params,
+        org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        ECDomainParameters dp = params.getParameters();
+
+        this.algorithm = algorithm;
+        this.ecPublicKey = params;
+
+        if (spec == null)
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+            this.ecSpec = createSpec(ellipticCurve, dp);
+        }
+        else
+        {
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
+
+            this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec);
+        }
+    }
+
+    /*
+     * called for implicitCA
+     */
+    public BCECGOST3410_2012PublicKey(
+        String algorithm,
+        ECPublicKeyParameters params)
+    {
+        this.algorithm = algorithm;
+        this.ecPublicKey = params;
+        this.ecSpec = null;
+    }
+
+    private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
+    {
+        return new ECParameterSpec(
+            ellipticCurve,
+            EC5Util.convertPoint(dp.getG()),
+            dp.getN(),
+            dp.getH().intValue());
+    }
+
+    public BCECGOST3410_2012PublicKey(
+        ECPublicKey key)
+    {
+        this.algorithm = key.getAlgorithm();
+        this.ecSpec = key.getParams();
+        this.ecPublicKey = new ECPublicKeyParameters(EC5Util.convertPoint(this.ecSpec, key.getW(), false), EC5Util.getDomainParameters(null, key.getParams()));
+    }
+
+    BCECGOST3410_2012PublicKey(
+        SubjectPublicKeyInfo info)
+    {
+        populateFromPubKeyInfo(info);
+    }
+
+    private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
+    {
+        ASN1ObjectIdentifier algOid = info.getAlgorithm().getAlgorithm();
+        DERBitString bits = info.getPublicKeyData();
+        ASN1OctetString key;
+        this.algorithm = "ECGOST3410-2012";
+
+        try
+        {
+            key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
+        }
+        catch (IOException ex)
+        {
+            throw new IllegalArgumentException("error recovering public key");
+        }
+
+        byte[] keyEnc = key.getOctets();
+
+        int fieldSize = 32;
+        if (algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512))
+        {
+            fieldSize = 64;
+        }
+
+        int keySize = 2 * fieldSize;
+
+        byte[] x9Encoding = new byte[1 + keySize];
+        x9Encoding[0] = 0x04;
+        for (int i = 1; i <= fieldSize; ++i)
+        {
+            x9Encoding[i            ] = keyEnc[fieldSize - i];
+            x9Encoding[i + fieldSize] = keyEnc[keySize - i];
+        }
+
+        this.gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters());
+
+        ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+
+        ECCurve curve = spec.getCurve();
+        EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
+
+        this.ecPublicKey = new ECPublicKeyParameters(curve.decodePoint(x9Encoding), ECUtil.getDomainParameters(null, spec));
+
+        this.ecSpec = new ECNamedCurveSpec(
+            ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+            ellipticCurve,
+            EC5Util.convertPoint(spec.getG()),
+            spec.getN(), spec.getH());
+    }
+
+    public String getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    public byte[] getEncoded()
+    {
+        ASN1Encodable params;
+        SubjectPublicKeyInfo info;
+
+//        ecPublicKey.getQ().
+        BigInteger bX = this.ecPublicKey.getQ().getAffineXCoord().toBigInteger();
+        BigInteger bY = this.ecPublicKey.getQ().getAffineYCoord().toBigInteger();
+
+        // need to detect key size
+        boolean is512 = (bX.bitLength() > 256);
+
+        params = getGostParams();
+
+        if (params == null)
+        {
+            if (ecSpec instanceof ECNamedCurveSpec)
+            {
+                if (is512)
+                {
+                    params = new GOST3410PublicKeyAlgParameters(
+                        ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                        RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+                }
+                else
+                {
+                    params = new GOST3410PublicKeyAlgParameters(
+                        ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                        RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+                }
+            }
+            else
+            {   // strictly speaking this may not be applicable...
+                ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+
+                X9ECParameters ecP = new X9ECParameters(
+                    curve,
+                    EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+                    ecSpec.getOrder(),
+                    BigInteger.valueOf(ecSpec.getCofactor()),
+                    ecSpec.getCurve().getSeed());
+
+                params = new X962Parameters(ecP);
+            }
+        }
+
+        int encKeySize;
+        int offset;
+        ASN1ObjectIdentifier algIdentifier;
+        if (is512)
+        {
+            encKeySize = 128;
+            offset = 64;
+            algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512;
+        }
+        else
+        {
+            encKeySize = 64;
+            offset = 32;
+            algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+        }
+
+        byte[] encKey = new byte[encKeySize];
+
+        extractBytes(encKey, encKeySize / 2, 0, bX);
+        extractBytes(encKey, encKeySize / 2, offset, bY);
+
+        try
+        {
+            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(algIdentifier, params),
+                new DEROctetString(encKey));
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+
+        return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
+    }
+
+    private void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI)
+    {
+        byte[] val = bI.toByteArray();
+        if (val.length < size)
+        {
+            byte[] tmp = new byte[size];
+            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+            val = tmp;
+        }
+
+        for (int i = 0; i != size; i++)
+        {
+            encKey[offSet + i] = val[val.length - 1 - i];
+        }
+    }
+
+    public ECParameterSpec getParams()
+    {
+        return ecSpec;
+    }
+
+    public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
+    {
+        if (ecSpec == null)     // implictlyCA
+        {
+            return null;
+        }
+
+        return EC5Util.convertSpec(ecSpec, withCompression);
+    }
+
+    public ECPoint getW()
+    {
+        return EC5Util.convertPoint(ecPublicKey.getQ());
+    }
+
+    public org.bouncycastle.math.ec.ECPoint getQ()
+    {
+        if (ecSpec == null)
+        {
+            return ecPublicKey.getQ().getDetachedPoint();
+        }
+
+        return ecPublicKey.getQ();
+    }
+
+    ECPublicKeyParameters engineGetKeyParameters()
+    {
+        return ecPublicKey;
+    }
+
+    org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
+    {
+        if (ecSpec != null)
+        {
+            return EC5Util.convertSpec(ecSpec, withCompression);
+        }
+
+        return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+    }
+
+    public String toString()
+    {
+        return ECUtil.publicKeyToString(algorithm, ecPublicKey.getQ(), engineGetSpec());
+    }
+
+    public void setPointFormat(String style)
+    {
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+    }
+
+    public boolean equals(Object o)
+    {
+        if (!(o instanceof BCECGOST3410_2012PublicKey))
+        {
+            return false;
+        }
+
+        BCECGOST3410_2012PublicKey other = (BCECGOST3410_2012PublicKey)o;
+
+        return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
+    }
+
+    public int hashCode()
+    {
+        return ecPublicKey.getQ().hashCode() ^ engineGetSpec().hashCode();
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+
+    public GOST3410PublicKeyAlgParameters getGostParams()
+    {
+        if (gostParams == null && ecSpec instanceof ECNamedCurveSpec)
+        {
+            BigInteger bX = this.ecPublicKey.getQ().getAffineXCoord().toBigInteger();
+
+            // need to detect key size
+            boolean is512 = (bX.bitLength() > 256);
+            if (is512)
+            {
+                this.gostParams = new GOST3410PublicKeyAlgParameters(
+                    ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                    RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+            }
+            else
+            {
+                this.gostParams = new GOST3410PublicKeyAlgParameters(
+                    ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                    RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+            }
+        }
+        return gostParams;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi256.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi256.java
new file mode 100644
index 0000000..bfa73e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi256.java
@@ -0,0 +1,228 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.DSAExt;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECGOST3410_2012Signer;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jce.interfaces.ECKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * Signature for GOST34.10 2012 256. Algorithm is the same as for GOST34.10 2001
+ */
+public class ECGOST2012SignatureSpi256
+    extends java.security.SignatureSpi
+    implements PKCSObjectIdentifiers, X509ObjectIdentifiers
+{
+    private Digest                  digest;
+    private DSAExt                  signer;
+    private int size = 64;
+    private int halfSize = size/2;
+
+    public ECGOST2012SignatureSpi256()
+    {
+        this.digest = new GOST3411_2012_256Digest();
+        this.signer = new ECGOST3410_2012Signer();
+    }
+
+    protected void engineInitVerify(
+        PublicKey   publicKey)
+        throws InvalidKeyException
+    {
+        ECKeyParameters    param;
+
+        if (publicKey instanceof ECPublicKey)
+        {
+            param = (ECKeyParameters)generatePublicKeyParameter(publicKey);
+        }
+        else
+        {
+            try
+            {
+                byte[]  bytes = publicKey.getEncoded();
+
+                publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes));
+
+                param = (ECKeyParameters)ECUtil.generatePublicKeyParameter(publicKey);
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeyException("cannot recognise key type in ECGOST-2012-256 signer");
+            }
+        }
+
+        if (param.getParameters().getN().bitLength() > 256)
+        {
+            throw new InvalidKeyException("key out of range for ECGOST-2012-256");
+        }
+
+        digest.reset();
+        signer.init(false, param);
+    }
+
+    protected void engineInitSign(
+        PrivateKey  privateKey)
+        throws InvalidKeyException
+    {
+        ECKeyParameters param;
+
+        if (privateKey instanceof ECKey)
+        {
+            param = (ECKeyParameters)ECUtil.generatePrivateKeyParameter(privateKey);
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot recognise key type in ECGOST-2012-256 signer");
+        }
+
+        if (param.getParameters().getN().bitLength() > 256)
+        {
+            throw new InvalidKeyException("key out of range for ECGOST-2012-256");
+        }
+        
+        digest.reset();
+
+        if (appRandom != null)
+        {
+            signer.init(true, new ParametersWithRandom(param, appRandom));
+        }
+        else
+        {
+            signer.init(true, param);
+        }
+    }
+
+    protected void engineUpdate(
+        byte    b)
+        throws SignatureException
+    {
+        digest.update(b);
+    }
+
+    protected void engineUpdate(
+        byte[]  b,
+        int     off,
+        int     len) 
+        throws SignatureException
+    {
+        digest.update(b, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        byte[]  hash = new byte[digest.getDigestSize()];
+
+        digest.doFinal(hash, 0);
+
+        try
+        {
+            byte[]          sigBytes = new byte[size];
+            BigInteger[]    sig = signer.generateSignature(hash);
+            byte[]          r = sig[0].toByteArray();
+            byte[]          s = sig[1].toByteArray();
+
+            if (s[0] != 0)
+            {
+                System.arraycopy(s, 0, sigBytes, halfSize - s.length, s.length);
+            }
+            else
+            {
+                System.arraycopy(s, 1, sigBytes, halfSize - (s.length - 1), s.length - 1);
+            }
+            
+            if (r[0] != 0)
+            {
+                System.arraycopy(r, 0, sigBytes, size - r.length, r.length);
+            }
+            else
+            {
+                System.arraycopy(r, 1, sigBytes, size - (r.length - 1), r.length - 1);
+            }
+
+            return sigBytes;
+        }
+        catch (Exception e)
+        {
+            throw new SignatureException(e.toString());
+        }
+    }
+    
+    protected boolean engineVerify(
+        byte[]  sigBytes) 
+        throws SignatureException
+    {
+        byte[]  hash = new byte[digest.getDigestSize()];
+
+        digest.doFinal(hash, 0);
+
+        BigInteger[]    sig;
+
+        try
+        {
+            byte[] r = new byte[halfSize];
+            byte[] s = new byte[halfSize];
+
+            System.arraycopy(sigBytes, 0, s, 0, halfSize);
+
+            System.arraycopy(sigBytes, halfSize, r, 0, halfSize);
+            
+            sig = new BigInteger[2];
+            sig[0] = new BigInteger(1, r);
+            sig[1] = new BigInteger(1, s);
+        }
+        catch (Exception e)
+        {
+            throw new SignatureException("error decoding signature bytes.");
+        }
+
+        return signer.verifySignature(hash, sig[0], sig[1]);
+    }
+
+    protected void engineSetParameter(
+        AlgorithmParameterSpec params)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated replaced with "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"
+     */
+    protected void engineSetParameter(
+        String  param,
+        Object  value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated
+     */
+    protected Object engineGetParameter(
+        String      param)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    static AsymmetricKeyParameter generatePublicKeyParameter(
+            PublicKey key)
+    throws InvalidKeyException
+    {
+        return (key instanceof BCECGOST3410_2012PublicKey) ? ((BCECGOST3410_2012PublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi512.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi512.java
new file mode 100644
index 0000000..4ea3e92
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/ECGOST2012SignatureSpi512.java
@@ -0,0 +1,230 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.DSAExt;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECGOST3410_2012Signer;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jce.interfaces.ECKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * Signature for GOST34.10 2012 512. Algorithm is the same as for GOST34.10 2001
+ */
+public class ECGOST2012SignatureSpi512
+    extends java.security.SignatureSpi
+    implements PKCSObjectIdentifiers, X509ObjectIdentifiers
+{
+
+    private Digest digest;
+    private DSAExt signer;
+    private int size = 128;
+    private int halfSize = 64;
+
+
+    public ECGOST2012SignatureSpi512()
+    {
+        this.digest = new GOST3411_2012_512Digest();
+        this.signer = new ECGOST3410_2012Signer();
+    }
+
+    protected void engineInitVerify(
+        PublicKey publicKey)
+        throws InvalidKeyException
+    {
+        ECKeyParameters param;
+
+        if (publicKey instanceof ECPublicKey)
+        {
+            param = (ECKeyParameters)generatePublicKeyParameter(publicKey);
+        }
+        else
+        {
+            try
+            {
+                byte[] bytes = publicKey.getEncoded();
+
+                publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes));
+
+                param = (ECKeyParameters)ECUtil.generatePublicKeyParameter(publicKey);
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeyException("cannot recognise key type in ECGOST-2012-512 signer");
+            }
+        }
+
+        if (param.getParameters().getN().bitLength() < 505)
+        {
+            throw new InvalidKeyException("key too weak for ECGOST-2012-512");
+        }
+
+        digest.reset();
+        signer.init(false, param);
+    }
+
+    protected void engineInitSign(
+        PrivateKey privateKey)
+        throws InvalidKeyException
+    {
+        ECKeyParameters param;
+
+        if (privateKey instanceof ECKey)
+        {
+            param = (ECKeyParameters)ECUtil.generatePrivateKeyParameter(privateKey);
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot recognise key type in ECGOST-2012-512 signer");
+        }
+
+        if (param.getParameters().getN().bitLength() < 505)
+        {
+            throw new InvalidKeyException("key too weak for ECGOST-2012-512");
+        }
+
+        digest.reset();
+
+        if (appRandom != null)
+        {
+            signer.init(true, new ParametersWithRandom(param, appRandom));
+        }
+        else
+        {
+            signer.init(true, param);
+        }
+    }
+
+    protected void engineUpdate(
+        byte b)
+        throws SignatureException
+    {
+        digest.update(b);
+    }
+
+    protected void engineUpdate(
+        byte[] b,
+        int off,
+        int len)
+        throws SignatureException
+    {
+        digest.update(b, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        byte[] hash = new byte[digest.getDigestSize()];
+
+        digest.doFinal(hash, 0);
+
+        try
+        {
+            byte[] sigBytes = new byte[size];
+            BigInteger[] sig = signer.generateSignature(hash);
+            byte[] r = sig[0].toByteArray();
+            byte[] s = sig[1].toByteArray();
+
+            if (s[0] != 0)
+            {
+                System.arraycopy(s, 0, sigBytes, halfSize - s.length, s.length);
+            }
+            else
+            {
+                System.arraycopy(s, 1, sigBytes, halfSize - (s.length - 1), s.length - 1);
+            }
+
+            if (r[0] != 0)
+            {
+                System.arraycopy(r, 0, sigBytes, size - r.length, r.length);
+            }
+            else
+            {
+                System.arraycopy(r, 1, sigBytes, size - (r.length - 1), r.length - 1);
+            }
+
+            return sigBytes;
+        }
+        catch (Exception e)
+        {
+            throw new SignatureException(e.toString());
+        }
+    }
+
+    protected boolean engineVerify(
+        byte[] sigBytes)
+        throws SignatureException
+    {
+        byte[] hash = new byte[digest.getDigestSize()];
+
+        digest.doFinal(hash, 0);
+
+        BigInteger[] sig;
+
+        try
+        {
+            byte[] r = new byte[halfSize];
+            byte[] s = new byte[halfSize];
+
+            System.arraycopy(sigBytes, 0, s, 0, halfSize);
+
+            System.arraycopy(sigBytes, halfSize, r, 0, halfSize);
+
+            sig = new BigInteger[2];
+            sig[0] = new BigInteger(1, r);
+            sig[1] = new BigInteger(1, s);
+        }
+        catch (Exception e)
+        {
+            throw new SignatureException("error decoding signature bytes.");
+        }
+
+        return signer.verifySignature(hash, sig[0], sig[1]);
+    }
+
+    protected void engineSetParameter(
+        AlgorithmParameterSpec params)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
+     */
+    protected void engineSetParameter(
+        String param,
+        Object value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated
+     */
+    protected Object engineGetParameter(
+        String param)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    static AsymmetricKeyParameter generatePublicKeyParameter(
+        PublicKey key)
+        throws InvalidKeyException
+    {
+        return (key instanceof BCECGOST3410_2012PublicKey) ? ((BCECGOST3410_2012PublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyAgreementSpi.java
new file mode 100644
index 0000000..6091e9d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyAgreementSpi.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.agreement.ECVKOAgreement;
+import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest;
+import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+
+public class KeyAgreementSpi
+    extends BaseAgreementSpi
+{
+    private static final X9IntegerConverter converter = new X9IntegerConverter();
+
+    private String                 kaAlgorithm;
+
+    private ECDomainParameters     parameters;
+    private ECVKOAgreement agreement;
+
+    private byte[]             result;
+
+    protected KeyAgreementSpi(
+        String kaAlgorithm,
+        ECVKOAgreement agreement,
+        DerivationFunction kdf)
+    {
+        super(kaAlgorithm, kdf);
+
+        this.kaAlgorithm = kaAlgorithm;
+        this.agreement = agreement;
+    }
+
+    protected Key engineDoPhase(
+        Key     key,
+        boolean lastPhase) 
+        throws InvalidKeyException, IllegalStateException
+    {
+        if (parameters == null)
+        {
+            throw new IllegalStateException(kaAlgorithm + " not initialised.");
+        }
+
+        if (!lastPhase)
+        {
+            throw new IllegalStateException(kaAlgorithm + " can only be between two parties.");
+        }
+
+        CipherParameters pubKey;
+        {
+            if (!(key instanceof PublicKey))
+            {
+                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
+                    + getSimpleName(ECPublicKey.class) + " for doPhase");
+            }
+
+            pubKey = generatePublicKeyParameter((PublicKey)key);
+        }
+
+        try
+        {
+            result = agreement.calculateAgreement(pubKey);
+        }
+        catch (final Exception e)
+        {
+            throw new InvalidKeyException("calculation failed: " + e.getMessage())
+            {
+                public Throwable getCause()
+                            {
+                                return e;
+                            }
+            };
+        }
+
+        return null;
+    }
+
+    protected void engineInit(
+        Key                     key,
+        AlgorithmParameterSpec  params,
+        SecureRandom            random) 
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        if (params != null && !(params instanceof UserKeyingMaterialSpec))
+        {
+            throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
+        }
+
+        initFromKey(key, params);
+    }
+
+    protected void engineInit(
+        Key             key,
+        SecureRandom    random) 
+        throws InvalidKeyException
+    {
+        initFromKey(key, null);
+    }
+
+    private void initFromKey(Key key, AlgorithmParameterSpec parameterSpec)
+        throws InvalidKeyException
+    {
+        {
+            if (!(key instanceof PrivateKey))
+            {
+                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
+                    + getSimpleName(ECPrivateKey.class) + " for initialisation");
+            }
+
+            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            this.parameters = privKey.getParameters();
+            ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
+            agreement.init(new ParametersWithUKM(privKey, ukmParameters));
+        }
+    }
+
+    private static String getSimpleName(Class clazz)
+    {
+        String fullName = clazz.getName();
+
+        return fullName.substring(fullName.lastIndexOf('.') + 1);
+    }
+
+    static AsymmetricKeyParameter generatePublicKeyParameter(
+            PublicKey key)
+        throws InvalidKeyException
+    {
+        return (key instanceof BCECGOST3410_2012PublicKey) ? ((BCECGOST3410_2012PublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
+    }
+
+    protected byte[] calcSecret()
+    {
+        return result;
+    }
+
+    public static class ECVKO256
+        extends KeyAgreementSpi
+    {
+        public ECVKO256()
+        {
+            super("ECGOST3410-2012-256", new ECVKOAgreement(new GOST3411_2012_256Digest()), null);
+        }
+    }
+
+    public static class ECVKO512
+        extends KeyAgreementSpi
+    {
+        public ECVKO512()
+        {
+            super("ECGOST3410-2012-512", new ECVKOAgreement(new GOST3411_2012_512Digest()), null);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyFactorySpi.java
new file mode 100644
index 0000000..ebd9f42
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyFactorySpi.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.jce.spec.ECPrivateKeySpec;
+import org.bouncycastle.jce.spec.ECPublicKeySpec;
+
+public class KeyFactorySpi
+    extends BaseKeyFactorySpi
+{
+    public KeyFactorySpi()
+    {
+    }
+
+    protected KeySpec engineGetKeySpec(
+        Key key,
+        Class spec)
+        throws InvalidKeySpecException
+    {
+       if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey)
+       {
+           ECPublicKey k = (ECPublicKey)key;
+           if (k.getParams() != null)
+           {
+               return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams());
+           }
+           else
+           {
+               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+
+               return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec));
+           }
+       }
+       else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
+       {
+           ECPrivateKey k = (ECPrivateKey)key;
+
+           if (k.getParams() != null)
+           {
+               return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams());
+           }
+           else
+           {
+               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+
+               return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec));
+           }
+       }
+       else if (spec.isAssignableFrom(ECPublicKeySpec.class) && key instanceof ECPublicKey)
+       {
+           ECPublicKey k = (ECPublicKey)key;
+           if (k.getParams() != null)
+           {
+               return new ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false));
+           }
+           else
+           {
+               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+
+               return new ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec);
+           }
+       }
+       else if (spec.isAssignableFrom(ECPrivateKeySpec.class) && key instanceof ECPrivateKey)
+       {
+           ECPrivateKey k = (ECPrivateKey)key;
+
+           if (k.getParams() != null)
+           {
+               return new ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false));
+           }
+           else
+           {
+               ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+
+               return new ECPrivateKeySpec(k.getS(), implicitSpec);
+           }
+       }
+
+       return super.engineGetKeySpec(key, spec);
+    }
+
+    protected Key engineTranslateKey(
+        Key key)
+        throws InvalidKeyException
+    {
+        throw new InvalidKeyException("key type unknown");
+    }
+
+    protected PrivateKey engineGeneratePrivate(
+        KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof ECPrivateKeySpec)
+        {
+            return new BCECGOST3410_2012PrivateKey((ECPrivateKeySpec)keySpec);
+        }
+        else if (keySpec instanceof java.security.spec.ECPrivateKeySpec)
+        {
+            return new BCECGOST3410_2012PrivateKey((java.security.spec.ECPrivateKeySpec)keySpec);
+        }
+
+        return super.engineGeneratePrivate(keySpec);
+    }
+
+    public PublicKey engineGeneratePublic(
+        KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof ECPublicKeySpec)
+        {
+            return new BCECGOST3410_2012PublicKey((ECPublicKeySpec)keySpec, BouncyCastleProvider.CONFIGURATION);
+        }
+        else if (keySpec instanceof java.security.spec.ECPublicKeySpec)
+        {
+            return new BCECGOST3410_2012PublicKey((java.security.spec.ECPublicKeySpec)keySpec);
+        }
+
+        return super.engineGeneratePublic(keySpec);
+    }
+
+    public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm();
+
+        if (isValid(algOid))
+        {
+            return new BCECGOST3410_2012PrivateKey(keyInfo);
+        }
+        else
+        {
+            throw new IOException("algorithm identifier " + algOid + " in key not recognised");
+        }
+    }
+
+    public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm();
+
+        if (isValid(algOid))
+        {
+            return new BCECGOST3410_2012PublicKey(keyInfo);
+        }
+        else
+        {
+            throw new IOException("algorithm identifier " + algOid + " in key not recognised");
+        }
+    }
+
+    private boolean isValid(ASN1ObjectIdentifier algOid)
+    {
+        return algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256)
+            || algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512)
+            || algOid.equals(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256)
+            || algOid.equals(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyPairGeneratorSpi.java
new file mode 100644
index 0000000..50431de
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost12/KeyPairGeneratorSpi.java
@@ -0,0 +1,202 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ecgost12;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECGOST3410Parameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.spec.GOST3410ParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * KeyPairGenerator for GOST34.10 2012. Algorithm is the same as for GOST34.10 2001
+ */
+public class KeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    Object ecParams = null;
+    ECKeyPairGenerator engine = new ECKeyPairGenerator();
+
+    String algorithm = "ECGOST3410-2012";
+    ECKeyGenerationParameters param;
+    int strength = 239;
+    SecureRandom random = null;
+    boolean initialised = false;
+
+    public KeyPairGeneratorSpi()
+    {
+        super("ECGOST3410-2012");
+    }
+
+    public void initialize(
+        int strength,
+        SecureRandom random)
+    {
+        this.strength = strength;
+        this.random = random;
+
+        if (ecParams != null)
+        {
+            try
+            {
+                initialize((ECGenParameterSpec)ecParams, random);
+            }
+            catch (InvalidAlgorithmParameterException e)
+            {
+                throw new InvalidParameterException("key size not configurable.");
+            }
+        }
+        else
+        {
+            throw new InvalidParameterException("unknown key size.");
+        }
+    }
+
+    public void initialize(
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        if (params instanceof GOST3410ParameterSpec)
+        {
+            GOST3410ParameterSpec gostParams = (GOST3410ParameterSpec)params;
+
+            init(gostParams, random);
+        }
+        else if (params instanceof ECParameterSpec)
+        {
+            ECParameterSpec p = (ECParameterSpec)params;
+            this.ecParams = params;
+
+            param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), random);
+
+            engine.init(param);
+            initialised = true;
+        }
+        else if (params instanceof java.security.spec.ECParameterSpec)
+        {
+            java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)params;
+            this.ecParams = params;
+
+            ECCurve curve = EC5Util.convertCurve(p.getCurve());
+            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false);
+
+            param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
+
+            engine.init(param);
+            initialised = true;
+        }
+        else if (params instanceof ECGenParameterSpec || params instanceof ECNamedCurveGenParameterSpec)
+        {
+            String curveName;
+
+            if (params instanceof ECGenParameterSpec)
+            {
+                curveName = ((ECGenParameterSpec)params).getName();
+            }
+            else
+            {
+                curveName = ((ECNamedCurveGenParameterSpec)params).getName();
+            }
+
+            init(new GOST3410ParameterSpec(curveName), random);
+        }
+        else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() != null)
+        {
+            ECParameterSpec p = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+            this.ecParams = params;
+
+            param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), random);
+
+            engine.init(param);
+            initialised = true;
+        }
+        else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() == null)
+        {
+            throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set");
+        }
+        else
+        {
+            throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec: " + params.getClass().getName());
+        }
+    }
+
+    private void init(GOST3410ParameterSpec gostParams, SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        ECDomainParameters ecP = ECGOST3410NamedCurves.getByOID(gostParams.getPublicKeyParamSet());
+        if (ecP == null)
+        {
+            throw new InvalidAlgorithmParameterException("unknown curve: " + gostParams.getPublicKeyParamSet());
+        }
+
+        this.ecParams = new ECNamedCurveSpec(
+            ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+            ecP.getCurve(),
+            ecP.getG(),
+            ecP.getN(),
+            ecP.getH(),
+            ecP.getSeed());
+
+        param = new ECKeyGenerationParameters(
+            new ECGOST3410Parameters(
+                new ECNamedDomainParameters(gostParams.getPublicKeyParamSet(), ecP),
+                gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet()), random);
+
+        engine.init(param);
+        initialised = true;
+    }
+
+    public KeyPair generateKeyPair()
+    {
+        if (!initialised)
+        {
+            throw new IllegalStateException("EC Key Pair Generator not initialised");
+        }
+
+        AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+        ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic();
+        ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate();
+
+        if (ecParams instanceof ECParameterSpec)
+        {
+            ECParameterSpec p = (ECParameterSpec)ecParams;
+
+            BCECGOST3410_2012PublicKey pubKey = new BCECGOST3410_2012PublicKey(algorithm, pub, p);
+            return new KeyPair(pubKey,
+                new BCECGOST3410_2012PrivateKey(algorithm, priv, pubKey, p));
+        }
+        else if (ecParams == null)
+        {
+            return new KeyPair(new BCECGOST3410_2012PublicKey(algorithm, pub),
+                new BCECGOST3410_2012PrivateKey(algorithm, priv));
+        }
+        else
+        {
+            java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
+
+            BCECGOST3410_2012PublicKey pubKey = new BCECGOST3410_2012PublicKey(algorithm, pub, p);
+
+            return new KeyPair(pubKey, new BCECGOST3410_2012PrivateKey(algorithm, priv, pubKey, p));
+        }
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPrivateKey.java
new file mode 100644
index 0000000..92061da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPrivateKey.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PrivateKey;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.jcajce.interfaces.EdDSAKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCEdDSAPrivateKey
+    implements EdDSAKey, PrivateKey
+{
+    static final long serialVersionUID = 1L;
+    
+    private transient AsymmetricKeyParameter eddsaPrivateKey;
+
+    private final boolean hasPublicKey;
+    private final byte[] attributes;
+
+    BCEdDSAPrivateKey(AsymmetricKeyParameter privKey)
+    {
+        this.hasPublicKey = true;
+        this.attributes = null;
+        this.eddsaPrivateKey = privKey;
+    }
+
+    BCEdDSAPrivateKey(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.hasPublicKey = keyInfo.hasPublicKey();
+        this.attributes = (keyInfo.getAttributes() != null) ? keyInfo.getAttributes().getEncoded() : null;
+
+        populateFromPrivateKeyInfo(keyInfo);
+    }
+
+    private void populateFromPrivateKeyInfo(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1Encodable keyOcts = keyInfo.parsePrivateKey();
+        if (EdECObjectIdentifiers.id_Ed448.equals(keyInfo.getPrivateKeyAlgorithm().getAlgorithm()))
+        {
+            eddsaPrivateKey = new Ed448PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0);
+        }
+        else
+        {
+            eddsaPrivateKey = new Ed25519PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0);
+        }
+    }
+
+    public String getAlgorithm()
+    {
+        return (eddsaPrivateKey instanceof Ed448PrivateKeyParameters) ? "Ed448" : "Ed25519";
+    }
+
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            ASN1Set attrSet = ASN1Set.getInstance(attributes);
+            PrivateKeyInfo privInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(eddsaPrivateKey, attrSet);
+
+            if (hasPublicKey)
+            {
+                return privInfo.getEncoded();
+            }
+            else
+            {
+                return new PrivateKeyInfo(privInfo.getPrivateKeyAlgorithm(), privInfo.parsePrivateKey(), attrSet).getEncoded();
+            }
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    AsymmetricKeyParameter engineGetKeyParameters()
+    {
+        return eddsaPrivateKey;
+    }
+
+    public String toString()
+    {
+        AsymmetricKeyParameter pubKey;
+        if (eddsaPrivateKey instanceof Ed448PrivateKeyParameters)
+        {
+            pubKey = ((Ed448PrivateKeyParameters)eddsaPrivateKey).generatePublicKey();
+        }
+        else
+        {
+            pubKey = ((Ed25519PrivateKeyParameters)eddsaPrivateKey).generatePublicKey();
+        }
+        return Utils.keyToString("Private Key", getAlgorithm(), pubKey);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof BCEdDSAPrivateKey))
+        {
+            return false;
+        }
+
+        BCEdDSAPrivateKey other = (BCEdDSAPrivateKey)o;
+
+        return Arrays.areEqual(other.getEncoded(), this.getEncoded());
+    }
+
+    public int hashCode()
+    {
+        return Arrays.hashCode(this.getEncoded());
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPrivateKeyInfo(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java
new file mode 100644
index 0000000..c6b6d72
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCEdDSAPublicKey.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.jcajce.interfaces.EdDSAKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCEdDSAPublicKey
+    implements EdDSAKey, PublicKey
+{
+    static final long serialVersionUID = 1L;
+
+    private transient AsymmetricKeyParameter eddsaPublicKey;
+
+    BCEdDSAPublicKey(AsymmetricKeyParameter pubKey)
+    {
+        this.eddsaPublicKey = pubKey;
+    }
+
+    BCEdDSAPublicKey(SubjectPublicKeyInfo keyInfo)
+    {
+        populateFromPubKeyInfo(keyInfo);
+    }
+
+    BCEdDSAPublicKey(byte[] prefix, byte[] rawData)
+        throws InvalidKeySpecException
+    {
+        int prefixLength = prefix.length;
+
+        if (Utils.isValidPrefix(prefix, rawData))
+        {
+            if ((rawData.length - prefixLength) == Ed448PublicKeyParameters.KEY_SIZE)
+            {
+                eddsaPublicKey = new Ed448PublicKeyParameters(rawData, prefixLength);
+            }
+            else if ((rawData.length - prefixLength) == Ed25519PublicKeyParameters.KEY_SIZE)
+            {
+                eddsaPublicKey = new Ed25519PublicKeyParameters(rawData, prefixLength);
+            }
+            else
+            {
+                throw new InvalidKeySpecException("raw key data not recognised");
+            }
+        }
+        else
+        {
+            throw new InvalidKeySpecException("raw key data not recognised");
+        }
+    }
+
+    private void populateFromPubKeyInfo(SubjectPublicKeyInfo keyInfo)
+    {
+        if (EdECObjectIdentifiers.id_Ed448.equals(keyInfo.getAlgorithm().getAlgorithm()))
+        {
+            eddsaPublicKey = new Ed448PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0);
+        }
+        else
+        {
+            eddsaPublicKey = new Ed25519PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0);
+        }
+    }
+
+    public String getAlgorithm()
+    {
+        return (eddsaPublicKey instanceof Ed448PublicKeyParameters) ? "Ed448" : "Ed25519";
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    public byte[] getEncoded()
+    {
+        if (eddsaPublicKey instanceof Ed448PublicKeyParameters)
+        {
+            byte[] encoding = new byte[KeyFactorySpi.Ed448Prefix.length + Ed448PublicKeyParameters.KEY_SIZE];
+
+            System.arraycopy(KeyFactorySpi.Ed448Prefix, 0, encoding, 0, KeyFactorySpi.Ed448Prefix.length);
+
+            ((Ed448PublicKeyParameters)eddsaPublicKey).encode(encoding, KeyFactorySpi.Ed448Prefix.length);
+
+            return encoding;
+        }
+        else
+        {
+            byte[] encoding = new byte[KeyFactorySpi.Ed25519Prefix.length + Ed25519PublicKeyParameters.KEY_SIZE];
+
+            System.arraycopy(KeyFactorySpi.Ed25519Prefix, 0, encoding, 0, KeyFactorySpi.Ed25519Prefix.length);
+
+            ((Ed25519PublicKeyParameters)eddsaPublicKey).encode(encoding, KeyFactorySpi.Ed25519Prefix.length);
+
+            return encoding;
+        }
+    }
+
+    AsymmetricKeyParameter engineGetKeyParameters()
+    {
+        return eddsaPublicKey;
+    }
+
+    public String toString()
+    {
+        return Utils.keyToString("Public Key", getAlgorithm(), eddsaPublicKey);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof BCEdDSAPublicKey))
+        {
+            return false;
+        }
+
+        BCEdDSAPublicKey other = (BCEdDSAPublicKey)o;
+
+        return Arrays.areEqual(other.getEncoded(), this.getEncoded());
+    }
+
+    public int hashCode()
+    {
+        return Arrays.hashCode(this.getEncoded());
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPrivateKey.java
new file mode 100644
index 0000000..3485158
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPrivateKey.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PrivateKey;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.jcajce.interfaces.XDHKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXDHPrivateKey
+    implements XDHKey, PrivateKey
+{
+    static final long serialVersionUID = 1L;
+
+    private transient AsymmetricKeyParameter xdhPrivateKey;
+
+    private final boolean hasPublicKey;
+    private final byte[] attributes;
+
+    BCXDHPrivateKey(AsymmetricKeyParameter privKey)
+    {
+        this.hasPublicKey = true;
+        this.attributes = null;
+        this.xdhPrivateKey = privKey;
+    }
+
+    BCXDHPrivateKey(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.hasPublicKey = keyInfo.hasPublicKey();
+        this.attributes = (keyInfo.getAttributes() != null) ? keyInfo.getAttributes().getEncoded() : null;
+
+        populateFromPrivateKeyInfo(keyInfo);
+    }
+
+    private void populateFromPrivateKeyInfo(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1Encodable keyOcts = keyInfo.parsePrivateKey();
+        if (EdECObjectIdentifiers.id_X448.equals(keyInfo.getPrivateKeyAlgorithm().getAlgorithm()))
+        {
+            xdhPrivateKey = new X448PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0);
+        }
+        else
+        {
+            xdhPrivateKey = new X25519PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0);
+        }
+    }
+
+    public String getAlgorithm()
+    {
+        return (xdhPrivateKey instanceof X448PrivateKeyParameters) ? "X448" : "X25519";
+    }
+
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            ASN1Set attrSet = ASN1Set.getInstance(attributes);
+            PrivateKeyInfo privInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(xdhPrivateKey, attrSet);
+
+            if (hasPublicKey)
+            {
+                return privInfo.getEncoded();
+            }
+            else
+            {
+                return new PrivateKeyInfo(privInfo.getPrivateKeyAlgorithm(), privInfo.parsePrivateKey(), attrSet).getEncoded();
+            }
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    AsymmetricKeyParameter engineGetKeyParameters()
+    {
+        return xdhPrivateKey;
+    }
+
+    public String toString()
+    {
+        AsymmetricKeyParameter pubKey;
+        if (xdhPrivateKey instanceof X448PrivateKeyParameters)
+        {
+            pubKey = ((X448PrivateKeyParameters)xdhPrivateKey).generatePublicKey();
+        }
+        else
+        {
+            pubKey = ((X25519PrivateKeyParameters)xdhPrivateKey).generatePublicKey();
+        }
+        return Utils.keyToString("Private Key", getAlgorithm(), pubKey);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof BCXDHPrivateKey))
+        {
+            return false;
+        }
+
+        BCXDHPrivateKey other = (BCXDHPrivateKey)o;
+
+        return Arrays.areEqual(other.getEncoded(), this.getEncoded());
+    }
+
+    public int hashCode()
+    {
+        return Arrays.hashCode(this.getEncoded());
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPrivateKeyInfo(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPublicKey.java
new file mode 100644
index 0000000..143bf11
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/BCXDHPublicKey.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+import org.bouncycastle.jcajce.interfaces.XDHKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXDHPublicKey
+    implements XDHKey, PublicKey
+{
+    static final long serialVersionUID = 1L;
+
+    private transient AsymmetricKeyParameter xdhPublicKey;
+
+    BCXDHPublicKey(AsymmetricKeyParameter pubKey)
+    {
+        this.xdhPublicKey = pubKey;
+    }
+
+    BCXDHPublicKey(SubjectPublicKeyInfo keyInfo)
+    {
+        populateFromPubKeyInfo(keyInfo);
+    }
+
+    BCXDHPublicKey(byte[] prefix, byte[] rawData)
+        throws InvalidKeySpecException
+    {
+        int prefixLength = prefix.length;
+
+        if (Utils.isValidPrefix(prefix, rawData))
+        {
+            if ((rawData.length - prefixLength) == X448PublicKeyParameters.KEY_SIZE)
+            {
+                xdhPublicKey = new X448PublicKeyParameters(rawData, prefixLength);
+            }
+            else if ((rawData.length - prefixLength) == X25519PublicKeyParameters.KEY_SIZE)
+            {
+                xdhPublicKey = new X25519PublicKeyParameters(rawData, prefixLength);
+            }
+            else
+            {
+                throw new InvalidKeySpecException("raw key data not recognised");
+            }
+        }
+        else
+        {
+            throw new InvalidKeySpecException("raw key data not recognised");
+        }
+    }
+
+    private void populateFromPubKeyInfo(SubjectPublicKeyInfo keyInfo)
+    {
+        if (EdECObjectIdentifiers.id_X448.equals(keyInfo.getAlgorithm().getAlgorithm()))
+        {
+            xdhPublicKey = new X448PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0);
+        }
+        else
+        {
+            xdhPublicKey = new X25519PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0);
+        }
+    }
+
+    public String getAlgorithm()
+    {
+        return (xdhPublicKey instanceof X448PublicKeyParameters) ? "X448" : "X25519";
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    public byte[] getEncoded()
+    {
+        if (xdhPublicKey instanceof X448PublicKeyParameters)
+        {
+            byte[] encoding = new byte[KeyFactorySpi.x448Prefix.length + X448PublicKeyParameters.KEY_SIZE];
+
+            System.arraycopy(KeyFactorySpi.x448Prefix, 0, encoding, 0, KeyFactorySpi.x448Prefix.length);
+
+            ((X448PublicKeyParameters)xdhPublicKey).encode(encoding, KeyFactorySpi.x448Prefix.length);
+
+            return encoding;
+        }
+        else
+        {
+            byte[] encoding = new byte[KeyFactorySpi.x25519Prefix.length + X25519PublicKeyParameters.KEY_SIZE];
+
+            System.arraycopy(KeyFactorySpi.x25519Prefix, 0, encoding, 0, KeyFactorySpi.x25519Prefix.length);
+
+            ((X25519PublicKeyParameters)xdhPublicKey).encode(encoding, KeyFactorySpi.x25519Prefix.length);
+
+            return encoding;
+        }
+    }
+
+    AsymmetricKeyParameter engineGetKeyParameters()
+    {
+        return xdhPublicKey;
+    }
+
+    public String toString()
+    {
+        return Utils.keyToString("Public Key", getAlgorithm(), xdhPublicKey);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof BCXDHPublicKey))
+        {
+            return false;
+        }
+
+        BCXDHPublicKey other = (BCXDHPublicKey)o;
+
+        return Arrays.areEqual(other.getEncoded(), this.getEncoded());
+    }
+
+    public int hashCode()
+    {
+        return Arrays.hashCode(this.getEncoded());
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyAgreementSpi.java
new file mode 100644
index 0000000..0cfd2f5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyAgreementSpi.java
@@ -0,0 +1,343 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.RawAgreement;
+import org.bouncycastle.crypto.agreement.X25519Agreement;
+import org.bouncycastle.crypto.agreement.X448Agreement;
+import org.bouncycastle.crypto.agreement.XDHUnifiedAgreement;
+import org.bouncycastle.crypto.agreement.kdf.ConcatenationKDFGenerator;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.XDHUPrivateParameters;
+import org.bouncycastle.crypto.params.XDHUPublicParameters;
+import org.bouncycastle.crypto.util.DigestFactory;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;
+import org.bouncycastle.jcajce.spec.DHUParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+
+public class KeyAgreementSpi
+    extends BaseAgreementSpi
+{
+    private RawAgreement agreement;
+    private DHUParameterSpec dhuSpec;
+    private byte[] result;
+
+    KeyAgreementSpi(String algorithm)
+    {
+        super(algorithm, null);
+    }
+
+    KeyAgreementSpi(String algorithm, DerivationFunction kdf)
+    {
+        super(algorithm, kdf);
+    }
+
+    protected byte[] calcSecret()
+    {
+        return result;
+    }
+
+    protected void engineInit(Key key, SecureRandom secureRandom)
+        throws InvalidKeyException
+    {
+        if (key instanceof BCXDHPrivateKey)
+        {
+            AsymmetricKeyParameter priv = ((BCXDHPrivateKey)key).engineGetKeyParameters();
+
+            if (priv instanceof X448PrivateKeyParameters)
+            {
+                agreement = getAgreement("X448");
+            }
+            else
+            {
+                agreement = getAgreement("X25519");
+            }
+
+            agreement.init(priv);
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot identify XDH private key");
+        }
+
+        if (kdf != null)
+        {
+            ukmParameters = new byte[0];
+        }
+        else
+        {
+            ukmParameters = null;
+        }
+    }
+
+    protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom secureRandom)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        AsymmetricKeyParameter priv;
+
+        if (key instanceof BCXDHPrivateKey)
+        {
+            priv = ((BCXDHPrivateKey)key).engineGetKeyParameters();
+
+            if (priv instanceof X448PrivateKeyParameters)
+            {
+                agreement = getAgreement("X448");
+            }
+            else
+            {
+                agreement = getAgreement("X25519");
+            }
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot identify XDH private key");
+        }
+
+        ukmParameters = null;
+        if (params instanceof DHUParameterSpec)
+        {
+            if (kaAlgorithm.indexOf('U') < 0)
+            {
+                throw new InvalidAlgorithmParameterException("agreement algorithm not DHU based");
+            }
+
+            dhuSpec = (DHUParameterSpec)params;
+
+            ukmParameters = dhuSpec.getUserKeyingMaterial();
+            
+            agreement.init(new XDHUPrivateParameters(
+                priv, ((BCXDHPrivateKey)dhuSpec.getEphemeralPrivateKey()).engineGetKeyParameters(),
+                ((BCXDHPublicKey)dhuSpec.getEphemeralPublicKey()).engineGetKeyParameters()));
+        }
+        else
+        {
+            agreement.init(priv);
+
+            if (params instanceof UserKeyingMaterialSpec)
+            {
+                if (kdf == null)
+                {
+                    throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
+                }
+                this.ukmParameters = ((UserKeyingMaterialSpec)params).getUserKeyingMaterial();
+            }
+            else
+            {
+                throw new InvalidAlgorithmParameterException("unknown ParameterSpec");
+            }
+        }
+
+        if (kdf != null && ukmParameters == null)
+        {
+            ukmParameters = new byte[0];
+        }
+    }
+
+    protected Key engineDoPhase(Key key, boolean lastPhase)
+        throws InvalidKeyException, IllegalStateException
+    {
+        if (agreement == null)
+        {
+            throw new IllegalStateException(kaAlgorithm + " not initialised.");
+        }
+
+        if (!lastPhase)
+        {
+            throw new IllegalStateException(kaAlgorithm + " can only be between two parties.");
+        }
+
+        if (!(key instanceof BCXDHPublicKey))
+        {
+            throw new InvalidKeyException("cannot identify XDH private key");
+        }
+
+        AsymmetricKeyParameter pub = ((BCXDHPublicKey)key).engineGetKeyParameters();
+
+        result = new byte[agreement.getAgreementSize()];
+
+        if (dhuSpec != null)
+        {
+            agreement.calculateAgreement(new XDHUPublicParameters(pub, ((BCXDHPublicKey)dhuSpec.getOtherPartyEphemeralKey()).engineGetKeyParameters()), result, 0);
+        }
+        else
+        {
+            agreement.calculateAgreement(pub, result, 0);
+        }
+
+        return null;
+    }
+
+    private RawAgreement getAgreement(String alg)
+        throws InvalidKeyException
+    {
+        if (!(kaAlgorithm.equals("XDH") || kaAlgorithm.startsWith(alg)))
+        {
+            throw new InvalidKeyException("inappropriate key for " + kaAlgorithm);
+        }
+
+        if (kaAlgorithm.indexOf('U') > 0)
+        {
+            if (alg.startsWith("X448"))
+            {
+                return new XDHUnifiedAgreement(new X448Agreement());
+            }
+            else
+            {
+                return new XDHUnifiedAgreement(new X25519Agreement());
+            }
+        }
+        else
+        {
+            if (alg.startsWith("X448"))
+            {
+                return new X448Agreement();
+            }
+            else
+            {
+                return new X25519Agreement();
+            }
+        }
+    }
+
+    public final static class XDH
+        extends KeyAgreementSpi
+    {
+        public XDH()
+        {
+            super("XDH");
+        }
+    }
+
+    public final static class X448
+        extends KeyAgreementSpi
+    {
+        public X448()
+        {
+            super("X448");
+        }
+    }
+
+    public final static class X25519
+        extends KeyAgreementSpi
+    {
+        public X25519()
+        {
+            super("X25519");
+        }
+    }
+
+    public final static class X25519withSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public X25519withSHA256CKDF()
+        {
+            super("X25519withSHA256CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class X25519withSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public X25519withSHA384CKDF()
+        {
+            super("X25519withSHA384CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public static class X25519withSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public X25519withSHA512CKDF()
+        {
+            super("X25519withSHA512CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public final static class X448withSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public X448withSHA256CKDF()
+        {
+            super("X448withSHA256CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class X448withSHA384CKDF
+        extends KeyAgreementSpi
+    {
+        public X448withSHA384CKDF()
+        {
+            super("X448withSHA384CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
+        }
+    }
+
+    public final static class X448withSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public X448withSHA512CKDF()
+        {
+            super("X448withSHA512CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public final static class X25519withSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public X25519withSHA256KDF()
+        {
+            super("X25519withSHA256KDF", new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public final static class X448withSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public X448withSHA512KDF()
+        {
+            super("X448withSHA512KDF", new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class X25519UwithSHA256CKDF
+        extends KeyAgreementSpi
+    {
+        public X25519UwithSHA256CKDF()
+        {
+            super("X25519UwithSHA256CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class X448UwithSHA512CKDF
+        extends KeyAgreementSpi
+    {
+        public X448UwithSHA512CKDF()
+        {
+            super("X448UwithSHA512CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
+        }
+    }
+
+    public static class X25519UwithSHA256KDF
+        extends KeyAgreementSpi
+    {
+        public X25519UwithSHA256KDF()
+        {
+            super("X25519UwithSHA256KDF", new KDF2BytesGenerator(DigestFactory.createSHA256()));
+        }
+    }
+
+    public static class X448UwithSHA512KDF
+        extends KeyAgreementSpi
+    {
+        public X448UwithSHA512KDF()
+        {
+            super("X448UwithSHA512KDF", new KDF2BytesGenerator(DigestFactory.createSHA512()));
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java
new file mode 100644
index 0000000..9fd7945
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java
@@ -0,0 +1,277 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
+import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+import org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPublicKeySpec;
+import org.bouncycastle.util.encoders.Hex;
+
+public class KeyFactorySpi
+    extends BaseKeyFactorySpi
+    implements AsymmetricKeyInfoConverter
+{
+    static final byte[] x448Prefix = Hex.decode("3042300506032b656f033900");
+    static final byte[] x25519Prefix = Hex.decode("302a300506032b656e032100");
+    static final byte[] Ed448Prefix = Hex.decode("3043300506032b6571033a00");
+    static final byte[] Ed25519Prefix = Hex.decode("302a300506032b6570032100");
+
+    private static final byte x448_type = 0x6f;
+    private static final byte x25519_type = 0x6e;
+    private static final byte Ed448_type = 0x71;
+    private static final byte Ed25519_type = 0x70;
+
+    String algorithm;
+    private final boolean isXdh;
+    private final int specificBase;
+
+    public KeyFactorySpi(
+        String algorithm,
+        boolean isXdh,
+        int specificBase)
+    {
+        this.algorithm = algorithm;
+        this.isXdh = isXdh;
+        this.specificBase = specificBase;
+    }
+
+    protected Key engineTranslateKey(
+        Key key)
+        throws InvalidKeyException
+    {
+        throw new InvalidKeyException("key type unknown");
+    }
+
+    protected KeySpec engineGetKeySpec(
+        Key key,
+        Class spec)
+        throws InvalidKeySpecException
+    {
+        if (spec.isAssignableFrom(OpenSSHPrivateKeySpec.class) && key instanceof BCEdDSAPrivateKey)
+        {
+            try
+            {
+                //
+                // The DEROctetString at element 2 is an encoded DEROctetString with the private key value
+                // within it.
+                //
+
+                ASN1Sequence seq = ASN1Sequence.getInstance(key.getEncoded());
+                DEROctetString val = (DEROctetString)seq.getObjectAt(2);
+                ASN1InputStream in = new ASN1InputStream(val.getOctets());
+
+                return new OpenSSHPrivateKeySpec(OpenSSHPrivateKeyUtil.encodePrivateKey(new Ed25519PrivateKeyParameters(((DEROctetString)in.readObject()).getOctets(), 0)));
+            }
+            catch (IOException ex)
+            {
+                throw new InvalidKeySpecException(ex.getMessage(), ex.getCause());
+            }
+
+        }
+        else if (spec.isAssignableFrom(OpenSSHPublicKeySpec.class) && key instanceof BCEdDSAPublicKey)
+        {
+            try
+            {
+                return new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(new Ed25519PublicKeyParameters(key.getEncoded(), Ed25519Prefix.length)));
+            }
+            catch (IOException ex)
+            {
+                throw new InvalidKeySpecException(ex.getMessage(), ex.getCause());
+            }
+        }
+
+        return super.engineGetKeySpec(key, spec);
+    }
+
+    protected PrivateKey engineGeneratePrivate(
+        KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof OpenSSHPrivateKeySpec)
+        {
+            CipherParameters parameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(((OpenSSHPrivateKeySpec)keySpec).getEncoded());
+            if (parameters instanceof Ed25519PrivateKeyParameters)
+            {
+                return new BCEdDSAPrivateKey((Ed25519PrivateKeyParameters)parameters);
+            }
+            throw new IllegalStateException("openssh private key not Ed25519 private key");
+        }
+
+        return super.engineGeneratePrivate(keySpec);
+    }
+
+    protected PublicKey engineGeneratePublic(
+        KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof X509EncodedKeySpec)
+        {
+            byte[] enc = ((X509EncodedKeySpec)keySpec).getEncoded();
+            // optimise if we can
+            if (specificBase == 0 || specificBase == enc[8])
+            {
+                switch (enc[8])
+                {
+                case x448_type:
+                    return new BCXDHPublicKey(x448Prefix, enc);
+                case x25519_type:
+                    return new BCXDHPublicKey(x25519Prefix, enc);
+                case Ed448_type:
+                    return new BCEdDSAPublicKey(Ed448Prefix, enc);
+                case Ed25519_type:
+                    return new BCEdDSAPublicKey(Ed25519Prefix, enc);
+                default:
+                    return super.engineGeneratePublic(keySpec);
+                }
+            }
+        }
+        else if (keySpec instanceof OpenSSHPublicKeySpec)
+        {
+            CipherParameters parameters = OpenSSHPublicKeyUtil.parsePublicKey(((OpenSSHPublicKeySpec)keySpec).getEncoded());
+            if (parameters instanceof Ed25519PublicKeyParameters)
+            {
+                return new BCEdDSAPublicKey(new byte[0], ((Ed25519PublicKeyParameters)parameters).getEncoded());
+            }
+
+            throw new IllegalStateException("openssh public key not Ed25519 public key");
+        }
+
+        return super.engineGeneratePublic(keySpec);
+    }
+
+    public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm();
+
+        if (isXdh)
+        {
+            if ((specificBase == 0 || specificBase == x448_type) && algOid.equals(EdECObjectIdentifiers.id_X448))
+            {
+                return new BCXDHPrivateKey(keyInfo);
+            }
+            if ((specificBase == 0 || specificBase == x25519_type) && algOid.equals(EdECObjectIdentifiers.id_X25519))
+            {
+                return new BCXDHPrivateKey(keyInfo);
+            }
+        }
+        else if (algOid.equals(EdECObjectIdentifiers.id_Ed448) || algOid.equals(EdECObjectIdentifiers.id_Ed25519))
+        {
+            if ((specificBase == 0 || specificBase == Ed448_type) && algOid.equals(EdECObjectIdentifiers.id_Ed448))
+            {
+                return new BCEdDSAPrivateKey(keyInfo);
+            }
+            if ((specificBase == 0 || specificBase == Ed25519_type) && algOid.equals(EdECObjectIdentifiers.id_Ed25519))
+            {
+                return new BCEdDSAPrivateKey(keyInfo);
+            }
+        }
+
+        throw new IOException("algorithm identifier " + algOid + " in key not recognized");
+    }
+
+    public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm();
+
+        if (isXdh)
+        {
+            if ((specificBase == 0 || specificBase == x448_type) && algOid.equals(EdECObjectIdentifiers.id_X448))
+            {
+                return new BCXDHPublicKey(keyInfo);
+            }
+            if ((specificBase == 0 || specificBase == x25519_type) && algOid.equals(EdECObjectIdentifiers.id_X25519))
+            {
+                return new BCXDHPublicKey(keyInfo);
+            }
+        }
+        else if (algOid.equals(EdECObjectIdentifiers.id_Ed448) || algOid.equals(EdECObjectIdentifiers.id_Ed25519))
+        {
+            if ((specificBase == 0 || specificBase == Ed448_type) && algOid.equals(EdECObjectIdentifiers.id_Ed448))
+            {
+                return new BCEdDSAPublicKey(keyInfo);
+            }
+            if ((specificBase == 0 || specificBase == Ed25519_type) && algOid.equals(EdECObjectIdentifiers.id_Ed25519))
+            {
+                return new BCEdDSAPublicKey(keyInfo);
+            }
+        }
+
+        throw new IOException("algorithm identifier " + algOid + " in key not recognized");
+    }
+
+    public static class XDH
+        extends KeyFactorySpi
+    {
+        public XDH()
+        {
+            super("XDH", true, 0);
+        }
+    }
+
+    public static class X448
+        extends KeyFactorySpi
+    {
+        public X448()
+        {
+            super("X448", true, x448_type);
+        }
+    }
+
+    public static class X25519
+        extends KeyFactorySpi
+    {
+        public X25519()
+        {
+            super("X25519", true, x25519_type);
+        }
+    }
+
+    public static class EDDSA
+        extends KeyFactorySpi
+    {
+        public EDDSA()
+        {
+            super("EdDSA", false, 0);
+        }
+    }
+
+    public static class ED448
+        extends KeyFactorySpi
+    {
+        public ED448()
+        {
+            super("Ed448", false, Ed448_type);
+        }
+    }
+
+    public static class ED25519
+        extends KeyFactorySpi
+    {
+        public ED25519()
+        {
+            super("Ed25519", false, Ed25519_type);
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java
new file mode 100644
index 0000000..b5cad9a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java
@@ -0,0 +1,281 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
+import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator;
+import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
+import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters;
+import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
+import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+
+public class KeyPairGeneratorSpi
+    extends java.security.KeyPairGeneratorSpi
+{
+    private static final int EdDSA = -1;
+    private static final int XDH = -2;
+
+    private static final int Ed448 = 0;
+    private static final int Ed25519 = 1;
+    private static final int X448 = 2;
+    private static final int X25519 = 3;
+
+    private int algorithm;
+    private AsymmetricCipherKeyPairGenerator generator;
+
+    private boolean initialised;
+    private SecureRandom secureRandom;
+
+    KeyPairGeneratorSpi(int algorithm, AsymmetricCipherKeyPairGenerator generator)
+    {
+        this.algorithm = algorithm;
+        this.generator = generator;
+    }
+
+    public void initialize(int strength, SecureRandom secureRandom)
+    {
+        this.secureRandom = secureRandom;
+
+        switch (strength)
+        {
+        case 255:
+        case 256:
+            switch (algorithm)
+            {
+            case EdDSA:
+            case Ed25519:
+                setupGenerator(Ed25519);
+                break;
+            case XDH:
+            case X25519:
+                setupGenerator(X25519);
+                break;
+            default:
+                throw new InvalidParameterException("key size not configurable");
+            }
+            break;
+        case 448:
+            switch (algorithm)
+            {
+            case EdDSA:
+            case Ed448:
+                setupGenerator(Ed448);
+                break;
+            case XDH:
+            case X448:
+                setupGenerator(X448);
+                break;
+            default:
+                throw new InvalidParameterException("key size not configurable");
+            }
+            break;
+        default:
+            throw new InvalidParameterException("unknown key size");
+        }
+    }
+
+    public void initialize(AlgorithmParameterSpec paramSpec, SecureRandom secureRandom)
+        throws InvalidAlgorithmParameterException
+    {
+        this.secureRandom = secureRandom;
+
+        if (paramSpec instanceof ECGenParameterSpec)
+        {
+            initializeGenerator(((ECGenParameterSpec)paramSpec).getName());
+        }
+        else if (paramSpec instanceof ECNamedCurveGenParameterSpec)
+        {
+            initializeGenerator(((ECNamedCurveGenParameterSpec)paramSpec).getName());
+        }
+        else if (paramSpec instanceof EdDSAParameterSpec)
+        {
+            initializeGenerator(((EdDSAParameterSpec)paramSpec).getCurveName());
+        }
+        else if (paramSpec instanceof XDHParameterSpec)
+        {
+            initializeGenerator(((XDHParameterSpec)paramSpec).getCurveName());
+        }
+        else
+        {
+            throw new InvalidAlgorithmParameterException("invalid parameterSpec: " + paramSpec);
+        }
+    }
+
+    private void algorithmCheck(int algorithm)
+        throws InvalidAlgorithmParameterException
+    {
+        if (this.algorithm != algorithm)
+        {
+            if (this.algorithm == Ed25519 || this.algorithm == Ed448)
+            {
+                throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type");
+            }
+            if (this.algorithm == EdDSA && (algorithm != Ed25519 && algorithm != Ed448))
+            {
+                throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type");
+            }
+            if (this.algorithm == X25519 || this.algorithm == X448)
+            {
+                throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type");
+            }
+            if (this.algorithm == XDH && (algorithm != X25519 && algorithm != X448))
+            {
+                throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type");
+            }
+            this.algorithm = algorithm;
+        }
+    }
+
+    private void initializeGenerator(String name)
+        throws InvalidAlgorithmParameterException
+    {
+        if (name.equalsIgnoreCase(EdDSAParameterSpec.Ed448) || name.equals(EdECObjectIdentifiers.id_Ed448.getId()))
+        {
+            algorithmCheck(Ed448);
+            this.generator = new Ed448KeyPairGenerator();
+            setupGenerator(Ed448);
+        }
+        else if (name.equalsIgnoreCase(EdDSAParameterSpec.Ed25519) || name.equals(EdECObjectIdentifiers.id_Ed25519.getId()))
+        {
+            algorithmCheck(Ed25519);
+            this.generator = new Ed25519KeyPairGenerator();
+            setupGenerator(Ed25519);
+        }
+        else if (name.equalsIgnoreCase(XDHParameterSpec.X448) || name.equals(EdECObjectIdentifiers.id_X448.getId()))
+        {
+            algorithmCheck(X448);
+            this.generator = new X448KeyPairGenerator();
+            setupGenerator(X448);
+        }
+        else if (name.equalsIgnoreCase(XDHParameterSpec.X25519) || name.equals(EdECObjectIdentifiers.id_X25519.getId()))
+        {
+            algorithmCheck(X25519);
+            this.generator = new X25519KeyPairGenerator();
+            setupGenerator(X25519);
+        }
+    }
+
+    public KeyPair generateKeyPair()
+    {
+        if (generator == null)
+        {
+            throw new IllegalStateException("generator not correctly initialized");
+        }
+
+        if (!initialised)
+        {
+            setupGenerator(algorithm);
+        }
+
+        AsymmetricCipherKeyPair kp = generator.generateKeyPair();
+
+        switch (algorithm)
+        {
+        case Ed448:
+            return new KeyPair(new BCEdDSAPublicKey(kp.getPublic()), new BCEdDSAPrivateKey(kp.getPrivate()));
+        case Ed25519:
+            return new KeyPair(new BCEdDSAPublicKey(kp.getPublic()), new BCEdDSAPrivateKey(kp.getPrivate()));
+        case X448:
+            return new KeyPair(new BCXDHPublicKey(kp.getPublic()), new BCXDHPrivateKey(kp.getPrivate()));
+        case X25519:
+            return new KeyPair(new BCXDHPublicKey(kp.getPublic()), new BCXDHPrivateKey(kp.getPrivate()));
+        }
+
+        throw new IllegalStateException("generator not correctly initialized");
+    }
+
+    private void setupGenerator(int algorithm)
+    {
+        initialised = true;
+
+        if (secureRandom == null)
+        {
+            secureRandom = new SecureRandom();
+        }
+
+        switch (algorithm)
+        {
+        case Ed448:
+            generator.init(new Ed448KeyGenerationParameters(secureRandom));
+            break;
+        case EdDSA:
+        case Ed25519:
+            generator.init(new Ed25519KeyGenerationParameters(secureRandom));
+            break;
+        case X448:
+            generator.init(new X448KeyGenerationParameters(secureRandom));
+            break;
+        case XDH:
+        case X25519:
+            generator.init(new X25519KeyGenerationParameters(secureRandom));
+            break;
+        }
+    }
+
+    public static final class EdDSA
+        extends KeyPairGeneratorSpi
+    {
+        public EdDSA()
+        {
+            super(EdDSA, null);
+        }
+    }
+
+    public static final class Ed448
+        extends KeyPairGeneratorSpi
+    {
+        public Ed448()
+        {
+            super(Ed448, new Ed448KeyPairGenerator());
+        }
+    }
+
+    public static final class Ed25519
+        extends KeyPairGeneratorSpi
+    {
+        public Ed25519()
+        {
+            super(Ed25519, new Ed25519KeyPairGenerator());
+        }
+    }
+
+    public static final class XDH
+        extends KeyPairGeneratorSpi
+    {
+        public XDH()
+        {
+            super(XDH, null);
+        }
+    }
+
+    public static final class X448
+        extends KeyPairGeneratorSpi
+    {
+        public X448()
+        {
+            super(X448, new X448KeyPairGenerator());
+        }
+    }
+
+    public static final class X25519
+        extends KeyPairGeneratorSpi
+    {
+        public X25519()
+        {
+            super(X25519, new X25519KeyPairGenerator());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/SignatureSpi.java
new file mode 100644
index 0000000..a88bb2c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/SignatureSpi.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+import org.bouncycastle.crypto.signers.Ed448Signer;
+
+public class SignatureSpi
+    extends java.security.SignatureSpi
+{
+    private static final byte[] EMPTY_CONTEXT = new byte[0];
+
+    private final String algorithm;
+
+    private Signer signer;
+
+    SignatureSpi(String algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    protected void engineInitVerify(PublicKey publicKey)
+        throws InvalidKeyException
+    {
+        if (publicKey instanceof BCEdDSAPublicKey)
+        {
+            AsymmetricKeyParameter pub = ((BCEdDSAPublicKey)publicKey).engineGetKeyParameters();
+
+            if (pub instanceof Ed448PublicKeyParameters)
+            {
+                signer = getSigner("Ed448");
+            }
+            else
+            {
+                signer = getSigner("Ed25519");
+            }
+
+            signer.init(false, pub);
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot identify EdDSA public key");
+        }
+    }
+
+    protected void engineInitSign(PrivateKey privateKey)
+        throws InvalidKeyException
+    {
+        if (privateKey instanceof BCEdDSAPrivateKey)
+        {
+            AsymmetricKeyParameter priv = ((BCEdDSAPrivateKey)privateKey).engineGetKeyParameters();
+
+            if (priv instanceof Ed448PrivateKeyParameters)
+            {
+                signer = getSigner("Ed448");
+            }
+            else
+            {
+                signer = getSigner("Ed25519");
+            }
+
+            signer.init(true, priv);
+        }
+        else
+        {
+            throw new InvalidKeyException("cannot identify EdDSA public key");
+        }
+    }
+
+    private Signer getSigner(String alg)
+        throws InvalidKeyException
+    {
+        if (algorithm != null && !alg.equals(algorithm))
+        {
+            throw new InvalidKeyException("inappropriate key for " + algorithm);
+        }
+
+        if (alg.equals("Ed448"))
+        {
+            return new Ed448Signer(EMPTY_CONTEXT);
+        }
+        else
+        {
+            return new Ed25519Signer();
+        }
+    }
+
+    protected void engineUpdate(byte b)
+        throws SignatureException
+    {
+        signer.update(b);
+    }
+
+    protected void engineUpdate(byte[] bytes, int off, int len)
+        throws SignatureException
+    {
+        signer.update(bytes, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        try
+        {
+            return signer.generateSignature();
+        }
+        catch (CryptoException e)
+        {
+            throw new SignatureException(e.getMessage());
+        }
+    }
+
+    protected boolean engineVerify(byte[] signature)
+        throws SignatureException
+    {
+        return signer.verifySignature(signature);
+    }
+
+    protected void engineSetParameter(String s, Object o)
+        throws InvalidParameterException
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    protected Object engineGetParameter(String s)
+        throws InvalidParameterException
+    {
+        throw new UnsupportedOperationException("engineGetParameter unsupported");
+    }
+
+    public final static class EdDSA
+        extends SignatureSpi
+    {
+        public EdDSA()
+        {
+            super(null);
+        }
+    }
+
+    public final static class Ed448
+        extends SignatureSpi
+    {
+        public Ed448()
+        {
+            super("Ed448");
+        }
+    }
+
+    public final static class Ed25519
+        extends SignatureSpi
+    {
+        public Ed25519()
+        {
+            super("Ed25519");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/Utils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/Utils.java
new file mode 100644
index 0000000..c3081ac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/edec/Utils.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.jcajce.provider.asymmetric.edec;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
+import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
+import org.bouncycastle.crypto.params.X448PublicKeyParameters;
+import org.bouncycastle.util.Fingerprint;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+class Utils
+{
+    static boolean isValidPrefix(byte[] prefix, byte[] encoding)
+    {
+        if (encoding.length < prefix.length)
+        {
+            return !isValidPrefix(prefix, prefix);
+        }
+
+        int nonEqual = 0;
+
+        for (int i = 0; i != prefix.length; i++)
+        {
+            nonEqual |= (prefix[i] ^ encoding[i]);
+        }
+
+        return nonEqual == 0;
+    }
+
+    static String keyToString(String label, String algorithm, AsymmetricKeyParameter pubKey)
+    {
+        StringBuffer buf = new StringBuffer();
+        String nl = Strings.lineSeparator();
+
+        byte[] keyBytes;
+        if (pubKey instanceof X448PublicKeyParameters)
+        {
+            keyBytes = ((X448PublicKeyParameters)pubKey).getEncoded();
+        }
+        else if (pubKey instanceof Ed448PublicKeyParameters)
+        {
+            keyBytes = ((Ed448PublicKeyParameters)pubKey).getEncoded();
+        }
+        else if (pubKey instanceof X25519PublicKeyParameters)
+        {
+            keyBytes = ((X25519PublicKeyParameters)pubKey).getEncoded();
+        }
+        else
+        {
+            keyBytes = ((Ed25519PublicKeyParameters)pubKey).getEncoded();
+        }
+
+        buf.append(algorithm)
+            .append(" ")
+            .append(label).append(" [")
+            .append(Utils.generateKeyFingerprint(keyBytes))
+            .append("]")
+            .append(nl)
+            .append("    public data: ")
+            .append(Hex.toHexString(keyBytes))
+            .append(nl);
+
+        return buf.toString();
+    }
+
+    private static String generateKeyFingerprint(byte[] keyBytes)
+    {
+        return new Fingerprint(keyBytes).toString();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java
index 6097c3c..18b9d9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java
@@ -8,6 +8,7 @@
 import javax.crypto.spec.DHGenParameterSpec;
 import javax.crypto.spec.DHParameterSpec;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
 import org.bouncycastle.crypto.params.ElGamalParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAlgorithmParameterGeneratorSpi;
@@ -54,7 +55,7 @@
         }
         else
         {
-            pGen.init(strength, 20, new SecureRandom());
+            pGen.init(strength, 20, CryptoServicesRegistrar.getSecureRandom());
         }
 
         ElGamalParameters p = pGen.generateParameters();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java
index 1cb1548..06ca108 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java
@@ -15,12 +15,15 @@
 import javax.crypto.BadPaddingException;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
 import javax.crypto.interfaces.DHKey;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.OAEPParameterSpec;
 import javax.crypto.spec.PSource;
 
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.BufferedAsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
@@ -33,21 +36,20 @@
 import org.bouncycastle.jcajce.provider.util.BadBlockException;
 import org.bouncycastle.jcajce.provider.util.DigestFactory;
 import org.bouncycastle.jce.interfaces.ElGamalKey;
-import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
-import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
 import org.bouncycastle.util.Strings;
 
 public class CipherSpi
     extends BaseCipherSpi
 {
-    private BufferedAsymmetricBlockCipher   cipher;
-    private AlgorithmParameterSpec          paramSpec;
-    private AlgorithmParameters             engineParams;
+    private AsymmetricBlockCipher   cipher;
+    private AlgorithmParameterSpec  paramSpec;
+    private AlgorithmParameters     engineParams;
+    private ErasableOutputStream    bOut = new ErasableOutputStream();
 
     public CipherSpi(
         AsymmetricBlockCipher engine)
     {
-        cipher = new BufferedAsymmetricBlockCipher(engine);
+        cipher = engine;
     }
    
     private void initFromSpec(
@@ -62,7 +64,7 @@
             throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm());
         }
 
-        cipher = new BufferedAsymmetricBlockCipher(new OAEPEncoding(new ElGamalEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue()));        
+        cipher = new OAEPEncoding(new ElGamalEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue());
         paramSpec = pSpec;
     }
     
@@ -139,15 +141,15 @@
 
         if (pad.equals("NOPADDING"))
         {
-            cipher = new BufferedAsymmetricBlockCipher(new ElGamalEngine());
+            cipher = new ElGamalEngine();
         }
         else if (pad.equals("PKCS1PADDING"))
         {
-            cipher = new BufferedAsymmetricBlockCipher(new PKCS1Encoding(new ElGamalEngine()));
+            cipher = new PKCS1Encoding(new ElGamalEngine());
         }
         else if (pad.equals("ISO9796-1PADDING"))
         {
-            cipher = new BufferedAsymmetricBlockCipher(new ISO9796d1Encoding(new ElGamalEngine()));
+            cipher = new ISO9796d1Encoding(new ElGamalEngine());
         }
         else if (pad.equals("OAEPPADDING"))
         {
@@ -177,6 +179,22 @@
         {
             initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT));
         }
+        else if (pad.equals("OAEPWITHSHA3-224ANDMGF1PADDING"))
+        {
+            initFromSpec(new OAEPParameterSpec("SHA3-224", "MGF1", new MGF1ParameterSpec("SHA3-224"), PSource.PSpecified.DEFAULT));
+        }
+        else if (pad.equals("OAEPWITHSHA3-256ANDMGF1PADDING"))
+        {
+            initFromSpec(new OAEPParameterSpec("SHA3-256", "MGF1", new MGF1ParameterSpec("SHA3-256"), PSource.PSpecified.DEFAULT));
+        }
+        else if (pad.equals("OAEPWITHSHA3-384ANDMGF1PADDING"))
+        {
+            initFromSpec(new OAEPParameterSpec("SHA3-384", "MGF1", new MGF1ParameterSpec("SHA3-384"), PSource.PSpecified.DEFAULT));
+        }
+        else if (pad.equals("OAEPWITHSHA3-512ANDMGF1PADDING"))
+        {
+            initFromSpec(new OAEPParameterSpec("SHA3-512", "MGF1", new MGF1ParameterSpec("SHA3-512"), PSource.PSpecified.DEFAULT));
+        }
         else
         {
             throw new NoSuchPaddingException(padding + " unavailable with ElGamal.");
@@ -187,29 +205,60 @@
         int                     opmode,
         Key                     key,
         AlgorithmParameterSpec  params,
-        SecureRandom            random) 
-    throws InvalidKeyException
+        SecureRandom            random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
     {
         CipherParameters        param;
 
-        if (params == null)
+        if (key instanceof DHPublicKey)
         {
-            if (key instanceof ElGamalPublicKey)
-            {
-                param = ElGamalUtil.generatePublicKeyParameter((PublicKey)key);
-            }
-            else if (key instanceof ElGamalPrivateKey)
-            {
-                param = ElGamalUtil.generatePrivateKeyParameter((PrivateKey)key);
-            }
-            else
-            {
-                throw new InvalidKeyException("unknown key type passed to ElGamal");
-            }
+            param = ElGamalUtil.generatePublicKeyParameter((PublicKey)key);
+        }
+        else if (key instanceof DHPrivateKey)
+        {
+            param = ElGamalUtil.generatePrivateKeyParameter((PrivateKey)key);
         }
         else
         {
-            throw new IllegalArgumentException("unknown parameter type.");
+            throw new InvalidKeyException("unknown key type passed to ElGamal");
+        }
+
+        if (params instanceof OAEPParameterSpec)
+        {
+            OAEPParameterSpec spec = (OAEPParameterSpec)params;
+
+            paramSpec = params;
+
+            if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !spec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId()))
+            {
+                throw new InvalidAlgorithmParameterException("unknown mask generation function specified");
+            }
+
+            if (!(spec.getMGFParameters() instanceof MGF1ParameterSpec))
+            {
+                throw new InvalidAlgorithmParameterException("unkown MGF parameters");
+            }
+
+            Digest digest = DigestFactory.getDigest(spec.getDigestAlgorithm());
+
+            if (digest == null)
+            {
+                throw new InvalidAlgorithmParameterException("no match on digest algorithm: "+ spec.getDigestAlgorithm());
+            }
+
+            MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)spec.getMGFParameters();
+            Digest mgfDigest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm());
+
+            if (mgfDigest == null)
+            {
+                throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm());
+            }
+
+            cipher = new OAEPEncoding(new ElGamalEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue());
+        }
+        else if (params != null)
+        {
+            throw new InvalidAlgorithmParameterException("unknown parameter type.");
         }
 
         if (random != null)
@@ -248,7 +297,15 @@
         SecureRandom        random) 
     throws InvalidKeyException
     {
-        engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+        try
+        {
+            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            // this shouldn't happen
+            throw new InvalidKeyException("Eeeek! " + e.toString(), e);
+        }
     }
 
     protected byte[] engineUpdate(
@@ -256,7 +313,7 @@
         int     inputOffset,
         int     inputLen) 
     {
-        cipher.processBytes(input, inputOffset, inputLen);
+        bOut.write(input, inputOffset, inputLen);
         return null;
     }
 
@@ -267,17 +324,36 @@
         byte[]  output,
         int     outputOffset) 
     {
-        cipher.processBytes(input, inputOffset, inputLen);
+        bOut.write(input, inputOffset, inputLen);
         return 0;
     }
 
+
     protected byte[] engineDoFinal(
         byte[]  input,
         int     inputOffset,
-        int     inputLen) 
+        int     inputLen)
         throws IllegalBlockSizeException, BadPaddingException
     {
-        cipher.processBytes(input, inputOffset, inputLen);
+        if (input != null)
+        {
+            bOut.write(input, inputOffset, inputLen);
+        }
+
+        if (cipher instanceof ElGamalEngine)
+        {
+            if (bOut.size() > cipher.getInputBlockSize() + 1)
+            {
+                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
+            }
+        }
+        else
+        {
+            if (bOut.size() > cipher.getInputBlockSize())
+            {
+                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
+            }
+        }
 
         return getOutput();
     }
@@ -287,10 +363,33 @@
         int     inputOffset,
         int     inputLen,
         byte[]  output,
-        int     outputOffset) 
-        throws IllegalBlockSizeException, BadPaddingException
+        int     outputOffset)
+        throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
     {
-        cipher.processBytes(input, inputOffset, inputLen);
+        if (outputOffset + engineGetOutputSize(inputLen) > output.length)
+        {
+            throw new ShortBufferException("output buffer too short for input.");
+        }
+
+        if (input != null)
+        {
+            bOut.write(input, inputOffset, inputLen);
+        }
+
+        if (cipher instanceof ElGamalEngine)
+        {
+            if (bOut.size() > cipher.getInputBlockSize() + 1)
+            {
+                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
+            }
+        }
+        else
+        {
+            if (bOut.size() > cipher.getInputBlockSize())
+            {
+                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
+            }
+        }
 
         byte[]  out = getOutput();
 
@@ -307,22 +406,20 @@
     {
         try
         {
-            return cipher.doFinal();
+            return cipher.processBlock(bOut.getBuf(), 0, bOut.size());
         }
-        catch (final InvalidCipherTextException e)
+        catch (InvalidCipherTextException e)
         {
-            throw new BadPaddingException("unable to decrypt block")
-            {
-                public synchronized Throwable getCause()
-                {
-                    return e;
-                }
-            };
+            throw new BadBlockException("unable to decrypt block", e);
         }
         catch (ArrayIndexOutOfBoundsException e)
         {
             throw new BadBlockException("unable to decrypt block", e);
         }
+        finally
+        {
+            bOut.erase();
+        }
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java
index 9455ece..0888543 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java
@@ -8,6 +8,7 @@
 import javax.crypto.spec.DHParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
 import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
 import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
@@ -24,7 +25,7 @@
     ElGamalKeyPairGenerator engine = new ElGamalKeyPairGenerator();
     int strength = 1024;
     int certainty = 20;
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public KeyPairGeneratorSpi()
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java
index 2e7ee7c..e706e68 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java
@@ -5,6 +5,7 @@
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.GOST3410ParametersGenerator;
 import org.bouncycastle.crypto.params.GOST3410Parameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAlgorithmParameterGeneratorSpi;
@@ -43,7 +44,7 @@
         }
         else
         {
-            pGen.init(strength, 2, new SecureRandom());
+            pGen.init(strength, 2, CryptoServicesRegistrar.getSecureRandom());
         }
 
         GOST3410Parameters p = pGen.generateParameters();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java
index 8da4998..8992d5f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java
@@ -4,19 +4,21 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.math.BigInteger;
+import java.security.InvalidKeyException;
 import java.util.Enumeration;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.GOST3410Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import org.bouncycastle.jce.interfaces.GOST3410Params;
 import org.bouncycastle.jce.interfaces.GOST3410PrivateKey;
@@ -57,17 +59,28 @@
         PrivateKeyInfo info)
         throws IOException
     {
-        GOST3410PublicKeyAlgParameters    params = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters());
-        ASN1OctetString      derX = ASN1OctetString.getInstance(info.parsePrivateKey());
-        byte[]              keyEnc = derX.getOctets();
-        byte[]              keyBytes = new byte[keyEnc.length];
-        
-        for (int i = 0; i != keyEnc.length; i++)
+        GOST3410PublicKeyAlgParameters    params = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+
+        ASN1Encodable privKey = info.parsePrivateKey();
+
+        if (privKey instanceof ASN1Integer)
         {
-            keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // was little endian
+            this.x = ASN1Integer.getInstance(privKey).getPositiveValue();
         }
-        
-        this.x = new BigInteger(1, keyBytes);
+        else
+        {
+            ASN1OctetString derX = ASN1OctetString.getInstance(info.parsePrivateKey());
+            byte[] keyEnc = derX.getOctets();
+            byte[] keyBytes = new byte[keyEnc.length];
+
+            for (int i = 0; i != keyEnc.length; i++)
+            {
+                keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // was little endian
+            }
+
+            this.x = new BigInteger(1, keyBytes);
+        }
+
         this.gost3410Spec = GOST3410ParameterSpec.fromPublicKeyAlg(params);
     }
 
@@ -190,6 +203,19 @@
         return this.getX().hashCode() ^ gost3410Spec.hashCode();
     }
 
+    public String toString()
+    {
+        try
+        {
+            return GOSTUtil.privateKeyToString("GOST3410", x,
+                ((GOST3410PrivateKeyParameters)GOST3410Util.generatePrivateKeyParameter(this)).getParameters());
+        }
+        catch (InvalidKeyException e)
+        {
+            throw new IllegalStateException(e.getMessage()); // should not be possible
+        }
+    }
+
     public void setBagAttribute(
         ASN1ObjectIdentifier oid,
         ASN1Encodable        attribute)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java
index a3260cf..6a80e30 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java
@@ -4,22 +4,22 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.math.BigInteger;
+import java.security.InvalidKeyException;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.GOST3410Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
 import org.bouncycastle.jce.interfaces.GOST3410Params;
 import org.bouncycastle.jce.interfaces.GOST3410PublicKey;
 import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
 import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
 import org.bouncycastle.jce.spec.GOST3410PublicKeySpec;
-import org.bouncycastle.util.Strings;
 
 public class BCGOST3410PublicKey
     implements GOST3410PublicKey
@@ -62,7 +62,7 @@
     BCGOST3410PublicKey(
         SubjectPublicKeyInfo info)
     {
-        GOST3410PublicKeyAlgParameters    params = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters());
+        GOST3410PublicKeyAlgParameters    params = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters());
         DEROctetString                    derY;
 
         try
@@ -155,13 +155,15 @@
 
     public String toString()
     {
-        StringBuffer    buf = new StringBuffer();
-        String          nl = Strings.lineSeparator();
-
-        buf.append("GOST3410 Public Key").append(nl);
-        buf.append("            y: ").append(this.getY().toString(16)).append(nl);
-
-        return buf.toString();
+        try
+        {
+            return GOSTUtil.publicKeyToString("GOST3410", y,
+                ((GOST3410PublicKeyParameters)GOST3410Util.generatePublicKeyParameter(this)).getParameters());
+        }
+        catch (InvalidKeyException e)
+        {
+            throw new IllegalStateException(e.getMessage()); // should not be possible
+        }
     }
     
     public boolean equals(Object o)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/GOSTUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/GOSTUtil.java
new file mode 100644
index 0000000..772f253
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/GOSTUtil.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.jcajce.provider.asymmetric.gost;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Fingerprint;
+import org.bouncycastle.util.Strings;
+
+class GOSTUtil
+{
+    static String privateKeyToString(String algorithm, BigInteger x, GOST3410Parameters gostParams)
+    {
+        StringBuffer buf = new StringBuffer();
+        String        nl = Strings.lineSeparator();
+
+        BigInteger y = gostParams.getA().modPow(x, gostParams.getP());
+
+        buf.append(algorithm);
+        buf.append(" Private Key [").append(generateKeyFingerprint(y, gostParams)).append("]").append(nl);
+        buf.append("                  Y: ").append(y.toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    static String publicKeyToString(String algorithm, BigInteger y, GOST3410Parameters gostParams)
+    {
+        StringBuffer buf = new StringBuffer();
+        String nl = Strings.lineSeparator();
+
+        buf.append(algorithm);
+        buf.append(" Public Key [").append(generateKeyFingerprint(y, gostParams)).append("]").append(nl);
+        buf.append("                 Y: ").append(y.toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    private static String generateKeyFingerprint(BigInteger y, GOST3410Parameters dhParams)
+    {
+            return new Fingerprint(
+                Arrays.concatenate(
+                    y.toByteArray(),
+                    dhParams.getP().toByteArray(), dhParams.getA().toByteArray())).toString();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java
index 0a6a40e..4d2bf86 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java
@@ -7,6 +7,7 @@
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
 import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
 import org.bouncycastle.crypto.params.GOST3410Parameters;
@@ -69,7 +70,7 @@
     {
         if (!initialised)
         {
-            init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom());
+            init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), CryptoServicesRegistrar.getSecureRandom());
         }
 
         AsymmetricCipherKeyPair pair = engine.generateKeyPair();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java
index 30a6660..65ed09f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java
@@ -12,24 +12,24 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.GOST3411Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.GOST3410Signer;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.GOST3410Util;
 import org.bouncycastle.jce.interfaces.ECKey;
 import org.bouncycastle.jce.interfaces.ECPublicKey;
 import org.bouncycastle.jce.interfaces.GOST3410Key;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jcajce.provider.asymmetric.util.GOST3410Util;
 
 public class SignatureSpi
     extends java.security.SignatureSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
     private Digest                  digest;
-    private DSA                     signer;
+    private DSAExt                  signer;
     private SecureRandom            random;
 
     public SignatureSpi()
@@ -209,7 +209,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(
         String  param,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
index e60c36a..fc37d6e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
@@ -95,7 +95,7 @@
             {
                 return currentSpec;
             }
-    
+
             throw new InvalidParameterSpecException("unknown parameter spec passed to OAEP parameters object.");
         }
     
@@ -203,11 +203,11 @@
             Class paramSpec)
             throws InvalidParameterSpecException
         {
-            if (paramSpec == PSSParameterSpec.class && currentSpec != null)
+            if (paramSpec == PSSParameterSpec.class || paramSpec == AlgorithmParameterSpec.class)
             {
                 return currentSpec;
             }
-    
+
             throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
         }
     
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java
index d81d197..0ac494f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java
@@ -227,16 +227,15 @@
         StringBuffer    buf = new StringBuffer();
         String          nl = Strings.lineSeparator();
 
-        buf.append("RSA Private CRT Key").append(nl);
-        buf.append("            modulus: ").append(this.getModulus().toString(16)).append(nl);
-        buf.append("    public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
-        buf.append("   private exponent: ").append(this.getPrivateExponent().toString(16)).append(nl);
-        buf.append("             primeP: ").append(this.getPrimeP().toString(16)).append(nl);
-        buf.append("             primeQ: ").append(this.getPrimeQ().toString(16)).append(nl);
-        buf.append("     primeExponentP: ").append(this.getPrimeExponentP().toString(16)).append(nl);
-        buf.append("     primeExponentQ: ").append(this.getPrimeExponentQ().toString(16)).append(nl);
-        buf.append("     crtCoefficient: ").append(this.getCrtCoefficient().toString(16)).append(nl);
-
+        buf.append("RSA Private CRT Key [").append(
+                    RSAUtil.generateKeyFingerprint(this.getModulus())).append("]")
+            .append(",[")
+            .append(RSAUtil.generateExponentFingerprint(this.getPublicExponent()))
+            .append("]")
+            .append(nl);
+        buf.append("             modulus: ").append(this.getModulus().toString(16)).append(nl);
+        buf.append("     public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
+        
         return buf.toString();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java
index b82c5f8..f529d9b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java
@@ -17,6 +17,7 @@
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Strings;
 
 public class BCRSAPrivateKey
     implements RSAPrivateKey, PKCS12BagAttributeCarrier
@@ -142,4 +143,16 @@
     {
         out.defaultWriteObject();
     }
+
+    public String toString()
+    {
+        StringBuffer    buf = new StringBuffer();
+        String          nl = Strings.lineSeparator();
+
+        buf.append("RSA Private Key [").append(
+                    RSAUtil.generateKeyFingerprint(this.getModulus())).append("],[]").append(nl);
+        buf.append("            modulus: ").append(this.getModulus().toString(16)).append(nl);
+
+        return buf.toString();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
index 669cf2b..dd53dce 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
@@ -1,10 +1,8 @@
 package org.bouncycastle.jcajce.provider.asymmetric.rsa;
 
-import java.io.EOFException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.io.OptionalDataException;
 import java.math.BigInteger;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.RSAPublicKeySpec;
@@ -137,10 +135,14 @@
         StringBuffer    buf = new StringBuffer();
         String          nl = Strings.lineSeparator();
 
-        buf.append("RSA Public Key").append(nl);
-        buf.append("            modulus: ").append(this.getModulus().toString(16)).append(nl);
-        buf.append("    public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
-
+        buf.append("RSA Public Key [").append(RSAUtil.generateKeyFingerprint(this.getModulus())).append("]")
+            .append(",[")
+            .append(RSAUtil.generateExponentFingerprint(this.getPublicExponent()))
+            .append("]")
+            .append(nl);
+        buf.append("        modulus: ").append(this.getModulus().toString(16)).append(nl);
+        buf.append("public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
+        
         return buf.toString();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
index 1edab93..cb02462 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.jcajce.provider.asymmetric.rsa;
 
-import java.io.ByteArrayOutputStream;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -18,12 +17,14 @@
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
 import javax.crypto.spec.OAEPParameterSpec;
 import javax.crypto.spec.PSource;
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
@@ -43,12 +44,12 @@
 {
     private final JcaJceHelper helper = new BCJcaJceHelper();
 
-    private AsymmetricBlockCipher cipher;
-    private AlgorithmParameterSpec paramSpec;
-    private AlgorithmParameters engineParams;
+    private AsymmetricBlockCipher   cipher;
+    private AlgorithmParameterSpec  paramSpec;
+    private AlgorithmParameters     engineParams;
     private boolean                 publicKeyOnly = false;
     private boolean                 privateKeyOnly = false;
-    private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+    private ErasableOutputStream    bOut = new ErasableOutputStream();
 
     public CipherSpi(
         AsymmetricBlockCipher engine)
@@ -338,7 +339,7 @@
             }
             else
             {
-                param = new ParametersWithRandom(param, new SecureRandom());
+                param = new ParametersWithRandom(param, CryptoServicesRegistrar.getSecureRandom());
             }
         }
 
@@ -488,8 +489,13 @@
         int     inputLen,
         byte[]  output,
         int     outputOffset) 
-        throws IllegalBlockSizeException, BadPaddingException
+        throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
     {
+        if (outputOffset + engineGetOutputSize(inputLen) > output.length)
+        {
+            throw new ShortBufferException("output buffer too short for input.");
+        }
+
         if (input != null)
         {
             bOut.write(input, inputOffset, inputLen);
@@ -525,9 +531,7 @@
     {
         try
         {
-            byte[]  bytes = bOut.toByteArray();
-
-            return cipher.processBlock(bytes, 0, bytes.length);
+            return cipher.processBlock(bOut.getBuf(), 0, bOut.size());
         }
         catch (InvalidCipherTextException e)
         {
@@ -539,7 +543,7 @@
         }
         finally
         {
-            bOut.reset();
+            bOut.erase();
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java
index 6dd055f..bc00222 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java
@@ -171,15 +171,14 @@
         }
         else if (sig.length == expected.length - 2)  // NULL left out
         {
-            int sigOffset = sig.length - hash.length - 2;
-            int expectedOffset = expected.length - hash.length - 2;
-
             expected[1] -= 2;      // adjust lengths
             expected[3] -= 2;
 
+            int sigOffset = 4 + expected[3];
+            int expectedOffset = sigOffset + 2;
             int nonEqual = 0;
 
-            for (int i = 0; i < hash.length; i++)
+            for (int i = 0; i < expected.length - expectedOffset; i++)
             {
                 nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]);
             }
@@ -206,7 +205,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(
         String param,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java
index 80690f7..8e5d951 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java
@@ -18,8 +18,15 @@
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
+import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ExtendedInvalidKeySpecException;
+import org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPublicKeySpec;
 
 public class KeyFactorySpi
     extends BaseKeyFactorySpi
@@ -56,6 +63,44 @@
                 k.getPrimeExponentP(), k.getPrimeExponentQ(),
                 k.getCrtCoefficient());
         }
+        else if (spec.isAssignableFrom(OpenSSHPublicKeySpec.class) && key instanceof RSAPublicKey)
+        {
+            try
+            {
+                return new OpenSSHPublicKeySpec(
+                    OpenSSHPublicKeyUtil.encodePublicKey(
+                        new RSAKeyParameters(
+                            false,
+                            ((RSAPublicKey)key).getModulus(),
+                            ((RSAPublicKey)key).getPublicExponent())
+                    )
+                );
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("unable to produce encoding: " + e.getMessage());
+            }
+        }
+        else if (spec.isAssignableFrom(OpenSSHPrivateKeySpec.class) && key instanceof RSAPrivateCrtKey)
+        {
+            try
+            {
+                return new OpenSSHPrivateKeySpec(OpenSSHPrivateKeyUtil.encodePrivateKey(new RSAPrivateCrtKeyParameters(
+                    ((RSAPrivateCrtKey)key).getModulus(),
+                    ((RSAPrivateCrtKey)key).getPublicExponent(),
+                    ((RSAPrivateCrtKey)key).getPrivateExponent(),
+                    ((RSAPrivateCrtKey)key).getPrimeP(),
+                    ((RSAPrivateCrtKey)key).getPrimeQ(),
+                    ((RSAPrivateCrtKey)key).getPrimeExponentP(),
+                    ((RSAPrivateCrtKey)key).getPrimeExponentQ(),
+                    ((RSAPrivateCrtKey)key).getCrtCoefficient()
+                )));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("unable to produce encoding: " + e.getMessage());
+            }
+        }
 
         return super.engineGetKeySpec(key, spec);
     }
@@ -114,8 +159,19 @@
         {
             return new BCRSAPrivateKey((RSAPrivateKeySpec)keySpec);
         }
+        else if (keySpec instanceof OpenSSHPrivateKeySpec)
+        {
+            CipherParameters parameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(((OpenSSHPrivateKeySpec)keySpec).getEncoded());
 
-        throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+            if (parameters instanceof RSAPrivateCrtKeyParameters)
+            {
+                return new BCRSAPrivateCrtKey((RSAPrivateCrtKeyParameters)parameters);
+            }
+
+            throw new InvalidKeySpecException("open SSH public key is not RSA private key");
+        }
+
+        throw new InvalidKeySpecException("unknown KeySpec type: " + keySpec.getClass().getName());
     }
 
     protected PublicKey engineGeneratePublic(
@@ -126,6 +182,18 @@
         {
             return new BCRSAPublicKey((RSAPublicKeySpec)keySpec);
         }
+        else if (keySpec instanceof OpenSSHPublicKeySpec)
+        {
+
+            CipherParameters parameters = OpenSSHPublicKeyUtil.parsePublicKey(((OpenSSHPublicKeySpec)keySpec).getEncoded());
+            if (parameters instanceof RSAKeyParameters)
+            {
+                return new BCRSAPublicKey((RSAKeyParameters)parameters);
+            }
+
+            throw new InvalidKeySpecException("Open SSH public key is not RSA public key");
+
+        }
 
         return super.engineGeneratePublic(keySpec);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
index 4159241..4334815 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
@@ -8,6 +8,7 @@
 import java.security.spec.RSAKeyGenParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
 import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
@@ -34,7 +35,7 @@
 
         engine = new RSAKeyPairGenerator();
         param = new RSAKeyGenerationParameters(defaultPublicExponent,
-            new SecureRandom(), 2048, PrimeCertaintyCalculator.getDefaultCertainty(2048));
+            CryptoServicesRegistrar.getSecureRandom(), 2048, PrimeCertaintyCalculator.getDefaultCertainty(2048));
         engine.init(param);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java
index 5532b49..f8872f8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java
@@ -5,6 +5,7 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
+import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.SignatureException;
@@ -21,6 +22,7 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.engines.RSABlindedEngine;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.jcajce.provider.util.DigestFactory;
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
@@ -39,8 +41,10 @@
     private int saltLength;
     private byte trailer;
     private boolean isRaw;
+    private RSAKeyParameters key;
 
     private org.bouncycastle.crypto.signers.PSSSigner pss;
+    private boolean isInitState = true;
 
     private byte getTrailer(
         int trailerField)
@@ -108,9 +112,10 @@
             throw new InvalidKeyException("Supplied key is not a RSAPublicKey instance");
         }
 
+        key = RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey);
         pss = new org.bouncycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer);
-        pss.init(false,
-            RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey));
+        pss.init(false, key);
+        isInitState = true;
     }
 
     protected void engineInitSign(
@@ -123,8 +128,10 @@
             throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
         }
 
+        key = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey);
         pss = new org.bouncycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer);
-        pss.init(true, new ParametersWithRandom(RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey), random));
+        pss.init(true, new ParametersWithRandom(key, random));
+        isInitState = true;
     }
 
     protected void engineInitSign(
@@ -136,8 +143,10 @@
             throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
         }
 
+        key = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey);
         pss = new org.bouncycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer);
-        pss.init(true, RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey));
+        pss.init(true, key);
+        isInitState = true;
     }
 
     protected void engineUpdate(
@@ -145,6 +154,7 @@
         throws SignatureException
     {
         pss.update(b);
+        isInitState = false;
     }
 
     protected void engineUpdate(
@@ -154,11 +164,13 @@
         throws SignatureException
     {
         pss.update(b, off, len);
+        isInitState = false;
     }
 
     protected byte[] engineSign()
         throws SignatureException
     {
+        isInitState = true;
         try
         {
             return pss.generateSignature();
@@ -173,6 +185,7 @@
         byte[]  sigBytes) 
         throws SignatureException
     {
+        isInitState = true;
         return pss.verifySignature(sigBytes);
     }
 
@@ -180,6 +193,23 @@
         AlgorithmParameterSpec params)
         throws InvalidAlgorithmParameterException
     {
+        if (params == null)
+        {
+            if (originalSpec != null)
+            {
+                params = originalSpec;
+            }
+            else
+            {
+                return;  // Java 11 bug
+            }
+        }
+
+        if (!isInitState)
+        {
+            throw new ProviderException("cannot call setParameter in the middle of update");
+        }
+
         if (params instanceof PSSParameterSpec)
         {
             PSSParameterSpec newParamSpec = (PSSParameterSpec)params;
@@ -222,6 +252,19 @@
             this.trailer = getTrailer(paramSpec.getTrailerField());
 
             setupContentDigest();
+
+            if (key != null)
+            {
+                pss = new org.bouncycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer);
+                if (key.isPrivate())
+                {
+                    pss.init(true, key);
+                }
+                else
+                {
+                    pss.init(false, key);
+                }
+            }
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java
index 4943a99..4d046b8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.asymmetric.rsa;
 
+import java.math.BigInteger;
 import java.security.interfaces.RSAPrivateCrtKey;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
@@ -9,6 +10,7 @@
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.Fingerprint;
 
 /**
  * utility class for converting java.security RSA objects into their
@@ -63,4 +65,14 @@
             return new RSAKeyParameters(true, k.getModulus(), k.getPrivateExponent());
         }
     }
+
+    static String generateKeyFingerprint(BigInteger modulus)
+    {
+        return new Fingerprint(modulus.toByteArray()).toString();
+    }
+
+    static String generateExponentFingerprint(BigInteger exponent)
+    {
+        return new Fingerprint(exponent.toByteArray(), 32).toString();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
index 3c0800e..abac079 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
@@ -24,6 +24,7 @@
 import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
 import org.bouncycastle.crypto.params.DESParameters;
 import org.bouncycastle.crypto.params.KDFParameters;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Strings;
 
@@ -79,6 +80,10 @@
         keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), i192);
         keySizes.put(OIWObjectIdentifiers.desCBC.getId(), i64);
 
+        keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb.getId(), i256);
+        keySizes.put(CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap.getId(), i256);
+        keySizes.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.getId(), i256);
+
         keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), Integers.valueOf(160));
         keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), i256);
         keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), Integers.valueOf(384));
@@ -135,8 +140,8 @@
         des.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DES");
     }
 
-    private final String kaAlgorithm;
-    private final DerivationFunction kdf;
+    protected final String kaAlgorithm;
+    protected final DerivationFunction kdf;
 
     protected byte[]     ukmParameters;
 
@@ -215,8 +220,15 @@
     {
         if (kdf != null)
         {
-            throw new UnsupportedOperationException(
-                "KDF can only be used when algorithm is known");
+            byte[] secret = calcSecret();
+            try
+            {
+                return getSharedSecretBytes(secret, null, secret.length * 8);
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                throw new IllegalStateException(e.getMessage());
+            }
         }
 
         return calcSecret();
@@ -243,7 +255,6 @@
         String algorithm)
         throws NoSuchAlgorithmException
     {
-        byte[] secret = calcSecret();
         String algKey = Strings.toUpperCase(algorithm);
         String oidAlgorithm = algorithm;
 
@@ -254,6 +265,21 @@
 
         int    keySize = getKeySize(oidAlgorithm);
 
+        byte[] secret = getSharedSecretBytes(calcSecret(), oidAlgorithm, keySize);
+
+        String algName = getAlgorithm(algorithm);
+
+        if (des.containsKey(algName))
+        {
+            DESParameters.setOddParity(secret);
+        }
+
+        return new SecretKeySpec(secret, algName);
+    }
+
+    private byte[] getSharedSecretBytes(byte[] secret, String oidAlgorithm, int keySize)
+        throws NoSuchAlgorithmException
+    {
         if (kdf != null)
         {
             if (keySize < 0)
@@ -264,6 +290,10 @@
 
             if (kdf instanceof DHKEKGenerator)
             {
+                if (oidAlgorithm == null)
+                {
+                    throw new NoSuchAlgorithmException("algorithm OID is null");
+                }
                 ASN1ObjectIdentifier oid;
                 try
                 {
@@ -286,7 +316,9 @@
 
             kdf.generateBytes(keyBytes, 0, keyBytes.length);
 
-            secret = keyBytes;
+            Arrays.clear(secret);
+
+            return keyBytes;
         }
         else
         {
@@ -296,18 +328,13 @@
 
                 System.arraycopy(secret, 0, keyBytes, 0, keyBytes.length);
 
-                secret = keyBytes;
+                Arrays.clear(secret);
+
+                return keyBytes;
             }
+
+            return secret;
         }
-
-        String algName = getAlgorithm(algorithm);
-
-        if (des.containsKey(algName))
-        {
-            DESParameters.setOddParity(secret);
-        }
-
-        return new SecretKeySpec(secret, algName);
     }
 
     protected abstract byte[] calcSecret();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java
index 6747de5..3284ad0 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.asymmetric.util;
 
+import java.io.ByteArrayOutputStream;
 import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -28,6 +29,7 @@
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
 
 public abstract class BaseCipherSpi
     extends CipherSpi
@@ -228,4 +230,23 @@
             throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
         }
     }
+
+    protected static final class ErasableOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ErasableOutputStream()
+        {
+        }
+
+        public byte[] getBuf()
+        {
+            return buf;
+        }
+
+        public void erase()
+        {
+            Arrays.fill(this.buf, (byte)0);
+            reset();
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSABase.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSABase.java
index 463de89..28538fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSABase.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSABase.java
@@ -7,25 +7,26 @@
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
-import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.DSAExt;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.signers.DSAEncoding;
 
 public abstract class DSABase
     extends SignatureSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
-    protected Digest digest;
-    protected DSA                     signer;
-    protected DSAEncoder              encoder;
+    protected Digest        digest;
+    protected DSAExt        signer;
+    protected DSAEncoding   encoding;
 
     protected DSABase(
         Digest                  digest,
-        DSA                     signer,
-        DSAEncoder              encoder)
+        DSAExt                  signer,
+        DSAEncoding             encoding)
     {
         this.digest = digest;
         this.signer = signer;
-        this.encoder = encoder;
+        this.encoding = encoding;
     }
 
     protected void engineUpdate(
@@ -47,15 +48,14 @@
     protected byte[] engineSign()
         throws SignatureException
     {
-        byte[]  hash = new byte[digest.getDigestSize()];
-
+        byte[] hash = new byte[digest.getDigestSize()];
         digest.doFinal(hash, 0);
 
         try
         {
-            BigInteger[]    sig = signer.generateSignature(hash);
+            BigInteger[] sig = signer.generateSignature(hash);
 
-            return encoder.encode(sig[0], sig[1]);
+            return encoding.encode(signer.getOrder(), sig[0], sig[1]);
         }
         catch (Exception e)
         {
@@ -67,15 +67,13 @@
         byte[]  sigBytes) 
         throws SignatureException
     {
-        byte[]  hash = new byte[digest.getDigestSize()];
-
+        byte[] hash = new byte[digest.getDigestSize()];
         digest.doFinal(hash, 0);
 
-        BigInteger[]    sig;
-
+        BigInteger[] sig;
         try
         {
-            sig = encoder.decode(sigBytes);
+            sig = encoding.decode(signer.getOrder(), sigBytes);
         }
         catch (Exception e)
         {
@@ -92,7 +90,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
+     * @deprecated replaced with "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"
      */
     protected void engineSetParameter(
         String  param,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSAEncoder.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSAEncoder.java
index 4ea0ff9..a4d6241 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSAEncoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DSAEncoder.java
@@ -3,6 +3,9 @@
 import java.io.IOException;
 import java.math.BigInteger;
 
+/**
+ * @deprecated No longer used
+ */
 public interface DSAEncoder
 {
     byte[] encode(BigInteger r, BigInteger s)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
index bb35788..de7536b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
@@ -47,12 +47,16 @@
             }
         }
 
-        X9ECParameters c25519 = CustomNamedCurves.getByName("Curve25519");
+        X9ECParameters x9_25519 = CustomNamedCurves.getByName("Curve25519");
+        ECCurve c_25519 = x9_25519.getCurve();
 
         customCurves.put(new ECCurve.Fp(
-            c25519.getCurve().getField().getCharacteristic(),
-            c25519.getCurve().getA().toBigInteger(),
-            c25519.getCurve().getB().toBigInteger()), c25519.getCurve());
+            c_25519.getField().getCharacteristic(),
+            c_25519.getA().toBigInteger(),
+            c_25519.getB().toBigInteger(),
+            c_25519.getOrder(),
+            c_25519.getCofactor()
+            ), c_25519);
     }
 
     public static ECCurve getCurve(
@@ -144,9 +148,7 @@
             ecSpec = new ECNamedCurveSpec(
                 ECUtil.getCurveName(oid),
                 ellipticCurve,
-                new ECPoint(
-                    ecP.getG().getAffineXCoord().toBigInteger(),
-                    ecP.getG().getAffineYCoord().toBigInteger()),
+                convertPoint(ecP.getG()),
                 ecP.getN(),
                 ecP.getH());
         }
@@ -164,9 +166,7 @@
             {
                 ecSpec = new ECParameterSpec(
                     ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getAffineXCoord().toBigInteger(),
-                        ecP.getG().getAffineYCoord().toBigInteger()),
+                    convertPoint(ecP.getG()),
                     ecP.getN(),
                     ecP.getH().intValue());
             }
@@ -174,10 +174,9 @@
             {
                 ecSpec = new ECParameterSpec(
                     ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getAffineXCoord().toBigInteger(),
-                        ecP.getG().getAffineYCoord().toBigInteger()),
-                    ecP.getN(), 1);      // TODO: not strictly correct... need to fix the test data...
+                    convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    1);      // TODO: not strictly correct... need to fix the test data...
             }
         }
 
@@ -189,9 +188,17 @@
     {
         return new ECParameterSpec(
             convertCurve(domainParameters.getCurve(), null),  // JDK 1.5 has trouble with this if it's not null...
-            new ECPoint(
-                domainParameters.getG().getAffineXCoord().toBigInteger(),
-                domainParameters.getG().getAffineYCoord().toBigInteger()),
+            EC5Util.convertPoint(domainParameters.getG()),
+            domainParameters.getN(),
+            domainParameters.getH().intValue());
+    }
+
+    public static ECParameterSpec convertToSpec(
+        ECDomainParameters domainParameters)
+    {
+        return new ECParameterSpec(
+            convertCurve(domainParameters.getCurve(), null),  // JDK 1.5 has trouble with this if it's not null...
+            EC5Util.convertPoint(domainParameters.getG()),
             domainParameters.getN(),
             domainParameters.getH().intValue());
     }
@@ -259,9 +266,7 @@
             return new ECNamedCurveSpec(
                 ((ECNamedCurveParameterSpec)spec).getName(),
                 ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
+                convertPoint(spec.getG()),
                 spec.getN(),
                 spec.getH());
         }
@@ -269,9 +274,7 @@
         {
             return new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                    spec.getG().getAffineXCoord().toBigInteger(),
-                    spec.getG().getAffineYCoord().toBigInteger()),
+                convertPoint(spec.getG()),
                 spec.getN(),
                 spec.getH().intValue());
         }
@@ -283,12 +286,25 @@
     {
         ECCurve curve = convertCurve(ecSpec.getCurve());
 
-        return new org.bouncycastle.jce.spec.ECParameterSpec(
-            curve,
-            convertPoint(curve, ecSpec.getGenerator(), withCompression),
-            ecSpec.getOrder(),
-            BigInteger.valueOf(ecSpec.getCofactor()),
-            ecSpec.getCurve().getSeed());
+        if (ecSpec instanceof ECNamedCurveSpec)
+        {
+            return new org.bouncycastle.jce.spec.ECNamedCurveParameterSpec(
+                ((ECNamedCurveSpec)ecSpec).getName(),
+                curve,
+                convertPoint(curve, ecSpec.getGenerator(), withCompression),
+                ecSpec.getOrder(),
+                BigInteger.valueOf(ecSpec.getCofactor()),
+                ecSpec.getCurve().getSeed());
+        }
+        else
+        {
+            return new org.bouncycastle.jce.spec.ECParameterSpec(
+                curve,
+                convertPoint(curve, ecSpec.getGenerator(), withCompression),
+                ecSpec.getOrder(),
+                BigInteger.valueOf(ecSpec.getCofactor()),
+                ecSpec.getCurve().getSeed());
+        }
     }
 
     public static org.bouncycastle.math.ec.ECPoint convertPoint(
@@ -306,4 +322,13 @@
     {
         return curve.createPoint(point.getAffineX(), point.getAffineY());
     }
+
+    public static ECPoint convertPoint(org.bouncycastle.math.ec.ECPoint point)
+    {
+        point = point.normalize();
+
+        return new ECPoint(
+            point.getAffineXCoord().toBigInteger(),
+            point.getAffineYCoord().toBigInteger());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
index d4d9138..f0511a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
@@ -8,16 +8,9 @@
 import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.anssi.ANSSINamedCurves;
-import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
-import org.bouncycastle.asn1.gm.GMNamedCurves;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.ec.CustomNamedCurves;
@@ -32,6 +25,11 @@
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Fingerprint;
+import org.bouncycastle.util.Strings;
 
 /**
  * utility class for converting jce/jca ECDSA, ECDH, and ECDHC
@@ -233,9 +231,20 @@
                 s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
             }
 
-            return new ECPrivateKeyParameters(
-                            k.getD(),
-                            new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            if (k.getParameters() instanceof ECNamedCurveParameterSpec)
+            {
+                String name = ((ECNamedCurveParameterSpec)k.getParameters()).getName();
+                return new ECPrivateKeyParameters(
+                    k.getD(),
+                    new ECNamedDomainParameters(ECNamedCurveTable.getOID(name),
+                        s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            }
+            else
+            {
+                return new ECPrivateKeyParameters(
+                    k.getD(),
+                    new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            }
         }
         else if (key instanceof java.security.interfaces.ECPrivateKey)
         {
@@ -295,15 +304,12 @@
     public static ASN1ObjectIdentifier getNamedCurveOid(
         String curveName)
     {
-        String name;
+        String name = curveName;
 
-        if (curveName.indexOf(' ') > 0)
+        int spacePos = name.indexOf(' ');
+        if (spacePos > 0)
         {
-            name = curveName.substring(curveName.indexOf(' ') + 1);
-        }
-        else
-        {
-            name = curveName;
+            name = name.substring(spacePos + 1);
         }
 
         try
@@ -312,47 +318,12 @@
             {
                 return new ASN1ObjectIdentifier(name);
             }
-            else
-            {
-                return lookupOidByName(name);
-            }
         }
         catch (IllegalArgumentException ex)
         {
-            return lookupOidByName(name);
-        }
-    }
-
-    private static ASN1ObjectIdentifier lookupOidByName(String name)
-    {
-        ASN1ObjectIdentifier oid = X962NamedCurves.getOID(name);
-
-        if (oid == null)
-        {
-            oid = SECNamedCurves.getOID(name);
-            if (oid == null)
-            {
-                oid = NISTNamedCurves.getOID(name);
-            }
-            if (oid == null)
-            {
-                oid = TeleTrusTNamedCurves.getOID(name);
-            }
-            if (oid == null)
-            {
-                oid = ECGOST3410NamedCurves.getOID(name);
-            }
-            if (oid == null)
-            {
-                oid = ANSSINamedCurves.getOID(name);
-            }
-            if (oid == null)
-            {
-                oid = GMNamedCurves.getOID(name);
-            }
         }
 
-        return oid;
+        return ECNamedCurveTable.getOID(name);
     }
 
     public static ASN1ObjectIdentifier getNamedCurveOid(
@@ -383,23 +354,7 @@
 
         if (params == null)
         {
-            params = X962NamedCurves.getByOID(oid);
-            if (params == null)
-            {
-                params = SECNamedCurves.getByOID(oid);
-            }
-            if (params == null)
-            {
-                params = NISTNamedCurves.getByOID(oid);
-            }
-            if (params == null)
-            {
-                params = TeleTrusTNamedCurves.getByOID(oid);
-            }
-            if (params == null)
-            {
-                params = GMNamedCurves.getByOID(oid);
-            }
+            params = ECNamedCurveTable.getByOID(oid);
         }
 
         return params;
@@ -412,23 +367,7 @@
 
         if (params == null)
         {
-            params = X962NamedCurves.getByName(curveName);
-            if (params == null)
-            {
-                params = SECNamedCurves.getByName(curveName);
-            }
-            if (params == null)
-            {
-                params = NISTNamedCurves.getByName(curveName);
-            }
-            if (params == null)
-            {
-                params = TeleTrusTNamedCurves.getByName(curveName);
-            }
-            if (params == null)
-            {
-                params = GMNamedCurves.getByName(curveName);
-            }
+            params = ECNamedCurveTable.getByName(curveName);
         }
 
         return params;
@@ -437,25 +376,52 @@
     public static String getCurveName(
         ASN1ObjectIdentifier oid)
     {
-        String name = X962NamedCurves.getName(oid);
-        
-        if (name == null)
+        return ECNamedCurveTable.getName(oid);
+    }
+
+    public static String privateKeyToString(String algorithm, BigInteger d, org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        StringBuffer buf = new StringBuffer();
+        String nl = Strings.lineSeparator();
+
+        org.bouncycastle.math.ec.ECPoint q = calculateQ(d, spec);
+
+        buf.append(algorithm);
+        buf.append(" Private Key [").append(ECUtil.generateKeyFingerprint(q, spec)).append("]").append(nl);
+        buf.append("            X: ").append(q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    private static org.bouncycastle.math.ec.ECPoint calculateQ(BigInteger d, org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        return spec.getG().multiply(d).normalize();
+    }
+
+    public static String publicKeyToString(String algorithm, org.bouncycastle.math.ec.ECPoint q, org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        StringBuffer buf = new StringBuffer();
+        String nl = Strings.lineSeparator();
+
+        buf.append(algorithm);
+        buf.append(" Public Key [").append(ECUtil.generateKeyFingerprint(q, spec)).append("]").append(nl);
+        buf.append("            X: ").append(q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
+
+        return buf.toString();
+    }
+
+    public static String generateKeyFingerprint(ECPoint publicPoint, org.bouncycastle.jce.spec.ECParameterSpec spec)
+    {
+        ECCurve curve = spec.getCurve();
+        ECPoint g = spec.getG();
+
+        if (curve != null)
         {
-            name = SECNamedCurves.getName(oid);
-            if (name == null)
-            {
-                name = NISTNamedCurves.getName(oid);
-            }
-            if (name == null)
-            {
-                name = TeleTrusTNamedCurves.getName(oid);
-            }
-            if (name == null)
-            {
-                name = ECGOST3410NamedCurves.getName(oid);
-            }
+            return new Fingerprint(Arrays.concatenate(publicPoint.getEncoded(false), curve.getA().getEncoded(), curve.getB().getEncoded(), g.getEncoded(false))).toString();
         }
 
-        return name;
+        return new Fingerprint(publicPoint.getEncoded(false)).toString();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
index 7765c27..f6cec93 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
@@ -237,7 +237,7 @@
         }
         catch (Exception e)
         {
-            throw new ExCertificateException(e);
+            throw new ExCertificateException("parsing issue: " + e.getMessage(), e);
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java
index b82d091..1664a9c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java
@@ -41,8 +41,7 @@
 
 /**
  * CertPath implementation for X.509 certificates.
- * <br />
- **/
+ */
 public  class PKIXCertPath
     extends CertPath
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
index dc8930b..4870cdb 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -31,6 +31,7 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.util.ASN1Dump;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.CRLDistPoint;
@@ -269,6 +270,19 @@
             throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
         }
 
+        if (sigAlgParams != null)
+        {
+            try
+            {
+                // needs to be called before initVerify().
+                X509SignatureUtil.setSignatureParameters(sig, ASN1Primitive.fromByteArray(sigAlgParams));
+            }
+            catch (IOException e)
+            {
+                throw new SignatureException("cannot decode signature parameters: " + e.getMessage());
+            }
+        }
+
         sig.initVerify(key);
         sig.update(this.getTBSCertList());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ProviderConfiguration.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ProviderConfiguration.java
index e293d9f..e5b05b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ProviderConfiguration.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ProviderConfiguration.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.config;
 
+import java.security.spec.DSAParameterSpec;
 import java.util.Map;
 import java.util.Set;
 
@@ -13,6 +14,8 @@
 
     DHParameterSpec getDHDefaultParameters(int keySize);
 
+    DSAParameterSpec getDSADefaultParameters(int keySize);
+
     Set getAcceptableNamedCurves();
 
     Map getAdditionalECParameters();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2s.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2s.java
new file mode 100644
index 0000000..a79fbe9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2s.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.crypto.digests.Blake2sDigest;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+public class Blake2s
+{
+    private Blake2s()
+    {
+
+    }
+
+    static public class Blake2s256
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Blake2s256()
+        {
+            super(new Blake2sDigest(256));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Blake2s256 d = (Blake2s256)super.clone();
+            d.digest = new Blake2sDigest((Blake2sDigest)digest);
+
+            return d;
+        }
+    }
+
+    static public class Blake2s224
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Blake2s224()
+        {
+            super(new Blake2sDigest(224));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Blake2s224 d = (Blake2s224)super.clone();
+            d.digest = new Blake2sDigest((Blake2sDigest)digest);
+
+            return d;
+        }
+    }
+
+    static public class Blake2s160
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Blake2s160()
+        {
+            super(new Blake2sDigest(160));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Blake2s160 d = (Blake2s160)super.clone();
+            d.digest = new Blake2sDigest((Blake2sDigest)digest);
+
+            return d;
+        }
+    }
+
+    static public class Blake2s128
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Blake2s128()
+        {
+            super(new Blake2sDigest(128));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Blake2s128 d = (Blake2s128)super.clone();
+            d.digest = new Blake2sDigest((Blake2sDigest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = Blake2s.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.BLAKE2S-256", PREFIX + "$Blake2s256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2s256, "BLAKE2S-256");
+
+            provider.addAlgorithm("MessageDigest.BLAKE2S-224", PREFIX + "$Blake2s224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2s224, "BLAKE2S-224");
+
+            provider.addAlgorithm("MessageDigest.BLAKE2S-160", PREFIX + "$Blake2s160");
+            provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2s160, "BLAKE2S-160");
+
+            provider.addAlgorithm("MessageDigest.BLAKE2S-128", PREFIX + "$Blake2s128");
+            provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2s128, "BLAKE2S-128");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DSTU7564.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DSTU7564.java
new file mode 100644
index 0000000..3f354cf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DSTU7564.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.digests.DSTU7564Digest;
+import org.bouncycastle.crypto.macs.DSTU7564Mac;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+
+public class DSTU7564
+{
+    private DSTU7564()
+    {
+
+    }
+
+    static public class DigestDSTU7564
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public DigestDSTU7564(int size)
+        {
+            super(new DSTU7564Digest(size));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            BCMessageDigest d = (BCMessageDigest)super.clone();
+            d.digest = new DSTU7564Digest((DSTU7564Digest)digest);
+
+            return d;
+        }
+    }
+
+    static public class Digest256
+        extends DigestDSTU7564
+    {
+        public Digest256()
+        {
+            super(256);
+        }
+    }
+
+    static public class Digest384
+        extends DigestDSTU7564
+    {
+        public Digest384()
+        {
+            super(384);
+        }
+    }
+
+    static public class Digest512
+        extends DigestDSTU7564
+    {
+        public Digest512()
+        {
+            super(512);
+        }
+    }
+
+    public static class HashMac256
+        extends BaseMac
+    {
+        public HashMac256()
+        {
+            super(new DSTU7564Mac(256));
+        }
+    }
+
+    public static class HashMac384
+        extends BaseMac
+    {
+        public HashMac384()
+        {
+            super(new DSTU7564Mac(384));
+        }
+    }
+
+    public static class HashMac512
+        extends BaseMac
+    {
+        public HashMac512()
+        {
+            super(new DSTU7564Mac(512));
+        }
+    }
+
+    public static class KeyGenerator256
+        extends BaseKeyGenerator
+    {
+        public KeyGenerator256()
+        {
+            super("HMACDSTU7564-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class KeyGenerator384
+        extends BaseKeyGenerator
+    {
+        public KeyGenerator384()
+        {
+            super("HMACDSTU7564-384", 384, new CipherKeyGenerator());
+        }
+    }
+
+    public static class KeyGenerator512
+        extends BaseKeyGenerator
+    {
+        public KeyGenerator512()
+        {
+            super("HMACDSTU7564-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = DSTU7564.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.DSTU7564-256", PREFIX + "$Digest256");
+            provider.addAlgorithm("MessageDigest.DSTU7564-384", PREFIX + "$Digest384");
+            provider.addAlgorithm("MessageDigest.DSTU7564-512", PREFIX + "$Digest512");
+
+            provider.addAlgorithm("MessageDigest", UAObjectIdentifiers.dstu7564digest_256, PREFIX + "$Digest256");
+            provider.addAlgorithm("MessageDigest", UAObjectIdentifiers.dstu7564digest_384, PREFIX + "$Digest384");
+            provider.addAlgorithm("MessageDigest", UAObjectIdentifiers.dstu7564digest_512, PREFIX + "$Digest512");
+
+            addHMACAlgorithm(provider, "DSTU7564-256", PREFIX + "$HashMac256", PREFIX + "$KeyGenerator256");
+            addHMACAlgorithm(provider, "DSTU7564-384", PREFIX + "$HashMac384", PREFIX + "$KeyGenerator384");
+            addHMACAlgorithm(provider, "DSTU7564-512", PREFIX + "$HashMac512", PREFIX + "$KeyGenerator512");
+
+            addHMACAlias(provider, "DSTU7564-256", UAObjectIdentifiers.dstu7564mac_256);
+            addHMACAlias(provider, "DSTU7564-384", UAObjectIdentifiers.dstu7564mac_384);
+            addHMACAlias(provider, "DSTU7564-512", UAObjectIdentifiers.dstu7564mac_512);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java
index 8050e35..e93facc 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.digest;
 
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.crypto.digests.SM3Digest;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 
@@ -41,7 +42,8 @@
         {
             provider.addAlgorithm("MessageDigest.SM3", PREFIX + "$Digest");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SM3", "SM3");
-            provider.addAlgorithm("Alg.Alias.MessageDigest.1.2.156.197.1.401", "SM3");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.1.2.156.197.1.401", "SM3");  // old draft OID - deprecated
+            provider.addAlgorithm("Alg.Alias.MessageDigest." + GMObjectIdentifiers.sm3, "SM3");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Whirlpool.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Whirlpool.java
index bf1c06c..95bd9bc 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Whirlpool.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Whirlpool.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.digest;
 
+import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.digests.WhirlpoolDigest;
 import org.bouncycastle.crypto.macs.HMac;
@@ -66,6 +67,7 @@
         public void configure(ConfigurableProvider provider)
         {
             provider.addAlgorithm("MessageDigest.WHIRLPOOL", PREFIX + "$Digest");
+            provider.addAlgorithm("MessageDigest", ISOIECObjectIdentifiers.whirlpool, PREFIX + "$Digest");
 
             addHMACAlgorithm(provider, "WHIRLPOOL", PREFIX + "$HashMac", PREFIX + "$KeyGenerator");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java
index d48c401..9939b8c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java
@@ -1,10 +1,14 @@
 package org.bouncycastle.jcajce.provider.drbg;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.Provider;
 import java.security.SecureRandom;
 import java.security.SecureRandomSpi;
+import java.security.Security;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -16,6 +20,7 @@
 import org.bouncycastle.crypto.prng.SP800SecureRandom;
 import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
 import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
@@ -73,13 +78,64 @@
     // to the JVM's seed generator.
     private static SecureRandom createInitialEntropySource()
     {
+        boolean hasGetInstanceStrong = AccessController.doPrivileged(new PrivilegedAction<Boolean>()
+        {
+            public Boolean run()
+            {
+                try
+                {
+                    Class def = SecureRandom.class;
+
+                    return def.getMethod("getInstanceStrong") != null;
+                }
+                catch (Exception e)
+                {
+                    return false;
+                }
+            }
+        });
+
+        if (hasGetInstanceStrong)
+        {
+            return AccessController.doPrivileged(new PrivilegedAction<SecureRandom>()
+            {
+                public SecureRandom run()
+                {
+                    try
+                    {
+                        return (SecureRandom)SecureRandom.class.getMethod("getInstanceStrong").invoke(null);
+                    }
+                    catch (Exception e)
+                    {
+                        return createCoreSecureRandom();
+                    }
+                }
+            });
+        }
+        else
+        {
+            return createCoreSecureRandom();
+        }
+    }
+
+    private static SecureRandom createCoreSecureRandom()
+    {
         if (initialEntropySourceAndSpi != null)
         {
             return new CoreSecureRandom();
         }
         else
         {
-            return new SecureRandom();  // we're desperate, it's worth a try.
+            try
+            {
+                String source = Security.getProperty("securerandom.source");
+
+                return new URLSeededSecureRandom(new URL(source));
+            }
+            catch (Exception e)
+            {
+                return new SecureRandom();  // we're desperate, it's worth a try.
+            }
         }
     }
 
@@ -93,7 +149,7 @@
             {
                 try
                 {
-                    Class clazz = DRBG.class.getClassLoader().loadClass(sourceClass);
+                    Class clazz = ClassUtil.loadClass(DRBG.class, sourceClass);
 
                     return (EntropySourceProvider)clazz.newInstance();
                 }
@@ -209,16 +265,104 @@
             Pack.longToLittleEndian(Thread.currentThread().getId()), Pack.longToLittleEndian(System.currentTimeMillis()));
     }
 
+    private static class HybridRandomProvider
+        extends Provider
+    {
+        protected HybridRandomProvider()
+        {
+            super("BCHEP", 1.0, "Bouncy Castle Hybrid Entropy Provider");
+        }
+    }
+
+    private static class URLSeededSecureRandom
+        extends SecureRandom
+    {
+        private final InputStream seedStream;
+
+        URLSeededSecureRandom(final URL url)
+        {
+            super(null, new HybridRandomProvider());
+
+            this.seedStream = AccessController.doPrivileged(new PrivilegedAction<InputStream>()
+            {
+                public InputStream run()
+                {
+                    try
+                    {
+                        return url.openStream();
+                    }
+                    catch (IOException e)
+                    {
+                        throw new InternalError("unable to open random source");
+                    }
+                }
+            });
+        }
+
+        public void setSeed(byte[] seed)
+        {
+            // ignore
+        }
+
+        public void setSeed(long seed)
+        {
+            // ignore
+        }
+
+        public byte[] generateSeed(int numBytes)
+        {
+            synchronized (this)
+            {
+                byte[] data = new byte[numBytes];
+
+                int off = 0;
+                int len;
+
+                while (off != data.length && (len = privilegedRead(data, off, data.length - off)) > -1)
+                {
+                    off += len;
+                }
+
+                if (off != data.length)
+                {
+                    throw new InternalError("unable to fully read random source");
+                }
+
+                return data;
+            }
+        }
+
+        private int privilegedRead(final byte[] data, final int off, final int len)
+        {
+            return AccessController.doPrivileged(new PrivilegedAction<Integer>()
+            {
+                public Integer run()
+                {
+                    try
+                    {
+                        return seedStream.read(data, off, len);
+                    }
+                    catch (IOException e)
+                    {
+                        throw new InternalError("unable to read random source");
+                    }
+                }
+            });
+        }
+    }
+
     private static class HybridSecureRandom
         extends SecureRandom
     {
         private final AtomicBoolean seedAvailable = new AtomicBoolean(false);
         private final AtomicInteger samples = new AtomicInteger(0);
         private final SecureRandom baseRandom = createInitialEntropySource();
+
         private final SP800SecureRandom drbg;
 
         HybridSecureRandom()
         {
+            super(null, new HybridRandomProvider());
             drbg = new SP800SecureRandomBuilder(new EntropySourceProvider()
                 {
                     public EntropySource get(final int bitsRequired)
@@ -230,6 +374,22 @@
                 .buildHMAC(new HMac(new SHA512Digest()), baseRandom.generateSeed(32), false);     // 32 byte nonce
         }
 
+        public void setSeed(byte[] seed)
+        {
+            if (drbg != null)
+            {
+                drbg.setSeed(seed);
+            }
+        }
+
+        public void setSeed(long seed)
+        {
+            if (drbg != null)
+            {
+                drbg.setSeed(seed);
+            }
+        }
+
         public byte[] generateSeed(int numBytes)
         {
             byte[] data = new byte[numBytes];
@@ -240,7 +400,7 @@
                 if (seedAvailable.getAndSet(false))
                 {
                     samples.set(0);
-                    drbg.reseed(null);
+                    drbg.reseed((byte[])null);    // need for Java 1.9
                 }
             }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BCFKS.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BCFKS.java
index 9eb290a..1f84e58 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BCFKS.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BCFKS.java
@@ -18,6 +18,9 @@
         {
             provider.addAlgorithm("KeyStore.BCFKS", PREFIX + "BcFKSKeyStoreSpi$Std");
             provider.addAlgorithm("KeyStore.BCFKS-DEF", PREFIX + "BcFKSKeyStoreSpi$Def");
+
+            provider.addAlgorithm("KeyStore.IBCFKS", PREFIX + "BcFKSKeyStoreSpi$StdShared");
+            provider.addAlgorithm("KeyStore.IBCFKS-DEF", PREFIX + "BcFKSKeyStoreSpi$DefShared");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
index 0640669..275b49f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
@@ -8,16 +8,13 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.Key;
-import java.security.KeyFactory;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
-import java.security.Provider;
 import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.Security;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
@@ -38,7 +35,10 @@
 import javax.crypto.spec.PBEParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.PBEParametersGenerator;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -88,7 +88,7 @@
 
     protected Hashtable       table = new Hashtable();
 
-    protected SecureRandom    random = new SecureRandom();
+    protected SecureRandom    random = CryptoServicesRegistrar.getSecureRandom();
 
     protected int              version;
 
@@ -442,9 +442,9 @@
             switch (keyType)
             {
             case KEY_PRIVATE:
-                return helper.createKeyFactory(algorithm).generatePrivate(spec);
+                return BouncyCastleProvider.getPrivateKey(PrivateKeyInfo.getInstance(enc));
             case KEY_PUBLIC:
-                return  helper.createKeyFactory(algorithm).generatePublic(spec);
+                return  BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(enc));
             case KEY_SECRET:
                 return  helper.createSecretKeyFactory(algorithm).generateSecret(spec);
             default:
@@ -730,7 +730,7 @@
                     table.put(alias, new StoreEntry(alias, date, type, b, chain));
                     break;
             default:
-                    throw new RuntimeException("Unknown object type in store.");
+                    throw new IOException("Unknown object type in store.");
             }
 
             type = dIn.read();
@@ -782,7 +782,7 @@
                     dOut.write(b);
                     break;
             default:
-                    throw new RuntimeException("Unknown object type in store.");
+                    throw new IOException("Unknown object type in store.");
             }
         }
 
@@ -978,7 +978,7 @@
     
             int         iterationCount = dIn.readInt();
     
-            if ((iterationCount < 0) || (iterationCount > 4 *  MIN_ITERATIONS))
+            if ((iterationCount < 0) || (iterationCount > (MIN_ITERATIONS << 6)))
             {
                 throw new IOException("Key store corrupted.");
             }
@@ -1047,18 +1047,6 @@
         }
     }
 
-    static Provider getBouncyCastleProvider()
-    {
-        if (Security.getProvider("BC") != null)
-        {
-            return Security.getProvider("BC");
-        }
-        else
-        {
-            return new BouncyCastleProvider();
-        }
-    }
-
     public static class Std
        extends BcKeyStoreSpi
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bcfks/BcFKSKeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bcfks/BcFKSKeyStoreSpi.java
index fdf886b..884c9a5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bcfks/BcFKSKeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bcfks/BcFKSKeyStoreSpi.java
@@ -6,20 +6,27 @@
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.KeyFactory;
+import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.Signature;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.RSAKey;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.text.ParseException;
 import java.util.Date;
@@ -37,8 +44,13 @@
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
@@ -52,8 +64,14 @@
 import org.bouncycastle.asn1.bc.ObjectStoreIntegrityCheck;
 import org.bouncycastle.asn1.bc.PbkdMacIntegrityCheck;
 import org.bouncycastle.asn1.bc.SecretKeyData;
+import org.bouncycastle.asn1.bc.SignatureCheck;
 import org.bouncycastle.asn1.cms.CCMParameters;
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.nsri.NSRIObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.EncryptionScheme;
@@ -63,12 +81,25 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.SHA3Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.bouncycastle.crypto.generators.SCrypt;
 import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.crypto.util.PBKDF2Config;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+import org.bouncycastle.crypto.util.ScryptConfig;
+import org.bouncycastle.jcajce.BCFKSLoadStoreParameter;
+import org.bouncycastle.jcajce.BCFKSStoreParameter;
+import org.bouncycastle.jcajce.BCLoadStoreParameter;
+import org.bouncycastle.jcajce.util.BCJcaJceHelper;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.jce.interfaces.ECKey;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Strings;
 
@@ -89,6 +120,15 @@
         oidMap.put("HMACSHA256", PKCSObjectIdentifiers.id_hmacWithSHA256);
         oidMap.put("HMACSHA384", PKCSObjectIdentifiers.id_hmacWithSHA384);
         oidMap.put("HMACSHA512", PKCSObjectIdentifiers.id_hmacWithSHA512);
+        oidMap.put("SEED", KISAObjectIdentifiers.id_seedCBC);
+
+        oidMap.put("CAMELLIA.128", NTTObjectIdentifiers.id_camellia128_cbc);
+        oidMap.put("CAMELLIA.192", NTTObjectIdentifiers.id_camellia192_cbc);
+        oidMap.put("CAMELLIA.256", NTTObjectIdentifiers.id_camellia256_cbc);
+
+        oidMap.put("ARIA.128", NSRIObjectIdentifiers.id_aria128_cbc);
+        oidMap.put("ARIA.192", NSRIObjectIdentifiers.id_aria192_cbc);
+        oidMap.put("ARIA.256", NSRIObjectIdentifiers.id_aria256_cbc);
 
         publicAlgMap.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
         publicAlgMap.put(X9ObjectIdentifiers.id_ecPublicKey, "EC");
@@ -97,6 +137,9 @@
         publicAlgMap.put(X9ObjectIdentifiers.id_dsa, "DSA");
     }
 
+    private PublicKey verificationKey;
+    private BCFKSLoadStoreParameter.CertChainValidator validator;
+
     private static String getPublicKeyAlg(ASN1ObjectIdentifier oid)
     {
         String algName = (String)publicAlgMap.get(oid);
@@ -115,18 +158,20 @@
     private final static BigInteger PROTECTED_PRIVATE_KEY = BigInteger.valueOf(3);
     private final static BigInteger PROTECTED_SECRET_KEY = BigInteger.valueOf(4);
 
-    private final BouncyCastleProvider provider;
+    private final JcaJceHelper helper;
     private final Map<String, ObjectData> entries = new HashMap<String, ObjectData>();
     private final Map<String, PrivateKey> privateKeyCache = new HashMap<String, PrivateKey>();
 
     private AlgorithmIdentifier hmacAlgorithm;
     private KeyDerivationFunc hmacPkbdAlgorithm;
+    private AlgorithmIdentifier signatureAlgorithm;
     private Date creationDate;
     private Date lastModifiedDate;
+    private ASN1ObjectIdentifier storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM;
 
-    BcFKSKeyStoreSpi(BouncyCastleProvider provider)
+    BcFKSKeyStoreSpi(JcaJceHelper helper)
     {
-        this.provider = provider;
+        this.helper = helper;
     }
 
     public Key engineGetKey(String alias, char[] password)
@@ -151,15 +196,7 @@
                 {
                     PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(decryptData("PRIVATE_KEY_ENCRYPTION", encInfo.getEncryptionAlgorithm(), password, encInfo.getEncryptedData()));
 
-                    KeyFactory kFact;
-                    if (provider != null)
-                    {
-                        kFact = KeyFactory.getInstance(pInfo.getPrivateKeyAlgorithm().getAlgorithm().getId(), provider);
-                    }
-                    else
-                    {
-                        kFact = KeyFactory.getInstance(getPublicKeyAlg(pInfo.getPrivateKeyAlgorithm().getAlgorithm()));
-                    }
+                    KeyFactory kFact = helper.createKeyFactory(getPublicKeyAlg(pInfo.getPrivateKeyAlgorithm().getAlgorithm()));
 
                     PrivateKey privateKey = kFact.generatePrivate(new PKCS8EncodedKeySpec(pInfo.getEncoded()));
 
@@ -182,15 +219,7 @@
                 try
                 {
                     SecretKeyData keyData = SecretKeyData.getInstance(decryptData("SECRET_KEY_ENCRYPTION", encKeyData.getKeyEncryptionAlgorithm(), password, encKeyData.getEncryptedKeyData()));
-                    SecretKeyFactory kFact;
-                    if (provider != null)
-                    {
-                        kFact = SecretKeyFactory.getInstance(keyData.getKeyAlgorithm().getId(), provider);
-                    }
-                    else
-                    {
-                        kFact = SecretKeyFactory.getInstance(keyData.getKeyAlgorithm().getId());
-                    }
+                    SecretKeyFactory kFact = helper.createSecretKeyFactory(keyData.getKeyAlgorithm().getId());
 
                     return kFact.generateSecret(new SecretKeySpec(keyData.getKeyBytes(), keyData.getKeyAlgorithm().getId()));
                 }
@@ -256,11 +285,11 @@
 
     private Certificate decodeCertificate(Object cert)
     {
-        if (provider != null)
+        if (helper != null)
         {
             try
             {
-                CertificateFactory certFact = CertificateFactory.getInstance("X.509", provider);
+                CertificateFactory certFact = helper.createCertificateFactory("X.509");
 
                 return certFact.generateCertificate(new ByteArrayInputStream(org.bouncycastle.asn1.x509.Certificate.getInstance(cert).getEncoded()));
             }
@@ -332,29 +361,33 @@
 
                 byte[] encodedKey = key.getEncoded();
 
-                KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(256 / 8);
-                byte[] keyBytes = generateKey(pbkdAlgId, "PRIVATE_KEY_ENCRYPTION", ((password != null) ? password : new char[0]));
+                KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8);
+                byte[] keyBytes = generateKey(pbkdAlgId, "PRIVATE_KEY_ENCRYPTION", ((password != null) ? password : new char[0]), 32);
 
-                Cipher c;
-                if (provider == null)
+                EncryptedPrivateKeyInfo keyInfo;
+                if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM))
                 {
-                    c = Cipher.getInstance("AES/CCM/NoPadding");
+                    Cipher c = createCipher("AES/CCM/NoPadding", keyBytes);
+
+                    byte[] encryptedKey = c.doFinal(encodedKey);
+
+                    AlgorithmParameters algParams = c.getParameters();
+
+                    PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded())));
+
+                    keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
                 }
                 else
                 {
-                    c = Cipher.getInstance("AES/CCM/NoPadding", provider);
+                    Cipher c = createCipher("AESKWP", keyBytes);
+
+                    byte[] encryptedKey = c.doFinal(encodedKey);
+
+                    PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad));
+
+                    keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
                 }
 
-                c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
-
-                byte[] encryptedKey = c.doFinal(encodedKey);
-
-                AlgorithmParameters algParams = c.getParameters();
-
-                PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded())));
-
-                EncryptedPrivateKeyInfo keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
-
                 EncryptedPrivateKeyData keySeq = createPrivateKeySequence(keyInfo, chain);
 
                 entries.put(alias, new ObjectData(PRIVATE_KEY, alias, creationDate, lastEditDate, keySeq.getEncoded(), null));
@@ -375,49 +408,60 @@
             {
                 byte[] encodedKey = key.getEncoded();
 
-                KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(256 / 8);
-                byte[] keyBytes = generateKey(pbkdAlgId, "SECRET_KEY_ENCRYPTION", ((password != null) ? password : new char[0]));
-
-                Cipher c;
-                if (provider == null)
-                {
-                    c = Cipher.getInstance("AES/CCM/NoPadding");
-                }
-                else
-                {
-                    c = Cipher.getInstance("AES/CCM/NoPadding", provider);
-                }
-
-                c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
-
+                KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8);
+                byte[] keyBytes = generateKey(pbkdAlgId, "SECRET_KEY_ENCRYPTION", ((password != null) ? password : new char[0]), 32);
 
                 String keyAlg = Strings.toUpperCase(key.getAlgorithm());
-                byte[] encryptedKey;
+                SecretKeyData secKeyData;
 
                 if (keyAlg.indexOf("AES") > -1)
                 {
-                    encryptedKey = c.doFinal(new SecretKeyData(NISTObjectIdentifiers.aes, encodedKey).getEncoded());
+                    secKeyData = new SecretKeyData(NISTObjectIdentifiers.aes, encodedKey);
                 }
                 else
                 {
                     ASN1ObjectIdentifier algOid = (ASN1ObjectIdentifier)oidMap.get(keyAlg);
                     if (algOid != null)
                     {
-                        encryptedKey = c.doFinal(new SecretKeyData(algOid, encodedKey).getEncoded());
+                        secKeyData = new SecretKeyData(algOid, encodedKey);
                     }
                     else
                     {
-                        throw new KeyStoreException("BCFKS KeyStore cannot recognize secret key (" + keyAlg + ") for storage.");
+                        algOid = (ASN1ObjectIdentifier)oidMap.get(keyAlg + "." + (encodedKey.length * 8));
+                        if (algOid != null)
+                        {
+                            secKeyData = new SecretKeyData(algOid, encodedKey);
+                        }
+                        else
+                        {
+                            throw new KeyStoreException("BCFKS KeyStore cannot recognize secret key (" + keyAlg + ") for storage.");
+                        }
                     }
                 }
 
+                EncryptedSecretKeyData keyData;
+                if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM))
+                {
+                    Cipher c = createCipher("AES/CCM/NoPadding", keyBytes);
 
-                AlgorithmParameters algParams = c.getParameters();
+                    byte[] encryptedKey = c.doFinal(secKeyData.getEncoded());
 
-                PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded())));
+                    AlgorithmParameters algParams = c.getParameters();
 
-                EncryptedSecretKeyData keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
+                    PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded())));
 
+                    keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
+                }
+                else
+                {
+                    Cipher c = createCipher("AESKWP", keyBytes);
+
+                    byte[] encryptedKey = c.doFinal(secKeyData.getEncoded());
+
+                    PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad));
+
+                    keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey);
+                }
                 entries.put(alias, new ObjectData(SECRET_KEY, alias, creationDate, lastEditDate, keyData.getEncoded(), null));
             }
             catch (Exception e)
@@ -433,9 +477,19 @@
         lastModifiedDate = lastEditDate;
     }
 
+    private Cipher createCipher(String algorithm, byte[] keyBytes)
+        throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, NoSuchProviderException
+    {
+        Cipher c = helper.createCipher(algorithm);
+
+        c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
+
+        return c;
+    }
+
     private SecureRandom getDefaultSecureRandom()
     {
-        return new SecureRandom();
+        return CryptoServicesRegistrar.getSecureRandom();
     }
 
     private EncryptedPrivateKeyData createPrivateKeySequence(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, Certificate[] chain)
@@ -666,68 +720,107 @@
         return null;
     }
 
-    private byte[] generateKey(KeyDerivationFunc pbkdAlgorithm, String purpose, char[] password)
+    private byte[] generateKey(KeyDerivationFunc pbkdAlgorithm, String purpose, char[] password, int defKeySize)
         throws IOException
     {
         byte[] encPassword = PBEParametersGenerator.PKCS12PasswordToBytes(password);
         byte[] differentiator = PBEParametersGenerator.PKCS12PasswordToBytes(purpose.toCharArray());
 
-        int keySizeInBytes;
-        PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA512Digest());
+        int keySizeInBytes = defKeySize;
 
-        if (pbkdAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2))
+        if (MiscObjectIdentifiers.id_scrypt.equals(pbkdAlgorithm.getAlgorithm()))
+        {
+            ScryptParams params = ScryptParams.getInstance(pbkdAlgorithm.getParameters());
+
+            if (params.getKeyLength() != null)
+            {
+                keySizeInBytes = params.getKeyLength().intValue();
+            }
+            else if (keySizeInBytes == -1)
+            {
+                throw new IOException("no keyLength found in ScryptParams");
+            }
+            return SCrypt.generate(Arrays.concatenate(encPassword, differentiator), params.getSalt(),
+                params.getCostParameter().intValue(), params.getBlockSize().intValue(),
+                params.getBlockSize().intValue(), keySizeInBytes);
+        }
+        else if (pbkdAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2))
         {
             PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(pbkdAlgorithm.getParameters());
 
+            if (pbkdf2Params.getKeyLength() != null)
+            {
+                keySizeInBytes = pbkdf2Params.getKeyLength().intValue();
+            }
+            else if (keySizeInBytes == -1)
+            {
+                throw new IOException("no keyLength found in PBKDF2Params");
+            }
+
             if (pbkdf2Params.getPrf().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512))
             {
+                PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA512Digest());
 
                 pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue());
 
-                keySizeInBytes = pbkdf2Params.getKeyLength().intValue();
+                return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey();
+            }
+            else if (pbkdf2Params.getPrf().getAlgorithm().equals(NISTObjectIdentifiers.id_hmacWithSHA3_512))
+            {
+                PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA3Digest(512));
+
+                pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue());
+
+                return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey();
             }
             else
             {
-                throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD PRF.");
+                throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD PRF: " + pbkdf2Params.getPrf().getAlgorithm());
             }
         }
         else
         {
             throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD.");
         }
+    }
 
-        return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey();
+    private void verifySig(ASN1Encodable store, SignatureCheck integrityCheck, PublicKey key)
+        throws GeneralSecurityException, IOException
+    {
+        Signature sig = helper.createSignature(integrityCheck.getSignatureAlgorithm().getAlgorithm().getId());
+
+        sig.initVerify(key);
+
+        sig.update(store.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+
+        if (!sig.verify(integrityCheck.getSignature().getOctets()))
+        {
+            throw new IOException("BCFKS KeyStore corrupted: signature calculation failed");
+        }
     }
 
     private void verifyMac(byte[] content, PbkdMacIntegrityCheck integrityCheck, char[] password)
-        throws NoSuchAlgorithmException, IOException
+        throws NoSuchAlgorithmException, IOException, NoSuchProviderException
     {
         byte[] check = calculateMac(content, integrityCheck.getMacAlgorithm(), integrityCheck.getPbkdAlgorithm(), password);
 
         if (!Arrays.constantTimeAreEqual(check, integrityCheck.getMac()))
         {
-            throw new IOException("BCFKS KeyStore corrupted: MAC calculation failed.");
+            throw new IOException("BCFKS KeyStore corrupted: MAC calculation failed");
         }
     }
 
     private byte[] calculateMac(byte[] content, AlgorithmIdentifier algorithm, KeyDerivationFunc pbkdAlgorithm, char[] password)
-        throws NoSuchAlgorithmException, IOException
+        throws NoSuchAlgorithmException, IOException, NoSuchProviderException
     {
         String algorithmId = algorithm.getAlgorithm().getId();
 
-        Mac mac;
-        if (provider != null)
-        {
-            mac = Mac.getInstance(algorithmId, provider);
-        }
-        else
-        {
-            mac = Mac.getInstance(algorithmId);
-        }
+        Mac mac = helper.createMac(algorithmId);
 
         try
         {
-            mac.init(new SecretKeySpec(generateKey(pbkdAlgorithm, "INTEGRITY_CHECK", ((password != null) ? password : new char[0])), algorithmId));
+            // no default key size for MAC.
+            mac.init(new SecretKeySpec(generateKey(pbkdAlgorithm, "INTEGRITY_CHECK", ((password != null) ? password : new char[0]), -1), algorithmId));
         }
         catch (InvalidKeyException e)
         {
@@ -737,38 +830,243 @@
         return mac.doFinal(content);
     }
 
+    private char[] extractPassword(KeyStore.LoadStoreParameter bcParam)
+        throws IOException
+    {
+        KeyStore.ProtectionParameter protParam = bcParam.getProtectionParameter();
+
+        if (protParam == null)
+        {
+            return null;
+        }
+        else if (protParam instanceof KeyStore.PasswordProtection)
+        {
+            return ((KeyStore.PasswordProtection)protParam).getPassword();
+        }
+        else if (protParam instanceof KeyStore.CallbackHandlerProtection)
+        {
+            CallbackHandler handler = ((KeyStore.CallbackHandlerProtection)protParam).getCallbackHandler();
+
+            PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
+
+            try
+            {
+                handler.handle(new Callback[]{passwordCallback});
+
+                return passwordCallback.getPassword();
+            }
+            catch (UnsupportedCallbackException e)
+            {
+                throw new IllegalArgumentException("PasswordCallback not recognised: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for protection parameter of type " + protParam.getClass().getName());
+        }
+    }
+
+    public void engineStore(KeyStore.LoadStoreParameter parameter)
+        throws CertificateException, NoSuchAlgorithmException, IOException
+    {
+        if (parameter == null)
+        {
+            throw new IllegalArgumentException("'parameter' arg cannot be null");
+        }
+
+        if (parameter instanceof BCFKSStoreParameter)
+        {
+            BCFKSStoreParameter bcParam = (BCFKSStoreParameter)parameter;
+
+            char[] password = extractPassword(parameter);
+
+            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8);
+
+            engineStore(bcParam.getOutputStream(), password);
+        }
+        else if (parameter instanceof BCFKSLoadStoreParameter)
+        {
+            BCFKSLoadStoreParameter bcParam = (BCFKSLoadStoreParameter)parameter;
+
+            if (bcParam.getStoreSignatureKey() != null)
+            {
+                signatureAlgorithm = generateSignatureAlgId(bcParam.getStoreSignatureKey(), bcParam.getStoreSignatureAlgorithm());
+
+                hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8);
+
+                if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM)
+                {
+                    storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM;
+                }
+                else
+                {
+                    storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad;
+                }
+
+                if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512)
+                {
+                    hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+                }
+                else
+                {
+                    hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+                }
+
+                char[] password = extractPassword(bcParam);
+                
+                EncryptedObjectStoreData encStoreData = getEncryptedObjectStoreData(signatureAlgorithm, password);
+                
+                try
+                {
+                    Signature sig = helper.createSignature(signatureAlgorithm.getAlgorithm().getId());
+
+                    sig.initSign((PrivateKey)bcParam.getStoreSignatureKey());
+
+                    sig.update(encStoreData.getEncoded());
+
+                    SignatureCheck signatureCheck;
+                    X509Certificate[] certs = bcParam.getStoreCertificates();
+
+                    if (certs != null)
+                    {
+                        org.bouncycastle.asn1.x509.Certificate[] certificates = new org.bouncycastle.asn1.x509.Certificate[certs.length];
+                        for (int i = 0; i != certificates.length; i++)
+                        {
+                            certificates[i] = org.bouncycastle.asn1.x509.Certificate.getInstance(certs[i].getEncoded());
+                        }
+                        signatureCheck = new SignatureCheck(signatureAlgorithm, certificates, sig.sign());
+                    }
+                    else
+                    {
+                        signatureCheck = new SignatureCheck(signatureAlgorithm, sig.sign());
+                    }
+                    ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(signatureCheck));
+
+                    bcParam.getOutputStream().write(store.getEncoded());
+
+                    bcParam.getOutputStream().flush();
+                }
+                catch (GeneralSecurityException e)
+                {
+                    throw new IOException("error creating signature: " + e.getMessage(), e);
+                }
+            }
+            else
+            {
+                char[] password = extractPassword(bcParam);
+
+                hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8);
+
+                if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM)
+                {
+                    storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM;
+                }
+                else
+                {
+                    storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad;
+                }
+
+                if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512)
+                {
+                    hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+                }
+                else
+                {
+                    hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+                }
+
+                engineStore(bcParam.getOutputStream(), password);
+            }
+        }
+        else if (parameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)parameter;
+
+            engineStore(bcParam.getOutputStream(), extractPassword(parameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'parameter' of type " + parameter.getClass().getName());
+        }
+
+    }
+
     public void engineStore(OutputStream outputStream, char[] password)
         throws IOException, NoSuchAlgorithmException, CertificateException
     {
+        if (creationDate == null)
+        {
+            throw new IOException("KeyStore not initialized");
+        }
+
+        EncryptedObjectStoreData encStoreData = getEncryptedObjectStoreData(hmacAlgorithm, password);
+
+        // update the salt
+        if (MiscObjectIdentifiers.id_scrypt.equals(hmacPkbdAlgorithm.getAlgorithm()))
+        {
+            ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters());
+
+            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, sParams.getKeyLength().intValue());
+        }
+        else
+        {
+            PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters());
+
+            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, pbkdf2Params.getKeyLength().intValue());
+        }
+        byte[] mac;
+        try
+        {
+            mac = calculateMac(encStoreData.getEncoded(), hmacAlgorithm, hmacPkbdAlgorithm, password);
+        }
+        catch (NoSuchProviderException e)
+        {
+            throw new IOException("cannot calculate mac: " + e.getMessage());
+        }
+
+        ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(new PbkdMacIntegrityCheck(hmacAlgorithm, hmacPkbdAlgorithm, mac)));
+
+        outputStream.write(store.getEncoded());
+
+        outputStream.flush();
+    }
+
+    private EncryptedObjectStoreData getEncryptedObjectStoreData(AlgorithmIdentifier integrityAlgorithm, char[] password)
+        throws IOException, NoSuchAlgorithmException
+    {
         ObjectData[] dataArray = (ObjectData[])entries.values().toArray(new ObjectData[entries.size()]);
 
-        KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(256 / 8);
-        byte[] keyBytes = generateKey(pbkdAlgId, "STORE_ENCRYPTION", ((password != null) ? password : new char[0]));
+        KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, 256 / 8);
+        byte[] keyBytes = generateKey(pbkdAlgId, "STORE_ENCRYPTION", ((password != null) ? password : new char[0]), 256 / 8);
 
-        ObjectStoreData storeData = new ObjectStoreData(hmacAlgorithm, creationDate, lastModifiedDate, new ObjectDataSequence(dataArray), null);
+        ObjectStoreData storeData = new ObjectStoreData(integrityAlgorithm, creationDate, lastModifiedDate, new ObjectDataSequence(dataArray), null);
         EncryptedObjectStoreData encStoreData;
 
         try
         {
-            Cipher c;
-            if (provider == null)
+            if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM))
             {
-                c = Cipher.getInstance("AES/CCM/NoPadding");
+                Cipher c = createCipher("AES/CCM/NoPadding", keyBytes);
+
+                byte[] encOut = c.doFinal(storeData.getEncoded());
+
+                AlgorithmParameters algorithmParameters = c.getParameters();
+
+                PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algorithmParameters.getEncoded())));
+
+                encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut);
             }
             else
             {
-                c = Cipher.getInstance("AES/CCM/NoPadding", provider);
+                Cipher c = createCipher("AESKWP", keyBytes);
+
+                byte[] encOut = c.doFinal(storeData.getEncoded());
+                PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad));
+
+                encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut);
             }
-
-            c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
-
-            byte[] encOut = c.doFinal(storeData.getEncoded());
-
-            AlgorithmParameters algorithmParameters = c.getParameters();
-
-            PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algorithmParameters.getEncoded())));
-
-            encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut);
         }
         catch (NoSuchPaddingException e)
         {
@@ -786,24 +1084,126 @@
         {
             throw new IOException(e.toString());
         }
-
-        // update the salt
-        PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters());
-
-        byte[] pbkdSalt = new byte[pbkdf2Params.getSalt().length];
-        getDefaultSecureRandom().nextBytes(pbkdSalt);
-
-        hmacPkbdAlgorithm = new KeyDerivationFunc(hmacPkbdAlgorithm.getAlgorithm(), new PBKDF2Params(pbkdSalt, pbkdf2Params.getIterationCount().intValue(), pbkdf2Params.getKeyLength().intValue(), pbkdf2Params.getPrf()));
-
-        byte[] mac = calculateMac(encStoreData.getEncoded(), hmacAlgorithm, hmacPkbdAlgorithm, password);
-
-        ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(new PbkdMacIntegrityCheck(hmacAlgorithm, hmacPkbdAlgorithm, mac)));
-
-        outputStream.write(store.getEncoded());
-
-        outputStream.flush();
+        catch (NoSuchProviderException e)
+        {
+            throw new IOException(e.toString());
+        }
+        return encStoreData;
     }
 
+    public void engineLoad(KeyStore.LoadStoreParameter parameter)
+        throws CertificateException, NoSuchAlgorithmException, IOException
+    {
+        if (parameter == null)
+        {
+            throw new IllegalArgumentException("'parameter' arg cannot be null");
+        }
+
+        if (parameter instanceof BCFKSLoadStoreParameter)
+        {
+            BCFKSLoadStoreParameter bcParam = (BCFKSLoadStoreParameter)parameter;
+
+            char[] password = extractPassword(bcParam);
+
+            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8);
+
+            if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM)
+            {
+                storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM;
+            }
+            else
+            {
+                storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad;
+            }
+
+            if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512)
+            {
+                hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+            }
+            else
+            {
+                hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+            }
+
+            this.verificationKey = (PublicKey)bcParam.getStoreSignatureKey();
+            this.validator = bcParam.getCertChainValidator();
+            this.signatureAlgorithm = generateSignatureAlgId(verificationKey, bcParam.getStoreSignatureAlgorithm());
+
+            AlgorithmIdentifier presetHmacAlgorithm = hmacAlgorithm;
+            ASN1ObjectIdentifier presetStoreEncryptionAlgorithm = storeEncryptionAlgorithm;
+
+            InputStream inputStream = bcParam.getInputStream();
+
+            engineLoad(inputStream, password);
+
+            if (inputStream != null)
+            {
+                if (//!presetHmacAlgorithm.equals(hmacAlgorithm)
+                     !isSimilarHmacPbkd(bcParam.getStorePBKDFConfig(), hmacPkbdAlgorithm)
+                    || !presetStoreEncryptionAlgorithm.equals(storeEncryptionAlgorithm))
+                {
+                    throw new IOException("configuration parameters do not match existing store");
+                }
+            }
+        }
+        else if (parameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)parameter;
+
+            engineLoad(bcParam.getInputStream(), extractPassword(parameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'parameter' of type " + parameter.getClass().getName());
+        }
+    }
+
+    private boolean isSimilarHmacPbkd(PBKDFConfig storePBKDFConfig, KeyDerivationFunc hmacPkbdAlgorithm)
+    {
+        if (!storePBKDFConfig.getAlgorithm().equals(hmacPkbdAlgorithm.getAlgorithm()))
+        {
+            return false;
+        }
+
+        if (MiscObjectIdentifiers.id_scrypt.equals(hmacPkbdAlgorithm.getAlgorithm()))
+        {
+            if (!(storePBKDFConfig instanceof ScryptConfig))
+            {
+                return false;
+            }
+
+            ScryptConfig scryptConfig = (ScryptConfig)storePBKDFConfig;
+            ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters());
+
+            if (scryptConfig.getSaltLength() != sParams.getSalt().length
+                || scryptConfig.getBlockSize() != sParams.getBlockSize().intValue()
+                || scryptConfig.getCostParameter() != sParams.getCostParameter().intValue()
+                || scryptConfig.getParallelizationParameter() != sParams.getParallelizationParameter().intValue())
+            {
+                return false;
+            }
+        }
+        else
+        {
+            if (!(storePBKDFConfig instanceof PBKDF2Config))
+            {
+                return false;
+            }
+
+            PBKDF2Config pbkdf2Config = (PBKDF2Config)storePBKDFConfig;
+            PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters());
+
+            if (pbkdf2Config.getSaltLength() != pbkdf2Params.getSalt().length
+                || pbkdf2Config.getIterationCount() != pbkdf2Params.getIterationCount().intValue())
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+    
     public void engineLoad(InputStream inputStream, char[] password)
         throws IOException, NoSuchAlgorithmException, CertificateException
     {
@@ -818,18 +1218,32 @@
         {
             // initialise defaults
             lastModifiedDate = creationDate = new Date();
+            verificationKey = null;
+            validator = null;
 
+            // basic initialisation
             hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
-            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(512 / 8);
+            hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 512 / 8);
 
             return;
         }
 
         ASN1InputStream aIn = new ASN1InputStream(inputStream);
 
-        ObjectStore store = ObjectStore.getInstance(aIn.readObject());
+        ObjectStore store;
+
+        try
+        {
+            store = ObjectStore.getInstance(aIn.readObject());
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage());
+        }
 
         ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck();
+        AlgorithmIdentifier integrityAlg;
+
         if (integrityCheck.getType() == ObjectStoreIntegrityCheck.PBKD_MAC_CHECK)
         {
             PbkdMacIntegrityCheck pbkdMacIntegrityCheck = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck());
@@ -837,7 +1251,59 @@
             hmacAlgorithm = pbkdMacIntegrityCheck.getMacAlgorithm();
             hmacPkbdAlgorithm = pbkdMacIntegrityCheck.getPbkdAlgorithm();
 
-            verifyMac(store.getStoreData().toASN1Primitive().getEncoded(), pbkdMacIntegrityCheck, password);
+            integrityAlg = hmacAlgorithm;
+
+            try
+            {
+                verifyMac(store.getStoreData().toASN1Primitive().getEncoded(), pbkdMacIntegrityCheck, password);
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new IOException(e.getMessage());
+            }
+        }
+        else if (integrityCheck.getType() == ObjectStoreIntegrityCheck.SIG_CHECK)
+        {
+            SignatureCheck sigCheck = SignatureCheck.getInstance(integrityCheck.getIntegrityCheck());
+
+            integrityAlg = sigCheck.getSignatureAlgorithm();
+
+            try
+            {
+                org.bouncycastle.asn1.x509.Certificate[] certificates = sigCheck.getCertificates();
+                if (validator != null)
+                {
+                    if (certificates == null)
+                    {
+                        throw new IOException("validator specified but no certifcates in store");
+                    }
+                    CertificateFactory certFact = helper.createCertificateFactory("X.509");
+                    X509Certificate[] certs = new X509Certificate[certificates.length];
+
+                    for (int i = 0; i != certs.length; i++)
+                    {
+                        certs[i] = (X509Certificate)certFact.generateCertificate(
+                                        new ByteArrayInputStream(certificates[i].getEncoded()));
+                    }
+
+                    if (validator.isValid(certs))
+                    {
+                        verifySig(store.getStoreData(), sigCheck, certs[0].getPublicKey());
+                    }
+                    else
+                    {
+                        throw new IOException("certificate chain in key store signature not valid");
+                    }
+                }
+                else
+                {
+                    verifySig(store.getStoreData(), sigCheck, verificationKey);
+                }
+            }
+            catch (GeneralSecurityException e)
+            {
+                throw new IOException("error verifying signature: " + e.getMessage(), e);
+            }
         }
         else
         {
@@ -869,7 +1335,7 @@
             throw new IOException("BCFKS KeyStore unable to parse store data information.");
         }
 
-        if (!storeData.getIntegrityAlgorithm().equals(hmacAlgorithm))
+        if (!storeData.getIntegrityAlgorithm().equals(integrityAlg))
         {
             throw new IOException("BCFKS KeyStore storeData integrity algorithm does not match store integrity algorithm.");
         }
@@ -893,47 +1359,157 @@
         PBES2Parameters pbes2Parameters = PBES2Parameters.getInstance(protectAlgId.getParameters());
         EncryptionScheme algId = pbes2Parameters.getEncryptionScheme();
 
-        if (!algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_CCM))
-        {
-            throw new IOException("BCFKS KeyStore cannot recognize protection encryption algorithm.");
-        }
-
         try
         {
-            CCMParameters ccmParameters = CCMParameters.getInstance(algId.getParameters());
             Cipher c;
             AlgorithmParameters algParams;
-            if (provider == null)
+            if (algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_CCM))
             {
-                c = Cipher.getInstance("AES/CCM/NoPadding");
-                algParams = AlgorithmParameters.getInstance("CCM");
+                c = helper.createCipher("AES/CCM/NoPadding");
+                algParams = helper.createAlgorithmParameters("CCM");
+
+                CCMParameters ccmParameters = CCMParameters.getInstance(algId.getParameters());
+
+                algParams.init(ccmParameters.getEncoded());
+            }
+            else if (algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_wrap_pad))
+            {
+                c = helper.createCipher("AESKWP");
+                algParams = null;
             }
             else
             {
-                c = Cipher.getInstance("AES/CCM/NoPadding", provider);
-                algParams = AlgorithmParameters.getInstance("CCM", provider);
+                throw new IOException("BCFKS KeyStore cannot recognize protection encryption algorithm.");
             }
 
-            algParams.init(ccmParameters.getEncoded());
-
-            byte[] keyBytes = generateKey(pbes2Parameters.getKeyDerivationFunc(), purpose, ((password != null) ? password : new char[0]));
+            byte[] keyBytes = generateKey(pbes2Parameters.getKeyDerivationFunc(), purpose, ((password != null) ? password : new char[0]), 32);
 
             c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), algParams);
 
             byte[] rv = c.doFinal(encryptedData);
             return rv;
         }
+        catch (IOException e)
+        {
+            throw e;
+        }
         catch (Exception e)
         {
             throw new IOException(e.toString());
         }
     }
 
-    private KeyDerivationFunc generatePkbdAlgorithmIdentifier(int keySizeInBytes)
+    private AlgorithmIdentifier generateSignatureAlgId(Key key, BCFKSLoadStoreParameter.SignatureAlgorithm sigAlg)
+        throws IOException
+    {
+        if (key== null)
+        {
+            return null;
+        }
+
+        if (key instanceof ECKey)
+        {
+            if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            {
+                return new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA512);
+            }
+            else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withECDSA)
+            {
+                return new AlgorithmIdentifier(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+            }
+        }
+        if (key instanceof DSAKey)
+        {
+            if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withDSA)
+            {
+                return new AlgorithmIdentifier(NISTObjectIdentifiers.dsa_with_sha512);
+            }
+            else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withDSA)
+            {
+                return new AlgorithmIdentifier(NISTObjectIdentifiers.id_dsa_with_sha3_512);
+            }
+        }
+        if (key instanceof RSAKey)
+        {
+            if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withRSA)
+            {
+                return new AlgorithmIdentifier(PKCSObjectIdentifiers.sha512WithRSAEncryption, DERNull.INSTANCE);
+            }
+            else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withRSA)
+            {
+                return new AlgorithmIdentifier(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, DERNull.INSTANCE);
+            }
+        }
+        throw new IOException("unknown signature algorithm");
+    }
+
+    private KeyDerivationFunc generatePkbdAlgorithmIdentifier(PBKDFConfig pbkdfConfig, int keySizeInBytes)
+    {
+        if (MiscObjectIdentifiers.id_scrypt.equals(pbkdfConfig.getAlgorithm()))
+        {
+            ScryptConfig scryptConfig = (ScryptConfig)pbkdfConfig;
+
+            byte[] pbkdSalt = new byte[scryptConfig.getSaltLength()];
+            getDefaultSecureRandom().nextBytes(pbkdSalt);
+
+            ScryptParams params = new ScryptParams(
+                pbkdSalt,
+                scryptConfig.getCostParameter(), scryptConfig.getBlockSize(), scryptConfig.getParallelizationParameter(), keySizeInBytes);
+
+            return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params);
+        }
+        else
+        {
+            PBKDF2Config pbkdf2Config = (PBKDF2Config)pbkdfConfig;
+
+            byte[] pbkdSalt = new byte[pbkdf2Config.getSaltLength()];
+            getDefaultSecureRandom().nextBytes(pbkdSalt);
+
+            return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, pbkdf2Config.getIterationCount(), keySizeInBytes, pbkdf2Config.getPRF()));
+        }
+    }
+
+    private KeyDerivationFunc generatePkbdAlgorithmIdentifier(KeyDerivationFunc baseAlg, int keySizeInBytes)
+    {
+        if (MiscObjectIdentifiers.id_scrypt.equals(baseAlg.getAlgorithm()))
+        {
+            ScryptParams oldParams = ScryptParams.getInstance(baseAlg.getParameters());
+
+            byte[] pbkdSalt = new byte[oldParams.getSalt().length];
+            getDefaultSecureRandom().nextBytes(pbkdSalt);
+
+            ScryptParams params = new ScryptParams(
+                pbkdSalt,
+                oldParams.getCostParameter(), oldParams.getBlockSize(), oldParams.getParallelizationParameter(), BigInteger.valueOf(keySizeInBytes));
+
+            return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params);
+        }
+        else
+        {
+            PBKDF2Params oldParams = PBKDF2Params.getInstance(baseAlg.getParameters());
+
+            byte[] pbkdSalt = new byte[oldParams.getSalt().length];
+            getDefaultSecureRandom().nextBytes(pbkdSalt);
+
+            PBKDF2Params params = new PBKDF2Params(pbkdSalt,
+                oldParams.getIterationCount().intValue(), keySizeInBytes, oldParams.getPrf());
+            return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, params);
+        }
+    }
+
+    private KeyDerivationFunc generatePkbdAlgorithmIdentifier(ASN1ObjectIdentifier derivationAlgorithm, int keySizeInBytes)
     {
         byte[] pbkdSalt = new byte[512 / 8];
         getDefaultSecureRandom().nextBytes(pbkdSalt);
-        return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, 1024, keySizeInBytes, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE)));
+
+        if (PKCSObjectIdentifiers.id_PBKDF2.equals(derivationAlgorithm))
+        {
+            return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, 50 * 1024, keySizeInBytes, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE)));
+        }
+        else
+        {
+            throw new IllegalStateException("unknown derivation algorithm: " + derivationAlgorithm);
+        }
     }
 
     public static class Std
@@ -941,7 +1517,7 @@
     {
         public Std()
         {
-            super(new BouncyCastleProvider());
+            super(new BCJcaJceHelper());
         }
     }
 
@@ -950,7 +1526,127 @@
     {
         public Def()
         {
-            super(null);
+            super(new DefaultJcaJceHelper());
+        }
+    }
+
+    private static class SharedKeyStoreSpi
+        extends BcFKSKeyStoreSpi
+        implements PKCSObjectIdentifiers, X509ObjectIdentifiers
+    {
+        private final Map<String, byte[]> cache;
+        private final byte[] seedKey;
+
+        public SharedKeyStoreSpi(JcaJceHelper provider)
+        {
+            super(provider);
+
+            try
+            {
+                this.seedKey = new byte[32];
+                provider.createSecureRandom("DEFAULT").nextBytes(seedKey);
+            }
+            catch (GeneralSecurityException e)
+            {
+                throw new IllegalArgumentException("can't create random - " + e.toString());
+            }
+
+            this.cache = new HashMap<String, byte[]>();
+        }
+
+        public void engineDeleteEntry(
+            String alias)
+            throws KeyStoreException
+        {
+            throw new KeyStoreException("delete operation not supported in shared mode");
+        }
+
+        public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+            throws KeyStoreException
+        {
+            throw new KeyStoreException("set operation not supported in shared mode");
+        }
+
+        public void engineSetKeyEntry(String alias, byte[] keyEncoding, Certificate[] chain)
+            throws KeyStoreException
+        {
+            throw new KeyStoreException("set operation not supported in shared mode");
+        }
+
+        public void engineSetCertificateEntry(String alias, Certificate cert)
+            throws KeyStoreException
+        {
+            throw new KeyStoreException("set operation not supported in shared mode");
+        }
+
+        public Key engineGetKey(
+            String alias,
+            char[] password)
+            throws NoSuchAlgorithmException, UnrecoverableKeyException
+        {
+            byte[] mac;
+
+            try
+            {
+                mac = calculateMac(alias, password);
+            }
+            catch (InvalidKeyException e)
+            {   // this should never happen...
+                throw new UnrecoverableKeyException("unable to recover key (" + alias + "): " + e.getMessage());
+            }
+
+            if (cache.containsKey(alias))
+            {
+                byte[] hash = cache.get(alias);
+
+                if (!Arrays.constantTimeAreEqual(hash, mac))
+                {
+                    throw new UnrecoverableKeyException("unable to recover key (" + alias + ")");
+                }
+            }
+
+            Key key = super.engineGetKey(alias, password);
+
+            if (key != null && !cache.containsKey(alias))
+            {
+                cache.put(alias, mac);
+            }
+
+            return key;
+        }
+
+        private byte[] calculateMac(String alias, char[] password)
+            throws NoSuchAlgorithmException, InvalidKeyException
+        {
+            byte[] encoding;
+            if (password != null)
+            {
+                encoding = Arrays.concatenate(Strings.toUTF8ByteArray(password), Strings.toUTF8ByteArray(alias));
+            }
+            else
+            {
+                encoding = Arrays.concatenate(seedKey, Strings.toUTF8ByteArray(alias));
+            }
+
+            return SCrypt.generate(encoding, seedKey, 16384, 8, 1, 32);
+        }
+    }
+
+    public static class StdShared
+        extends SharedKeyStoreSpi
+    {
+        public StdShared()
+        {
+            super(new BCJcaJceHelper());
+        }
+    }
+
+    public static class DefShared
+        extends SharedKeyStoreSpi
+    {
+        public DefShared()
+        {
+            super(new DefaultJcaJceHelper());
         }
     }
 
@@ -967,7 +1663,7 @@
 
         public Throwable getCause()
         {
-           return cause;
+            return cause;
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
index c112f41..b12aabe 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -6,6 +6,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -18,7 +19,6 @@
 import java.security.NoSuchProviderException;
 import java.security.Principal;
 import java.security.PrivateKey;
-import java.security.Provider;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
@@ -68,6 +68,7 @@
 import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.AuthenticatedSafe;
 import org.bouncycastle.asn1.pkcs.CertBag;
 import org.bouncycastle.asn1.pkcs.ContentInfo;
@@ -87,6 +88,7 @@
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.jcajce.PKCS12Key;
@@ -94,6 +96,7 @@
 import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jce.interfaces.BCKeyStore;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
@@ -101,6 +104,7 @@
 import org.bouncycastle.jce.provider.JDKPKCS12StoreParameter;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Properties;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -108,10 +112,12 @@
     extends KeyStoreSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers, BCKeyStore
 {
+    static final String PKCS12_MAX_IT_COUNT_PROPERTY = "org.bouncycastle.pkcs12.max_it_count";
+
     private final JcaJceHelper helper = new BCJcaJceHelper();
 
     private static final int SALT_SIZE = 20;
-    private static final int MIN_ITERATIONS = 1024;
+    private static final int MIN_ITERATIONS = 50 * 1024;
 
     private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider();
 
@@ -137,13 +143,17 @@
     static final int KEY_PUBLIC = 1;
     static final int KEY_SECRET = 2;
 
-    protected SecureRandom random = new SecureRandom();
+    protected SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
 
     // use of final causes problems with JDK 1.2 compiler
     private CertificateFactory certFact;
     private ASN1ObjectIdentifier keyAlgorithm;
     private ASN1ObjectIdentifier certAlgorithm;
 
+    private AlgorithmIdentifier macAlgorithm = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+    private int itCount = 2 * MIN_ITERATIONS;
+    private int saltLength = 20;
+
     private class CertId
     {
         byte[] id;
@@ -185,7 +195,7 @@
     }
 
     public PKCS12KeyStoreSpi(
-        Provider provider,
+        JcaJceHelper helper,
         ASN1ObjectIdentifier keyAlgorithm,
         ASN1ObjectIdentifier certAlgorithm)
     {
@@ -194,14 +204,7 @@
 
         try
         {
-            if (provider != null)
-            {
-                certFact = CertificateFactory.getInstance("X.509", provider);
-            }
-            else
-            {
-                certFact = CertificateFactory.getInstance("X.509");
-            }
+            certFact = helper.createCertificateFactory("X.509");
         }
         catch (Exception e)
         {
@@ -612,7 +615,7 @@
                 PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
                 PBEParameterSpec defParams = new PBEParameterSpec(
                     pbeParams.getIV(),
-                    pbeParams.getIterations().intValue());
+                    validateIterationCount(pbeParams.getIterations()));
 
                 Cipher cipher = helper.createCipher(algorithm.getId());
 
@@ -685,8 +688,6 @@
         if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds))
         {
             PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
-            PBEKeySpec pbeSpec = new PBEKeySpec(password);
-
             try
             {
                 PBEParameterSpec defParams = new PBEParameterSpec(
@@ -735,17 +736,15 @@
 
         if (func.isDefaultPrf())
         {
-            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), validateIterationCount(func.getIterationCount()), keySizeProvider.getKeySize(encScheme)));
         }
         else
         {
-            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), validateIterationCount(func.getIterationCount()), keySizeProvider.getKeySize(encScheme), func.getPrf()));
         }
 
         Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId());
 
-        AlgorithmIdentifier encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
-
         ASN1Encodable encParams = alg.getEncryptionScheme().getParameters();
         if (encParams instanceof ASN1OctetString)
         {
@@ -790,8 +789,17 @@
         bufIn.reset();
 
         ASN1InputStream bIn = new ASN1InputStream(bufIn);
-        ASN1Sequence obj = (ASN1Sequence)bIn.readObject();
-        Pfx bag = Pfx.getInstance(obj);
+        
+        Pfx bag;
+        try
+        {
+            bag = Pfx.getInstance(bIn.readObject());
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage());
+        }
+
         ContentInfo info = bag.getAuthSafe();
         Vector chain = new Vector();
         boolean unmarkedKey = false;
@@ -801,15 +809,16 @@
         {
             MacData mData = bag.getMacData();
             DigestInfo dInfo = mData.getMac();
-            AlgorithmIdentifier algId = dInfo.getAlgorithmId();
+            macAlgorithm = dInfo.getAlgorithmId();
             byte[] salt = mData.getSalt();
-            int itCount = mData.getIterationCount().intValue();
+            itCount = validateIterationCount(mData.getIterationCount());
+            saltLength = salt.length;
 
             byte[] data = ((ASN1OctetString)info.getContent()).getOctets();
 
             try
             {
-                byte[] res = calculatePbeMac(algId.getAlgorithm(), salt, itCount, password, false, data);
+                byte[] res = calculatePbeMac(macAlgorithm.getAlgorithm(), salt, itCount, password, false, data);
                 byte[] dig = dInfo.getDigest();
 
                 if (!Arrays.constantTimeAreEqual(res, dig))
@@ -820,7 +829,7 @@
                     }
 
                     // Try with incorrect zero length password
-                    res = calculatePbeMac(algId.getAlgorithm(), salt, itCount, password, true, data);
+                    res = calculatePbeMac(macAlgorithm.getAlgorithm(), salt, itCount, password, true, data);
 
                     if (!Arrays.constantTimeAreEqual(res, dig))
                     {
@@ -868,7 +877,6 @@
                             //
                             // set the attributes on the key
                             //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
                             String alias = null;
                             ASN1OctetString localId = null;
 
@@ -886,19 +894,23 @@
                                     {
                                         attr = (ASN1Primitive)attrSet.getObjectAt(0);
 
-                                        ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                        if (existing != null)
+                                        if (privKey instanceof PKCS12BagAttributeCarrier)
                                         {
-                                            // OK, but the value has to be the same
-                                            if (!existing.toASN1Primitive().equals(attr))
+                                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+                                            ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                                            if (existing != null)
                                             {
-                                                throw new IOException(
-                                                    "attempt to add existing attribute with different value");
+                                                // OK, but the value has to be the same
+                                                if (!existing.toASN1Primitive().equals(attr))
+                                                {
+                                                    throw new IOException(
+                                                        "attempt to add existing attribute with different value");
+                                                }
                                             }
-                                        }
-                                        else
-                                        {
-                                            bagAttr.setBagAttribute(aOid, attr);
+                                            else
+                                            {
+                                                bagAttr.setBagAttribute(aOid, attr);
+                                            }
                                         }
                                     }
 
@@ -1208,6 +1220,27 @@
         }
     }
 
+    private int validateIterationCount(BigInteger i)
+    {
+        int count = i.intValue();
+
+        if (count < 0)
+        {
+            throw new IllegalStateException("negative iteration count found");
+        }
+
+        BigInteger maxValue = Properties.asBigInteger(PKCS12_MAX_IT_COUNT_PROPERTY);
+        if (maxValue != null)
+        {
+            if (maxValue.intValue() < count)
+            {
+                throw new IllegalStateException("iteration count " + count + " greater than " + maxValue.intValue());
+            }
+        }
+
+        return count;
+    }
+
     public void engineStore(LoadStoreParameter param)
         throws IOException,
         NoSuchAlgorithmException, CertificateException
@@ -1615,8 +1648,7 @@
         //
         // create the mac
         //
-        byte[] mSalt = new byte[20];
-        int itCount = MIN_ITERATIONS;
+        byte[] mSalt = new byte[saltLength];
 
         random.nextBytes(mSalt);
 
@@ -1626,10 +1658,9 @@
 
         try
         {
-            byte[] res = calculatePbeMac(id_SHA1, mSalt, itCount, password, false, data);
+            byte[] res = calculatePbeMac(macAlgorithm.getAlgorithm(), mSalt, itCount, password, false, data);
 
-            AlgorithmIdentifier algId = new AlgorithmIdentifier(id_SHA1, DERNull.INSTANCE);
-            DigestInfo dInfo = new DigestInfo(algId, res);
+            DigestInfo dInfo = new DigestInfo(macAlgorithm, res);
 
             mData = new MacData(dInfo, mSalt, itCount);
         }
@@ -1706,7 +1737,7 @@
     {
         public BCPKCS12KeyStore()
         {
-            super(new BouncyCastleProvider(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
         }
     }
 
@@ -1715,7 +1746,7 @@
     {
         public BCPKCS12KeyStore3DES()
         {
-            super(new BouncyCastleProvider(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
         }
     }
 
@@ -1724,7 +1755,7 @@
     {
         public DefPKCS12KeyStore()
         {
-            super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
         }
     }
 
@@ -1733,7 +1764,7 @@
     {
         public DefPKCS12KeyStore3DES()
         {
-            super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
index 09da054..c68a53f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
@@ -50,8 +50,6 @@
 
 public final class AES
 {
-    private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
-
     private static final Map<String, String> generalAesAttributes = new HashMap<String, String>();
 
     static
@@ -1029,18 +1027,4 @@
             addPoly1305Algorithm(provider, "AES", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
-
-    private static Class lookup(String className)
-    {
-        try
-        {
-            Class def = AES.class.getClassLoader().loadClass(className);
-
-            return def;
-        }
-        catch (Exception e)
-        {
-            return null;
-        }
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java
index 819a832..7d9cb45 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java
@@ -15,6 +15,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.ARIAEngine;
 import org.bouncycastle.crypto.engines.ARIAWrapEngine;
 import org.bouncycastle.crypto.engines.ARIAWrapPadEngine;
@@ -31,6 +32,7 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
@@ -120,6 +122,14 @@
         }
     }
 
+    static public class KeyFactory
+         extends BaseSecretKeyFactory
+    {
+        public KeyFactory()
+        {
+            super("ARIA", null);
+        }
+    }
     public static class Poly1305
         extends BaseMac
     {
@@ -196,7 +206,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -483,6 +493,11 @@
             provider.addAlgorithm("KeyGenerator", NSRIObjectIdentifiers.id_aria192_gcm, PREFIX + "$KeyGen192");
             provider.addAlgorithm("KeyGenerator", NSRIObjectIdentifiers.id_aria256_gcm, PREFIX + "$KeyGen256");
 
+            provider.addAlgorithm("SecretKeyFactory.ARIA", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NSRIObjectIdentifiers.id_aria128_cbc, "ARIA");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NSRIObjectIdentifiers.id_aria192_cbc, "ARIA");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NSRIObjectIdentifiers.id_aria256_cbc, "ARIA");
+
             provider.addAlgorithm("AlgorithmParameterGenerator.ARIACCM", PREFIX + "$AlgParamGenCCM");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + NSRIObjectIdentifiers.id_aria128_ccm, "CCM");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + NSRIObjectIdentifiers.id_aria192_ccm, "CCM");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST5.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST5.java
index 4fbced6..7c29095 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST5.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST5.java
@@ -13,6 +13,7 @@
 import org.bouncycastle.asn1.misc.CAST5CBCParameters;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.CAST5Engine;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
@@ -72,7 +73,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
index d16e6c7..b9d683e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
 import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 
 public final class CAST6
 {
@@ -69,6 +70,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "CAST6 IV";
+        }
+    }
+
     public static class Mappings
         extends SymmetricAlgorithmProvider
     {
@@ -82,7 +92,8 @@
         {
             provider.addAlgorithm("Cipher.CAST6", PREFIX + "$ECB");
             provider.addAlgorithm("KeyGenerator.CAST6", PREFIX + "$KeyGen");
-
+            provider.addAlgorithm("AlgorithmParameters.CAST6", PREFIX + "$AlgParams");
+            
             addGMacAlgorithm(provider, "CAST6", PREFIX + "$GMAC", PREFIX + "$KeyGen");
             addPoly1305Algorithm(provider, "CAST6", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
index 33029c4..139810c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.CamelliaEngine;
 import org.bouncycastle.crypto.engines.CamelliaWrapEngine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
@@ -22,6 +23,7 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
@@ -83,6 +85,15 @@
         }
     }
 
+    static public class KeyFactory
+         extends BaseSecretKeyFactory
+    {
+        public KeyFactory()
+        {
+            super("Camellia", null);
+        }
+    }
+
     public static class Poly1305
         extends BaseMac
     {
@@ -159,7 +170,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -222,6 +233,11 @@
             provider.addAlgorithm("Alg.Alias.Cipher", NTTObjectIdentifiers.id_camellia192_wrap, "CAMELLIAWRAP");
             provider.addAlgorithm("Alg.Alias.Cipher", NTTObjectIdentifiers.id_camellia256_wrap, "CAMELLIAWRAP");
 
+            provider.addAlgorithm("SecretKeyFactory.CAMELLIA", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NTTObjectIdentifiers.id_camellia128_cbc, "CAMELLIA");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NTTObjectIdentifiers.id_camellia192_cbc, "CAMELLIA");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", NTTObjectIdentifiers.id_camellia256_cbc, "CAMELLIA");
+
             provider.addAlgorithm("KeyGenerator.CAMELLIA", PREFIX + "$KeyGen");
             provider.addAlgorithm("KeyGenerator", NTTObjectIdentifiers.id_camellia128_wrap, PREFIX + "$KeyGen128");
             provider.addAlgorithm("KeyGenerator", NTTObjectIdentifiers.id_camellia192_wrap, PREFIX + "$KeyGen192");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java
index af71b4e..07ad6e9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java
@@ -6,6 +6,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class ChaCha
@@ -50,6 +51,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "ChaCha7539 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -67,6 +77,10 @@
 
             provider.addAlgorithm("Cipher.CHACHA7539", PREFIX + "$Base7539");
             provider.addAlgorithm("KeyGenerator.CHACHA7539", PREFIX + "$KeyGen7539");
+            provider.addAlgorithm("AlgorithmParameters.CHACHA7539", PREFIX + "$AlgParams");
+            provider.addAlgorithm("Alg.Alias.Cipher.CHACHA20", "CHACHA7539");
+            provider.addAlgorithm("Alg.Alias.KeyGenerator.CHACHA20", "CHACHA7539");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.CHACHA20", "CHACHA7539");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DES.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DES.java
index 20a5dad..91a25ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DES.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DES.java
@@ -17,7 +17,9 @@
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.PasswordConverter;
 import org.bouncycastle.crypto.engines.DESEngine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
 import org.bouncycastle.crypto.generators.DESKeyGenerator;
@@ -30,6 +32,7 @@
 import org.bouncycastle.crypto.params.DESParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.jcajce.PBKDF1Key;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
@@ -169,7 +172,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -216,7 +219,7 @@
         {
             if (uninitialised)
             {
-                engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize));
+                engine.init(new KeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), defaultKeySize));
                 uninitialised = false;
             }
 
@@ -319,7 +322,15 @@
 
                 if (pbeSpec.getSalt() == null)
                 {
-                    return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, null);
+                    if (scheme == PKCS5S1 || scheme == PKCS5S1_UTF8)
+                    {
+                        return new PBKDF1Key(pbeSpec.getPassword(),
+                            scheme == PKCS5S1 ? PasswordConverter.ASCII : PasswordConverter.UTF8);
+                    }
+                    else
+                    {
+                        return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, null);
+                    }
                 }
 
                 if (forCipher)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java
index ab7718c..d433abe 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DESede.java
@@ -14,6 +14,7 @@
 
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.engines.DESedeEngine;
 import org.bouncycastle.crypto.engines.DESedeWrapEngine;
@@ -158,7 +159,7 @@
         {
             if (uninitialised)
             {
-                engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize));
+                engine.init(new KeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), defaultKeySize));
                 uninitialised = false;
             }
 
@@ -258,7 +259,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -424,6 +425,7 @@
 
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", PREFIX + "$PBEWithSHAAndDES3KeyFactory");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", PREFIX + "$PBEWithSHAAndDES2KeyFactory");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDDESEDE", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
 
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
@@ -432,6 +434,7 @@
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
 
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBE", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.3", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWithSHAAnd3KeyTripleDES", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DSTU7624.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DSTU7624.java
new file mode 100644
index 0000000..4ef48cb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/DSTU7624.java
@@ -0,0 +1,564 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.spec.IvParameterSpec;
+
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.engines.DSTU7624Engine;
+import org.bouncycastle.crypto.engines.DSTU7624WrapEngine;
+import org.bouncycastle.crypto.macs.KGMac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.KCCMBlockCipher;
+import org.bouncycastle.crypto.modes.KCTRBlockCipher;
+import org.bouncycastle.crypto.modes.KGCMBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+
+public class DSTU7624
+{
+    private DSTU7624()
+    {
+    }
+    
+    public static class ECB
+        extends BaseBlockCipher
+    {
+        public ECB()
+        {
+            super(new BlockCipherProvider()
+            {
+                public BlockCipher get()
+                {
+                    return new DSTU7624Engine(128);
+                }
+            });
+        }
+    }
+
+    // these next three allow some variation on the keysize used in each case.
+    public static class ECB_128
+       extends BaseBlockCipher
+    {
+        public ECB_128()
+        {
+            super(new DSTU7624Engine(128));
+        }
+    }
+
+    public static class ECB_256
+       extends BaseBlockCipher
+    {
+        public ECB_256()
+        {
+            super(new DSTU7624Engine(256));
+        }
+    }
+
+    public static class ECB_512
+       extends BaseBlockCipher
+    {
+        public ECB_512()
+        {
+            super(new DSTU7624Engine(512));
+        }
+    }
+
+    // what follows is fixed with a key size the same as the block size.
+    public static class ECB128
+       extends BaseBlockCipher
+    {
+        public ECB128()
+        {    // TODO: key size is also meant to be fixed
+            super(new DSTU7624Engine(128));
+        }
+    }
+
+    public static class ECB256
+       extends BaseBlockCipher
+    {
+        public ECB256()
+        {
+            super(new DSTU7624Engine(256));
+        }
+    }
+
+    public static class ECB512
+       extends BaseBlockCipher
+    {
+        public ECB512()
+        {
+            super(new DSTU7624Engine(512));
+        }
+    }
+
+    public static class CBC128
+       extends BaseBlockCipher
+    {
+        public CBC128()
+        {    // TODO: key size is also meant to be fixed
+            super(new CBCBlockCipher(new DSTU7624Engine(128)), 128);
+        }
+    }
+
+    public static class CBC256
+       extends BaseBlockCipher
+    {
+        public CBC256()
+        {
+            super(new CBCBlockCipher(new DSTU7624Engine(256)), 256);
+        }
+    }
+
+    public static class CBC512
+       extends BaseBlockCipher
+    {
+        public CBC512()
+        {
+            super(new CBCBlockCipher(new DSTU7624Engine(512)), 512);
+        }
+    }
+
+    public static class OFB128
+       extends BaseBlockCipher
+    {
+        public OFB128()
+        {    // TODO: key size is also meant to be fixed
+            super(new BufferedBlockCipher(new OFBBlockCipher(new DSTU7624Engine(128), 128)), 128);
+        }
+    }
+
+    public static class OFB256
+       extends BaseBlockCipher
+    {
+        public OFB256()
+        {
+            super(new BufferedBlockCipher(new OFBBlockCipher(new DSTU7624Engine(256), 256)), 256);
+        }
+    }
+
+    public static class OFB512
+       extends BaseBlockCipher
+    {
+        public OFB512()
+        {
+            super(new BufferedBlockCipher(new OFBBlockCipher(new DSTU7624Engine(512), 512)), 512);
+        }
+    }
+
+    public static class CFB128
+       extends BaseBlockCipher
+    {
+        public CFB128()
+        {    // TODO: key size is also meant to be fixed
+            super(new BufferedBlockCipher(new CFBBlockCipher(new DSTU7624Engine(128), 128)), 128);
+        }
+    }
+
+    public static class CFB256
+       extends BaseBlockCipher
+    {
+        public CFB256()
+        {
+            super(new BufferedBlockCipher(new CFBBlockCipher(new DSTU7624Engine(256), 256)), 256);
+        }
+    }
+
+    public static class CFB512
+       extends BaseBlockCipher
+    {
+        public CFB512()
+        {
+            super(new BufferedBlockCipher(new CFBBlockCipher(new DSTU7624Engine(512), 512)), 512);
+        }
+    }
+
+    public static class CTR128
+       extends BaseBlockCipher
+    {
+        public CTR128()
+        {    // TODO: key size is also meant to be fixed
+            super(new BufferedBlockCipher(new KCTRBlockCipher(new DSTU7624Engine(128))), 128);
+        }
+    }
+
+    public static class CTR256
+       extends BaseBlockCipher
+    {
+        public CTR256()
+        {
+            super(new BufferedBlockCipher(new KCTRBlockCipher(new DSTU7624Engine(256))), 256);
+        }
+    }
+
+    public static class CTR512
+       extends BaseBlockCipher
+    {
+        public CTR512()
+        {
+            super(new BufferedBlockCipher(new KCTRBlockCipher(new DSTU7624Engine(512))), 512);
+        }
+    }
+
+    public static class CCM128
+       extends BaseBlockCipher
+    {
+        public CCM128()
+        {    // TODO: key size is also meant to be fixed
+            super(new KCCMBlockCipher(new DSTU7624Engine(128)));
+        }
+    }
+
+    public static class CCM256
+       extends BaseBlockCipher
+    {
+        public CCM256()
+        {
+            super(new KCCMBlockCipher(new DSTU7624Engine(256)));
+        }
+    }
+
+    public static class CCM512
+       extends BaseBlockCipher
+    {
+        public CCM512()
+        {
+            super(new KCCMBlockCipher(new DSTU7624Engine(512)));
+        }
+    }
+
+    public static class GCM128
+       extends BaseBlockCipher
+    {
+        public GCM128()
+        {    // TODO: key size is also meant to be fixed
+            super(new KGCMBlockCipher(new DSTU7624Engine(128)));
+        }
+    }
+
+    public static class GCM256
+       extends BaseBlockCipher
+    {
+        public GCM256()
+        {
+            super(new KGCMBlockCipher(new DSTU7624Engine(256)));
+        }
+    }
+
+    public static class GCM512
+       extends BaseBlockCipher
+    {
+        public GCM512()
+        {
+            super(new KGCMBlockCipher(new DSTU7624Engine(512)));
+        }
+    }
+
+    public static class Wrap
+        extends BaseWrapCipher
+    {
+        public Wrap()
+        {
+            super(new DSTU7624WrapEngine(128));
+        }
+    }
+
+    public static class Wrap128
+        extends BaseWrapCipher
+    {
+        public Wrap128()
+        {
+            super(new DSTU7624WrapEngine(128));
+        }
+    }
+
+    public static class Wrap256
+        extends BaseWrapCipher
+    {
+        public Wrap256()
+        {
+            super(new DSTU7624WrapEngine(256));
+        }
+    }
+
+    public static class Wrap512
+        extends BaseWrapCipher
+    {
+        public Wrap512()
+        {
+            super(new DSTU7624WrapEngine(512));
+        }
+    }
+
+    public static class GMAC
+        extends BaseMac
+    {
+        public GMAC()
+        {
+            super(new KGMac(new KGCMBlockCipher(new DSTU7624Engine(128)), 128));
+        }
+    }
+       // TODO: enforce key size restriction
+    public static class GMAC128
+        extends BaseMac
+    {
+        public GMAC128()
+        {
+            super(new KGMac(new KGCMBlockCipher(new DSTU7624Engine(128)), 128));
+        }
+    }
+
+    public static class GMAC256
+        extends BaseMac
+    {
+        public GMAC256()
+        {
+            super(new KGMac(new KGCMBlockCipher(new DSTU7624Engine(256)), 256));
+        }
+    }
+
+    public static class GMAC512
+        extends BaseMac
+    {
+        public GMAC512()
+        {
+            super(new KGMac(new KGCMBlockCipher(new DSTU7624Engine(512)), 512));
+        }
+    }
+
+    public static class KeyGen
+        extends BaseKeyGenerator
+    {
+        public KeyGen()
+        {
+            this(256);
+        }
+
+        public KeyGen(int keySize)
+        {
+            super("DSTU7624", keySize, new CipherKeyGenerator());
+        }
+    }
+
+    public static class KeyGen128
+        extends DSTU7624.KeyGen
+    {
+        public KeyGen128()
+        {
+            super(128);
+        }
+    }
+
+    public static class KeyGen256
+        extends DSTU7624.KeyGen
+    {
+        public KeyGen256()
+        {
+            super(256);
+        }
+    }
+
+    public static class KeyGen512
+        extends DSTU7624.KeyGen
+    {
+        public KeyGen512()
+        {
+            super(512);
+        }
+    }
+
+    public static class AlgParamGen
+        extends BaseAlgorithmParameterGenerator
+    {
+        private final int ivLength;
+
+        public AlgParamGen(int blockSize)
+        {
+            this.ivLength = blockSize / 8;
+        }
+
+        protected void engineInit(
+            AlgorithmParameterSpec genParamSpec,
+            SecureRandom random)
+            throws InvalidAlgorithmParameterException
+        {
+            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DSTU7624 parameter generation.");
+        }
+
+        protected AlgorithmParameters engineGenerateParameters()
+        {
+            byte[] iv = new byte[ivLength];
+
+            if (random == null)
+            {
+                random = CryptoServicesRegistrar.getSecureRandom();
+            }
+
+            random.nextBytes(iv);
+
+            AlgorithmParameters params;
+
+            try
+            {
+                params = createParametersInstance("DSTU7624");
+                params.init(new IvParameterSpec(iv));
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e.getMessage());
+            }
+
+            return params;
+        }
+    }
+
+    public static class AlgParamGen128
+        extends AlgParamGen
+    {
+        AlgParamGen128()
+        {
+            super(128);
+        }
+    }
+
+    public static class AlgParamGen256
+        extends AlgParamGen
+    {
+        AlgParamGen256()
+        {
+            super(256);
+        }
+    }
+
+    public static class AlgParamGen512
+        extends AlgParamGen
+    {
+        AlgParamGen512()
+        {
+            super(512);
+        }
+    }
+
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "DSTU7624 IV";
+        }
+    }
+
+    public static class Mappings
+        extends SymmetricAlgorithmProvider
+    {
+        private static final String PREFIX = DSTU7624.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+
+            provider.addAlgorithm("AlgorithmParameters.DSTU7624", PREFIX + "$AlgParams128");
+            provider.addAlgorithm("AlgorithmParameters", UAObjectIdentifiers.dstu7624cbc_128, PREFIX + "$AlgParams");
+            provider.addAlgorithm("AlgorithmParameters",  UAObjectIdentifiers.dstu7624cbc_256, PREFIX + "$AlgParams");
+            provider.addAlgorithm("AlgorithmParameters",  UAObjectIdentifiers.dstu7624cbc_512, PREFIX + "$AlgParams");
+
+            provider.addAlgorithm("AlgorithmParameterGenerator.DSTU7624", PREFIX + "$AlgParamGen128");
+            provider.addAlgorithm("AlgorithmParameterGenerator", UAObjectIdentifiers.dstu7624cbc_128, PREFIX + "$AlgParamGen128");
+            provider.addAlgorithm("AlgorithmParameterGenerator", UAObjectIdentifiers.dstu7624cbc_256, PREFIX + "$AlgParamGen256");
+            provider.addAlgorithm("AlgorithmParameterGenerator", UAObjectIdentifiers.dstu7624cbc_512, PREFIX + "$AlgParamGen512");
+
+            provider.addAlgorithm("Cipher.DSTU7624", PREFIX + "$ECB_128");
+            provider.addAlgorithm("Cipher.DSTU7624-128", PREFIX + "$ECB_128");
+            provider.addAlgorithm("Cipher.DSTU7624-256", PREFIX + "$ECB_256");
+            provider.addAlgorithm("Cipher.DSTU7624-512", PREFIX + "$ECB_512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ecb_128, PREFIX + "$ECB128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ecb_256, PREFIX + "$ECB256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ecb_512, PREFIX + "$ECB512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cbc_128, PREFIX + "$CBC128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cbc_256, PREFIX + "$CBC256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cbc_512, PREFIX + "$CBC512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ofb_128, PREFIX + "$OFB128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ofb_256, PREFIX + "$OFB256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ofb_512, PREFIX + "$OFB512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cfb_128, PREFIX + "$CFB128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cfb_256, PREFIX + "$CFB256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624cfb_512, PREFIX + "$CFB512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ctr_128, PREFIX + "$CTR128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ctr_256, PREFIX + "$CTR256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ctr_512, PREFIX + "$CTR512");
+
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ccm_128, PREFIX + "$CCM128");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ccm_256, PREFIX + "$CCM256");
+            provider.addAlgorithm("Cipher", UAObjectIdentifiers.dstu7624ccm_512, PREFIX + "$CCM512");
+
+            provider.addAlgorithm("Cipher.DSTU7624KW", PREFIX + "$Wrap");
+            provider.addAlgorithm("Alg.Alias.Cipher.DSTU7624WRAP", "DSTU7624KW");
+            provider.addAlgorithm("Cipher.DSTU7624-128KW", PREFIX + "$Wrap128");
+            provider.addAlgorithm("Alg.Alias.Cipher." + UAObjectIdentifiers.dstu7624kw_128.getId(), "DSTU7624-128KW");
+            provider.addAlgorithm("Alg.Alias.Cipher.DSTU7624-128WRAP", "DSTU7624-128KW");
+            provider.addAlgorithm("Cipher.DSTU7624-256KW", PREFIX + "$Wrap256");
+            provider.addAlgorithm("Alg.Alias.Cipher." + UAObjectIdentifiers.dstu7624kw_256.getId(), "DSTU7624-256KW");
+            provider.addAlgorithm("Alg.Alias.Cipher.DSTU7624-256WRAP", "DSTU7624-256KW");
+            provider.addAlgorithm("Cipher.DSTU7624-512KW", PREFIX + "$Wrap512");
+            provider.addAlgorithm("Alg.Alias.Cipher." + UAObjectIdentifiers.dstu7624kw_512.getId(), "DSTU7624-512KW");
+            provider.addAlgorithm("Alg.Alias.Cipher.DSTU7624-512WRAP", "DSTU7624-512KW");
+
+            provider.addAlgorithm("Mac.DSTU7624GMAC", PREFIX + "$GMAC");
+            provider.addAlgorithm("Mac.DSTU7624-128GMAC", PREFIX + "$GMAC128");
+            provider.addAlgorithm("Alg.Alias.Mac." + UAObjectIdentifiers.dstu7624gmac_128.getId(), "DSTU7624-128GMAC");
+            provider.addAlgorithm("Mac.DSTU7624-256GMAC", PREFIX + "$GMAC256");
+            provider.addAlgorithm("Alg.Alias.Mac." + UAObjectIdentifiers.dstu7624gmac_256.getId(), "DSTU7624-256GMAC");
+            provider.addAlgorithm("Mac.DSTU7624-512GMAC", PREFIX + "$GMAC512");
+            provider.addAlgorithm("Alg.Alias.Mac." + UAObjectIdentifiers.dstu7624gmac_512.getId(), "DSTU7624-512GMAC");
+            
+            provider.addAlgorithm("KeyGenerator.DSTU7624", PREFIX + "$KeyGen");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624kw_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624kw_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624kw_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ecb_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ecb_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ecb_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cbc_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cbc_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cbc_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ofb_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ofb_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ofb_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cfb_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cfb_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624cfb_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ctr_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ctr_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ctr_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ccm_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ccm_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624ccm_512, PREFIX + "$KeyGen512");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624gmac_128, PREFIX + "$KeyGen128");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624gmac_256, PREFIX + "$KeyGen256");
+            provider.addAlgorithm("KeyGenerator", UAObjectIdentifiers.dstu7624gmac_512, PREFIX + "$KeyGen512");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
index a849a18..e08ca0b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
@@ -1,33 +1,67 @@
 package org.bouncycastle.jcajce.provider.symmetric;
 
+import java.io.IOException;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.crypto.spec.IvParameterSpec;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.engines.CryptoProWrapEngine;
 import org.bouncycastle.crypto.engines.GOST28147Engine;
+import org.bouncycastle.crypto.engines.GOST28147WrapEngine;
 import org.bouncycastle.crypto.macs.GOST28147Mac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
-import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 
 public final class GOST28147
 {
+    private static Map<ASN1ObjectIdentifier, String> oidMappings = new HashMap<ASN1ObjectIdentifier, String>();
+    private static Map<String, ASN1ObjectIdentifier> nameMappings = new HashMap<String, ASN1ObjectIdentifier>();
+
+    static
+    {
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_TestParamSet, "E-TEST");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, "E-A");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet, "E-B");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet, "E-C");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet, "E-D");
+        oidMappings.put(RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z, "Param-Z");
+        
+        nameMappings.put("E-A", CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet);
+        nameMappings.put("E-B", CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet);
+        nameMappings.put("E-C", CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet);
+        nameMappings.put("E-D", CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet);
+        nameMappings.put("Param-Z", RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z);
+    }
+
     private GOST28147()
     {
     }
-    
+
     public static class ECB
         extends BaseBlockCipher
     {
@@ -38,7 +72,7 @@
     }
 
     public static class CBC
-       extends BaseBlockCipher
+        extends BaseBlockCipher
     {
         public CBC()
         {
@@ -47,7 +81,7 @@
     }
 
     public static class GCFB
-       extends BaseBlockCipher
+        extends BaseBlockCipher
     {
         public GCFB()
         {
@@ -55,6 +89,24 @@
         }
     }
 
+    public static class GostWrap
+        extends BaseWrapCipher
+    {
+        public GostWrap()
+        {
+            super(new GOST28147WrapEngine());
+        }
+    }
+
+    public static class CryptoProWrap
+        extends BaseWrapCipher
+    {
+        public CryptoProWrap()
+        {
+            super(new CryptoProWrapEngine());
+        }
+    }
+
     /**
      * GOST28147
      */
@@ -84,21 +136,29 @@
     public static class AlgParamGen
         extends BaseAlgorithmParameterGenerator
     {
+        byte[] iv = new byte[8];
+        byte[] sBox = GOST28147Engine.getSBox("E-A");
+
         protected void engineInit(
             AlgorithmParameterSpec genParamSpec,
             SecureRandom random)
             throws InvalidAlgorithmParameterException
         {
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST28147 parameter generation.");
+            if (genParamSpec instanceof GOST28147ParameterSpec)
+            {
+                  this.sBox = ((GOST28147ParameterSpec)genParamSpec).getSBox();
+            }
+            else
+            {
+                throw new InvalidAlgorithmParameterException("parameter spec not supported");
+            }
         }
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[16];
-
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -108,7 +168,7 @@
             try
             {
                 params = createParametersInstance("GOST28147");
-                params.init(new IvParameterSpec(iv));
+                params.init(new GOST28147ParameterSpec(sBox, iv));
             }
             catch (Exception e)
             {
@@ -119,12 +179,217 @@
         }
     }
 
-    public static class AlgParams
-        extends IvAlgorithmParameters
+    public abstract static class BaseAlgParams
+        extends BaseAlgorithmParameters
     {
+        private ASN1ObjectIdentifier sBox = CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet;
+        private byte[] iv;
+
+        protected final void engineInit(byte[] encoding)
+            throws IOException
+        {
+            engineInit(encoding, "ASN.1");
+        }
+
+        protected final byte[] engineGetEncoded()
+            throws IOException
+        {
+            return engineGetEncoded("ASN.1");
+        }
+
+        protected final byte[] engineGetEncoded(
+            String format)
+            throws IOException
+        {
+            if (isASN1FormatString(format))
+            {
+                return localGetEncoded();
+            }
+
+            throw new IOException("Unknown parameter format: " + format);
+        }
+
+        protected final void engineInit(
+            byte[] params,
+            String format)
+            throws IOException
+        {
+            if (params == null)
+            {
+                throw new NullPointerException("Encoded parameters cannot be null");
+            }
+
+            if (isASN1FormatString(format))
+            {
+                try
+                {
+                    localInit(params);
+                }
+                catch (IOException e)
+                {
+                    throw e;
+                }
+                catch (Exception e)
+                {
+                    throw new IOException("Parameter parsing failed: " + e.getMessage());
+                }
+            }
+            else
+            {
+                throw new IOException("Unknown parameter format: " + format);
+            }
+        }
+
+        protected byte[] localGetEncoded()
+            throws IOException
+        {
+            return new GOST28147Parameters(iv, sBox).getEncoded();
+        }
+
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+            Class paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (paramSpec == IvParameterSpec.class)
+            {
+                return new IvParameterSpec(iv);
+            }
+
+            if (paramSpec == GOST28147ParameterSpec.class || paramSpec == AlgorithmParameterSpec.class)
+            {
+                return new GOST28147ParameterSpec(sBox, iv);
+            }
+
+            throw new InvalidParameterSpecException("AlgorithmParameterSpec not recognized: " + paramSpec.getName());
+        }
+
+        protected void engineInit(
+            AlgorithmParameterSpec paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (paramSpec instanceof IvParameterSpec)
+            {
+                this.iv = ((IvParameterSpec)paramSpec).getIV();
+            }
+            else if (paramSpec instanceof GOST28147ParameterSpec)
+            {
+                this.iv = ((GOST28147ParameterSpec)paramSpec).getIV();
+                try
+                {
+                    this.sBox = getSBoxOID((((GOST28147ParameterSpec)paramSpec).getSBox()));
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw new InvalidParameterSpecException(e.getMessage());
+                }
+            }
+            else
+            {
+                throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object");
+            }
+        }
+
+        protected static ASN1ObjectIdentifier getSBoxOID(String name)
+        {
+            ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)nameMappings.get(name);
+
+            if (oid == null)
+            {
+                throw new IllegalArgumentException("Unknown SBOX name: " + name);
+            }
+
+            return oid;
+        }
+
+        protected static ASN1ObjectIdentifier getSBoxOID(byte[] sBox)
+        {
+            return getSBoxOID(GOST28147Engine.getSBoxName(sBox));
+        }
+
+        abstract void localInit(byte[] params) throws IOException;
+    }
+
+    public static class AlgParams
+        extends BaseAlgParams
+    {
+        private ASN1ObjectIdentifier sBox = CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet;
+        private byte[] iv;
+
+        protected byte[] localGetEncoded()
+            throws IOException
+        {
+            return new GOST28147Parameters(iv, sBox).getEncoded();
+        }
+
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+            Class paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (paramSpec == IvParameterSpec.class)
+            {
+                return new IvParameterSpec(iv);
+            }
+
+            if (paramSpec == GOST28147ParameterSpec.class || paramSpec == AlgorithmParameterSpec.class)
+            {
+                return new GOST28147ParameterSpec(sBox, iv);
+            }
+
+            throw new InvalidParameterSpecException("AlgorithmParameterSpec not recognized: " + paramSpec.getName());
+        }
+
+        protected void engineInit(
+            AlgorithmParameterSpec paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (paramSpec instanceof IvParameterSpec)
+            {
+                this.iv = ((IvParameterSpec)paramSpec).getIV();
+            }
+            else if (paramSpec instanceof GOST28147ParameterSpec)
+            {
+                this.iv = ((GOST28147ParameterSpec)paramSpec).getIV();
+                try
+                {
+                    this.sBox = getSBoxOID((((GOST28147ParameterSpec)paramSpec).getSBox()));
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw new InvalidParameterSpecException(e.getMessage());
+                }
+            }
+            else
+            {
+                throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object");
+            }
+        }
+
+        protected void localInit(
+            byte[] params)
+            throws IOException
+        {
+            ASN1Primitive asn1Params = ASN1Primitive.fromByteArray(params);
+
+            if (asn1Params instanceof ASN1OctetString)
+            {
+                this.iv = ASN1OctetString.getInstance(asn1Params).getOctets();
+            }
+            else if (asn1Params instanceof ASN1Sequence)
+            {
+                GOST28147Parameters gParams = GOST28147Parameters.getInstance(asn1Params);
+
+                this.sBox = gParams.getEncryptionParamSet();
+                this.iv = gParams.getIV();
+            }
+            else
+            {
+                throw new IOException("Unable to recognize parameters");
+            }
+        }
+
         protected String engineToString()
         {
-            return "GOST IV";
+            return "GOST 28147 IV Parameters";
         }
     }
 
@@ -149,6 +414,15 @@
             provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147");
             provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
 
+            provider.addAlgorithm("AlgorithmParameters." + "GOST28147", PREFIX + "$AlgParams");
+            provider.addAlgorithm("AlgorithmParameterGenerator." + "GOST28147", PREFIX + "$AlgParamGen");
+
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
+
+            provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap, PREFIX + "$CryptoProWrap");
+            provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap, PREFIX + "$GostWrap");
+
             provider.addAlgorithm("Mac.GOST28147MAC", PREFIX + "$Mac");
             provider.addAlgorithm("Alg.Alias.Mac.GOST28147", "GOST28147MAC");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST3412_2015.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST3412_2015.java
new file mode 100644
index 0000000..3134e00
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST3412_2015.java
@@ -0,0 +1,129 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.GOST3412_2015Engine;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.modes.G3413CBCBlockCipher;
+import org.bouncycastle.crypto.modes.G3413CFBBlockCipher;
+import org.bouncycastle.crypto.modes.G3413CTRBlockCipher;
+import org.bouncycastle.crypto.modes.G3413OFBBlockCipher;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+
+public class GOST3412_2015
+{
+    public static class ECB
+        extends BaseBlockCipher
+    {
+        public ECB()
+        {
+            super(new GOST3412_2015Engine());
+        }
+    }
+
+    public static class CBC
+        extends BaseBlockCipher
+    {
+        public CBC()
+        {
+            super(new G3413CBCBlockCipher(new GOST3412_2015Engine()), false, 128);
+        }
+    }
+
+    public static class GCFB
+        extends BaseBlockCipher
+    {
+        public GCFB()
+        {
+            super(new BufferedBlockCipher(new G3413CFBBlockCipher(new GOST3412_2015Engine())), false, 128);
+        }
+    }
+
+    public static class GCFB8
+        extends BaseBlockCipher
+    {
+        public GCFB8()
+        {
+            super(new BufferedBlockCipher(new G3413CFBBlockCipher(new GOST3412_2015Engine(), 8)), false, 128);
+        }
+    }
+
+    public static class OFB
+        extends BaseBlockCipher
+    {
+        public OFB()
+        {
+            super(new BufferedBlockCipher(new G3413OFBBlockCipher(new GOST3412_2015Engine())), false, 128);
+        }
+
+    }
+
+    public static class CTR
+        extends BaseBlockCipher
+    {
+        public CTR()
+        {
+            super(new BufferedBlockCipher(new G3413CTRBlockCipher(new GOST3412_2015Engine())), 128);
+        }
+
+    }
+
+    /**
+     * GOST3412 2015 CMAC( OMAC1)
+     */
+    public static class Mac
+        extends BaseMac
+    {
+        public Mac()
+        {
+            super(new CMac(new GOST3412_2015Engine()));
+        }
+    }
+
+
+    public static class KeyGen
+        extends BaseKeyGenerator
+    {
+        public KeyGen()
+        {
+            this(256);
+        }
+
+        public KeyGen(int keySize)
+        {
+            super("GOST3412-2015", keySize, new CipherKeyGenerator());
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = GOST3412_2015.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("Cipher.GOST3412-2015", PREFIX + "$ECB");
+            provider.addAlgorithm("Cipher.GOST3412-2015/CFB", PREFIX + "$GCFB");
+            provider.addAlgorithm("Cipher.GOST3412-2015/CFB8", PREFIX + "$GCFB8");
+            provider.addAlgorithm("Cipher.GOST3412-2015/OFB", PREFIX + "$OFB");
+            provider.addAlgorithm("Cipher.GOST3412-2015/CBC", PREFIX + "$CBC");
+            provider.addAlgorithm("Cipher.GOST3412-2015/CTR", PREFIX + "$CTR");
+
+            provider.addAlgorithm("KeyGenerator.GOST3412-2015", PREFIX + "$KeyGen");;
+
+            provider.addAlgorithm("Mac.GOST3412MAC", PREFIX + "$Mac");
+            provider.addAlgorithm("Alg.Alias.Mac.GOST3412-2015", "GOST3412MAC");
+        }
+    }
+
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
index 5ccc8ff..a28cf3e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
@@ -7,11 +7,12 @@
 
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.cms.GCMParameters;
+import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
 import org.bouncycastle.util.Integers;
 
 class GcmSpecUtil
 {
-    static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
 
     static boolean gcmSpecExists()
     {
@@ -63,16 +64,4 @@
             throw new InvalidParameterSpecException("Cannot process GCMParameterSpec");
         }
     }
-
-    private static Class lookup(String className)
-    {
-        try
-        {
-            return GcmSpecUtil.class.getClassLoader().loadClass(className);
-        }
-        catch (Exception e)
-        {
-            return null;
-        }
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Grainv1.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Grainv1.java
index fce224d..4fa544c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Grainv1.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Grainv1.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class Grainv1
@@ -31,6 +32,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Grainv1 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -44,6 +54,7 @@
         {
             provider.addAlgorithm("Cipher.Grainv1", PREFIX + "$Base");
             provider.addAlgorithm("KeyGenerator.Grainv1", PREFIX + "$KeyGen");
+            provider.addAlgorithm("AlgorithmParameters.Grainv1", PREFIX + "$AlgParams");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC128.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC128.java
index efe7ede..755cc09 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC128.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC128.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class HC128
@@ -31,6 +32,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "HC128 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -44,6 +54,7 @@
         {
             provider.addAlgorithm("Cipher.HC128", PREFIX + "$Base");
             provider.addAlgorithm("KeyGenerator.HC128", PREFIX + "$KeyGen");
+            provider.addAlgorithm("AlgorithmParameters.HC128", PREFIX + "$AlgParams");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC256.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC256.java
index dd93445..1335719 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC256.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/HC256.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class HC256
@@ -31,6 +32,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "HC256 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -44,6 +54,7 @@
         {
             provider.addAlgorithm("Cipher.HC256", PREFIX + "$Base");
             provider.addAlgorithm("KeyGenerator.HC256", PREFIX + "$KeyGen");
+            provider.addAlgorithm("AlgorithmParameters.HC256", PREFIX + "$AlgParams");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/IDEA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/IDEA.java
index d0e3178..f4d16f8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/IDEA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/IDEA.java
@@ -9,11 +9,10 @@
 
 import javax.crypto.spec.IvParameterSpec;
 
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.misc.IDEACBCPar;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.IDEAEngine;
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
 import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
@@ -95,7 +94,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -192,8 +191,7 @@
             }
             if (format.equals("ASN.1"))
             {
-                ASN1InputStream aIn = new ASN1InputStream(params);
-                IDEACBCPar      oct = new IDEACBCPar((ASN1Sequence)aIn.readObject());
+                IDEACBCPar      oct = IDEACBCPar.getInstance(params);
 
                 engineInit(oct.getIV());
                 return;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
index 1fefd14..1e32091 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
@@ -9,6 +9,7 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.NoekeonEngine;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
@@ -95,7 +96,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF1.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF1.java
new file mode 100644
index 0000000..122f918
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF1.java
@@ -0,0 +1,125 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.io.IOException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.pkcs.PBEParameter;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+public class PBEPBKDF1
+{
+    private PBEPBKDF1()
+    {
+
+    }
+
+    public static class AlgParams
+        extends BaseAlgorithmParameters
+    {
+        PBEParameter params;
+
+        protected byte[] engineGetEncoded()
+        {
+            try
+            {
+                return params.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException("Oooops! " + e.toString());
+            }
+        }
+
+        protected byte[] engineGetEncoded(
+            String format)
+        {
+            if (this.isASN1FormatString(format))
+            {
+                return engineGetEncoded();
+            }
+
+            return null;
+        }
+
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+            Class paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (paramSpec == PBEParameterSpec.class)
+            {
+                return new PBEParameterSpec(params.getSalt(),
+                                params.getIterationCount().intValue());
+            }
+
+            throw new InvalidParameterSpecException("unknown parameter spec passed to PBKDF1 PBE parameters object.");
+        }
+
+        protected void engineInit(
+            AlgorithmParameterSpec paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (!(paramSpec instanceof PBEParameterSpec))
+            {
+                throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PBKDF1 PBE parameters algorithm parameters object");
+            }
+
+            PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
+
+            this.params = new PBEParameter(pbeSpec.getSalt(),
+                                pbeSpec.getIterationCount());
+        }
+
+        protected void engineInit(
+            byte[] params)
+            throws IOException
+        {
+            this.params = PBEParameter.getInstance(params);
+        }
+
+        protected void engineInit(
+            byte[] params,
+            String format)
+            throws IOException
+        {
+            if (this.isASN1FormatString(format))
+            {
+                engineInit(params);
+                return;
+            }
+
+            throw new IOException("Unknown parameters format in PBKDF2 parameters object");
+        }
+
+        protected String engineToString()
+        {
+            return "PBKDF1 Parameters";
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = PBEPBKDF1.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("AlgorithmParameters.PBKDF1", PREFIX + "$AlgParams");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBKDF1");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBKDF1");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBKDF1");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBKDF1");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBKDF1");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
index 7f41877..8c77e96 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
@@ -5,6 +5,8 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.InvalidParameterSpecException;
 import java.security.spec.KeySpec;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.PBEKeySpec;
@@ -14,9 +16,12 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.PasswordConverter;
+import org.bouncycastle.jcajce.PBKDF2Key;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters;
@@ -24,9 +29,26 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
+import org.bouncycastle.util.Integers;
 
 public class PBEPBKDF2
 {
+    private static final Map prfCodes = new HashMap();
+
+    static
+    {
+        prfCodes.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(PBE.GOST3411));
+        prfCodes.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(PBE.SHA1));
+        prfCodes.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(PBE.SHA256));
+        prfCodes.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(PBE.SHA224));
+        prfCodes.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(PBE.SHA384));
+        prfCodes.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(PBE.SHA512));
+        prfCodes.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(PBE.SHA3_256));
+        prfCodes.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(PBE.SHA3_224));
+        prfCodes.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(PBE.SHA3_384));
+        prfCodes.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(PBE.SHA3_512));
+    }
+
     private PBEPBKDF2()
     {
 
@@ -67,7 +89,7 @@
             if (paramSpec == PBEParameterSpec.class)
             {
                 return new PBEParameterSpec(params.getSalt(),
-                                params.getIterationCount().intValue());
+                    params.getIterationCount().intValue());
             }
 
             throw new InvalidParameterSpecException("unknown parameter spec passed to PBKDF2 PBE parameters object.");
@@ -82,10 +104,10 @@
                 throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PBKDF2 PBE parameters algorithm parameters object");
             }
 
-            PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
+            PBEParameterSpec pbeSpec = (PBEParameterSpec)paramSpec;
 
             this.params = new PBKDF2Params(pbeSpec.getSalt(),
-                                pbeSpec.getIterationCount());
+                pbeSpec.getIterationCount());
         }
 
         protected void engineInit(
@@ -144,7 +166,8 @@
 
                 if (pbeSpec.getSalt() == null)
                 {
-                    throw new InvalidKeySpecException("missing required salt");
+                    return new PBKDF2Key(((PBEKeySpec)keySpec).getPassword(),
+                        scheme == PKCS5S2 ? PasswordConverter.ASCII : PasswordConverter.UTF8);
                 }
 
                 if (pbeSpec.getIterationCount() <= 0)
@@ -193,31 +216,12 @@
         private int getDigestCode(ASN1ObjectIdentifier algorithm)
             throws InvalidKeySpecException
         {
-            if (algorithm.equals(CryptoProObjectIdentifiers.gostR3411Hmac))
+            Integer code = (Integer)prfCodes.get(algorithm);
+            if (code != null)
             {
-                return GOST3411;
+                return code.intValue();
             }
-            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA1))
-            {
-                return SHA1;
-            }
-            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA256))
-            {
-                return SHA256;
-            }
-            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA224))
-            {
-                return SHA224;
-            }
-            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA384))
-            {
-                return SHA384;
-            }
-            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA512))
-            {
-                return SHA512;
-            }
-
+            
             throw new InvalidKeySpecException("Invalid KeySpec: unknown PRF algorithm " + algorithm);
         }
     }
@@ -257,6 +261,7 @@
             super("PBKDF2", PKCS5S2_UTF8, SHA384);
         }
     }
+
     public static class PBKDF2withSHA512
         extends BasePBKDF2
     {
@@ -266,6 +271,51 @@
         }
     }
 
+    public static class PBKDF2withGOST3411
+        extends BasePBKDF2
+    {
+        public PBKDF2withGOST3411()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, GOST3411);
+        }
+    }
+
+    public static class PBKDF2withSHA3_224
+        extends BasePBKDF2
+    {
+        public PBKDF2withSHA3_224()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SHA3_224);
+        }
+    }
+
+    public static class PBKDF2withSHA3_256
+        extends BasePBKDF2
+    {
+        public PBKDF2withSHA3_256()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SHA3_256);
+        }
+    }
+
+    public static class PBKDF2withSHA3_384
+        extends BasePBKDF2
+    {
+        public PBKDF2withSHA3_384()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SHA3_384);
+        }
+    }
+
+    public static class PBKDF2withSHA3_512
+        extends BasePBKDF2
+    {
+        public PBKDF2withSHA3_512()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SHA3_512);
+        }
+    }
+
     public static class PBKDF2with8BIT
         extends BasePBKDF2
     {
@@ -299,6 +349,11 @@
             provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA256", PREFIX + "$PBKDF2withSHA256");
             provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA384", PREFIX + "$PBKDF2withSHA384");
             provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA512", PREFIX + "$PBKDF2withSHA512");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA3-224", PREFIX + "$PBKDF2withSHA3_224");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA3-256", PREFIX + "$PBKDF2withSHA3_256");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA3-384", PREFIX + "$PBKDF2withSHA3_384");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACSHA3-512", PREFIX + "$PBKDF2withSHA3_512");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2WITHHMACGOST3411", PREFIX + "$PBKDF2withGOST3411");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC2.java
index 42058e5..36b2068 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC2.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC2.java
@@ -14,6 +14,7 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.RC2Engine;
 import org.bouncycastle.crypto.engines.RC2WrapEngine;
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
@@ -227,7 +228,7 @@
 
                 if (random == null)
                 {
-                    random = new SecureRandom();
+                    random = CryptoServicesRegistrar.getSecureRandom();
                 }
 
                 random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC5.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC5.java
index 2f1d83a..1547554 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC5.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC5.java
@@ -8,6 +8,7 @@
 import javax.crypto.spec.IvParameterSpec;
 
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.RC532Engine;
 import org.bouncycastle.crypto.engines.RC564Engine;
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
@@ -20,7 +21,6 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
 public final class RC5
 {
@@ -99,7 +99,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
index 674ea48..81271ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.RC6Engine;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
@@ -24,7 +25,6 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
 import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
 public final class RC6
 {
@@ -127,7 +127,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SCRYPT.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SCRYPT.java
new file mode 100644
index 0000000..82e4179
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SCRYPT.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.SecretKey;
+
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.PasswordConverter;
+import org.bouncycastle.crypto.generators.SCrypt;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+import org.bouncycastle.jcajce.spec.ScryptKeySpec;
+
+public class SCRYPT
+{
+    private SCRYPT()
+    {
+
+    }
+
+    public static class BasePBKDF2
+        extends BaseSecretKeyFactory
+    {
+        private int scheme;
+
+        public BasePBKDF2(String name, int scheme)
+        {
+            super(name, MiscObjectIdentifiers.id_scrypt);
+
+            this.scheme = scheme;
+        }
+
+        protected SecretKey engineGenerateSecret(
+            KeySpec keySpec)
+            throws InvalidKeySpecException
+        {
+            if (keySpec instanceof ScryptKeySpec)
+            {
+                ScryptKeySpec pbeSpec = (ScryptKeySpec)keySpec;
+
+                if (pbeSpec.getSalt() == null)
+                {
+                    throw new IllegalArgumentException("Salt S must be provided.");
+                }
+                if (pbeSpec.getCostParameter() <= 1)
+                {
+                    throw new IllegalArgumentException("Cost parameter N must be > 1.");
+                }
+
+                if (pbeSpec.getKeyLength() <= 0)
+                {
+                    throw new InvalidKeySpecException("positive key length required: "
+                        + pbeSpec.getKeyLength());
+                }
+
+                if (pbeSpec.getPassword().length == 0)
+                {
+                    throw new IllegalArgumentException("password empty");
+                }
+
+                CipherParameters param = new KeyParameter(SCrypt.generate(
+                        PasswordConverter.UTF8.convert(pbeSpec.getPassword()), pbeSpec.getSalt(),
+                        pbeSpec.getCostParameter(), pbeSpec.getBlockSize(), pbeSpec.getParallelizationParameter(),
+                        pbeSpec.getKeyLength() / 8));
+
+                return new BCPBEKey(this.algName, pbeSpec, param);
+            }
+
+            throw new InvalidKeySpecException("Invalid KeySpec");
+        }
+    }
+
+    public static class ScryptWithUTF8
+        extends BasePBKDF2
+    {
+        public ScryptWithUTF8()
+        {
+            super("SCRYPT", PKCS5S2_UTF8);
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = SCRYPT.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("SecretKeyFactory.SCRYPT", PREFIX + "$ScryptWithUTF8");
+            provider.addAlgorithm("SecretKeyFactory", MiscObjectIdentifiers.id_scrypt, PREFIX + "$ScryptWithUTF8");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
index f485772..62a4efa 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.SEEDEngine;
 import org.bouncycastle.crypto.engines.SEEDWrapEngine;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
@@ -22,6 +23,7 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
@@ -92,6 +94,15 @@
         }
     }
 
+    static public class KeyFactory
+         extends BaseSecretKeyFactory
+    {
+        public KeyFactory()
+        {
+            super("SEED", null);
+        }
+    }
+
     public static class Poly1305
         extends BaseMac
     {
@@ -127,7 +138,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
@@ -186,6 +197,9 @@
             provider.addAlgorithm("KeyGenerator", KISAObjectIdentifiers.id_seedCBC, PREFIX + "$KeyGen");
             provider.addAlgorithm("KeyGenerator", KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, PREFIX + "$KeyGen");
 
+            provider.addAlgorithm("SecretKeyFactory.SEED", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory", KISAObjectIdentifiers.id_seedCBC, "SEED");
+
             addCMacAlgorithm(provider, "SEED", PREFIX + "$CMAC", PREFIX + "$KeyGen");
             addGMacAlgorithm(provider, "SEED", PREFIX + "$GMAC", PREFIX + "$KeyGen");
             addPoly1305Algorithm(provider, "SEED", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java
index 05e037a..6e0202f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java
@@ -9,6 +9,7 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.SM4Engine;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.CMac;
@@ -105,7 +106,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Salsa20.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Salsa20.java
index 88b27a6..86774f9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Salsa20.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Salsa20.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class Salsa20
@@ -31,6 +32,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Salsa20 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -45,7 +55,7 @@
 
             provider.addAlgorithm("Cipher.SALSA20", PREFIX + "$Base");
             provider.addAlgorithm("KeyGenerator.SALSA20", PREFIX + "$KeyGen");
-
+            provider.addAlgorithm("AlgorithmParameters.SALSA20", PREFIX + "$AlgParams");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
index e8a0af2..52a7e1e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
@@ -6,7 +6,6 @@
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.SerpentEngine;
 import org.bouncycastle.crypto.engines.TnepresEngine;
-import org.bouncycastle.crypto.engines.TwofishEngine;
 import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -124,7 +123,7 @@
     {
         public Poly1305()
         {
-            super(new org.bouncycastle.crypto.macs.Poly1305(new TwofishEngine()));
+            super(new org.bouncycastle.crypto.macs.Poly1305(new SerpentEngine()));
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java
index 1f29bec..f10c68f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java
@@ -9,6 +9,7 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.engines.Shacal2Engine;
 import org.bouncycastle.crypto.macs.CMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -85,7 +86,7 @@
 
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             random.nextBytes(iv);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java
index 5be0640..027f2d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 
 public final class XSalsa20
@@ -31,6 +32,15 @@
         }
     }
 
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "XSalsa20 IV";
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -45,7 +55,7 @@
 
             provider.addAlgorithm("Cipher.XSALSA20", PREFIX + "$Base");
             provider.addAlgorithm("KeyGenerator.XSALSA20", PREFIX + "$KeyGen");
-
+            provider.addAlgorithm("AlgorithmParameters.XSALSA20", PREFIX + "$AlgParams");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
index a471972..85113e1 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.jcajce.provider.symmetric.util;
 
+import java.security.spec.KeySpec;
+
 import javax.crypto.interfaces.PBEKey;
 import javax.crypto.spec.PBEKeySpec;
 
@@ -45,6 +47,13 @@
         this.param = param;
     }
 
+    public BCPBEKey(String algName,
+                    KeySpec pbeSpec, CipherParameters param)
+    {
+        this.algorithm = algName;
+        this.param = param;
+    }
+
     public String getAlgorithm()
     {
         return algorithm;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
index 6bd1ba9..c3ba97a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -28,9 +28,11 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.engines.DSTU7624Engine;
 import org.bouncycastle.crypto.modes.AEADBlockCipher;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.CCMBlockCipher;
@@ -40,6 +42,9 @@
 import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+import org.bouncycastle.crypto.modes.KCCMBlockCipher;
+import org.bouncycastle.crypto.modes.KCTRBlockCipher;
+import org.bouncycastle.crypto.modes.KGCMBlockCipher;
 import org.bouncycastle.crypto.modes.OCBBlockCipher;
 import org.bouncycastle.crypto.modes.OFBBlockCipher;
 import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
@@ -72,7 +77,7 @@
     extends BaseWrapCipher
     implements PBE
 {
-    private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.spec.GCMParameterSpec");
 
     //
     // specs we can handle.
@@ -82,9 +87,9 @@
                                         RC2ParameterSpec.class,
                                         RC5ParameterSpec.class,
                                         gcmSpecClass,
+                                        GOST28147ParameterSpec.class,
                                         IvParameterSpec.class,
-                                        PBEParameterSpec.class,
-                                        GOST28147ParameterSpec.class
+                                        PBEParameterSpec.class
                                     };
 
     private BlockCipher             baseEngine;
@@ -106,20 +111,6 @@
 
     private String                  modeName = null;
 
-    private static Class lookup(String className)
-    {
-        try
-        {
-            Class def = BaseBlockCipher.class.getClassLoader().loadClass(className);
-
-            return def;
-        }
-        catch (Exception e)
-        {
-            return null;
-        }
-    }
-
     protected BaseBlockCipher(
         BlockCipher engine)
     {
@@ -177,8 +168,17 @@
         org.bouncycastle.crypto.BlockCipher engine,
         int ivLength)
     {
+        this(engine, true, ivLength);
+    }
+
+    protected BaseBlockCipher(
+        org.bouncycastle.crypto.BlockCipher engine,
+        boolean fixedIv,
+        int ivLength)
+    {
         baseEngine = engine;
 
+        this.fixedIv = fixedIv;
         this.cipher = new BufferedGenericBlockCipher(engine);
         this.ivLength = ivLength / 8;
     }
@@ -187,9 +187,18 @@
         BufferedBlockCipher engine,
         int ivLength)
     {
+        this(engine, true, ivLength);
+    }
+
+    protected BaseBlockCipher(
+        BufferedBlockCipher engine,
+        boolean fixedIv,
+        int ivLength)
+    {
         baseEngine = engine.getUnderlyingCipher();
 
         this.cipher = new BufferedGenericBlockCipher(engine);
+        this.fixedIv = fixedIv;
         this.ivLength = ivLength / 8;
     }
 
@@ -260,7 +269,7 @@
                 try
                 {
                     engineParams = createParametersInstance(name);
-                    engineParams.init(ivParam.getIV());
+                    engineParams.init(new IvParameterSpec(ivParam.getIV()));
                 }
                 catch (Exception e)
                 {
@@ -350,8 +359,16 @@
         {
             ivLength = baseEngine.getBlockSize();
             fixedIv = false;
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                        new SICBlockCipher(baseEngine)));
+            if (baseEngine instanceof DSTU7624Engine)
+            {
+                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                                    new KCTRBlockCipher(baseEngine)));
+            }
+            else
+            {
+                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                    new SICBlockCipher(baseEngine)));
+            }
         }
         else if (modeName.startsWith("GOFB"))
         {
@@ -372,8 +389,15 @@
         }
         else if (modeName.startsWith("CCM"))
         {
-            ivLength = 13; // CCM nonce 7..13 bytes
-            cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+            ivLength = 12; // CCM nonce 7..13 bytes
+            if (baseEngine instanceof DSTU7624Engine)
+            {
+                cipher = new AEADGenericBlockCipher(new KCCMBlockCipher(baseEngine));
+            }
+            else
+            {
+                cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+            }
         }
         else if (modeName.startsWith("OCB"))
         {
@@ -398,7 +422,14 @@
         else if (modeName.startsWith("GCM"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+            if (baseEngine instanceof DSTU7624Engine)
+            {
+                cipher = new AEADGenericBlockCipher(new KGCMBlockCipher(baseEngine));
+            }
+            else
+            {
+                cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+            }
         }
         else
         {
@@ -419,7 +450,7 @@
                 cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
             }
         }
-        else if (paddingName.equals("WITHCTS"))
+        else if (paddingName.equals("WITHCTS") || paddingName.equals("CTSPADDING") || paddingName.equals("CS3PADDING"))
         {
             cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher()));
         }
@@ -799,7 +830,7 @@
 
             if (ivRandom == null)
             {
-                ivRandom = new SecureRandom();
+                ivRandom = CryptoServicesRegistrar.getSecureRandom();
             }
 
             if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
@@ -1223,7 +1254,7 @@
         private static final Constructor aeadBadTagConstructor;
 
         static {
-            Class aeadBadTagClass = lookup("javax.crypto.AEADBadTagException");
+            Class aeadBadTagClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.AEADBadTagException");
             if (aeadBadTagClass != null)
             {
                 aeadBadTagConstructor = findExceptionConstructor(aeadBadTagClass);
@@ -1328,21 +1359,4 @@
             }
         }
     }
-
-    private static class InvalidKeyOrParametersException
-        extends InvalidKeyException
-    {
-        private final Throwable cause;
-
-        InvalidKeyOrParametersException(String msg, Throwable cause)
-        {
-             super(msg);
-            this.cause = cause;
-        }
-
-        public Throwable getCause()
-        {
-            return cause;
-        }
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java
index 12d2b85..07b330c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java
@@ -10,6 +10,7 @@
 import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 
 public class BaseKeyGenerator
@@ -58,7 +59,7 @@
         {
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
             engine.init(new KeyGenerationParameters(random, keySize));
             uninitialised = false;
@@ -73,7 +74,7 @@
     {
         if (uninitialised)
         {
-            engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize));
+            engine.init(new KeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), defaultKeySize));
             uninitialised = false;
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index 7c41af0..e1ce811 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -31,7 +31,7 @@
 public class BaseMac
     extends MacSpi implements PBE
 {
-    private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseMac.class, "javax.crypto.spec.GCMParameterSpec");
 
     private Mac macEngine;
 
@@ -275,18 +275,4 @@
 
         return newTable;
     }
-
-    private static Class lookup(String className)
-    {
-        try
-        {
-            Class def = BaseBlockCipher.class.getClassLoader().loadClass(className);
-
-            return def;
-        }
-        catch (Exception e)
-        {
-            return null;
-        }
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
index 598d1fc..5877e8e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
@@ -19,6 +19,7 @@
 import javax.crypto.spec.RC5ParameterSpec;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.StreamCipher;
 import org.bouncycastle.crypto.params.KeyParameter;
@@ -110,6 +111,38 @@
                     return null;
                 }
             }
+            else if (ivParam != null)
+            {
+                String  name = cipher.getAlgorithmName();
+
+                if (name.indexOf('/') >= 0)
+                {
+                    name = name.substring(0, name.indexOf('/'));
+                }
+                if (name.startsWith("ChaCha7539"))
+                {
+                    name = "ChaCha7539";
+                }
+                else if (name.startsWith("Grain"))
+                {
+                    name = "Grainv1";
+                }
+                else if (name.startsWith("HC"))
+                {
+                    int endIndex = name.indexOf('-');
+                    name = name.substring(0, endIndex) + name.substring(endIndex + 1);
+                }
+
+                try
+                {
+                    engineParams = createParametersInstance(name);
+                    engineParams.init(new IvParameterSpec(ivParam.getIV()));
+                }
+                catch (Exception e)
+                {
+                    throw new RuntimeException(e.toString());
+                }
+            }
         }
 
         return engineParams;
@@ -122,7 +155,7 @@
         String  mode)
         throws NoSuchAlgorithmException
     {
-        if (!mode.equalsIgnoreCase("ECB"))
+        if (!(mode.equalsIgnoreCase("ECB") || mode.equals("NONE")))
         {
             throw new NoSuchAlgorithmException("can't support mode " + mode);
         }
@@ -231,7 +264,7 @@
 
             if (ivRandom == null)
             {
-                ivRandom = new SecureRandom();
+                ivRandom = CryptoServicesRegistrar.getSecureRandom();
             }
 
             if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java
index cdf57ac..f0d6044 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java
@@ -1,8 +1,10 @@
 package org.bouncycastle.jcajce.provider.symmetric.util;
 
+import java.io.ByteArrayOutputStream;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -33,6 +35,9 @@
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.crypto.params.ParametersWithUKM;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -47,10 +52,11 @@
     //
     private Class[]                 availableSpecs =
                                     {
-                                        IvParameterSpec.class,
+                                        GOST28147WrapParameterSpec.class,
                                         PBEParameterSpec.class,
                                         RC2ParameterSpec.class,
-                                        RC5ParameterSpec.class
+                                        RC5ParameterSpec.class,
+                                        IvParameterSpec.class
                                     };
 
     protected int                     pbeType = PKCS12;
@@ -65,6 +71,9 @@
     private int                       ivSize;
     private byte[]                    iv;
 
+    private ErasableOutputStream wrapStream = null;
+    private boolean                   forWrapping;
+
     private final JcaJceHelper helper = new BCJcaJceHelper();
 
     protected BaseWrapCipher()
@@ -109,7 +118,30 @@
 
     protected AlgorithmParameters engineGetParameters()
     {
-        return null;
+        if (engineParams == null)
+        {
+            if (iv != null)
+            {
+                String  name = wrapEngine.getAlgorithmName();
+
+                if (name.indexOf('/') >= 0)
+                {
+                    name = name.substring(0, name.indexOf('/'));
+                }
+
+                try
+                {
+                    engineParams = createParametersInstance(name);
+                    engineParams.init(new IvParameterSpec(iv));
+                }
+                catch (Exception e)
+                {
+                    throw new RuntimeException(e.toString());
+                }
+            }
+        }
+
+        return engineParams;
     }
 
     protected final AlgorithmParameters createParametersInstance(String algorithm)
@@ -165,15 +197,31 @@
 
         if (params instanceof IvParameterSpec)
         {
-            IvParameterSpec iv = (IvParameterSpec) params;
-            param = new ParametersWithIV(param, iv.getIV());
+            IvParameterSpec ivSpec = (IvParameterSpec)params;
+            this.iv = ivSpec.getIV();
+            param = new ParametersWithIV(param, iv);
+        }
+
+        if (params instanceof GOST28147WrapParameterSpec)
+        {
+            GOST28147WrapParameterSpec spec = (GOST28147WrapParameterSpec) params;
+
+            byte[] sBox = spec.getSBox();
+            if (sBox != null)
+            {
+                param = new ParametersWithSBox(param, sBox);
+            }
+            param = new ParametersWithUKM(param, spec.getUKM());
         }
 
         if (param instanceof KeyParameter && ivSize != 0)
         {
-            iv = new byte[ivSize];
-            random.nextBytes(iv);
-            param = new ParametersWithIV(param, iv);
+            if (opmode == Cipher.WRAP_MODE || opmode == Cipher.ENCRYPT_MODE)
+            {
+                iv = new byte[ivSize];
+                random.nextBytes(iv);
+                param = new ParametersWithIV(param, iv);
+            }
         }
 
         if (random != null)
@@ -181,19 +229,37 @@
             param = new ParametersWithRandom(param, random);
         }
 
-        switch (opmode)
+        try
         {
-        case Cipher.WRAP_MODE:
-            wrapEngine.init(true, param);
-            break;
-        case Cipher.UNWRAP_MODE:
-            wrapEngine.init(false, param);
-            break;
-        case Cipher.ENCRYPT_MODE:
-        case Cipher.DECRYPT_MODE:
-            throw new IllegalArgumentException("engine only valid for wrapping");
-        default:
-            System.out.println("eeek!");
+            switch (opmode)
+            {
+            case Cipher.WRAP_MODE:
+                wrapEngine.init(true, param);
+                this.wrapStream = null;
+                this.forWrapping = true;
+                break;
+            case Cipher.UNWRAP_MODE:
+                wrapEngine.init(false, param);
+                this.wrapStream = null;
+                this.forWrapping = false;
+                break;
+            case Cipher.ENCRYPT_MODE:
+                wrapEngine.init(true, param);
+                this.wrapStream = new ErasableOutputStream();
+                this.forWrapping = true;
+                break;
+            case Cipher.DECRYPT_MODE:
+                wrapEngine.init(false, param);
+                this.wrapStream = new ErasableOutputStream();
+                this.forWrapping = false;
+                break;
+            default:
+                throw new InvalidParameterException("Unknown mode parameter passed to init.");
+            }
+        }
+        catch (Exception e)
+        {
+            throw new InvalidKeyOrParametersException(e.getMessage(), e);
         }
     }
 
@@ -243,7 +309,7 @@
         }
         catch (InvalidAlgorithmParameterException e)
         {
-            throw new IllegalArgumentException(e.getMessage());
+            throw new InvalidKeyOrParametersException(e.getMessage(), e);
         }
     }
 
@@ -252,7 +318,14 @@
         int     inputOffset,
         int     inputLen)
     {
-        throw new RuntimeException("not supported for wrapping");
+        if (wrapStream == null)
+        {
+            throw new IllegalStateException("not supported in a wrapping mode");
+        }
+
+        wrapStream.write(input, inputOffset, inputLen);
+
+        return null;
     }
 
     protected int engineUpdate(
@@ -263,7 +336,14 @@
         int     outputOffset)
         throws ShortBufferException
     {
-        throw new RuntimeException("not supported for wrapping");
+        if (wrapStream == null)
+        {
+            throw new IllegalStateException("not supported in a wrapping mode");
+        }
+
+        wrapStream.write(input, inputOffset, inputLen);
+
+        return 0;
     }
 
     protected byte[] engineDoFinal(
@@ -272,7 +352,42 @@
         int     inputLen)
         throws IllegalBlockSizeException, BadPaddingException
     {
-        return null;
+        if (wrapStream == null)
+        {
+            throw new IllegalStateException("not supported in a wrapping mode");
+        }
+
+        wrapStream.write(input, inputOffset, inputLen);
+
+        try
+        {
+            if (forWrapping)
+            {
+                try
+                {
+                    return wrapEngine.wrap(wrapStream.getBuf(), 0, wrapStream.size());
+                }
+                catch (Exception e)
+                {
+                    throw new IllegalBlockSizeException(e.getMessage());
+                }
+            }
+            else
+            {
+                try
+                {
+                    return wrapEngine.unwrap(wrapStream.getBuf(), 0, wrapStream.size());
+                }
+                catch (InvalidCipherTextException e)
+                {
+                    throw new BadPaddingException(e.getMessage());
+                }
+            }
+        }
+        finally
+        {
+            wrapStream.erase();
+        }
     }
 
     protected int engineDoFinal(
@@ -283,7 +398,53 @@
         int     outputOffset)
         throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
     {
-        return 0;
+        if (wrapStream == null)
+        {
+            throw new IllegalStateException("not supported in a wrapping mode");
+        }
+
+        wrapStream.write(input, inputOffset, inputLen);
+
+        try
+        {
+            byte[] enc;
+
+            if (forWrapping)
+            {
+                try
+                {
+                    enc = wrapEngine.wrap(wrapStream.getBuf(), 0, wrapStream.size());
+                }
+                catch (Exception e)
+                {
+                    throw new IllegalBlockSizeException(e.getMessage());
+                }
+            }
+            else
+            {
+                try
+                {
+                    enc = wrapEngine.unwrap(wrapStream.getBuf(), 0, wrapStream.size());
+                }
+                catch (InvalidCipherTextException e)
+                {
+                    throw new BadPaddingException(e.getMessage());
+                }
+            }
+
+            if (outputOffset + enc.length > output.length)
+            {
+                throw new ShortBufferException("output buffer too short for input.");
+            }
+
+            System.arraycopy(enc, 0, output, outputOffset, enc.length);
+
+            return enc.length;
+        }
+        finally
+        {
+            wrapStream.erase();
+        }
     }
 
     protected byte[] engineWrap(
@@ -402,4 +563,39 @@
         }
     }
 
+    protected static final class ErasableOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ErasableOutputStream()
+        {
+        }
+
+        public byte[] getBuf()
+        {
+            return buf;
+        }
+
+        public void erase()
+        {
+            Arrays.fill(this.buf, (byte)0);
+            reset();
+        }
+    }
+
+    protected static class InvalidKeyOrParametersException
+        extends InvalidKeyException
+    {
+        private final Throwable cause;
+
+        InvalidKeyOrParametersException(String msg, Throwable cause)
+        {
+             super(msg);
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
new file mode 100644
index 0000000..37ea08b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.jcajce.provider.symmetric.util;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class ClassUtil
+{
+    public static Class loadClass(Class sourceClass, final String className)
+    {
+        try
+        {
+            ClassLoader loader = sourceClass.getClassLoader();
+
+            if (loader != null)
+            {
+                return loader.loadClass(className);
+            }
+            else
+            {
+                return (Class)AccessController.doPrivileged(new PrivilegedAction()
+                {
+                    public Object run()
+                    {
+                        try
+                        {
+                            return Class.forName(className);
+                        }
+                        catch (Exception e)
+                        {
+                            // ignore - maybe log?
+                        }
+
+                        return null;
+                    }
+                });
+            }
+        }
+        catch (ClassNotFoundException e)
+        {
+            // ignore - maybe log?
+        }
+
+        return null;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
index f19df92..1df06fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
@@ -37,6 +37,10 @@
     static final int        SHA224       = 7;
     static final int        SHA384       = 8;
     static final int        SHA512       = 9;
+    static final int        SHA3_224     = 10;
+    static final int        SHA3_256     = 11;
+    static final int        SHA3_384     = 12;
+    static final int        SHA3_512     = 13;
 
     static final int        PKCS5S1      = 0;
     static final int        PKCS5S2      = 1;
@@ -107,6 +111,18 @@
                 case SHA512:
                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512());
                     break;
+                case SHA3_224:
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224());
+                    break;
+                case SHA3_256:
+                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256());
+                     break;
+                case SHA3_384:
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384());
+                    break;
+                case SHA3_512:
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512());
+                    break;
                 default:
                     throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption.");
                 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
index 448c352..fc4cf4f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
@@ -5,7 +5,18 @@
 
 public abstract class AsymmetricAlgorithmProvider
     extends AlgorithmProvider
-{       
+{
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Signature." + algorithm, className);
+        provider.addAlgorithm("Alg.Alias.Signature." + oid, algorithm);
+        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
+    }
+
     protected void addSignatureAlgorithm(
         ConfigurableProvider provider,
         String digest,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHDomainParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHDomainParameterSpec.java
new file mode 100644
index 0000000..13b8d45
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHDomainParameterSpec.java
@@ -0,0 +1,129 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.math.BigInteger;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHValidationParameters;
+
+/**
+ * Extension class for DHParameterSpec that wraps a DHDomainParameters object and provides the q domain parameter.
+ */
+public class DHDomainParameterSpec
+    extends DHParameterSpec
+{
+    private final BigInteger q;
+    private final BigInteger j;
+    private final int m;
+
+    private DHValidationParameters validationParameters;
+
+    /**
+     * Base constructor - use the values in an existing set of domain parameters.
+     *
+     * @param domainParameters the Diffie-Hellman domain parameters to wrap.
+     */
+    public DHDomainParameterSpec(DHParameters domainParameters)
+    {
+        this(domainParameters.getP(), domainParameters.getQ(), domainParameters.getG(), domainParameters.getJ(), domainParameters.getM(), domainParameters.getL());
+        this.validationParameters = domainParameters.getValidationParameters();
+    }
+
+    /**
+     * Minimal constructor for parameters able to be used to verify a public key, or use with MQV.
+     *
+     * @param p the prime p defining the Galois field.
+     * @param g the generator of the multiplicative subgroup of order g.
+     * @param q specifies the prime factor of p - 1
+     */
+    public DHDomainParameterSpec(BigInteger p, BigInteger q, BigInteger g)
+    {
+        this(p, q, g, null, 0);
+    }
+
+    /**
+     * Minimal constructor for parameters able to be used to verify a public key, or use with MQV, and a private value length.
+     *
+     * @param p the prime p defining the Galois field.
+     * @param g the generator of the multiplicative subgroup of order g.
+     * @param q specifies the prime factor of p - 1
+     * @param l the maximum bit length for the private value.
+     */
+    public DHDomainParameterSpec(BigInteger p, BigInteger q, BigInteger g, int l)
+    {
+        this(p, q, g, null, l);
+    }
+
+    /**
+     * X9.42 parameters with private value length.
+     *
+     * @param p the prime p defining the Galois field.
+     * @param g the generator of the multiplicative subgroup of order g.
+     * @param q specifies the prime factor of p - 1
+     * @param j optionally specifies the value that satisfies the equation p = jq+1
+     * @param l the maximum bit length for the private value.
+     */
+    public DHDomainParameterSpec(BigInteger p, BigInteger q, BigInteger g, BigInteger j, int l)
+    {
+        this(p, q, g, j, 0, l);
+    }
+
+    /**
+     * Base constructor - the full domain parameter set.
+     *
+     * @param p the prime p defining the Galois field.
+     * @param g the generator of the multiplicative subgroup of order g.
+     * @param q specifies the prime factor of p - 1
+     * @param j optionally specifies the value that satisfies the equation p = jq+1
+     * @param m the minimum bit length for the private value.
+     * @param l the maximum bit length for the private value.
+     */
+    public DHDomainParameterSpec(BigInteger p, BigInteger q, BigInteger g, BigInteger j, int m, int l)
+    {
+        super(p, g, l);
+        this.q = q;
+        this.j = j;
+        this.m = m;
+    }
+
+    /**
+     * Return the Q value for the domain parameter set.
+     *
+     * @return the value Q.
+     */
+    public BigInteger getQ()
+    {
+        return q;
+    }
+
+    /**
+     * Return the J value for the domain parameter set if available.
+     *
+     * @return the value J, null otherwise.
+     */
+    public BigInteger getJ()
+    {
+        return j;
+    }
+
+    /**
+     * Return the minimum bitlength for a private value to be generated from these parameters, 0 if not set.
+     *
+     * @return minimum bitlength for private value.
+     */
+    public int getM()
+    {
+        return m;
+    }
+
+    /**
+     * Return the DHDomainParameters object we represent.
+     *
+     * @return the internal DHDomainParameters.
+     */
+    public DHParameters getDomainParameters()
+    {
+        return new DHParameters(getP(), getG(), q, m, getL(), j, validationParameters);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHUParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHUParameterSpec.java
new file mode 100644
index 0000000..1bd86c3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DHUParameterSpec.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec to provide Diffie-Hellman Unified model keys and user keying material.
+ */
+public class DHUParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final PublicKey ephemeralPublicKey;
+    private final PrivateKey ephemeralPrivateKey;
+    private final PublicKey otherPartyEphemeralKey;
+    private final byte[] userKeyingMaterial;
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model.
+     *
+     * @param ephemeralPublicKey our ephemeral public key.
+     * @param ephemeralPrivateKey our ephemeral private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     * @param userKeyingMaterial key generation material to mix with the calculated secret.
+     */
+    public DHUParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+    {
+        if (ephemeralPrivateKey == null)
+        {
+            throw new IllegalArgumentException("ephemeral private key cannot be null");
+        }
+        if (otherPartyEphemeralKey == null)
+        {
+            throw new IllegalArgumentException("other party ephemeral key cannot be null");
+        }
+        this.ephemeralPublicKey = ephemeralPublicKey;
+        this.ephemeralPrivateKey = ephemeralPrivateKey;
+        this.otherPartyEphemeralKey = otherPartyEphemeralKey;
+        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+    }
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model without user keying material.
+     *
+     * @param ephemeralPublicKey our ephemeral public key.
+     * @param ephemeralPrivateKey our ephemeral private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     */
+    public DHUParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey)
+    {
+        this(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
+    }
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model using a key pair.
+     *
+     * @param ephemeralKeyPair our ephemeral public and private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     * @param userKeyingMaterial key generation material to mix with the calculated secret.
+     */
+    public DHUParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+    {
+        this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, userKeyingMaterial);
+    }
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model - calculation of our ephemeral public key
+     * is required.
+     *
+     * @param ephemeralPrivateKey our ephemeral private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     * @param userKeyingMaterial key generation material to mix with the calculated secret.
+     */
+    public DHUParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial)
+    {
+        this(null, ephemeralPrivateKey, otherPartyEphemeralKey, userKeyingMaterial);
+    }
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model using a key pair without user keying material.
+     *
+     * @param ephemeralKeyPair our ephemeral public and private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     */
+    public DHUParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey)
+    {
+        this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, null);
+    }
+
+    /**
+     * Base constructor for a Diffie-Hellman unified model - calculation of our ephemeral public key
+     * is required and no user keying material is provided.
+     *
+     * @param ephemeralPrivateKey our ephemeral private key.
+     * @param otherPartyEphemeralKey the ephemeral public key sent by the other party.
+     */
+    public DHUParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey)
+    {
+        this(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
+    }
+
+    /**
+     * Return our ephemeral private key.
+     *
+     * @return our ephemeral private key.
+     */
+    public PrivateKey getEphemeralPrivateKey()
+    {
+        return ephemeralPrivateKey;
+    }
+
+    /**
+     * Return our ephemeral public key, null if it was not provided.
+     *
+     * @return our ephemeral public key, can be null.
+     */
+    public PublicKey getEphemeralPublicKey()
+    {
+        return ephemeralPublicKey;
+    }
+
+    /**
+     * Return the ephemeral other party public key.
+     *
+     * @return the ephemeral other party public key.
+     */
+    public PublicKey getOtherPartyEphemeralKey()
+    {
+        return otherPartyEphemeralKey;
+    }
+
+    /**
+     * Return a copy of the user keying material, null if none is available.
+     *
+     * @return a copy of the user keying material, can be null.
+     */
+    public byte[] getUserKeyingMaterial()
+    {
+        return Arrays.clone(userKeyingMaterial);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DSTU4145ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DSTU4145ParameterSpec.java
new file mode 100644
index 0000000..161ba54
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/DSTU4145ParameterSpec.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.ECParameterSpec;
+
+import org.bouncycastle.asn1.ua.DSTU4145Params;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * ParameterSpec for a DSTU4145 key.
+ */
+public class DSTU4145ParameterSpec
+    extends ECParameterSpec
+{
+    private final byte[]             dke;
+    private final ECDomainParameters parameters;
+
+    public DSTU4145ParameterSpec(
+        ECDomainParameters parameters)
+    {
+        this(parameters, EC5Util.convertToSpec(parameters), DSTU4145Params.getDefaultDKE());
+    }
+
+    private DSTU4145ParameterSpec(ECDomainParameters parameters, ECParameterSpec ecParameterSpec, byte[] dke)
+    {
+        super(ecParameterSpec.getCurve(), ecParameterSpec.getGenerator(), ecParameterSpec.getOrder(), ecParameterSpec.getCofactor());
+
+        this.parameters = parameters;
+        this.dke = Arrays.clone(dke);
+    }
+
+    public byte[] getDKE()
+    {
+        return Arrays.clone(dke);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o instanceof DSTU4145ParameterSpec)
+        {
+            DSTU4145ParameterSpec other = (DSTU4145ParameterSpec)o;
+            
+            return this.parameters.equals(other.parameters);
+        }
+        
+        return false;
+    }
+    
+    public int hashCode()
+    {
+        return this.parameters.hashCode();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/EdDSAParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/EdDSAParameterSpec.java
new file mode 100644
index 0000000..1f0f203
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/EdDSAParameterSpec.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+
+/**
+ * ParameterSpec for EdDSA signature algorithms.
+ */
+public class EdDSAParameterSpec
+    implements AlgorithmParameterSpec
+{
+    public static final String Ed25519 = "Ed25519";
+    public static final String Ed448 = "Ed448";
+
+    private final String curveName;
+
+    /**
+     * Base constructor.
+     *
+     * @param curveName name of the curve to specify.
+     */
+    public EdDSAParameterSpec(String curveName)
+    {
+        if (curveName.equalsIgnoreCase(Ed25519))
+        {
+            this.curveName = Ed25519;
+        }
+        else if (curveName.equalsIgnoreCase(Ed448))
+        {
+            this.curveName = Ed448;
+        }
+        else if (curveName.equals(EdECObjectIdentifiers.id_Ed25519.getId()))
+        {
+            this.curveName = Ed25519;
+        }
+        else if (curveName.equals(EdECObjectIdentifiers.id_Ed448.getId()))
+        {
+            this.curveName = Ed448;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unrecognized curve name: " + curveName);
+        }
+
+    }
+
+    /**
+     * Return the curve name specified by this parameterSpec.
+     *
+     * @return the name of the curve this parameterSpec specifies.
+     */
+    public String getCurveName()
+    {
+        return curveName;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java
index 9496fcd..d06dc5a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java
@@ -6,6 +6,7 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.crypto.engines.GOST28147Engine;
 import org.bouncycastle.util.Arrays;
 
@@ -60,11 +61,19 @@
         this.iv = Arrays.clone(iv);
     }
 
+    /**
+     * @deprecated use getSBox()
+     */
     public byte[] getSbox()
     {
         return Arrays.clone(sBox);
     }
 
+    public byte[] getSBox()
+    {
+        return Arrays.clone(sBox);
+    }
+
     /**
      * Returns the IV or null if this parameter set does not contain an IV.
      *
@@ -83,6 +92,7 @@
         oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet, "E-B");
         oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet, "E-C");
         oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet, "E-D");
+        oidMappings.put(RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z, "Param-Z");
     }
 
     private static String getName(ASN1ObjectIdentifier sBoxOid)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147WrapParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147WrapParameterSpec.java
new file mode 100644
index 0000000..3ee3364
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147WrapParameterSpec.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.crypto.engines.GOST28147Engine;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A parameter spec for the GOST-28147 cipher.
+ */
+public class GOST28147WrapParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private byte[] ukm = null;
+    private byte[] sBox = null;
+
+    public GOST28147WrapParameterSpec(
+        byte[] sBox)
+    {
+        this.sBox = new byte[sBox.length];
+
+        System.arraycopy(sBox, 0, this.sBox, 0, sBox.length);
+    }
+
+    public GOST28147WrapParameterSpec(
+        byte[] sBox,
+        byte[] ukm)
+    {
+        this(sBox);
+        this.ukm = new byte[ukm.length];
+
+        System.arraycopy(ukm, 0, this.ukm, 0, ukm.length);
+    }
+
+    public GOST28147WrapParameterSpec(
+        String sBoxName)
+    {
+        this.sBox = GOST28147Engine.getSBox(sBoxName);
+    }
+
+    public GOST28147WrapParameterSpec(
+        String sBoxName,
+        byte[] ukm)
+    {
+        this(sBoxName);
+        this.ukm = new byte[ukm.length];
+
+        System.arraycopy(ukm, 0, this.ukm, 0, ukm.length);
+    }
+
+    public GOST28147WrapParameterSpec(
+        ASN1ObjectIdentifier sBoxName,
+        byte[] ukm)
+    {
+        this(getName(sBoxName));
+        this.ukm = Arrays.clone(ukm);
+    }
+
+    public byte[] getSBox()
+    {
+        return Arrays.clone(sBox);
+    }
+
+    /**
+     * Returns the UKM.
+     *
+     * @return the UKM.
+     */
+    public byte[] getUKM()
+    {
+        return Arrays.clone(ukm);
+    }
+
+    private static Map oidMappings = new HashMap();
+
+    static
+    {
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, "E-A");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet, "E-B");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet, "E-C");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet, "E-D");
+        oidMappings.put(RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z, "Param-Z");
+    }
+
+    private static String getName(ASN1ObjectIdentifier sBoxOid)
+    {
+        String sBoxName = (String)oidMappings.get(sBoxOid);
+
+        if (sBoxName == null)
+        {
+            throw new IllegalArgumentException("unknown OID: " + sBoxOid);
+        }
+
+        return sBoxName;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST3410ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST3410ParameterSpec.java
new file mode 100644
index 0000000..474c185
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST3410ParameterSpec.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+
+/**
+ * ParameterSpec for a GOST 3410-1994/2001/2012 algorithm parameters.
+ */
+public class GOST3410ParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final ASN1ObjectIdentifier publicKeyParamSet;
+    private final ASN1ObjectIdentifier digestParamSet;
+    private final ASN1ObjectIdentifier encryptionParamSet;
+
+    /**
+     * Constructor for signing parameters.
+     *
+     * @param publicKeyParamSet the curve parameter set name.
+     */
+    public GOST3410ParameterSpec(String publicKeyParamSet)
+    {
+        this(getOid(publicKeyParamSet), getDigestOid(publicKeyParamSet), null);
+    }
+
+    /**
+     * Constructor for signing parameters.
+     *
+     * @param publicKeyParamSet the public key parameter set object identifier.
+     * @param digestParamSet the object identifier for the digest algorithm to be associated with parameters.
+     */
+    public GOST3410ParameterSpec(ASN1ObjectIdentifier publicKeyParamSet, ASN1ObjectIdentifier digestParamSet)
+    {
+        this(publicKeyParamSet, digestParamSet, null);
+    }
+
+    /**
+     * Constructor for signing/encryption parameters.
+     *
+     * @param publicKeyParamSet the public key parameter set object identifier.
+     * @param digestParamSet the object identifier for the digest algorithm to be associated with parameters.
+     * @param encryptionParamSet the object identifier associated with encryption algorithm to use.
+     */
+    public GOST3410ParameterSpec(ASN1ObjectIdentifier publicKeyParamSet, ASN1ObjectIdentifier digestParamSet, ASN1ObjectIdentifier encryptionParamSet)
+    {
+        this.publicKeyParamSet = publicKeyParamSet;
+        this.digestParamSet = digestParamSet;
+        this.encryptionParamSet = encryptionParamSet;
+    }
+
+    public String getPublicKeyParamSetName()
+    {
+        return ECGOST3410NamedCurves.getName(this.getPublicKeyParamSet());
+    }
+
+    /**
+     * Return the object identifier for the public key parameter set.
+     *
+     * @return the OID for the public key parameter set.
+     */
+    public ASN1ObjectIdentifier getPublicKeyParamSet()
+    {
+        return publicKeyParamSet;
+    }
+
+    /**
+     * Return the object identifier for the digest parameter set.
+     *
+     * @return the OID for the digest parameter set.
+     */
+    public ASN1ObjectIdentifier getDigestParamSet()
+    {
+        return digestParamSet;
+    }
+
+    /**
+     * Return the object identifier for the encryption parameter set.
+     *
+     * @return the OID for the encryption parameter set.
+     */
+    public ASN1ObjectIdentifier getEncryptionParamSet()
+    {
+        return encryptionParamSet;
+    }
+
+    private static ASN1ObjectIdentifier getOid(String paramName)
+    {
+        return ECGOST3410NamedCurves.getOID(paramName);
+    }
+
+    private static ASN1ObjectIdentifier getDigestOid(String paramName)
+    {
+        if (paramName.indexOf("12-512") > 0)
+        {
+            return RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512;
+        }
+        if (paramName.indexOf("12-256") > 0)
+        {
+            return RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256;
+        }
+
+        return CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java
index 907abfb..76ae8d6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java
@@ -7,6 +7,9 @@
 
 import org.bouncycastle.util.Arrays;
 
+/**
+ * Parameter spec to provide MQV ephemeral keys and user keying material.
+ */
 public class MQVParameterSpec
     implements AlgorithmParameterSpec
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SM2ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SM2ParameterSpec.java
new file mode 100644
index 0000000..aefde8f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SM2ParameterSpec.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec for SM2 ID parameter
+ */
+public class SM2ParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private byte[] id;
+
+    /**
+     * Base constructor.
+     *
+     * @param id the ID string associated with this usage of SM2.
+     */
+    public SM2ParameterSpec(
+        byte[] id)
+    {
+        if (id == null)
+        {
+            throw new NullPointerException("id string cannot be null");
+        }
+
+        this.id = Arrays.clone(id);
+    }
+
+    /**
+     * Return the ID value.
+     *
+     * @return the ID string.
+     */
+    public byte[] getID()
+    {
+        return Arrays.clone(id);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/ScryptKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/ScryptKeySpec.java
new file mode 100644
index 0000000..92468dc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/ScryptKeySpec.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.KeySpec;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Key spec for use with the scrypt SecretKeyFactory.
+ */
+public class ScryptKeySpec
+    implements KeySpec
+{
+    private final char[] password;
+    private final byte[] salt;
+    private final int costParameter;
+    private final int blockSize;
+    private final int parallelizationParameter;
+    private final int keySize;
+
+    public ScryptKeySpec(char[] password, byte[] salt, int costParameter, int blockSize, int parallelizationParameter, int keySize)
+    {
+
+        this.password = password;
+        this.salt = Arrays.clone(salt);
+        this.costParameter = costParameter;
+        this.blockSize = blockSize;
+        this.parallelizationParameter = parallelizationParameter;
+        this.keySize = keySize;
+    }
+
+    public char[] getPassword()
+    {
+        return password;
+    }
+
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
+
+    public int getCostParameter()
+    {
+        return costParameter;
+    }
+
+    public int getBlockSize()
+    {
+        return blockSize;
+    }
+
+    public int getParallelizationParameter()
+    {
+        return parallelizationParameter;
+    }
+
+    /**
+     * Key length (in bits).
+     *
+     * @return length of the key to generate in bits.
+     */
+    public int getKeyLength()
+    {
+        return keySize;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java
index fb28089..c208548 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java
@@ -31,7 +31,6 @@
  * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(java.util.Date, String, String) recommended format} or
  * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
  * </ul>
- * </p>
  *
  * @see org.bouncycastle.crypto.digests.SkeinEngine
  * @see org.bouncycastle.crypto.digests.SkeinDigest
@@ -172,8 +171,8 @@
          * Parameter types must be in the range 0,5..62, and cannot use the value {@value
          * org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} (reserved for message body).
          * <p>
-         * Parameters with type < {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} are processed before
-         * the message content, parameters with type > {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE}
+         * Parameters with type &lt; {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} are processed before
+         * the message content, parameters with type &gt; {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE}
          * are processed after the message and prior to output.
          * </p>
          *
@@ -255,7 +254,7 @@
          * Implements the recommended personalisation format for Skein defined in Section 4.11 of
          * the Skein 1.3 specification. You may need to use this method if the default locale
          * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible implementations.
-         * <p/>
+         * <p>
          * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
          * sequence using UTF-8 encoding.
          *
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/XDHParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/XDHParameterSpec.java
new file mode 100644
index 0000000..600e2d6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/XDHParameterSpec.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+
+/**
+ * ParameterSpec for XDH key agreement algorithms.
+ */
+public class XDHParameterSpec
+    implements AlgorithmParameterSpec
+{
+    public static final String X25519 = "X25519";
+    public static final String X448 = "X448";
+
+    private final String curveName;
+
+    /**
+     * Base constructor.
+     *
+     * @param curveName name of the curve to specify.
+     */
+    public XDHParameterSpec(String curveName)
+    {
+        if (curveName.equalsIgnoreCase(X25519))
+        {
+            this.curveName = X25519;
+        }
+        else if (curveName.equalsIgnoreCase(X448))
+        {
+            this.curveName = X448;
+        }
+        else if (curveName.equals(EdECObjectIdentifiers.id_X25519.getId()))
+        {
+            this.curveName = X25519;
+        }
+        else if (curveName.equals(EdECObjectIdentifiers.id_X448.getId()))
+        {
+            this.curveName = X448;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unrecognized curve name: " + curveName);
+        }
+    }
+
+    /**
+     * Return the curve name specified by this parameterSpec.
+     *
+     * @return the name of the curve this parameterSpec specifies.
+     */
+    public String getCurveName()
+    {
+        return curveName;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/BCJcaJceHelper.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/BCJcaJceHelper.java
index 25897cf..892aa00 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/util/BCJcaJceHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/BCJcaJceHelper.java
@@ -13,7 +13,7 @@
 {
     private static volatile Provider bcProvider;
 
-    private static Provider getBouncyCastleProvider()
+    private static synchronized Provider getBouncyCastleProvider()
     {
         if (Security.getProvider("BC") != null)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
index bad54af..8951234 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers;
 import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -38,6 +39,7 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digestOidMap.put(GMObjectIdentifiers.sm3, "SM3");
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECKeyUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/ECKeyUtil.java
index 818926a..15f13cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/ECKeyUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/ECKeyUtil.java
@@ -70,13 +70,13 @@
         {
             SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(key.getEncoded()));
 
-            if (info.getAlgorithmId().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
+            if (info.getAlgorithm().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
             {
                 throw new IllegalArgumentException("cannot convert GOST key to explicit parameters.");
             }
             else
             {
-                X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters());
+                X962Parameters params = X962Parameters.getInstance(info.getAlgorithm().getParameters());
                 X9ECParameters curveParams;
 
                 if (params.isNamedCurve())
@@ -160,13 +160,13 @@
         {
             PrivateKeyInfo info = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(key.getEncoded()));
 
-            if (info.getAlgorithmId().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
+            if (info.getPrivateKeyAlgorithm().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
             {
                 throw new UnsupportedEncodingException("cannot convert GOST key to explicit parameters.");
             }
             else
             {
-                X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters());
+                X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
                 X9ECParameters curveParams;
 
                 if (params.isNamedCurve())
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
index 5ff966a..40e5beb 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
@@ -5,6 +5,7 @@
 import java.security.spec.ECPoint;
 import java.security.spec.EllipticCurve;
 
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.math.ec.ECCurve;
 
 /**
@@ -48,9 +49,7 @@
                         ((ECFieldF2m)curve.getField()).getM(), k[0], curve.getA(), curve.getB());
             }
         }
-        
-        org.bouncycastle.math.ec.ECPoint p = c.decodePoint(encoded);
 
-        return new ECPoint(p.getAffineXCoord().toBigInteger(), p.getAffineYCoord().toBigInteger());
+        return EC5Util.convertPoint(c.decodePoint(encoded));
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java b/bcprov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
index d0456ac..3dbd6ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
@@ -64,8 +64,8 @@
  *  Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
  *
  *  Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
- *    type    ATTRIBUTE.&id({IOSet}),
- *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ *    type    ATTRIBUTE.&amp;id({IOSet}),
+ *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type})
  *  }
  * </pre>
  * @deprecated use classes in org.bouncycastle.pkcs.
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPrivateKey.java
index a8caffd..74950b5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPrivateKey.java
@@ -6,6 +6,7 @@
 /**
  * Static/ephemeral private key (pair) for use with ECMQV key agreement
  * (Optionally provides the ephemeral public key)
+ * @deprecated use MQVParameterSpec for passing the ephemeral key.
  */
 public interface MQVPrivateKey
     extends PrivateKey
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPublicKey.java
index 1be14bd..539b73e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/MQVPublicKey.java
@@ -4,6 +4,7 @@
 
 /**
  * Static/ephemeral public key pair for use with ECMQV key agreement
+ * @deprecated use MQVParameterSpec for passing the ephemeral key.
  */
 public interface MQVPublicKey
     extends PublicKey
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java b/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
index f8a1a6f..159224f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
@@ -31,7 +31,7 @@
  *
  * 
  * Handles NetScape certificate request (KEYGEN), these are constructed as:
- * <pre><code>
+ * <pre>
  *   SignedPublicKeyAndChallenge ::= SEQUENCE {
  *     publicKeyAndChallenge    PublicKeyAndChallenge,
  *     signatureAlgorithm       AlgorithmIdentifier,
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 7a8d7c6..1d774b1 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -15,8 +15,18 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
 import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.jcajce.provider.mceliece.McElieceCCA2KeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.mceliece.McElieceKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.newhope.NHKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.qtesla.QTESLAKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.rainbow.RainbowKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.sphincs.Sphincs256KeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSMTKeyFactorySpi;
 
 /**
  * To add the provider at runtime use:
@@ -45,7 +55,7 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.57";
+    private static String info = "BouncyCastle Security Provider v1.61";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -60,7 +70,7 @@
 
     private static final String[] SYMMETRIC_GENERIC =
     {
-        "PBEPBKDF2", "PBEPKCS12", "TLSKDF"
+        "PBEPBKDF1", "PBEPBKDF2", "PBEPKCS12", "TLSKDF", "SCRYPT"
     };
 
     private static final String[] SYMMETRIC_MACS =
@@ -73,7 +83,7 @@
         "AES", "ARC4", "ARIA", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
         "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5",
         "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "SM4", "TEA", "Twofish", "Threefish",
-        "VMPC", "VMPCKSA3", "XTEA", "XSalsa20", "OpenSSLPBKDF"
+        "VMPC", "VMPCKSA3", "XTEA", "XSalsa20", "OpenSSLPBKDF", "DSTU7624", "GOST3412_2015"
     };
 
      /*
@@ -90,7 +100,7 @@
 
     private static final String[] ASYMMETRIC_CIPHERS =
     {
-        "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM"
+        "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC"
     };
 
     /*
@@ -100,7 +110,7 @@
     private static final String[] DIGESTS =
     {
         "GOST3411", "Keccak", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224",
-        "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool", "Blake2b"
+        "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool", "Blake2b", "Blake2s", "DSTU7564"
     };
 
     /*
@@ -128,7 +138,7 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.57, info);
+        super(PROVIDER_NAME, 1.61, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -158,6 +168,7 @@
 
         loadAlgorithms(SECURE_RANDOM_PACKAGE, SECURE_RANDOMS);
 
+        loadPQCKeys();  // so we can handle certificates containing them.
         //
         // X509Store
         //
@@ -206,24 +217,7 @@
     {
         for (int i = 0; i != names.length; i++)
         {
-            Class clazz = null;
-            try
-            {
-                ClassLoader loader = this.getClass().getClassLoader();
-
-                if (loader != null)
-                {
-                    clazz = loader.loadClass(packageName + names[i] + "$Mappings");
-                }
-                else
-                {
-                    clazz = Class.forName(packageName + names[i] + "$Mappings");
-                }
-            }
-            catch (ClassNotFoundException e)
-            {
-                // ignore
-            }
+            Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + names[i] + "$Mappings");
 
             if (clazz != null)
             {
@@ -240,6 +234,22 @@
         }
     }
 
+    private void loadPQCKeys()
+    {
+        addKeyInfoConverter(PQCObjectIdentifiers.sphincs256, new Sphincs256KeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.newHope, new NHKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.xmss, new XMSSKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.xmss_mt, new XMSSMTKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.mcEliece, new McElieceKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.mcElieceCca2, new McElieceCCA2KeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.rainbow, new RainbowKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_I, new QTESLAKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_III_size, new QTESLAKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_III_speed, new QTESLAKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_I, new QTESLAKeyFactorySpi());
+        addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_III, new QTESLAKeyFactorySpi());
+    }
+
     public void setParameter(String parameterName, Object parameter)
     {
         synchronized (CONFIGURATION)
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java
index f89b9fd..87b6927 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.jce.provider;
 
 import java.security.Permission;
+import java.security.spec.DSAParameterSpec;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -9,10 +10,14 @@
 
 import javax.crypto.spec.DHParameterSpec;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import org.bouncycastle.jcajce.provider.config.ProviderConfigurationPermission;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
 import org.bouncycastle.jce.spec.ECParameterSpec;
 
 class BouncyCastleProviderConfiguration
@@ -191,6 +196,23 @@
             }
         }
 
+        DHParameters dhParams = CryptoServicesRegistrar.getSizedProperty(CryptoServicesRegistrar.Property.DH_DEFAULT_PARAMS, keySize);
+        if (dhParams != null)
+        {
+            return new DHDomainParameterSpec(dhParams);
+        }
+
+        return null;
+    }
+
+    public DSAParameterSpec getDSADefaultParameters(int keySize)
+    {
+        DSAParameters dsaParams = CryptoServicesRegistrar.getSizedProperty(CryptoServicesRegistrar.Property.DSA_DEFAULT_PARAMS, keySize);
+        if (dsaParams != null)
+        {
+            return new DSAParameterSpec(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
+        }
+
         return null;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java
index cb88e20..a0f5692 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java
@@ -26,6 +26,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.engines.DESEngine;
@@ -292,7 +293,7 @@
         {
             if (random == null)
             {
-                random = new SecureRandom();
+                random = CryptoServicesRegistrar.getSecureRandom();
             }
 
             if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java
index a5ef1a1..0f328f6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.KDFParameters;
 
 /**
@@ -74,7 +75,7 @@
     {
         if ((out.length - len) < outOff)
         {
-            throw new DataLengthException("output buffer too small");
+            throw new OutputLengthException("output buffer too small");
         }
 
         long    oBits = len * 8L;
@@ -87,7 +88,7 @@
         //
         if (oBits > (digest.getDigestSize() * 8L * (1L<<32 - 1)))
         {
-            new IllegalArgumentException("Output length to large");
+            throw new IllegalArgumentException("Output length too large");
         }
     
         int cThreshold = (int)(oBits / digest.getDigestSize());
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
index 7b89cd7..c1975e7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -237,6 +237,22 @@
         return trust;
     }
 
+    static boolean isIssuerTrustAnchor(
+        X509Certificate cert,
+        Set trustAnchors,
+        String sigProvider)
+        throws AnnotatedException
+    {
+        try
+        {
+            return findTrustAnchor(cert, trustAnchors, sigProvider) != null;
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+
     static List<PKIXCertStore> getAdditionalStoresFromAltNames(
         byte[] issuerAlternativeName,
         Map<GeneralName, PKIXCertStore> altNameCertStoreMap)
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
index a30b2df..d4fa285 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -59,9 +59,9 @@
         PrivateKeyInfo  info)
         throws IOException
     {
-        ASN1Sequence    seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
+        ASN1Sequence    seq = ASN1Sequence.getInstance(info.getPrivateKeyAlgorithm().getParameters());
         ASN1Integer      derX = ASN1Integer.getInstance(info.parsePrivateKey());
-        ASN1ObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+        ASN1ObjectIdentifier id = info.getPrivateKeyAlgorithm().getAlgorithm();
 
         this.info = info;
         this.x = derX.getValue();
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
index eb6a95d..8cd9836 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -113,22 +113,19 @@
         JCEECPublicKey          pubKey,
         ECParameterSpec         spec)
     {
-        ECDomainParameters      dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getAffineXCoord().toBigInteger(),
-                                    dp.getG().getAffineYCoord().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
@@ -144,34 +141,29 @@
         JCEECPublicKey          pubKey,
         org.bouncycastle.jce.spec.ECParameterSpec         spec)
     {
-        ECDomainParameters      dp = params.getParameters();
-
         this.algorithm = algorithm;
         this.d = params.getD();
 
         if (spec == null)
         {
+            ECDomainParameters dp = params.getParameters();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getAffineXCoord().toBigInteger(),
-                                    dp.getG().getAffineYCoord().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                EC5Util.convertPoint(dp.getG()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
             EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
             
             this.ecSpec = new ECParameterSpec(
-                                ellipticCurve,
-                                new ECPoint(
-                                    spec.getG().getAffineXCoord().toBigInteger(),
-                                    spec.getG().getAffineYCoord().toBigInteger()),
-                                spec.getN(),
-                                spec.getH().intValue());
+                ellipticCurve,
+                EC5Util.convertPoint(spec.getG()),
+                spec.getN(),
+                spec.getH().intValue());
         }
 
         publicKey = getPublicKeyDetails(pubKey);
@@ -209,26 +201,22 @@
                 EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
 
                 ecSpec = new ECNamedCurveSpec(
-                        ECGOST3410NamedCurves.getName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                gParam.getG().getAffineXCoord().toBigInteger(),
-                                gParam.getG().getAffineYCoord().toBigInteger()),
-                        gParam.getN(),
-                        gParam.getH());
+                    ECGOST3410NamedCurves.getName(oid),
+                    ellipticCurve,
+                    EC5Util.convertPoint(gParam.getG()),
+                    gParam.getN(),
+                    gParam.getH());
             }
             else
             {
                 EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
                 ecSpec = new ECNamedCurveSpec(
-                        ECUtil.getCurveName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getAffineXCoord().toBigInteger(),
-                                ecP.getG().getAffineYCoord().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH());
+                    ECUtil.getCurveName(oid),
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH());
             }
         }
         else if (params.isImplicitlyCA())
@@ -242,9 +230,7 @@
 
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
-                new ECPoint(
-                        ecP.getG().getAffineXCoord().toBigInteger(),
-                        ecP.getG().getAffineYCoord().toBigInteger()),
+                EC5Util.convertPoint(ecP.getG()),
                 ecP.getN(),
                 ecP.getH().intValue());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
index 6c67431..da4ff8d 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -158,14 +158,12 @@
     private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
     {
         return new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                        dp.getG().getAffineXCoord().toBigInteger(),
-                        dp.getG().getAffineYCoord().toBigInteger()),
-                        dp.getN(),
-                        dp.getH().intValue());
+            ellipticCurve,
+            EC5Util.convertPoint(dp.getG()),
+            dp.getN(),
+            dp.getH().intValue());
     }
-    
+
     public JCEECPublicKey(
         ECPublicKey     key)
     {
@@ -197,18 +195,14 @@
                 throw new IllegalArgumentException("error recovering public key");
             }
 
-            byte[]          keyEnc = key.getOctets();
-            byte[]          x = new byte[32];
-            byte[]          y = new byte[32];
+            byte[] keyEnc = key.getOctets();
 
-            for (int i = 0; i != x.length; i++)
+            byte[] x9Encoding = new byte[65];
+            x9Encoding[0] = 0x04;
+            for (int i = 1; i <= 32; ++i)
             {
-                x[i] = keyEnc[32 - 1 - i];
-            }
-
-            for (int i = 0; i != y.length; i++)
-            {
-                y[i] = keyEnc[64 - 1 - i];
+                x9Encoding[i     ] = keyEnc[32 - i];
+                x9Encoding[i + 32] = keyEnc[64 - i];
             }
 
             gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters());
@@ -218,16 +212,14 @@
             ECCurve curve = spec.getCurve();
             EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
 
-            this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
+            this.q = curve.decodePoint(x9Encoding);
 
             ecSpec = new ECNamedCurveSpec(
-                    ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
-                    ellipticCurve,
-                    new ECPoint(
-                            spec.getG().getAffineXCoord().toBigInteger(),
-                            spec.getG().getAffineYCoord().toBigInteger()),
-                            spec.getN(), spec.getH());
-
+                ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+                ellipticCurve,
+                EC5Util.convertPoint(spec.getG()),
+                spec.getN(),
+                spec.getH());
         }
         else
         {
@@ -244,13 +236,11 @@
                 ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
 
                 ecSpec = new ECNamedCurveSpec(
-                        ECUtil.getCurveName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getAffineXCoord().toBigInteger(),
-                                ecP.getG().getAffineYCoord().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH());
+                    ECUtil.getCurveName(oid),
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH());
             }
             else if (params.isImplicitlyCA())
             {
@@ -259,18 +249,16 @@
             }
             else
             {
-                X9ECParameters          ecP = X9ECParameters.getInstance(params.getParameters());
+                X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
 
                 curve = ecP.getCurve();
                 ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
 
                 this.ecSpec = new ECParameterSpec(
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getAffineXCoord().toBigInteger(),
-                                ecP.getG().getAffineYCoord().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH().intValue());
+                    ellipticCurve,
+                    EC5Util.convertPoint(ecP.getG()),
+                    ecP.getN(),
+                    ecP.getH().intValue());
             }
 
             DERBitString    bits = info.getPublicKeyData();
@@ -435,7 +423,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
+        return EC5Util.convertPoint(q);
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
index cacedd4..7529b41 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
@@ -28,7 +28,7 @@
     protected BigInteger modulus;
     protected BigInteger privateExponent;
 
-    private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+    private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
     protected JCERSAPrivateKey()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java
index 115c198..bcc258c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java
@@ -236,8 +236,8 @@
         try
         {
             // check whether the issuer of <tbvCert> is a TrustAnchor
-            if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(),
-                pkixParams.getBaseParameters().getSigProvider()) != null)
+            if (CertPathValidatorUtilities.isIssuerTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(),
+                pkixParams.getBaseParameters().getSigProvider()))
             {
                 CertPath certPath;
                 PKIXCertPathValidatorResult result;
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
index dfe9cef..b908621 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
@@ -54,7 +54,6 @@
             {
                 ExtendedPKIXBuilderParameters extPKIX = (ExtendedPKIXBuilderParameters)params;
 
- ;
                 for (Iterator it = extPKIX.getAdditionalStores().iterator(); it.hasNext();)
                 {
                      paramsPKIXBldr.addCertificateStore((PKIXCertStore)it.next());
@@ -187,8 +186,8 @@
         try
         {
             // check whether the issuer of <tbvCert> is a TrustAnchor
-            if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(),
-                pkixParams.getBaseParameters().getSigProvider()) != null)
+            if (CertPathValidatorUtilities.isIssuerTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(),
+                pkixParams.getBaseParameters().getSigProvider()))
             {
                 // exception message from possibly later tried certification
                 // chains
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
index 991aa4b..9da3425 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jce.provider;
 
+import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
@@ -7,6 +8,7 @@
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertPathValidatorResult;
 import java.security.cert.CertPathValidatorSpi;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.PKIXCertPathChecker;
 import java.security.cert.PKIXCertPathValidatorResult;
 import java.security.cert.PKIXParameters;
@@ -23,6 +25,7 @@
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.TBSCertificate;
 import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
 import org.bouncycastle.jcajce.PKIXExtendedParameters;
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
@@ -116,15 +119,17 @@
         {
             trust = CertPathValidatorUtilities.findTrustAnchor((X509Certificate) certs.get(certs.size() - 1),
                     paramsPKIX.getTrustAnchors(), paramsPKIX.getSigProvider());
+
+            if (trust == null)
+            {
+                throw new CertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
+            }
+
+            checkCertificate(trust.getTrustedCert());
         }
         catch (AnnotatedException e)
         {
-            throw new CertPathValidatorException(e.getMessage(), e, certPath, certs.size() - 1);
-        }
-
-        if (trust == null)
-        {
-            throw new CertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
+            throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, certs.size() - 1);
         }
 
         // RFC 5280 - CRLs must originate from the same trust anchor as the target certificate.
@@ -292,6 +297,15 @@
             cert = (X509Certificate) certs.get(index);
             boolean verificationAlreadyPerformed = (index == certs.size() - 1);
 
+            try
+            {
+                checkCertificate(cert);
+            }
+            catch (AnnotatedException e)
+            {
+                throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, index);
+            }
+
             //
             // 6.1.3
             //
@@ -311,11 +325,15 @@
             //
             // 6.1.4
             //
-
             if (i != n)
             {
                 if (cert != null && cert.getVersion() == 1)
                 {
+                    // we've found the trust anchor at the top of the path, ignore and keep going
+                    if ((i == 1) && cert.equals(trust.getTrustedCert()))
+                    {
+                        continue;
+                    }
                     throw new CertPathValidatorException("Version 1 certificates can't be used as CA ones.", null,
                             certPath, index);
                 }
@@ -454,4 +472,20 @@
         throw new CertPathValidatorException("Path processing failed on policy.", null, certPath, index);
     }
 
+    static void checkCertificate(X509Certificate cert)
+        throws AnnotatedException
+    {
+        try
+        {
+            TBSCertificate.getInstance(cert.getTBSCertificate());
+        }
+        catch (CertificateEncodingException e)
+        {
+            throw new AnnotatedException("unable to process TBSCertificate", e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new AnnotatedException(e.getMessage());
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
index d67a77e..1ed22d5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -59,7 +59,6 @@
 import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
 import org.bouncycastle.jcajce.PKIXExtendedParameters;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
-import org.bouncycastle.jce.PrincipalUtil;
 import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
 import org.bouncycastle.util.Arrays;
 
@@ -2421,8 +2420,11 @@
             }
             catch (CertPathValidatorException e)
             {
-                throw new ExtCertPathValidatorException("Additional certificate path checker failed.", e, certPath,
-                    index);
+                throw new ExtCertPathValidatorException(e.getMessage(), e, certPath, index);
+            }
+            catch (Exception e)
+            {
+                throw new CertPathValidatorException("Additional certificate path checker failed.", e, certPath, index);
             }
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java
index b515306..ea026c7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java
@@ -5,7 +5,6 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.Principal;
-import java.security.Provider;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
 import java.security.cert.CertPathBuilder;
@@ -14,7 +13,6 @@
 import java.security.cert.CertPathValidator;
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertPathValidatorResult;
-import java.security.cert.CertSelector;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.TrustAnchor;
@@ -36,6 +34,7 @@
 import org.bouncycastle.asn1.x509.CRLReason;
 import org.bouncycastle.asn1.x509.DistributionPoint;
 import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.TargetInformation;
@@ -43,10 +42,9 @@
 import org.bouncycastle.jcajce.PKIXCRLStore;
 import org.bouncycastle.jcajce.PKIXCertStoreSelector;
 import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
+import org.bouncycastle.jcajce.PKIXExtendedParameters;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
-import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
-import org.bouncycastle.jcajce.PKIXExtendedParameters;
 import org.bouncycastle.x509.PKIXAttrCertChecker;
 import org.bouncycastle.x509.X509AttributeCertificate;
 import org.bouncycastle.x509.X509CertStoreSelector;
@@ -54,16 +52,16 @@
 class RFC3281CertPathUtilities
 {
 
-    private static final String TARGET_INFORMATION = X509Extensions.TargetInformation
+    private static final String TARGET_INFORMATION = Extension.targetInformation
         .getId();
 
-    private static final String NO_REV_AVAIL = X509Extensions.NoRevAvail
+    private static final String NO_REV_AVAIL = Extension.noRevAvail
         .getId();
 
-    private static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints
+    private static final String CRL_DISTRIBUTION_POINTS = Extension.cRLDistributionPoints
         .getId();
 
-    private static final String AUTHORITY_INFO_ACCESS = X509Extensions.AuthorityInfoAccess
+    private static final String AUTHORITY_INFO_ACCESS = Extension.authorityInfoAccess
         .getId();
 
     protected static void processAttrCert7(X509AttributeCertificate attrCert,
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
index 04f5a06..4d384f5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
@@ -69,7 +69,7 @@
      * Intersects this mask with the given reasons mask.
      * 
      * @param mask The mask to intersect with.
-     * @return The intersection of this and teh given mask.
+     * @return The intersection of this and the given mask.
      */
     ReasonsMask intersect(ReasonsMask mask)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BCFKSStoreTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BCFKSStoreTest.java
index 1a56471..2df1e7f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BCFKSStoreTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BCFKSStoreTest.java
@@ -19,13 +19,32 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.ECGenParameterSpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.asn1.bc.EncryptedObjectStoreData;
+import org.bouncycastle.asn1.bc.ObjectStore;
+import org.bouncycastle.asn1.bc.ObjectStoreIntegrityCheck;
+import org.bouncycastle.asn1.bc.PbkdMacIntegrityCheck;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PBES2Parameters;
+import org.bouncycastle.asn1.pkcs.PBKDF2Params;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.PBKDF2Config;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+import org.bouncycastle.crypto.util.ScryptConfig;
+import org.bouncycastle.jcajce.BCFKSLoadStoreParameter;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Base64;
@@ -50,6 +69,87 @@
     static char[] testPassword = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
     static char[] invalidTestPassword = {'Y', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
 
+    static byte[] kwpKeyStore = Base64.decode(
+        "MIIJ/TCCCT8wgZUGCSqGSIb3DQEFDTCBhzBlBgkqhkiG9w0BBQwwWARAKXQjiKdsRc8lgCbMh8wLqjNPCiLcXVxArXA4/n6Y72G8jn" +
+            "jWUsXqvMQFmruTbQF6USSVaMgS1UlTbdLtu7yH9wIDAMgAAgEgMAwGCCqGSIb3DQILBQAwHgYJYIZIAWUDBAEvMBEEDJMoeNdAkcnM" +
+            "QjtxowIBCASCCKMU5dCIAkTb84CUiUGy4no3nGgVZL+2t4MNPKhMiL+2Xv7Ok9rucD2SMitzm+kxnkVU+aYGLVUrwEPFCvq5GWdnzO" +
+            "yjCd3XzTieySlfxhIxYMixGfz8NAvPu+P2LwtE+j2C4poHS7+MG22OXpxTTLGzWGuYusxb1zVLTujP6gSVGbtBikLxOXRiYXapZQzL" +
+            "32bOIKV/tHLv3JCKvIGyJAnTQBDlHQxVsm8fcYBhc101qc9vd3qMborJEZK3E+znJ++lI0yIb+WcZJ3PDI11Fzf22M1D6qtV8RELsL" +
+            "b5zfLheFLc4rJcY0YSja24se0tFvT7X9cSyrpvXdNDmzBlBThPdINsKPf3N6fO/9ibn/0QIJPY5bQc3SwbN8c7vboHOJzbWjq7n7Q7" +
+            "1ZkFeiYO/NIXKZ4/KN8sqlRLjvEy4BFnbGoufim+K1zpFGdUPbYpDkuzCkfQfiEaQ9Zt69p5w5e6qh04kgHue0Ac/0IsnRIFy78k4J" +
+            "lK5TlrB3exqpuISZEWP72WDa+0yTaRM6ecMfIqieDNQmpD9U3HpmdMgiWZXTpCTtM/3I62Bv7EkwcVccRP9z4QUcoGZy81EemQ4d3e" +
+            "OVfYgvgZBCsbSpf+V8HlsnApTbJubTY1wJQAA19h49E7l3VCxSmeNcUSSE68xJjdJPPAzU1v83+RkYUDPlRx1YsO77zYBSuOwJr0g4" +
+            "BDTfnyd1vZCM6APt9N7Z2MfALoSSg4EF68nr144GLAMZw4ZVjfUeZ+kF3mjTDPujOoyI3vDztA5ZFa0JCQpp8Yh0CuO+sGnWLh+7Tb" +
+            "irH2ifEscmNI++csUwDPSInjfGzv722JY6c9XzbaqDGqstpykwwUVN01IceolCvgeHZW7P0feDyqbpgmpdRxiGuBWshFEdcttXDSl9" +
+            "mQVEyYAMHQFVQKIx2RrFD7QPWZhITGqCvF44GNst/3962Au9oyGAY6rRQfN/HdF4+ygWjOS0t/50c1eAyBj1Rfk/M4sHBi8dKDjOpX" +
+            "QzqfqLqHjevxQPw1761q629iTagOO/3AIebbraD2qLqDHjmqUAW0ZVLkdS5n8zYyiqGsVeKok7SSDDKQfqwouPHJvRmKzHAK6bZDdr" +
+            "qMBqNfNcRghWHSH0jM4j8G1w3H2FQsNfBHqTb+kiFx1jEovKkf2HumctWwI5hqV2R2I23ThRNQbh6bdtFc8D3a8YnuXpUK+Tw/RTzM" +
+            "eGtUsVeakGOZDHh9AlxsdcLChY9xTLMzbpLfb6VAE9kpZ86Uwe60i+S4ropyIp5cwXizAgJPh1T51ZWTzEu+s8BDEAkXSDgxs1PFML" +
+            "Ha2pWnHPMNSs4VF6eeyK0Vj66m4LcQ0AgE35jAGxWQm31KbWI/h8EMxiC/tDJfMJ3UUKxYCRdbcneDBRc4E4cNmZVqajc8o9Fexr97" +
+            "GLQ6Is1HVoG65qtq6I9Wt6wmA/5i8ptG7bl7NrIzn3Fg0bMbwHEaKIoXrFHTM0EjwnOkVtQWBNDhnBa66IDJXMxJzXDB2uoMU/wX2y" +
+            "4dGpM+mgomJt0U3i29HqeihEQjHDc0hTJLkp2SJ2tKw3+VtoXUinV1W7tsG9TMj3F+XNSeiGFrcZpryi6+Fml3Tohg/FaiJQLpB9pL" +
+            "tzNd61ln1Q6RTHcOMChNocCRaagH6ntX5j8GcVp0auPfw8zyR5iNGueQdnV38Q6MhiGxlMQKC/gjBdKAHRI2q+31tGK8ZslHFxDee1" +
+            "fy3wtRZpLDwgecH74g4+1TYTLPj/PNeYRQicRCa1BbvI3zB1d8t+LKTg/f34MeEzdMpRT8fRb6vw/O1CRhtdl/0pBQ7RZQSrZFPdEr" +
+            "KPRv4/1IG46crTCw1/AOMTXKjPeaUeADjff7aLKizJHUSPr6sTRxoMWQeOYfBDnRiLDZ/XYvSDkjnzesa0hdQIIe/tHnqSZ23Jbi46" +
+            "bLD7Lhf3lfZzbEOqKXAlq0m/ooidubndc0K1xVex4M/T+M0mMPRwO0uICJM4EtivU9Fp5/12GXdvimGEhr/adGodf+JduhsUoIUiz5" +
+            "TghRV0dSuLtQkcD2d0GkfxgHkCBlhbS3WifMWLTa3lHWrCVyhdIf6m5UOtqfzj5CEEkwE+L6urNBo3D4zHUjm8XJekjI3xjGbQHjBo" +
+            "sr+BFHkwGNfTXXBHVqRE0L8lH6kSpLaCF5iMpU2NuAeaJ/xdS7LUXBtn4Zvi34PR3/akwMYIr4X+uDM0eB0KkOyyqSXZVPsT7uGMef" +
+            "wOHmbx1eHe22mR/q1r1iczDwOtXNYo8OB9jSsL3XWFdt4STxdA7kFEsAvK001x0pjrpTa/j/4ixjKhBGu8V/WVuBl0Hlicybtdl7xF" +
+            "CgoeF3FrAsn2Rw0EjVJm4uLpdEHGIVCWWTgadhZ9YyMWoMenLOUoGMlWXGE9hLGUfJG1wOMlFg33zq4dwCj17O0ULdpHh7QFQFEEpM" +
+            "+zscDhOHKmrZZEuiJvhR0JFkZz2rml0TEfSjCmdQ8XfJMzLbQ8BKZhWLOQdVh8Scn96Hm0EGkFBkcb4dO/Ubw+cu+bGskxHL1Q6uW0" +
+            "hGOdejiS7yWclE//uzSlSTa7GRtZ1F/vziWIVno0IInEyiOsCGagagWmxMvv1GTnRJwJl8Bt0BPJmWS2L4CClD6ocH2DrCEEYjMraP" +
+            "dquGbe0/0eYv3qANDWjvzJs4o4/4SoKZuRBuVj5YQMs69XdaxPgnC3Xfx59pf1Q5qOQe94R8oVTnT6z6G1Radsoweh1UnwItjjt4pt" +
+            "pfjyUn4bF2Ovz6bs/Tprbo2B4gmBraimCVHT5pruScBY2q4Vd8XiGbiviS8SgqUnxhH/4XmRRdeYpHpZyet1DT+nNTdJdOCfrsE630" +
+            "9CEQNhQRXt9j5c9S8fnwEA3x/FsriCOAnXsmjVZTnMmctnEYs0aChPxnCBgW1vb2dVUTJQ+KR+2CD3xPNiIEwdk9rA+80k1z3JXek8" +
+            "tac4cwgbcwDAYIKoZIhvcNAgsFADBlBgkqhkiG9w0BBQwwWARAvH3U5H5R/XeTJYthNF/5aUAsqnHPEeperLR1iXVAiVH8t4iby2WP" +
+            "FbvQtoKDbREOo9NaULKIWlDlimxCJosvygIDAMgAAgFAMAwGCCqGSIb3DQILBQAEQGeIvocQlW6yjPCczqj+yNdn6sTcmuHI9AnFtn" +
+            "aY0K7Ki2oIlXl5D9TLznFhJuHDtrIA3VYy2XTCvyrY3qEIySo=");
+
+    static byte[] oldKeyStoreNoPW = Base64.decode(
+        "MIIF/jCCBUEwgZQGCSqGSIb3DQEFDTCBhjBkBgkqhkiG9w0BBQwwVwRAr1ik7Ut78AkRAXYcwhwjOjlSjfjzLzCqFFFsT2OHXWtCK1h" +
+            "FEnbVpdi5OFL+TQ6g8kU9w8EYrIZH7elwkMt+6wICBAACASAwDAYIKoZIhvcNAgsFADAeBglghkgBZQMEAS8wEQQMi3d/cdRmlkhW1" +
+            "B43AgEIBIIEpvp3Y9FgZwdOCGGd3pJmhTy4z3FQ+xQD5yQizR3GtuNwLQvrsDfaZOPdmt6bKSLQdjVXX214d2JmBlKNOj9MD6SDIsW" +
+            "+yEVoqk4asmQjY5KZi/l7o9IRMTAVFBSKyXYcmnV/0Wqpv/AEOaV1ytrxwu2TOW3gZcbNHs3YQvAArxMcqCLyyGJYJ73Qt2xuccZa8" +
+            "YgagCovr0t2KJYHdpeTIFvhaAU7/iHKa0z4ES0YjZoEKNu7jA91WCnKIaFdJCRLS5NKqcuHw93KgGNelEEJt9BbhmddlzZ3upxdw9Q" +
+            "vZsaasD30ezK6viROxAkerfXzI5QVS8Qlz1/TQ10/ri8Lf04H3+HNRV5YS0cH9ghoBxKvvu8whcA43FdvGE7MREIEykJBWWWK5bgul" +
+            "duf2ONNA5cIBTLwLOmPdT2I4PkXUjCROHBmX9m4F0p41+9DCpqS2z5H2oS4+n+sBLHFWZbsOu/NAXKswLDVRaBbSGJW270dc8Gv1Vo" +
+            "B9VWlkqX4wLZTLp+Gbk2aJaKlp9zeN5EHG/vh1wJWYq138h2MB+cYZ2TCl3+orhzlzx6xfRVAtbBz9kpPDpfgpnahM0+IdcuVc5B2s" +
+            "UR6hBM/GQY4cgFdsBI1XhoEjLxzD8PxF4Se2KbseB6fq0+h1GKSB8YXv+IVvroF1ueqsi8DisNcrkN3Bdbl28gopF7kG+aJ84JkEHP" +
+            "bmN+EaYIZZ6yRBHa/nfXltblCIRbSfB0x4L8Uz+/lbEen5hov7v/60+v+6nAlNWs3Af0ZlmOU4KAcSgmLBJzh3+83qld8BlJH1t1HI" +
+            "Ct/md7BQLXn4fRWeKUhbwvSvlut7knai1ZKaLxEhNCh+/7UDE7Y1wvzBfWJYfyAFkCxW9U0erkwp8euea7OgMd1U+6R9H8FEgEjzaj" +
+            "maMCKqmAizZromgxsiPzZgMkz9J1eY/VtWqk1Gu3mq7O/6ilWh/dogxVfeVZ2kyS17rXL152pcJHIx20Vsd4gnFx8sLqfqiO5n/qoA" +
+            "8BkbbwdrBmURNCVmDMuqlMl/yiOpqohQ8kcp81l6B6NHAtxAWCSz7ypfKw43G80tTKhHYDguCUvdbLCuR43DJj22SuuxoRKHjnhtYD" +
+            "xKL58W5HhIcSFliI5qBuRc+EHVOdHfFfqNhisitOzuTk9z1Emg0lweVFzaWkpqxiwtNfOmiYrg+EzDYiGmiQ7/r5Uxqku+aX69khXN" +
+            "OKQbx1d48PI/0mNJV7qUY6k1hhU3ZkMSnuR1akaq/Skds7BnC3yj8byDlWouJ5AYreHPc4uxoH6YwSrBGBWw9omxGPFE6aGWze8pV/" +
+            "95HOrftINptVRDPtuBvV8fo9qPJ7Xr6unG3kEbKoflYTbolguI4YN338+QIc6+53I7N7H+3kkb8TJhUPj4ImS1dvN5KfkSwYuKX8sQ" +
+            "r4MGUVTfJwbRCKkbimtJ1MY/Rcpe9No1xQObp/3G4Tfam1KlhhLaM3A9fCLm+WwS7zlemJ+KcWa7iOyoS5f646+aLRZ7sNeuoxYecq" +
+            "9ybT5W8mYitUdvxcPwMlh2w1DqwmDqXVqkevs8WnDBJM2FYWVJenoU98oPd3pbFicZsjuMIG2MAwGCCqGSIb3DQILBQAwZAYJKoZIh" +
+            "vcNAQUMMFcEQAI9+HvmFMWhbl/EmZBy/B2CDIKcCs4AuhrKu50UVHSHobnuX7phOAtxdI9VevE2ehMCbWkLrUa3Qtkv4CmozFwCAgQ" +
+            "AAgFAMAwGCCqGSIb3DQILBQAEQHGAl3x6ij1f4oSoeKX/KQOYByXY/Kk4BinJM0cG0zXapG4vYidgmTMPTguuWXxL1u3+ncAGmW2EY" +
+            "gEAHiOUu5c=");
+
+    static byte[] oldKeyStore = Base64.decode(
+        "MIIF/jCCBUEwgZQGCSqGSIb3DQEFDTCBhjBkBgkqhkiG9w0BBQwwVwRA1njcCRF+e+s3pQsVaifZNKCablZ+5cLEeJXEdsAtJt7ZG2" +
+            "6dq5iYzBhbol5L5D0n9RLYFW5IoK9rCd8UpD61GAICBAACASAwDAYIKoZIhvcNAgsFADAeBglghkgBZQMEAS8wEQQMhT2rGv09UX8P" +
+            "pmcZAgEIBIIEpu8KeIMyG7FZL+rxPCJ7YFNRXEjykNt70W1BD8VDsN/bGuW4kCnKXNlzV2SAx/44mhdR47qiKrXziCwZUgpck9d1R5" +
+            "nQQtTKw0Q2F1EuWGm9ErFpCMYl6E43/URmkmjuCMIpEbrKHTmuEjqsdHJ7+CST4cFU3lCsBj7dMl9G7tLxJhq5aCTYxhFX6R5kM5QH" +
+            "t/pkxE/5Ei94nh606cKNjLA7Zlrbn1c5WlTpesOjE1pZp/QY9UuSiSA0nucNd8Ir0H4PK120QerdQQ4EWY/KHEDn4EqGpaE1Z6WVAS" +
+            "qyYING7g1q4YeYeJjFA2V8fqsj0j/wxG29x5J5ghcERnrcQGTL2P3uLvy2chgHdqIaFxKStANVntW+dy9MD/uCZnYi7DzeS3qWEZcl" +
+            "cpp5oImL79k08uc9jpfOnNaqbxz8b76ABH39OVQVSGRhh7fkYYSlUEWpSlaFoKaywV3yJwXlilhX7JqyiqRt/hrlVLTlQZZeJbYMrE" +
+            "KA/Fn2ePmNt5hJRiHzF5w/YVd5My27QtPvInCgJ2bV+Z0Di3l+Sd4SCS1NiHtR6uB7G3xlI8E3uQVV4dRNXM8drb1Uu/eTGxGSH0hY" +
+            "2Z0N8TvSGdz+TAQRNn/nXaMA2nZdfhVmwiRPPP3BaiBCJM6y5FroOT1rkPupA+gpmlw1M7Ey+rABphsqEig2XyRe4FMMmIc4i8ga6m" +
+            "KH+F0e26ycsb+nSycdhLIs5Dcdo42wzmvmoG8fvM+/C1N98TfB0m2KbtS1TV9dohagJi4l685iUMnUbH3nmha7RPYUVnpZdDokiATV" +
+            "WjuSezCdpIxv1m6HOqXuArvDtvExDzyVZnPoIF4DEuRypDpW8tkppvLGA6VEo1TPJvjvyrX6SqorwDa1JINVnQGPpx5StjL+eIQBzV" +
+            "RHoy+NP2dcPBHUlAfwWrkk7V7CwST6uNYBL+YVhTYYIN1HnJY0CkmmraqMvkMks17WAd9hONzSLmNT3St6s3VIQMMPC7qNatB+570Q" +
+            "BxgiQC7ieFu1wqy9ZNnNLU9DC69HR37uUFyiCnbCb54XY/zmCUhc8ONBi3L8DwmiDZ2oF7WIEmWvblcbWaQNFPNBMS9KzejHLpvopW" +
+            "+XcfRX4jCz9PwZ9HhUwGk8R7b1MALgJhXxuAD/a4VQK2OtlTHeAgSGBrGcGgjzSa7JWM5ks+EHdTuLaiU3ViVXLrZq4lr/D8ni1Ipt" +
+            "kKPaVcWnl56i7AXZtPj5xVE5v2eVual3sBOkObpoObyrDfmouZW0A9GPk69jGTm5j2FU+50p7JxSfR78BZJitBqrcYS4boVDFmTZYN" +
+            "MBpgGkHqW79gWKIde/pf6nf9cSnDEjZEIZJQI5rnLqmGG6+vKxJQJt7be4vCzGTVMqiY3+QgVCuwtK7Vd44RaPDnzQDxC9OwJOhIUF" +
+            "s1UwoS/vU/n5kbaYmD+py3dgffw4EicaOv5hG7NELZRKxueCjnVwdeCGH+WgJL7AIUdruK/SvsQbJX1asEFKU5KCG4Z9Sw0Sw4MjL+" +
+            "OAiyIbpQpMfHtG+9ORfWWmlH8McA3rjT07fKelhPn1YauY2jGZLfBrpBrQKxvcL82og7rUMIG2MAwGCCqGSIb3DQILBQAwZAYJKoZI" +
+            "hvcNAQUMMFcEQOchs+KAXDWhUaENOgpSls0plNpIUYDkgnMa/iL4RzEOCwiZBOuBdGsEfP3oKLWUS3wO83vrgetSLK5fkN6QNnoCAg" +
+            "QAAgFAMAwGCCqGSIb3DQILBQAEQBLCR5e4teCd8JX0xJbGadSCFaO1oEehyXSZrnKahsYJ7yTHqJTvlcWvqTiwn7Gud/SJmMXPQkZC" +
+            "SQhMQ5k+xZ4=");
 
     public void shouldCreateEmptyBCFKSNoPassword()
         throws Exception
@@ -107,7 +207,7 @@
         }
         catch (IOException e)
         {
-            isTrue("wrong message", "BCFKS KeyStore corrupted: MAC calculation failed.".equals(e.getMessage()));
+            isTrue("wrong message", "BCFKS KeyStore corrupted: MAC calculation failed".equals(e.getMessage()));
         }
 
         isTrue("", 0 == store.size());
@@ -123,6 +223,224 @@
         checkOneCertificate(cert, testPassword);
     }
 
+    public void shouldStoreOneCertificateWithECDSASignature()
+        throws Exception
+    {
+        KeyPairGenerator kpG = KeyPairGenerator.getInstance("EC", "BC");
+
+        kpG.initialize(new ECGenParameterSpec("P-256"));
+
+        KeyPair kp = kpG.generateKeyPair();
+
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setCertificateEntry("cert", cert);
+
+        isTrue("", 1 == store1.size());
+        Enumeration<String> en1 = store1.aliases();
+
+        isTrue("", "cert".equals(en1.nextElement()));
+        isTrue("", !en1.hasMoreElements());
+
+        certStorageCheck(store1, "cert", cert);
+
+        Date entryDate = store1.getCreationDate("cert");
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        BCFKSLoadStoreParameter storeParameter = new BCFKSLoadStoreParameter.Builder(bOut, kp.getPrivate())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            .build();
+
+        store1.store(storeParameter);
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        BCFKSLoadStoreParameter loadParameter = new BCFKSLoadStoreParameter.Builder(
+                new ByteArrayInputStream(bOut.toByteArray()), kp.getPublic())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            .build();
+
+        store2.load(loadParameter);
+    }
+
+    public void shouldStoreOneCertificateWithECDSASignatureAndCertificates()
+        throws Exception
+    {
+        KeyPairGenerator kpG = KeyPairGenerator.getInstance("EC", "BC");
+
+        kpG.initialize(new ECGenParameterSpec("P-256"));
+
+        KeyPair kp = kpG.generateKeyPair();
+
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setCertificateEntry("cert", cert);
+
+        isTrue("", 1 == store1.size());
+        Enumeration<String> en1 = store1.aliases();
+
+        isTrue("", "cert".equals(en1.nextElement()));
+        isTrue("", !en1.hasMoreElements());
+
+        certStorageCheck(store1, "cert", cert);
+
+        Date entryDate = store1.getCreationDate("cert");
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        final X509Certificate selfSignedCert = TestUtils.createSelfSignedCert("CN=ECDSA", "SHA256withECDSA", kp);
+        BCFKSLoadStoreParameter storeParameter = new BCFKSLoadStoreParameter.Builder(bOut, kp.getPrivate())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            .withCertificates(
+                new X509Certificate[] {selfSignedCert})
+            .build();
+
+        store1.store(storeParameter);
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        final AtomicBoolean isCalled = new AtomicBoolean(false);
+
+        BCFKSLoadStoreParameter loadParameter = new BCFKSLoadStoreParameter.Builder(
+            new ByteArrayInputStream(bOut.toByteArray()), new BCFKSLoadStoreParameter.CertChainValidator()
+            {
+                public boolean isValid(X509Certificate[] chain)
+                {
+                    isEquals(selfSignedCert, chain[0]);
+                    isCalled.set(true);
+                    return true;
+                }
+            })
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            .build();
+
+        store2.load(loadParameter);
+
+        isTrue(isCalled.get());
+        
+        loadParameter = new BCFKSLoadStoreParameter.Builder(
+            new ByteArrayInputStream(bOut.toByteArray()), new BCFKSLoadStoreParameter.CertChainValidator()
+            {
+                public boolean isValid(X509Certificate[] chain)
+                {
+                    isEquals(selfSignedCert, chain[0]);
+
+                    return false;
+                }
+            })
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA)
+            .build();
+
+        try
+        {
+            store2.load(loadParameter);
+            fail("no exception");
+        }
+        catch (IOException e)
+        {
+            isEquals("certificate chain in key store signature not valid", e.getMessage());
+        }
+    }
+
+    public void shouldStoreOneCertificateWithDSASignature()
+        throws Exception
+    {
+        KeyPairGenerator kpG = KeyPairGenerator.getInstance("DSA", "BC");
+
+        kpG.initialize(2048);
+
+        KeyPair kp = kpG.generateKeyPair();
+
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setCertificateEntry("cert", cert);
+
+        isTrue("", 1 == store1.size());
+        Enumeration<String> en1 = store1.aliases();
+
+        isTrue("", "cert".equals(en1.nextElement()));
+        isTrue("", !en1.hasMoreElements());
+
+        certStorageCheck(store1, "cert", cert);
+
+        Date entryDate = store1.getCreationDate("cert");
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        BCFKSLoadStoreParameter storeParameter = new BCFKSLoadStoreParameter.Builder(bOut, kp.getPrivate())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withDSA)
+            .build();
+
+        store1.store(storeParameter);
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        BCFKSLoadStoreParameter loadParameter = new BCFKSLoadStoreParameter.Builder(
+                new ByteArrayInputStream(bOut.toByteArray()), kp.getPublic())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withDSA)
+            .build();
+
+        store2.load(loadParameter);
+    }
+
+    public void shouldStoreOneCertificateWithRSASignature()
+        throws Exception
+    {
+        KeyPairGenerator kpG = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpG.initialize(2048);
+
+        KeyPair kp = kpG.generateKeyPair();
+
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setCertificateEntry("cert", cert);
+
+        isTrue("", 1 == store1.size());
+        Enumeration<String> en1 = store1.aliases();
+
+        isTrue("", "cert".equals(en1.nextElement()));
+        isTrue("", !en1.hasMoreElements());
+
+        certStorageCheck(store1, "cert", cert);
+
+        Date entryDate = store1.getCreationDate("cert");
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        BCFKSLoadStoreParameter storeParameter = new BCFKSLoadStoreParameter.Builder(bOut, kp.getPrivate())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withRSA)
+            .build();
+
+        store1.store(storeParameter);
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        BCFKSLoadStoreParameter loadParameter = new BCFKSLoadStoreParameter.Builder(
+                new ByteArrayInputStream(bOut.toByteArray()), kp.getPublic())
+            .withStoreSignatureAlgorithm(BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withRSA)
+            .build();
+
+        store2.load(loadParameter);
+    }
+
     private void checkOneCertificate(X509Certificate cert, char[] passwd)
         throws KeyStoreException, NoSuchProviderException, IOException, NoSuchAlgorithmException, CertificateException
     {
@@ -189,10 +507,10 @@
 
         X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
 
-        checkOnePrivateKeyFips(privKey, new X509Certificate[] { cert }, null);
-        checkOnePrivateKeyFips(privKey, new X509Certificate[] { cert }, testPassword);
-        checkOnePrivateKeyDef(privKey, new X509Certificate[] { cert }, null);
-        checkOnePrivateKeyDef(privKey, new X509Certificate[] { cert }, testPassword);
+        checkOnePrivateKeyFips(privKey, new X509Certificate[]{cert}, null);
+        checkOnePrivateKeyFips(privKey, new X509Certificate[]{cert}, testPassword);
+        checkOnePrivateKeyDef(privKey, new X509Certificate[]{cert}, null);
+        checkOnePrivateKeyDef(privKey, new X509Certificate[]{cert}, testPassword);
     }
 
     public void shouldStoreOnePrivateKeyWithChain()
@@ -396,7 +714,7 @@
 
         store2.load(new ByteArrayInputStream(bOut.toByteArray()), testPassword);
 
-        isTrue("", 4 ==store2.size());
+        isTrue("", 4 == store2.size());
 
         Key storeDesEde = store2.getKey("secret2", "secretPwd2".toCharArray());
 
@@ -415,9 +733,53 @@
         Certificate storeCert = store2.getCertificate("trusted");
         isTrue("", cert.equals(storeCert));
 
-        isTrue("", null ==store2.getCertificate("unknown"));
+        isTrue("", null == store2.getCertificate("unknown"));
 
-        isTrue("", null ==store2.getCertificateChain("unknown"));
+        isTrue("", null == store2.getCertificateChain("unknown"));
+
+        isTrue("", !store2.isCertificateEntry("unknown"));
+
+        isTrue("", !store2.isKeyEntry("unknown"));
+
+        isTrue("", !store2.containsAlias("unknown"));
+    }
+
+    public void shouldParseKWPKeyStore()
+        throws Exception
+    {
+        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
+
+        X509Certificate cert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        SecretKeySpec aesKey = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"), "AES");
+        SecretKeySpec edeKey = new SecretKeySpec(Hex.decode("010102020404070708080b0b0d0d0e0e"), "DESede");
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        store2.load(new ByteArrayInputStream(kwpKeyStore), testPassword);
+
+        isTrue("", 4 == store2.size());
+
+        Key storeDesEde = store2.getKey("secret2", "secretPwd2".toCharArray());
+
+        isTrue("", edeKey.getAlgorithm().equals(storeDesEde.getAlgorithm()));
+
+        isTrue("", Arrays.areEqual(edeKey.getEncoded(), storeDesEde.getEncoded()));
+
+        Key storeAes = store2.getKey("secret1", "secretPwd1".toCharArray());
+        isTrue("", Arrays.areEqual(aesKey.getEncoded(), storeAes.getEncoded()));
+        isTrue("", aesKey.getAlgorithm().equals(storeAes.getAlgorithm()));
+
+        Key storePrivKey = store2.getKey("privkey", testPassword);
+        isTrue("", 2 == store2.getCertificateChain("privkey").length);
+        isTrue("", storePrivKey instanceof RSAPrivateCrtKey);
+
+        Certificate storeCert = store2.getCertificate("trusted");
+        isTrue("", cert.equals(storeCert));
+
+        isTrue("", null == store2.getCertificate("unknown"));
+
+        isTrue("", null == store2.getCertificateChain("unknown"));
 
         isTrue("", !store2.isCertificateEntry("unknown"));
 
@@ -443,6 +805,14 @@
         SecretKeySpec hmacKey384 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0eff0102ff"), "HmacSHA384");
         SecretKeySpec hmacKey512 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0eff010203ff"), "HmacSHA512");
 
+        SecretKeySpec camellia128 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f"), "Camellia");
+        SecretKeySpec camellia192 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f0001020304050607"), "Camellia");
+        SecretKeySpec camellia256 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"), "Camellia");
+        SecretKeySpec seed = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f"), "SEED");
+        SecretKeySpec aria128 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f"), "ARIA");
+        SecretKeySpec aria192 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f0001020304050607"), "ARIA");
+        SecretKeySpec aria256 = new SecretKeySpec(Hex.decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"), "ARIA");
+
         store1.setKeyEntry("secret1", aesKey, "secretPwd1".toCharArray(), null);
         store1.setKeyEntry("secret2", edeKey1, "secretPwd2".toCharArray(), null);
         store1.setKeyEntry("secret3", edeKey2, "secretPwd3".toCharArray(), null);
@@ -453,15 +823,32 @@
         store1.setKeyEntry("secret8", hmacKey384, "secretPwd8".toCharArray(), null);
         store1.setKeyEntry("secret9", hmacKey512, "secretPwd9".toCharArray(), null);
 
+        store1.setKeyEntry("secret10", camellia128, "secretPwd10".toCharArray(), null);
+        store1.setKeyEntry("secret11", camellia192, "secretPwd11".toCharArray(), null);
+        store1.setKeyEntry("secret12", camellia256, "secretPwd12".toCharArray(), null);
+        store1.setKeyEntry("secret13", seed, "secretPwd13".toCharArray(), null);
+        store1.setKeyEntry("secret14", aria128, "secretPwd14".toCharArray(), null);
+        store1.setKeyEntry("secret15", aria192, "secretPwd15".toCharArray(), null);
+        store1.setKeyEntry("secret16", aria256, "secretPwd16".toCharArray(), null);
+
         checkSecretKey(store1, "secret1", "secretPwd1".toCharArray(), aesKey);
         checkSecretKey(store1, "secret2", "secretPwd2".toCharArray(), edeKey1); // TRIPLEDES and TDEA will convert to DESEDE
         checkSecretKey(store1, "secret3", "secretPwd3".toCharArray(), edeKey1);
         checkSecretKey(store1, "secret4", "secretPwd4".toCharArray(), edeKey1);
-        checkSecretKey(store1, "secret5", "secretPwd5".toCharArray(), hmacKey1);
-        checkSecretKey(store1, "secret6", "secretPwd6".toCharArray(), hmacKey224);
-        checkSecretKey(store1, "secret7", "secretPwd7".toCharArray(), hmacKey256);
-        checkSecretKey(store1, "secret8", "secretPwd8".toCharArray(), hmacKey384);
-        checkSecretKey(store1, "secret9", "secretPwd9".toCharArray(), hmacKey512);
+        // TODO:
+//        checkSecretKey(store1, "secret5", "secretPwd5".toCharArray(), hmacKey1);
+//        checkSecretKey(store1, "secret6", "secretPwd6".toCharArray(), hmacKey224);
+//        checkSecretKey(store1, "secret7", "secretPwd7".toCharArray(), hmacKey256);
+//        checkSecretKey(store1, "secret8", "secretPwd8".toCharArray(), hmacKey384);
+//        checkSecretKey(store1, "secret9", "secretPwd9".toCharArray(), hmacKey512);
+
+        checkSecretKey(store1, "secret10", "secretPwd10".toCharArray(), camellia128);
+        checkSecretKey(store1, "secret11", "secretPwd11".toCharArray(), camellia192);
+        checkSecretKey(store1, "secret12", "secretPwd12".toCharArray(), camellia256);
+        checkSecretKey(store1, "secret13", "secretPwd13".toCharArray(), seed);
+        checkSecretKey(store1, "secret14", "secretPwd14".toCharArray(), aria128);
+        checkSecretKey(store1, "secret15", "secretPwd15".toCharArray(), aria192);
+        checkSecretKey(store1, "secret16", "secretPwd16".toCharArray(), aria256);
 
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
@@ -475,13 +862,69 @@
         checkSecretKey(store2, "secret2", "secretPwd2".toCharArray(), edeKey1); // TRIPLEDES and TDEA will convert to DESEDE
         checkSecretKey(store2, "secret3", "secretPwd3".toCharArray(), edeKey1);
         checkSecretKey(store2, "secret4", "secretPwd4".toCharArray(), edeKey1);
-        checkSecretKey(store2, "secret5", "secretPwd5".toCharArray(), hmacKey1);
-        checkSecretKey(store2, "secret6", "secretPwd6".toCharArray(), hmacKey224);
-        checkSecretKey(store2, "secret7", "secretPwd7".toCharArray(), hmacKey256);
-        checkSecretKey(store2, "secret8", "secretPwd8".toCharArray(), hmacKey384);
-        checkSecretKey(store2, "secret9", "secretPwd9".toCharArray(), hmacKey512);
+        // TODO:
+//        checkSecretKey(store2, "secret5", "secretPwd5".toCharArray(), hmacKey1);
+//        checkSecretKey(store2, "secret6", "secretPwd6".toCharArray(), hmacKey224);
+//        checkSecretKey(store2, "secret7", "secretPwd7".toCharArray(), hmacKey256);
+//        checkSecretKey(store2, "secret8", "secretPwd8".toCharArray(), hmacKey384);
+//        checkSecretKey(store2, "secret9", "secretPwd9".toCharArray(), hmacKey512);
 
-        isTrue("", null ==store2.getKey("secret10", new char[0]));
+        isTrue("", null == store2.getKey("secret17", new char[0]));
+    }
+
+    public void shouldFailOnWrongPassword()
+        throws Exception
+    {
+        failOnWrongPasswordTest("IBCFKS");
+        failOnWrongPasswordTest("IBCFKS-DEF");
+    }
+
+    public void failOnWrongPasswordTest(String storeName)
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpGen.initialize(512);
+
+        KeyPair kp1 = kpGen.generateKeyPair();
+        KeyPair kp2 = kpGen.generateKeyPair();
+
+        X509Certificate finalCert = TestUtils.createSelfSignedCert("CN=Final", "SHA1withRSA", kp2);
+        X509Certificate interCert = TestUtils.createCert(
+            X500Name.getInstance(finalCert.getSubjectX500Principal().getEncoded()),
+            kp2.getPrivate(),
+            "CN=EE",
+            "SHA1withRSA",
+            null,
+            kp1.getPublic());
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setKeyEntry("privkey", kp1.getPrivate(), testPassword, new X509Certificate[]{interCert, finalCert});
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        store1.store(bOut, testPassword);
+
+        store1 = KeyStore.getInstance(storeName, "BC");
+
+        store1.load(new ByteArrayInputStream(bOut.toByteArray()), testPassword);
+
+        isTrue("privKey test 1", store1.getKey("privkey", testPassword) != null);
+
+        try
+        {
+            store1.getKey("privkey", invalidTestPassword);
+            fail("no exception");
+        }
+        catch (UnrecoverableKeyException e)
+        {
+            isEquals("wrong message, got : " + e.getMessage(), "unable to recover key (privkey)", e.getMessage());
+        }
+
+        isTrue("privKey test 2", store1.getKey("privkey", testPassword) != null);
     }
 
     private void checkSecretKey(KeyStore store, String alias, char[] passwd, SecretKey key)
@@ -531,6 +974,77 @@
         return privKey;
     }
 
+    public void shouldFailOnRemovesOrOverwrite()
+        throws Exception
+    {
+        failOnRemovesOrOverwrite("IBCFKS");
+        failOnRemovesOrOverwrite("IBCFKS-DEF");
+    }
+
+    private void failOnRemovesOrOverwrite(String storeName)
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpGen.initialize(512);
+
+        KeyPair kp1 = kpGen.generateKeyPair();
+        KeyPair kp2 = kpGen.generateKeyPair();
+
+        X509Certificate finalCert = TestUtils.createSelfSignedCert("CN=Final", "SHA1withRSA", kp2);
+        X509Certificate interCert = TestUtils.createCert(
+            X500Name.getInstance(finalCert.getSubjectX500Principal().getEncoded()),
+            kp2.getPrivate(),
+            "CN=EE",
+            "SHA1withRSA",
+            null,
+            kp1.getPublic());
+
+        KeyStore store1 = KeyStore.getInstance(storeName, "BC");
+
+        store1.load(new ByteArrayInputStream(oldKeyStoreNoPW), null);
+
+        try
+        {
+            store1.setKeyEntry("privkey", kp1.getPrivate(), testPassword, new X509Certificate[]{interCert, finalCert});
+            fail("no exception");
+        }
+        catch (KeyStoreException e)
+        {
+            isTrue("set operation not supported in shared mode".equals(e.getMessage()));
+        }
+
+        try
+        {
+            store1.setKeyEntry("privkey", kp1.getPrivate().getEncoded(), new X509Certificate[]{interCert, finalCert});
+            fail("no exception");
+        }
+        catch (KeyStoreException e)
+        {
+            isTrue("set operation not supported in shared mode".equals(e.getMessage()));
+        }
+
+        try
+        {
+            store1.setCertificateEntry("cert", interCert);
+            fail("no exception");
+        }
+        catch (KeyStoreException e)
+        {
+            isTrue("set operation not supported in shared mode".equals(e.getMessage()));
+        }
+
+        try
+        {
+            store1.deleteEntry("privkey");
+            fail("no exception");
+        }
+        catch (KeyStoreException e)
+        {
+            isTrue("delete operation not supported in shared mode".equals(e.getMessage()));
+        }
+    }
+
     public void shouldStoreOneSecretKey()
         throws Exception
     {
@@ -624,14 +1138,14 @@
 
         if (password != null)
         {
-             try
-             {
-                 store.getKey(keyName, null);
-             }
-             catch (UnrecoverableKeyException e)
-             {
-                 isTrue("",e.getMessage().startsWith("BCFKS KeyStore unable to recover private key (privkey)"));
-             }
+            try
+            {
+                store.getKey(keyName, null);
+            }
+            catch (UnrecoverableKeyException e)
+            {
+                isTrue("", e.getMessage().startsWith("BCFKS KeyStore unable to recover private key (privkey)"));
+            }
         }
 
         Certificate[] certificateChain = store.getCertificateChain(keyName);
@@ -722,14 +1236,14 @@
 
         if (password != null)
         {
-             try
-             {
-                 store.getKey(keyName, null);
-             }
-             catch (UnrecoverableKeyException e)
-             {
-                 isTrue("", e.getMessage().startsWith("BCFKS KeyStore unable to recover secret key (seckey)"));
-             }
+            try
+            {
+                store.getKey(keyName, null);
+            }
+            catch (UnrecoverableKeyException e)
+            {
+                isTrue("", e.getMessage().startsWith("BCFKS KeyStore unable to recover secret key (seckey)"));
+            }
         }
 
         Certificate[] certificateChain = store.getCertificateChain(keyName);
@@ -754,6 +1268,257 @@
         }
     }
 
+    private void shouldParseOldStores()
+        throws Exception
+    {
+        KeyStore store = KeyStore.getInstance("BCFKS", "BC");
+
+        store.load(new ByteArrayInputStream(oldKeyStore), testPassword);
+
+        checkStore(store, oldKeyStore, testPassword);
+
+        store.load(new ByteArrayInputStream(oldKeyStoreNoPW), null);
+
+        checkStore(store, oldKeyStoreNoPW, null);
+    }
+
+    private void checkStore(KeyStore store1, byte[] data, char[] passwd)
+        throws Exception
+    {
+        isEquals(store1.getCertificateChain("privkey").length, 2);
+        isEquals(1, store1.size());
+        Enumeration<String> en2 = store1.aliases();
+
+        isEquals("privkey", en2.nextElement());
+        isTrue(!en2.hasMoreElements());
+
+        // check invalid load with content
+
+        checkInvalidLoad(store1, passwd, data);
+
+        try
+        {
+            store1.store(new ByteArrayOutputStream(), passwd);
+            fail("no exception");
+        }
+        catch (IOException e)
+        {
+            isEquals("KeyStore not initialized", e.getMessage());
+        }
+
+        // check deletion on purpose
+
+        store1.load(new ByteArrayInputStream(data), passwd);
+
+        store1.deleteEntry("privkey");
+
+        isEquals(0, store1.size());
+        isTrue(!store1.aliases().hasMoreElements());
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        store1.store(bOut, passwd);
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        store2.load(new ByteArrayInputStream(bOut.toByteArray()), passwd);
+
+        isEquals(0, store2.size());
+        isTrue(!store2.aliases().hasMoreElements());
+    }
+
+    private void shouldStoreUsingSCRYPT()
+        throws Exception
+    {
+        byte[] enc = doStoreUsingStoreParameter(new ScryptConfig.Builder(1024, 8, 1)
+            .withSaltLength(20).build());
+
+        ObjectStore store = ObjectStore.getInstance(enc);
+
+        ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck();
+
+        isEquals(integrityCheck.getType(), ObjectStoreIntegrityCheck.PBKD_MAC_CHECK);
+
+        PbkdMacIntegrityCheck check = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck());
+
+        isTrue("wrong MAC", check.getMacAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512));
+        isTrue("wrong PBE", check.getPbkdAlgorithm().getAlgorithm().equals(MiscObjectIdentifiers.id_scrypt));
+
+        ScryptParams sParams = ScryptParams.getInstance(check.getPbkdAlgorithm().getParameters());
+
+        isEquals(20, sParams.getSalt().length);
+        isEquals(1024, sParams.getCostParameter().intValue());
+        isEquals(8, sParams.getBlockSize().intValue());
+        isEquals(1, sParams.getParallelizationParameter().intValue());
+
+        EncryptedObjectStoreData objStore = EncryptedObjectStoreData.getInstance(store.getStoreData());
+
+        AlgorithmIdentifier encryptionAlgorithm = objStore.getEncryptionAlgorithm();
+        isTrue(encryptionAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2));
+
+        PBES2Parameters pbeParams = PBES2Parameters.getInstance(encryptionAlgorithm.getParameters());
+
+        isTrue(pbeParams.getKeyDerivationFunc().getAlgorithm().equals(MiscObjectIdentifiers.id_scrypt));
+
+        sParams = ScryptParams.getInstance(pbeParams.getKeyDerivationFunc().getParameters());
+
+        isEquals(20, sParams.getSalt().length);
+        isEquals(1024, sParams.getCostParameter().intValue());
+        isEquals(8, sParams.getBlockSize().intValue());
+        isEquals(1, sParams.getParallelizationParameter().intValue());
+    }
+
+    private void shouldStoreUsingKWP()
+        throws Exception
+    {
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(new BCFKSLoadStoreParameter.Builder().withStoreEncryptionAlgorithm(BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_KWP).build());
+
+        store1.setCertificateEntry("cert", cert);
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        store1.store(bOut, testPassword);
+
+        byte[] enc = bOut.toByteArray();
+
+        ObjectStore store = ObjectStore.getInstance(enc);
+
+        ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck();
+
+        isEquals(integrityCheck.getType(), ObjectStoreIntegrityCheck.PBKD_MAC_CHECK);
+
+        PbkdMacIntegrityCheck check = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck());
+
+        isTrue("wrong MAC", check.getMacAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512));
+        isTrue("wrong PBE", check.getPbkdAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2));
+
+        EncryptedObjectStoreData objStore = EncryptedObjectStoreData.getInstance(store.getStoreData());
+
+        AlgorithmIdentifier encryptionAlgorithm = objStore.getEncryptionAlgorithm();
+        isTrue(encryptionAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2));
+
+        PBES2Parameters pbeParams = PBES2Parameters.getInstance(encryptionAlgorithm.getParameters());
+
+        isTrue(pbeParams.getKeyDerivationFunc().getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2));
+
+        isTrue(pbeParams.getEncryptionScheme().getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_wrap_pad));
+    }
+
+    private void shouldStoreUsingPBKDF2()
+        throws Exception
+    {
+        doStoreUsingPBKDF2(PBKDF2Config.PRF_SHA512);
+        doStoreUsingPBKDF2(PBKDF2Config.PRF_SHA3_512);
+    }
+
+    private void doStoreUsingPBKDF2(AlgorithmIdentifier prf)
+        throws Exception
+    {
+        byte[] enc = doStoreUsingStoreParameter(new PBKDF2Config.Builder()
+            .withPRF(prf)
+            .withIterationCount(1024)
+            .withSaltLength(20).build());
+
+        ObjectStore store = ObjectStore.getInstance(enc);
+
+        ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck();
+
+        isEquals(integrityCheck.getType(), ObjectStoreIntegrityCheck.PBKD_MAC_CHECK);
+
+        PbkdMacIntegrityCheck check = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck());
+
+        isTrue("wrong MAC", check.getMacAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512));
+        isTrue("wrong PBE", check.getPbkdAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2));
+
+        PBKDF2Params pParams = PBKDF2Params.getInstance(check.getPbkdAlgorithm().getParameters());
+
+        isTrue(pParams.getPrf().equals(prf));
+        isEquals(20, pParams.getSalt().length);
+        isEquals(1024, pParams.getIterationCount().intValue());
+
+        EncryptedObjectStoreData objStore = EncryptedObjectStoreData.getInstance(store.getStoreData());
+
+        AlgorithmIdentifier encryptionAlgorithm = objStore.getEncryptionAlgorithm();
+        isTrue(encryptionAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2));
+
+        PBES2Parameters pbeParams = PBES2Parameters.getInstance(encryptionAlgorithm.getParameters());
+
+        isTrue(pbeParams.getKeyDerivationFunc().getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2));
+
+        pParams = PBKDF2Params.getInstance(check.getPbkdAlgorithm().getParameters());
+
+        isTrue(pParams.getPrf().equals(prf));
+        isEquals(20, pParams.getSalt().length);
+        isEquals(1024, pParams.getIterationCount().intValue());
+    }
+
+    private byte[] doStoreUsingStoreParameter(PBKDFConfig config)
+        throws Exception
+    {
+        X509Certificate cert = (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(trustedCertData));
+
+        KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
+
+        store1.load(null, null);
+
+        store1.setCertificateEntry("cert", cert);
+
+        isTrue("", 1 == store1.size());
+        Enumeration<String> en1 = store1.aliases();
+
+        isTrue("", "cert".equals(en1.nextElement()));
+        isTrue("", !en1.hasMoreElements());
+
+        certStorageCheck(store1, "cert", cert);
+
+        Date entryDate = store1.getCreationDate("cert");
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        store1.store(new BCFKSLoadStoreParameter.Builder(bOut, testPassword).withStorePBKDFConfig(config).build());
+
+        KeyStore store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        store2.load(new ByteArrayInputStream(bOut.toByteArray()), testPassword);
+
+        isTrue("", entryDate.equals(store2.getCreationDate("cert")));
+        isTrue("", 1 == store2.size());
+        Enumeration<String> en2 = store2.aliases();
+
+        isTrue("", "cert".equals(en2.nextElement()));
+        isTrue("", !en2.hasMoreElements());
+
+        certStorageCheck(store2, "cert", cert);
+
+        // check invalid load with content
+
+        checkInvalidLoad(store2, testPassword, bOut.toByteArray());
+
+        // check deletion on purpose
+
+        store1.deleteEntry("cert");
+
+        isTrue("", 0 == store1.size());
+        isTrue("", !store1.aliases().hasMoreElements());
+
+        bOut = new ByteArrayOutputStream();
+
+        store1.store(bOut, testPassword);
+
+        store2 = KeyStore.getInstance("BCFKS", "BC");
+
+        store2.load(new ByteArrayInputStream(bOut.toByteArray()), testPassword);
+
+        isTrue("", 0 == store2.size());
+        isTrue("", !store2.aliases().hasMoreElements());
+
+        return bOut.toByteArray();
+    }
+
     public String getName()
     {
         return "BCFKS";
@@ -766,13 +1531,27 @@
         shouldCreateEmptyBCFKSPassword();
         shouldStoreMultipleKeys();
         shouldStoreOneCertificate();
+        shouldStoreOneCertificateWithECDSASignature();
+        shouldStoreOneCertificateWithDSASignature();
+        shouldStoreOneCertificateWithRSASignature();
+        shouldStoreOneCertificateWithECDSASignatureAndCertificates();
         shouldStoreOneECKeyWithChain();
         shouldStoreOnePrivateKey();
         shouldStoreOnePrivateKeyWithChain();
+        shouldStoreOneSecretKey();
+        shouldStoreSecretKeys();
+        shouldStoreUsingSCRYPT();
+        shouldStoreUsingPBKDF2();
+        shouldFailOnWrongPassword();
+        shouldParseKWPKeyStore();
+        shouldFailOnRemovesOrOverwrite();
+        shouldParseOldStores();
+        shouldStoreUsingKWP();
+        //shouldRejectInconsistentKeys();
     }
 
     public static void main(
-        String[]    args)
+        String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
index 83cfdeb..c2475c3 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
@@ -378,7 +378,7 @@
         "SEED/OCB/NoPadding",
         "eb04b3612769e1ad681f975af1a6f401d94dc88276dd50fc3ebce791c28825c652b7351acbad8c63d4d66191de94c970",
         "SEED/CCM/NoPadding",
-        "8bb16b37e7f1d4eb97bb1fa3b9bfd411aca64a3581bb3c5b2a91346983aa334984d73ad629a847f7",
+        "da684e8cab782d4ebae835726f43c3aeea97ee270897255714d464e981ac39af06c9483153f8a05a",
         "SEED/GCM/NoPadding",
         "ed5f6293c9a4f280af6695750bfb3bb3b60c214565a049494df955152757812ebfb93705895606c4378498a93f2541b5",
         "SM4/GCM/NoPadding",
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
index 5457dab..be8416b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertPathValidatorTest.java
@@ -1,15 +1,32 @@
 package org.bouncycastle.jce.provider.test;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.Provider;
 import java.security.PublicKey;
 import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
 import java.security.cert.CertPath;
 import java.security.cert.CertPathValidator;
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertStore;
 import java.security.cert.CertStoreParameters;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.CollectionCertStoreParameters;
 import java.security.cert.PKIXBuilderParameters;
 import java.security.cert.PKIXCertPathChecker;
@@ -22,181 +39,218 @@
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
+import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.NetscapeCertType;
+import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
+import org.bouncycastle.asn1.misc.VerisignCzagExtension;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.RFC4519Style;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
 public class CertPathValidatorTest
     extends SimpleTest
 {
     private byte[] AC_PR = Base64.decode(
-           "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFU1RDQ0F6R2dBd0lC"
-        + "QWdJQkJUQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
-        + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
-        + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
-        + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
-        + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
-        + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
-        + "bHNaV2x5WVRBZUZ3MHdNakEwTURReE9UTTVNREJhRncwd05UQTBNRFF5DQpN"
-        + "elU1TURCYU1HRXhDekFKQmdOVkJBWVRBa0pTTVJNd0VRWURWUVFLRXdwSlEx"
-        + "QXRRbkpoYzJsc01UMHdPd1lEDQpWUVFERXpSQmRYUnZjbWxrWVdSbElFTmxj"
-        + "blJwWm1sallXUnZjbUVnWkdFZ1VISmxjMmxrWlc1amFXRWdaR0VnDQpVbVZ3"
-        + "ZFdKc2FXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJD"
-        + "Z0tDQVFFQXMwc0t5NGsrDQp6b016aldyMTQxeTVYQ045UGJMZERFQXN2cjZ4"
-        + "Z0NCN1l5bEhIQ1NBYmpGR3dOQ0R5NlVxN1h0VjZ6UHdIMXpGDQpFWENlS3Jm"
-        + "UUl5YXBXSEZ4V1VKajBMblFrY1RZM1FOR1huK0JuVk9EVTZDV3M1c3NoZktH"
-        + "RXZyVlQ1Z214V1NmDQp4OFlsdDgzY1dwUE1QZzg3VDlCaHVIbHQzazh2M2Ev"
-        + "NmRPbmF2dytOYTAyZExBaDBlNzZqcCtQUS9LK0pHZlBuDQphQjVVWURrZkd0"
-        + "em5uTTNBV01tY3VJK0o0ek5OMDZaa3ZnbDFsdEo2UU1qcnZEUFlSak9ndDlT"
-        + "cklpY1NmbEo4DQptVDdHWGRRaXJnQUNXc3g1QURBSklRK253TU1vNHlyTUtx"
-        + "SlFhNFFDMHhhT0QvdkdVcG9SaDQzT0FTZFp3c3YvDQpPWFlybmVJeVAwVCs4"
-        + "UUlEQVFBQm80RzNNSUcwTUQwR0ExVWRId1EyTURRd01xQXdvQzZHTEdoMGRI"
-        + "QTZMeTloDQpZM0poYVhvdWFXTndZbkpoYzJsc0xtZHZkaTVpY2k5TVExSmhZ"
-        + "M0poYVhvdVkzSnNNQklHQTFVZElBUUxNQWt3DQpCd1lGWUV3QkFRRXdIUVlE"
-        + "VlIwT0JCWUVGREpUVFlKNE9TWVB5T09KZkVMZXhDaHppK2hiTUI4R0ExVWRJ"
-        + "d1FZDQpNQmFBRklyNjhWZUVFUk0xa0VMNlYwbFVhUTJreFBBM01BNEdBMVVk"
-        + "RHdFQi93UUVBd0lCQmpBUEJnTlZIUk1CDQpBZjhFQlRBREFRSC9NQTBHQ1Nx"
-        + "R1NJYjNEUUVCQlFVQUE0SUJBUUJRUFNoZ1lidnFjaWV2SDVVb3ZMeXhkbkYr"
-        + "DQpFcjlOeXF1SWNkMnZ3Y0N1SnpKMkQ3WDBUcWhHQ0JmUEpVVkdBVWorS0NP"
-        + "SDFCVkgva1l1OUhsVHB1MGtKWFBwDQpBQlZkb2hJUERqRHhkbjhXcFFSL0Yr"
-        + "ejFDaWtVcldIMDR4eTd1N1p6UUpLSlBuR0loY1FpOElyRm1PYkllMEc3DQpY"
-        + "WTZPTjdPRUZxY21KTFFHWWdtRzFXMklXcytQd1JwWTdENGhLVEFoVjFSNkVv"
-        + "amE1L3BPcmVDL09kZXlQWmVxDQo1SUZTOUZZZk02U0Npd2hrK3l2Q1FHbVo0"
-        + "YzE5SjM0ZjVFYkRrK1NQR2tEK25EQ0E3L3VMUWNUMlJURE14SzBaDQpuZlo2"
-        + "Nm1Sc0ZjcXRGaWdScjVFcmtKZDdoUVV6eHNOV0VrNzJEVUFIcVgvNlNjeWtt"
-        + "SkR2V0plSUpqZlcNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==");
+        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFU1RDQ0F6R2dBd0lC"
+            + "QWdJQkJUQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+            + "bHNaV2x5WVRBZUZ3MHdNakEwTURReE9UTTVNREJhRncwd05UQTBNRFF5DQpN"
+            + "elU1TURCYU1HRXhDekFKQmdOVkJBWVRBa0pTTVJNd0VRWURWUVFLRXdwSlEx"
+            + "QXRRbkpoYzJsc01UMHdPd1lEDQpWUVFERXpSQmRYUnZjbWxrWVdSbElFTmxj"
+            + "blJwWm1sallXUnZjbUVnWkdFZ1VISmxjMmxrWlc1amFXRWdaR0VnDQpVbVZ3"
+            + "ZFdKc2FXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJD"
+            + "Z0tDQVFFQXMwc0t5NGsrDQp6b016aldyMTQxeTVYQ045UGJMZERFQXN2cjZ4"
+            + "Z0NCN1l5bEhIQ1NBYmpGR3dOQ0R5NlVxN1h0VjZ6UHdIMXpGDQpFWENlS3Jm"
+            + "UUl5YXBXSEZ4V1VKajBMblFrY1RZM1FOR1huK0JuVk9EVTZDV3M1c3NoZktH"
+            + "RXZyVlQ1Z214V1NmDQp4OFlsdDgzY1dwUE1QZzg3VDlCaHVIbHQzazh2M2Ev"
+            + "NmRPbmF2dytOYTAyZExBaDBlNzZqcCtQUS9LK0pHZlBuDQphQjVVWURrZkd0"
+            + "em5uTTNBV01tY3VJK0o0ek5OMDZaa3ZnbDFsdEo2UU1qcnZEUFlSak9ndDlT"
+            + "cklpY1NmbEo4DQptVDdHWGRRaXJnQUNXc3g1QURBSklRK253TU1vNHlyTUtx"
+            + "SlFhNFFDMHhhT0QvdkdVcG9SaDQzT0FTZFp3c3YvDQpPWFlybmVJeVAwVCs4"
+            + "UUlEQVFBQm80RzNNSUcwTUQwR0ExVWRId1EyTURRd01xQXdvQzZHTEdoMGRI"
+            + "QTZMeTloDQpZM0poYVhvdWFXTndZbkpoYzJsc0xtZHZkaTVpY2k5TVExSmhZ"
+            + "M0poYVhvdVkzSnNNQklHQTFVZElBUUxNQWt3DQpCd1lGWUV3QkFRRXdIUVlE"
+            + "VlIwT0JCWUVGREpUVFlKNE9TWVB5T09KZkVMZXhDaHppK2hiTUI4R0ExVWRJ"
+            + "d1FZDQpNQmFBRklyNjhWZUVFUk0xa0VMNlYwbFVhUTJreFBBM01BNEdBMVVk"
+            + "RHdFQi93UUVBd0lCQmpBUEJnTlZIUk1CDQpBZjhFQlRBREFRSC9NQTBHQ1Nx"
+            + "R1NJYjNEUUVCQlFVQUE0SUJBUUJRUFNoZ1lidnFjaWV2SDVVb3ZMeXhkbkYr"
+            + "DQpFcjlOeXF1SWNkMnZ3Y0N1SnpKMkQ3WDBUcWhHQ0JmUEpVVkdBVWorS0NP"
+            + "SDFCVkgva1l1OUhsVHB1MGtKWFBwDQpBQlZkb2hJUERqRHhkbjhXcFFSL0Yr"
+            + "ejFDaWtVcldIMDR4eTd1N1p6UUpLSlBuR0loY1FpOElyRm1PYkllMEc3DQpY"
+            + "WTZPTjdPRUZxY21KTFFHWWdtRzFXMklXcytQd1JwWTdENGhLVEFoVjFSNkVv"
+            + "amE1L3BPcmVDL09kZXlQWmVxDQo1SUZTOUZZZk02U0Npd2hrK3l2Q1FHbVo0"
+            + "YzE5SjM0ZjVFYkRrK1NQR2tEK25EQ0E3L3VMUWNUMlJURE14SzBaDQpuZlo2"
+            + "Nm1Sc0ZjcXRGaWdScjVFcmtKZDdoUVV6eHNOV0VrNzJEVUFIcVgvNlNjeWtt"
+            + "SkR2V0plSUpqZlcNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==");
 
     private byte[] AC_RAIZ_ICPBRASIL = Base64.decode(
-          "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFdURDQ0E2Q2dBd0lC"
-        + "QWdJQkJEQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
-        + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
-        + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
-        + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
-        + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
-        + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
-        + "bHNaV2x5WVRBZUZ3MHdNVEV4TXpBeE1qVTRNREJhRncweE1URXhNekF5DQpN"
-        + "elU1TURCYU1JRzBNUXN3Q1FZRFZRUUdFd0pDVWpFVE1CRUdBMVVFQ2hNS1NV"
-        + "TlFMVUp5WVhOcGJERTlNRHNHDQpBMVVFQ3hNMFNXNXpkR2wwZFhSdklFNWhZ"
-        + "Mmx2Ym1Gc0lHUmxJRlJsWTI1dmJHOW5hV0VnWkdFZ1NXNW1iM0p0DQpZV05o"
-        + "YnlBdElFbFVTVEVSTUE4R0ExVUVCeE1JUW5KaGMybHNhV0V4Q3pBSkJnTlZC"
-        + "QWdUQWtSR01URXdMd1lEDQpWUVFERXloQmRYUnZjbWxrWVdSbElFTmxjblJw"
-        + "Wm1sallXUnZjbUVnVW1GcGVpQkNjbUZ6YVd4bGFYSmhNSUlCDQpJakFOQmdr"
-        + "cWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd1BNdWR3WC9odm0r"
-        + "VWgyYi9sUUFjSFZBDQppc2FtYUxrV2Rrd1A5L1MvdE9LSWdSckw2T3krWklH"
-        + "bE9VZGQ2dVl0azlNYS8zcFVwZ2NmTkFqMHZZbTVnc3lqDQpRbzllbXNjK3g2"
-        + "bTRWV3drOWlxTVpTQ0s1RVFrQXEvVXQ0bjdLdUxFMStnZGZ0d2RJZ3hmVXNQ"
-        + "dDRDeU5yWTUwDQpRVjU3S00yVVQ4eDVycm16RWpyN1RJQ0dwU1VBbDJnVnFl"
-        + "NnhhaWkrYm1ZUjFRcm1XYUJTQUc1OUxya3Jqcll0DQpiUmhGYm9VRGUxREsr"
-        + "NlQ4czVMNms4Yzhva3BiSHBhOXZlTXp0RFZDOXNQSjYwTVdYaDZhblZLbzFV"
-        + "Y0xjYlVSDQp5RWVOdlpuZVZSS0FBVTZvdXdkakR2d2xzYUt5ZEZLd2VkMFRv"
-        + "UTQ3Ym1VS2djbSt3VjNlVFJrMzZVT25Ud0lEDQpBUUFCbzRIU01JSFBNRTRH"
-        + "QTFVZElBUkhNRVV3UXdZRllFd0JBUUF3T2pBNEJnZ3JCZ0VGQlFjQ0FSWXNh"
-        + "SFIwDQpjRG92TDJGamNtRnBlaTVwWTNCaWNtRnphV3d1WjI5MkxtSnlMMFJR"
-        + "UTJGamNtRnBlaTV3WkdZd1BRWURWUjBmDQpCRFl3TkRBeW9EQ2dMb1lzYUhS"
-        + "MGNEb3ZMMkZqY21GcGVpNXBZM0JpY21GemFXd3VaMjkyTG1KeUwweERVbUZq"
-        + "DQpjbUZwZWk1amNtd3dIUVlEVlIwT0JCWUVGSXI2OFZlRUVSTTFrRUw2VjBs"
-        + "VWFRMmt4UEEzTUE4R0ExVWRFd0VCDQovd1FGTUFNQkFmOHdEZ1lEVlIwUEFR"
-        + "SC9CQVFEQWdFR01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQVpBNWMxDQpV"
-        + "L2hnSWg2T2NnTEFmaUpnRldwdm1EWldxbFYzMC9iSEZwajhpQm9iSlNtNXVE"
-        + "cHQ3VGlyWWgxVXhlM2ZRYUdsDQpZakplKzl6ZCtpelBSYkJxWFBWUUEzNEVY"
-        + "Y3drNHFwV3VmMWhIcmlXZmRyeDhBY3FTcXI2Q3VRRndTcjc1Rm9zDQpTemx3"
-        + "REFEYTcwbVQ3d1pqQW1RaG5aeDJ4SjZ3ZldsVDlWUWZTLy9KWWVJYzdGdWUy"
-        + "Sk5MZDAwVU9TTU1haUsvDQp0NzllbktOSEVBMmZ1cEgzdkVpZ2Y1RWg0YlZB"
-        + "TjVWb2hyVG02TVk1M3g3WFFaWnIxTUU3YTU1bEZFblNlVDB1DQptbE9BalIy"
-        + "bUFidlNNNVg1b1NaTnJtZXRkenlUajJmbENNOENDN01MYWIwa2tkbmdSSWxV"
-        + "QkdIRjEvUzVubVBiDQpLKzlBNDZzZDMzb3FLOG44DQotLS0tLUVORCBDRVJU"
-        + "SUZJQ0FURS0tLS0tDQo=");
+        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFdURDQ0E2Q2dBd0lC"
+            + "QWdJQkJEQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+            + "bHNaV2x5WVRBZUZ3MHdNVEV4TXpBeE1qVTRNREJhRncweE1URXhNekF5DQpN"
+            + "elU1TURCYU1JRzBNUXN3Q1FZRFZRUUdFd0pDVWpFVE1CRUdBMVVFQ2hNS1NV"
+            + "TlFMVUp5WVhOcGJERTlNRHNHDQpBMVVFQ3hNMFNXNXpkR2wwZFhSdklFNWhZ"
+            + "Mmx2Ym1Gc0lHUmxJRlJsWTI1dmJHOW5hV0VnWkdFZ1NXNW1iM0p0DQpZV05o"
+            + "YnlBdElFbFVTVEVSTUE4R0ExVUVCeE1JUW5KaGMybHNhV0V4Q3pBSkJnTlZC"
+            + "QWdUQWtSR01URXdMd1lEDQpWUVFERXloQmRYUnZjbWxrWVdSbElFTmxjblJw"
+            + "Wm1sallXUnZjbUVnVW1GcGVpQkNjbUZ6YVd4bGFYSmhNSUlCDQpJakFOQmdr"
+            + "cWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd1BNdWR3WC9odm0r"
+            + "VWgyYi9sUUFjSFZBDQppc2FtYUxrV2Rrd1A5L1MvdE9LSWdSckw2T3krWklH"
+            + "bE9VZGQ2dVl0azlNYS8zcFVwZ2NmTkFqMHZZbTVnc3lqDQpRbzllbXNjK3g2"
+            + "bTRWV3drOWlxTVpTQ0s1RVFrQXEvVXQ0bjdLdUxFMStnZGZ0d2RJZ3hmVXNQ"
+            + "dDRDeU5yWTUwDQpRVjU3S00yVVQ4eDVycm16RWpyN1RJQ0dwU1VBbDJnVnFl"
+            + "NnhhaWkrYm1ZUjFRcm1XYUJTQUc1OUxya3Jqcll0DQpiUmhGYm9VRGUxREsr"
+            + "NlQ4czVMNms4Yzhva3BiSHBhOXZlTXp0RFZDOXNQSjYwTVdYaDZhblZLbzFV"
+            + "Y0xjYlVSDQp5RWVOdlpuZVZSS0FBVTZvdXdkakR2d2xzYUt5ZEZLd2VkMFRv"
+            + "UTQ3Ym1VS2djbSt3VjNlVFJrMzZVT25Ud0lEDQpBUUFCbzRIU01JSFBNRTRH"
+            + "QTFVZElBUkhNRVV3UXdZRllFd0JBUUF3T2pBNEJnZ3JCZ0VGQlFjQ0FSWXNh"
+            + "SFIwDQpjRG92TDJGamNtRnBlaTVwWTNCaWNtRnphV3d1WjI5MkxtSnlMMFJR"
+            + "UTJGamNtRnBlaTV3WkdZd1BRWURWUjBmDQpCRFl3TkRBeW9EQ2dMb1lzYUhS"
+            + "MGNEb3ZMMkZqY21GcGVpNXBZM0JpY21GemFXd3VaMjkyTG1KeUwweERVbUZq"
+            + "DQpjbUZwZWk1amNtd3dIUVlEVlIwT0JCWUVGSXI2OFZlRUVSTTFrRUw2VjBs"
+            + "VWFRMmt4UEEzTUE4R0ExVWRFd0VCDQovd1FGTUFNQkFmOHdEZ1lEVlIwUEFR"
+            + "SC9CQVFEQWdFR01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQVpBNWMxDQpV"
+            + "L2hnSWg2T2NnTEFmaUpnRldwdm1EWldxbFYzMC9iSEZwajhpQm9iSlNtNXVE"
+            + "cHQ3VGlyWWgxVXhlM2ZRYUdsDQpZakplKzl6ZCtpelBSYkJxWFBWUUEzNEVY"
+            + "Y3drNHFwV3VmMWhIcmlXZmRyeDhBY3FTcXI2Q3VRRndTcjc1Rm9zDQpTemx3"
+            + "REFEYTcwbVQ3d1pqQW1RaG5aeDJ4SjZ3ZldsVDlWUWZTLy9KWWVJYzdGdWUy"
+            + "Sk5MZDAwVU9TTU1haUsvDQp0NzllbktOSEVBMmZ1cEgzdkVpZ2Y1RWg0YlZB"
+            + "TjVWb2hyVG02TVk1M3g3WFFaWnIxTUU3YTU1bEZFblNlVDB1DQptbE9BalIy"
+            + "bUFidlNNNVg1b1NaTnJtZXRkenlUajJmbENNOENDN01MYWIwa2tkbmdSSWxV"
+            + "QkdIRjEvUzVubVBiDQpLKzlBNDZzZDMzb3FLOG44DQotLS0tLUVORCBDRVJU"
+            + "SUZJQ0FURS0tLS0tDQo=");
 
     private byte[] schefer = Base64.decode(
-          "MIIEnDCCBAWgAwIBAgICIPAwDQYJKoZIhvcNAQEEBQAwgcAxCzAJBgNVBAYT"
-        + "AkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1MDA4IFdpZXNiYWRl"
-        + "bjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAYBgNVBAsTEVNDSFVG"
-        + "QSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBCZW51dHplciBTZXJ2"
-        + "aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
-        + "ZGUwHhcNMDQwMzMwMTEwODAzWhcNMDUwMzMwMTEwODAzWjCBnTELMAkGA1UE"
-        + "BhMCREUxCjAIBgNVBAcTASAxIzAhBgNVBAoTGlNIUyBJbmZvcm1hdGlvbnNz"
-        + "eXN0ZW1lIEFHMRwwGgYDVQQLExM2MDAvMDU5NDktNjAwLzA1OTQ5MRgwFgYD"
-        + "VQQDEw9TY2hldHRlciBTdGVmYW4xJTAjBgkqhkiG9w0BCQEWFlN0ZWZhbi5T"
-        + "Y2hldHRlckBzaHMuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJD0"
-        + "95Bi76fkAMjJNTGPDiLPHmZXNsmakngDeS0juzKMeJA+TjXFouhYh6QyE4Bl"
-        + "Nf18fT4mInlgLefwf4t6meIWbiseeTo7VQdM+YrbXERMx2uHsRcgZMsiMYHM"
-        + "kVfYMK3SMJ4nhCmZxrBkoTRed4gXzVA1AA8YjjTqMyyjvt4TAgMBAAGjggHE"
-        + "MIIBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDALBgNVHQ8EBAMC"
-        + "BNAwOQYJYIZIAYb4QgENBCwWKlplcnRpZmlrYXQgbnVyIGZ1ZXIgU0NIVUZB"
-        + "LU9ubGluZSBndWVsdGlnLjAdBgNVHQ4EFgQUXReirhBfg0Yhf6MsBWoo/nPa"
-        + "hGwwge0GA1UdIwSB5TCB4oAUf2UyCaBV9JUeG9lS1Yo6OFBUdEKhgcakgcMw"
-        + "gcAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1"
-        + "MDA4IFdpZXNiYWRlbjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAY"
-        + "BgNVBAsTEVNDSFVGQSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBC"
-        + "ZW51dHplciBTZXJ2aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNj"
-        + "aHVmYS1vbmxpbmUuZGWCAQAwIQYDVR0RBBowGIEWU3RlZmFuLlNjaGV0dGVy"
-        + "QHNocy5kZTAmBgNVHRIEHzAdgRt6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
-        + "ZGUwDQYJKoZIhvcNAQEEBQADgYEAWzZtN9XQ9uyrFXqSy3hViYwV751+XZr0"
-        + "YH5IFhIS+9ixNAu8orP3bxqTaMhpwoU7T/oSsyGGSkb3fhzclgUADbA2lrOI"
-        + "GkeB/m+FArTwRbwpqhCNTwZywOp0eDosgPjCX1t53BB/m/2EYkRiYdDGsot0"
-        + "kQPOVGSjQSQ4+/D+TM8=");
+        "MIIEnDCCBAWgAwIBAgICIPAwDQYJKoZIhvcNAQEEBQAwgcAxCzAJBgNVBAYT"
+            + "AkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1MDA4IFdpZXNiYWRl"
+            + "bjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAYBgNVBAsTEVNDSFVG"
+            + "QSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBCZW51dHplciBTZXJ2"
+            + "aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+            + "ZGUwHhcNMDQwMzMwMTEwODAzWhcNMDUwMzMwMTEwODAzWjCBnTELMAkGA1UE"
+            + "BhMCREUxCjAIBgNVBAcTASAxIzAhBgNVBAoTGlNIUyBJbmZvcm1hdGlvbnNz"
+            + "eXN0ZW1lIEFHMRwwGgYDVQQLExM2MDAvMDU5NDktNjAwLzA1OTQ5MRgwFgYD"
+            + "VQQDEw9TY2hldHRlciBTdGVmYW4xJTAjBgkqhkiG9w0BCQEWFlN0ZWZhbi5T"
+            + "Y2hldHRlckBzaHMuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJD0"
+            + "95Bi76fkAMjJNTGPDiLPHmZXNsmakngDeS0juzKMeJA+TjXFouhYh6QyE4Bl"
+            + "Nf18fT4mInlgLefwf4t6meIWbiseeTo7VQdM+YrbXERMx2uHsRcgZMsiMYHM"
+            + "kVfYMK3SMJ4nhCmZxrBkoTRed4gXzVA1AA8YjjTqMyyjvt4TAgMBAAGjggHE"
+            + "MIIBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDALBgNVHQ8EBAMC"
+            + "BNAwOQYJYIZIAYb4QgENBCwWKlplcnRpZmlrYXQgbnVyIGZ1ZXIgU0NIVUZB"
+            + "LU9ubGluZSBndWVsdGlnLjAdBgNVHQ4EFgQUXReirhBfg0Yhf6MsBWoo/nPa"
+            + "hGwwge0GA1UdIwSB5TCB4oAUf2UyCaBV9JUeG9lS1Yo6OFBUdEKhgcakgcMw"
+            + "gcAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1"
+            + "MDA4IFdpZXNiYWRlbjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAY"
+            + "BgNVBAsTEVNDSFVGQSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBC"
+            + "ZW51dHplciBTZXJ2aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNj"
+            + "aHVmYS1vbmxpbmUuZGWCAQAwIQYDVR0RBBowGIEWU3RlZmFuLlNjaGV0dGVy"
+            + "QHNocy5kZTAmBgNVHRIEHzAdgRt6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+            + "ZGUwDQYJKoZIhvcNAQEEBQADgYEAWzZtN9XQ9uyrFXqSy3hViYwV751+XZr0"
+            + "YH5IFhIS+9ixNAu8orP3bxqTaMhpwoU7T/oSsyGGSkb3fhzclgUADbA2lrOI"
+            + "GkeB/m+FArTwRbwpqhCNTwZywOp0eDosgPjCX1t53BB/m/2EYkRiYdDGsot0"
+            + "kQPOVGSjQSQ4+/D+TM8=");
 
     // circular dependency certificates
     private static final byte[] circCA = Base64.decode(
         "MIIDTzCCAjegAwIBAgIDARAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
-      + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
-      + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
-      + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
-      + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3WyWDwcM58aU"
-      + "hPX4ueI1mwETt3WdQtMfIdRiCXeBrjCkYCc7nIgCmGbnfTzXSplHRgKColWh"
-      + "q/Z+1rHYayje1gjAEU2+4/r1P2pnBmPgquDuguktCIbDtCcGZu0ylyKeHh37"
-      + "aeIKzkcmRSLRzvGf/eO3RdFksrvaPaSjqCVfGRXVDKK2uftE8rIFJE+bCqow"
-      + "6+WiaAaDDiJaSJPuu5hC1NA5jw0/BFodlCuAvl1GJ8A+TICkYWcSpKS9bkSC"
-      + "0i8xdGbSSk94shA1PdDvRdFMfFys8g4aupBXV8yqqEAUkBYmOtZSJckc3W4y"
-      + "2Gx53y7vY07Xh63mcgtJs2T82WJICwIDAQABo2AwXjAdBgNVHQ4EFgQU8c/P"
-      + "NNJaL0srd9SwHwgtvwPB/3cwDgYDVR0PAQH/BAQDAgIEMBkGA1UdIAQSMBAw"
-      + "DgYMKoF6AUcDBwgAAAABMBIGA1UdEwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcN"
-      + "AQEFBQADggEBAHRjYDPJKlfUzID0YzajZpgR/i2ngJrJqYeaWCmwzBgNUPad"
-      + "uBKSGHmPVg21sfULMSnirnR+e90i/D0EVzLwQzcbjPDD/85rp9QDCeMxqqPe"
-      + "9ZCHGs2BpE/HOQMP0QfQ3/Kpk7SvOH/ZcpIf6+uE6lLBQYAGs5cxvtTGOzZk"
-      + "jCVFG+TrAnF4V5sNkn3maCWiYLmyqcnxtKEFSONy2bYqqudx/dBBlRrDbRfZ"
-      + "9XsCBdiXAHY1hFHldbfDs8rslmkXJi3fJC028HZYB6oiBX/JE7BbMk7bRnUf"
-      + "HSpP7Sjxeso2SY7Yit+hQDVAlqTDGmh6kLt/hQMpsOMry4vgBL6XHKw=");
+            + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
+            + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
+            + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
+            + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3WyWDwcM58aU"
+            + "hPX4ueI1mwETt3WdQtMfIdRiCXeBrjCkYCc7nIgCmGbnfTzXSplHRgKColWh"
+            + "q/Z+1rHYayje1gjAEU2+4/r1P2pnBmPgquDuguktCIbDtCcGZu0ylyKeHh37"
+            + "aeIKzkcmRSLRzvGf/eO3RdFksrvaPaSjqCVfGRXVDKK2uftE8rIFJE+bCqow"
+            + "6+WiaAaDDiJaSJPuu5hC1NA5jw0/BFodlCuAvl1GJ8A+TICkYWcSpKS9bkSC"
+            + "0i8xdGbSSk94shA1PdDvRdFMfFys8g4aupBXV8yqqEAUkBYmOtZSJckc3W4y"
+            + "2Gx53y7vY07Xh63mcgtJs2T82WJICwIDAQABo2AwXjAdBgNVHQ4EFgQU8c/P"
+            + "NNJaL0srd9SwHwgtvwPB/3cwDgYDVR0PAQH/BAQDAgIEMBkGA1UdIAQSMBAw"
+            + "DgYMKoF6AUcDBwgAAAABMBIGA1UdEwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcN"
+            + "AQEFBQADggEBAHRjYDPJKlfUzID0YzajZpgR/i2ngJrJqYeaWCmwzBgNUPad"
+            + "uBKSGHmPVg21sfULMSnirnR+e90i/D0EVzLwQzcbjPDD/85rp9QDCeMxqqPe"
+            + "9ZCHGs2BpE/HOQMP0QfQ3/Kpk7SvOH/ZcpIf6+uE6lLBQYAGs5cxvtTGOzZk"
+            + "jCVFG+TrAnF4V5sNkn3maCWiYLmyqcnxtKEFSONy2bYqqudx/dBBlRrDbRfZ"
+            + "9XsCBdiXAHY1hFHldbfDs8rslmkXJi3fJC028HZYB6oiBX/JE7BbMk7bRnUf"
+            + "HSpP7Sjxeso2SY7Yit+hQDVAlqTDGmh6kLt/hQMpsOMry4vgBL6XHKw=");
 
     private static final byte[] circCRLCA = Base64.decode(
-       "MIIDXDCCAkSgAwIBAgIDASAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
-     + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
-     + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
-     + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
-     + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfEcFK0g7Kfo"
-     + "o5f2IBF7VEd/AG+RVGSds0Yg+u2kNYu4k04HR/+tOdBQtJvyr4W5jrQKsC5X"
-     + "skeFWMyWaFKzAjZDWB52HWp/kiMivGcxnYDuYf5piukSC+d2+vL8YaAphDzV"
-     + "HPnxEKqoM/J66uUussDTqfcL3JC/Bc7kBwn4srrsZOsamMWTQQtEqVQxNN7A"
-     + "ROSRsdiTt3hMOKditc9/NBNmjZWxgc7Twr/SaZ8CfN5wf2wuOl23knWL0QsJ"
-     + "0lSMBSBTzTcfAke4/jIT7d4nVMp3t7dsna8rt56pFK4wpRFGuCt+1P5gi51x"
-     + "xVSdI+JoNXv6zGO4o8YVaRpC5rQeGQIDAQABo20wazAfBgNVHSMEGDAWgBTx"
-     + "z8800lovSyt31LAfCC2/A8H/dzAdBgNVHQ4EFgQUGa3SbBrJx/wa2MQwhWPl"
-     + "dwLw1+IwDgYDVR0PAQH/BAQDAgECMBkGA1UdIAQSMBAwDgYMKoF6AUcDBwgA"
-     + "AAABMA0GCSqGSIb3DQEBBQUAA4IBAQAPDpYe2WPYnXTLsXSIUREBNMLmg+/7"
-     + "4Yhq9uOm5Hb5LVkDuHoEHGfmpXXEvucx5Ehu69hw+F4YSrd9wPjOiG8G6GXi"
-     + "RcrK8nE8XDvvV+E1HpJ7NKN4fSAoSb+0gliiq3aF15bvXP8nfespdd/x1xWQ"
-     + "mpYCx/mJeuqONQv2/D/7hfRKYoDBaAkWGodenPFPVs6FxwnEuH2R+KWCUdA9"
-     + "L04v8JBeL3kZiALkU7+DCCm7A0imUAgeeArbAbfIPu6eDygm+XndZ9qi7o4O"
-     + "AntPxrqbeXFIbDrQ4GV1kpxnW+XpSGDd96SWKe715gxkkDBppR5IKYJwRb6O"
-     + "1TRQIf2F+muQ");
+        "MIIDXDCCAkSgAwIBAgIDASAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
+            + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
+            + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
+            + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
+            + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfEcFK0g7Kfo"
+            + "o5f2IBF7VEd/AG+RVGSds0Yg+u2kNYu4k04HR/+tOdBQtJvyr4W5jrQKsC5X"
+            + "skeFWMyWaFKzAjZDWB52HWp/kiMivGcxnYDuYf5piukSC+d2+vL8YaAphDzV"
+            + "HPnxEKqoM/J66uUussDTqfcL3JC/Bc7kBwn4srrsZOsamMWTQQtEqVQxNN7A"
+            + "ROSRsdiTt3hMOKditc9/NBNmjZWxgc7Twr/SaZ8CfN5wf2wuOl23knWL0QsJ"
+            + "0lSMBSBTzTcfAke4/jIT7d4nVMp3t7dsna8rt56pFK4wpRFGuCt+1P5gi51x"
+            + "xVSdI+JoNXv6zGO4o8YVaRpC5rQeGQIDAQABo20wazAfBgNVHSMEGDAWgBTx"
+            + "z8800lovSyt31LAfCC2/A8H/dzAdBgNVHQ4EFgQUGa3SbBrJx/wa2MQwhWPl"
+            + "dwLw1+IwDgYDVR0PAQH/BAQDAgECMBkGA1UdIAQSMBAwDgYMKoF6AUcDBwgA"
+            + "AAABMA0GCSqGSIb3DQEBBQUAA4IBAQAPDpYe2WPYnXTLsXSIUREBNMLmg+/7"
+            + "4Yhq9uOm5Hb5LVkDuHoEHGfmpXXEvucx5Ehu69hw+F4YSrd9wPjOiG8G6GXi"
+            + "RcrK8nE8XDvvV+E1HpJ7NKN4fSAoSb+0gliiq3aF15bvXP8nfespdd/x1xWQ"
+            + "mpYCx/mJeuqONQv2/D/7hfRKYoDBaAkWGodenPFPVs6FxwnEuH2R+KWCUdA9"
+            + "L04v8JBeL3kZiALkU7+DCCm7A0imUAgeeArbAbfIPu6eDygm+XndZ9qi7o4O"
+            + "AntPxrqbeXFIbDrQ4GV1kpxnW+XpSGDd96SWKe715gxkkDBppR5IKYJwRb6O"
+            + "1TRQIf2F+muQ");
 
     private static final byte[] circCRL = Base64.decode(
         "MIIB1DCBvQIBATANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGUjEQMA4G"
-      + "A1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9OWU1FFw0xMDAx"
-      + "MDcwMzAwMTVaFw0xMDAxMTMwMzAwMTVaMACgTjBMMB8GA1UdIwQYMBaAFBmt"
-      + "0mwaycf8GtjEMIVj5XcC8NfiMAsGA1UdFAQEAgILgzAcBgNVHRIEFTATgRFh"
-      + "Yy1naXBAZ2lwLWNwcy5mcjANBgkqhkiG9w0BAQUFAAOCAQEAtF1DdFl1MQvf"
-      + "vNkbrCPuppNYcHen4+za/ZDepKuwHsH/OpKuaDJc4LndRgd5IwzfpCHkQGzt"
-      + "shK50bakN8oaYJgthKIOIJzR+fn6NMjftfR2a27Hdk2o3eQXRHQ360qMbpSy"
-      + "qPb3WfuBhxO2/DlLChJP+OxZIHtT/rNYgE0tlIv7swYi81Gq+DafzaZ9+A5t"
-      + "I0L2Gp/NUDsp5dF6PllAGiXQzl27qkcu+r50w+u0gul3nobXgbwPcMSYuWUz"
-      + "1lhA+uDn/EUWV4RSiJciCGSS10WCkFh1/YPo++mV15KDB0m+8chscrSu/bAl"
-      + "B19LxL/pCX3qr5iLE9ss3olVImyFZg==");
+            + "A1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9OWU1FFw0xMDAx"
+            + "MDcwMzAwMTVaFw0xMDAxMTMwMzAwMTVaMACgTjBMMB8GA1UdIwQYMBaAFBmt"
+            + "0mwaycf8GtjEMIVj5XcC8NfiMAsGA1UdFAQEAgILgzAcBgNVHRIEFTATgRFh"
+            + "Yy1naXBAZ2lwLWNwcy5mcjANBgkqhkiG9w0BAQUFAAOCAQEAtF1DdFl1MQvf"
+            + "vNkbrCPuppNYcHen4+za/ZDepKuwHsH/OpKuaDJc4LndRgd5IwzfpCHkQGzt"
+            + "shK50bakN8oaYJgthKIOIJzR+fn6NMjftfR2a27Hdk2o3eQXRHQ360qMbpSy"
+            + "qPb3WfuBhxO2/DlLChJP+OxZIHtT/rNYgE0tlIv7swYi81Gq+DafzaZ9+A5t"
+            + "I0L2Gp/NUDsp5dF6PllAGiXQzl27qkcu+r50w+u0gul3nobXgbwPcMSYuWUz"
+            + "1lhA+uDn/EUWV4RSiJciCGSS10WCkFh1/YPo++mV15KDB0m+8chscrSu/bAl"
+            + "B19LxL/pCX3qr5iLE9ss3olVImyFZg==");
 
     private void checkCircProcessing()
         throws Exception
@@ -218,16 +272,16 @@
 
         Date validDate = new Date(crl.getThisUpdate().getTime() + 60 * 60 * 1000);
 
-            //validating path
+        //validating path
         List certchain = new ArrayList();
 
         certchain.add(crlCaCert);
-        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
 
         Set trust = new HashSet();
         trust.add(new TrustAnchor(caCert, null));
 
-        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
         //PKIXParameters param = new PKIXParameters(trust);
 
         PKIXBuilderParameters param = new PKIXBuilderParameters(trust, null);
@@ -246,10 +300,10 @@
     {
         CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
 
-        X509Certificate   root = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("qvRooCa3.crt"));
-        X509Certificate   ca1 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaRoot1.crt"));
-        X509Certificate   ca2 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEmail1.crt"));
-        X509Certificate   ee = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEE.crt"));
+        X509Certificate root = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("qvRooCa3.crt"));
+        X509Certificate ca1 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaRoot1.crt"));
+        X509Certificate ca2 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEmail1.crt"));
+        X509Certificate ee = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEE.crt"));
 
         List certchain = new ArrayList();
         certchain.add(ee);
@@ -259,7 +313,7 @@
         Set trust = new HashSet();
         trust.add(new TrustAnchor(root, null));
 
-        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
         PKIXParameters param = new PKIXParameters(trust);
         param.setRevocationEnabled(false);
         param.setDate(new Date(0x156445410b4L)); // around 1st August 2016
@@ -270,7 +324,7 @@
         param.addCertPathChecker(checker);
 
         PKIXCertPathValidatorResult result =
-            (PKIXCertPathValidatorResult) cpv.validate(cp, param);
+            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
     }
 
     public void testEmptyPath()
@@ -285,11 +339,11 @@
         CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
 
         List certchain = new ArrayList();
-        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
         Set trust = new HashSet();
         trust.add(new TrustAnchor(rootCert, null));
 
-        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
         PKIXParameters param = new PKIXParameters(trust);
         param.addCertStore(store);
         MyChecker checker = new MyChecker();
@@ -313,7 +367,7 @@
     {
         CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
 
-            // initialise CertStore
+        // initialise CertStore
         X509Certificate rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.rootCertBin));
         X509Certificate interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.interCertBin));
         X509Certificate finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.finalCertBin));
@@ -328,15 +382,16 @@
         CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
         CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
         Date validDate = new Date(rootCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
-            //validating path
+        //validating path
         List certchain = new ArrayList();
         certchain.add(finalCert);
         certchain.add(interCert);
-        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+      
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
         Set trust = new HashSet();
         trust.add(new TrustAnchor(rootCert, null));
 
-        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
         PKIXParameters param = new PKIXParameters(trust);
         param.addCertStore(store);
         param.setDate(validDate);
@@ -344,7 +399,7 @@
         param.addCertPathChecker(checker);
 
         PKIXCertPathValidatorResult result =
-            (PKIXCertPathValidatorResult) cpv.validate(cp, param);
+            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
         PolicyNode policyTree = result.getPolicyTree();
         PublicKey subjectPublicKey = result.getPublicKey();
 
@@ -352,22 +407,41 @@
         {
             fail("checker not evaluated for each certificate");
         }
-        
+
         if (!subjectPublicKey.equals(finalCert.getPublicKey()))
         {
             fail("wrong public key returned");
         }
 
+        isTrue(result.getTrustAnchor().getTrustedCert().equals(rootCert));
+
+        // try a path with trust anchor included.
+        certchain.clear();
+        certchain.add(finalCert);
+        certchain.add(interCert);
+        certchain.add(rootCert);
+
+        cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+
+        cpv = CertPathValidator.getInstance("PKIX", "BC");
+        param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+
+        result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+
+        isTrue(result.getTrustAnchor().getTrustedCert().equals(rootCert));
+        
         //
         // invalid path containing a valid one test
         //
         try
         {
-                // initialise CertStore
+            // initialise CertStore
             rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_RAIZ_ICPBRASIL));
             interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_PR));
             finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(schefer));
-    
+
             list = new ArrayList();
             list.add(rootCert);
             list.add(interCert);
@@ -377,24 +451,25 @@
             store = CertStore.getInstance("Collection", ccsp);
             validDate = new Date(finalCert.getNotBefore().getTime() + 60 * 60 * 1000);
 
-                //validating path
+            //validating path
             certchain = new ArrayList();
             certchain.add(finalCert);
             certchain.add(interCert);
-            cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+     
+            cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
             trust = new HashSet();
             trust.add(new TrustAnchor(rootCert, null));
 
-            cpv = CertPathValidator.getInstance("PKIX","BC");
+            cpv = CertPathValidator.getInstance("PKIX", "BC");
             param = new PKIXParameters(trust);
             param.addCertStore(store);
             param.setRevocationEnabled(false);
             param.setDate(validDate);
 
-            result =(PKIXCertPathValidatorResult) cpv.validate(cp, param);
+            result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
             policyTree = result.getPolicyTree();
             subjectPublicKey = result.getPublicKey();
-            
+
             fail("Invalid path validated");
         }
         catch (Exception e)
@@ -403,16 +478,17 @@
                 && e.getMessage().startsWith("Could not validate certificate signature.")))
             {
                 fail("unexpected exception", e);
-            } 
+            }
         }
 
         checkCircProcessing();
         checkPolicyProcessingAtDomainMatch();
         validateWithExtendedKeyUsage();
         testEmptyPath();
+        checkInvalidCertPath();
     }
 
-        // extended key usage chain
+    // extended key usage chain
     static byte[] extEE = Base64.decode("MIICtDCCAh2gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBkjELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxKDAmBgNVBAsMH0JvdW5jeSBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUxLzAtBgkqhkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTE1MDMyNDAzNTEwOVoXDTE1MDUyMzAzNTEwOVowgZYxCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIwEAYDVQQHDAlNZWxib3VybmUxGDAWBgNVBAMMD0VyaWMgSC4gRWNoaWRuYTEvMC0GCSqGSIb3DQEJARYgZmVlZGJhY2stY3J5cHRvQGJvdW5jeWNhc3RsZS5vcmcwWjANBgkqhkiG9w0BAQEFAANJADBGAkEAtKfkYXBXTxapcIKyK+WLaipil5hBm+EocqS9umJs+umQD3ar+xITnc5d5WVk+rK2VDFloEDGBoh0IOM9ke1+1wIBEaNaMFgwHQYDVR0OBBYEFNBs7G01g7xVEhsMyz7+1yamFmRoMB8GA1UdIwQYMBaAFJQIM28yQPeHN9rRIKrtLqduyckeMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBBQUAA4GBAICrsNswvaXFMreUHHRHrhU4QqPOds8XJe0INx3v/5TfyjPPDMihMEm8WtWbVpFgFAqUQoZscf8cE/SO5375unYFgxrK+p2/je9E82VLF4Xb0cWizjQoWvvTmvFYjt43cGGXgySFLTrW87ju9uNFr/l4W9xvI0hoLI96vEW7Ccho");
     static byte[] extCA = Base64.decode("MIIDIzCCAoygAwIBAgIBAjANBgkqhkiG9w0BAQUFADBcMQswCQYDVQQGEwJBVTEoMCYGA1UECgwfVGhlIExlZ2lvbiBvZiB0aGUgQm91bmN5IENhc3RsZTEjMCEGA1UECwwaQm91bmN5IFByaW1hcnkgQ2VydGlmaWNhdGUwHhcNMTUwMzI0MDM1MTA5WhcNMTUwNTIzMDM1MTA5WjCBkjELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxKDAmBgNVBAsMH0JvdW5jeSBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUxLzAtBgkqhkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCN4NETxec2lpyNKwR6JD+P4Y7a1kzenoQtNmkjDKSG98/d4fjuxU0ZBf/wSsyF5hCT4YDK3GzqQH8ZPUS7DpRJuNu0l4TNnjYmDDngapRymZeMbtgwByTohxmM/t4g8/veZY+ivQeL6Uajkr00nytJxIbiDEBViOMGcGyQFzCOaQIDAP//o4G9MIG6MB0GA1UdDgQWBBSUCDNvMkD3hzfa0SCq7S6nbsnJHjCBhAYDVR0jBH0we4AUwDYZB63EiJeoXnJvawnr5ebxKVyhYKReMFwxCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMSMwIQYDVQQLDBpCb3VuY3kgUHJpbWFyeSBDZXJ0aWZpY2F0ZYIBATASBgNVHRMBAf8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GBAJqUlDjse7Og+7qkkFsiXHzQ8FxT82hzfcji8W7bPwZddCPBEluxCJiJBPYXWsLvwo6BEmCDzT9lLQZ+QZyL1fVbOVHiI24hAalbEBEIrEO4GXMD9spqRQ5yoTJ8CgZHTPo0rJkH/ebprp0YHtahVF440zBOvuLM0QTYpERgO2Oe");
     static byte[] extTrust = Base64.decode("MIICJTCCAY4CAQEwDQYJKoZIhvcNAQEFBQAwXDELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxIzAhBgNVBAsMGkJvdW5jeSBQcmltYXJ5IENlcnRpZmljYXRlMB4XDTE1MDMyNDAzNTEwOVoXDTE1MDUyMzAzNTEwOVowXDELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxIzAhBgNVBAsMGkJvdW5jeSBQcmltYXJ5IENlcnRpZmljYXRlMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCyWdLW5ienaMlL42Fkwtn8edl6q5JTFA5b8XdRGXcx1vdUDSUJ57n/7gpwpuJtVuktLt1/hauoVgC2kInzX2vb88KY4FhCU12fBk5rA5HLfTBuCi0gxN+057SalkC96ibBCtacPwUAfOJRPO5Ez+AZmOYrbDY30/wDkQebJu421QIBETANBgkqhkiG9w0BAQUFAAOBgQCDNfqQnQbbmnGzZTl7ccWIyw7SPzWnijpKsQpuRNGkoXfkCcuQLZudytEFZGEL0cycNBnierjJWAn78zGpCQtab01r1GwytRMYz8qO5IIrhsJ4XNafNypYZbi0WtPa07UCQp8tipMbfQNLzSkvkIAaD5IfhdaWKLrSQJwmGg7YAg==");
@@ -434,11 +510,11 @@
         CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
         CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
         Date validDate = new Date(rootCert.getNotBefore().getTime() + 60 * 60 * 1000);
-            //validating path
+        //validating path
         List certchain = new ArrayList();
         certchain.add(finalCert);
         certchain.add(interCert);
-        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
         Set trust = new HashSet();
         trust.add(new TrustAnchor(rootCert, null));
 
@@ -448,7 +524,84 @@
         param.setDate(validDate);
         param.setRevocationEnabled(false);
 
-        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, param);
+        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+    }
+
+    // invalid EE certificate
+    static byte[] extInvEE = Base64.decode("MIICJjCCAY+gAwIBAAIGAV3Y0TnDMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTIyMzM5MzJaFw0xNzA4MTMwMDA5MzdaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuOcqkp2+HBCuwRDwfR7kkUYXMdhScDG8m6A3Af6hpG86nAimNoVIQe3REaQ6IO0XSdd13rjjRwIXsUFLsrQhQJczF5JeyWXcaYqZyNNbUwFuLeSqOsLS63ltjOJYqOJRxY03Cr//baGWvxGXcRvHoZkg1nEXPcMZhgsy/9JxVoUCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBSPMqzNmTdyjQmr9W1TSDW1h0ZzFaEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2NE5wjAdBgNVHQ4EFgQUC1rtYrQdQkA3CLTeV1kbVIdysKQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAGr841G7E84Ow9+fFGW1zzXeTRfxsafdT/bHXCS75bjF2YPitKLcRLkm92VPxANRXIpmt++3iU/oduWqkLsfXnfTGmCwtjj/XrCvkCBQ4GONwmegltJEThMud0XOEB1UN6tfTINfLYpbyfOdE/wLy4Rte0t43aOTTOBo+/SapYOE=");
+    static byte[] extInvCA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3Y0TnCMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMjIzMzkzMloXDTE3MDgxMzAwMDkzN1owETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7Qd/cTP5S0GoPcomcZU5QlJcb1uWydvmQx3U6p4/KOZBhk6JXQeSzT8QZ/gd+9vfosA62SEX+dq7MvxxzeERxdIsVU0zZ1TrYNxlQjnYXiYRVXBczowsxseQ9oSGD94Y4buhrMAltmIHijdzGRVMY41FZmWqNXqsEwQXj6ULX+QIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFAbfd2S3aiwFww3/0ocLa6ULQjJMoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3Y0TnBMB0GA1UdDgQWBBSPMqzNmTdyjQmr9W1TSDW1h0ZzFTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQCnmxQYy6LnvRSMxkTsGIQa4LB51O8skbWc4KYVDfcvTYQuvn6rE/ZoYf82jKXJzXksffanfjn/b38l4l8hwAcBQ8we9yjCkjO8OVDUlYiSGYUhH2ZJrl2+K2Z6wpakZ9Lz3pZ/PSS1FIsVd4I1jkexAdAm1+uMlfWXVt/uTZx98w==");
+    static byte[] extInvTrust = Base64.decode("MIIBmjCCAQMCBgFd2NE5wTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTIyMzM5MzJaFw0xNzA4MTMwMDA5MzdaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8U3p6Y9ah0yQ58wpI3H6vQPMdhN6Hh+zuiNzwX3AIpEspUFTfqXJ6EIhqh/EraDnLnoFBajzihwS1y6a+ZyXYKa5pxbFsslmzms+ozcTaJ4mSMiC+DHbGYdOAEzwx2nsEt7UKyrlnl5h2kQFusUPmnXXEorIxhpS2Lul+zEBo1wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBABClwXaJ8S66GmeySf1thOPc1GxIHuubezIjcBbECLZQqn7iwuzp+eft1vtnqNP7BWM1xBZkSe+/2xUsArc1rb1ffZHF3k92+WLbpDh3+NsQIod/91HRWUuu/S2g9oMK4b7BH8JrmBgy3ewtpNZwOaKF613GPCeGv3ya5Z24vBu+");
+
+    static byte[] extInvV2Trust = Base64.decode("MIIBmjCCAQMCBgFd2NhVgTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTIyMzQ3MThaFw0xNzA4MTMwMDE3MjNaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQaASWM5avAAJ57eHQ2zQ0k/mAiYSOkRKDLEzptDPYfzuQtTdAlBPn7tsYx+Ylge4YQwtx5bQZbc3apinBK9tn+c++go0kUF1cec2bacYyFokSP2r48j6ZhlY4MYGfrvfWHULrG2JL2BMeuZVP+wiqXktXCEKVG1fh1m6RY0TJPwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAC9mXO2i2vltBZZa7RMkizvhzhsehDHbEqvJd2aoWE9JG4sDo2tiIVN5vbq9EWLZVga3ejFzmQ+FI1Ty0xX3fwDgvUyxsveGTs40xwA9TEgVk1KNTQQs+sLE9rRB7L0giKn2DDmHFsOPL1KwxdzqD7vYhJr5av3eAsJpMxF+Anyg");
+    static byte[] extInvV2CA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3Y2FWCMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMjIzNDcxOFoXDTE3MDgxMzAwMTcyM1owETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgb8h3h9d/FzhIc+PMbF0vwdiDKw7N3dNyY6TrmzCMC1mYDXSKmxxDwNKZCKj6VSNfbTDqxYKlZMoGVT8Cl/iE/+XEhOKYLv73rzTqzdMizqcQTCvwps1enGxI5wPBYKGCMWrpJui5RWV9wH6hMvmzSSZq7bdWTvc/pIltCpIj8wIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFMOcs/uWpVOkGRQJrVIp6cN6tCJQoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3Y2FWBMB0GA1UdDgQWBBTsZ2B5JbgKm9/up2hOcYVyOaM1SjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQBI8J1bKh/e+uEZtfngKMADS1PSHztAPFFKXgeIfYeJDRznnamtbheensdxrA+aoriJbJfHxmjecr4xA8+s0uN9GPtQ3+ad1K5Sg6mfzsXtNPf3xa9y0pIWOGZavr1s/QugoPLQxEiuHrvkHX5+sZlx47KoBQJ8LBRmJydeSvxz1g==");
+    static byte[] extInvV2EE = Base64.decode("MIICJjCCAY+gAwIBAQIGAV3Y2FWDMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTIyMzQ3MThaFw0xNzA4MTMwMDE3MjNaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzWXxtMpnjz8Q1qTdwpB66W2D0vEHhqow2PTsvfQdENL4AFESE1C7Cj3lLBTei1vRHCnpM0jdNghBW8k/u2b2tqeeWLBqwul0tEGbjtUwkYV2WgtTGmiYZZFfMH35HIvqlZMwIIdZqz4lEdkPiAPEUOELvycpVDFnWjF0qah5LqsCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBTsZ2B5JbgKm9/up2hOcYVyOaM1SqEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2NhVgjAdBgNVHQ4EFgQUfeKdn63Gmlkub8m8bwjqJook5ywwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAco35KYLE683l53J6V1q2tcMV3EpM39tifkL7Kl38oX9d3SGiKkEO6YFeQekRyto0Z91mPq7Pe/oOfDrfsY3r9KX7oqnhOKBnnR/58atM9udVLvuLfCJpxiroAldSkhRKvHG5MrFwZyDcVkTZF4GDrP6bojp32wVfU5EYkfwcJN8=");
+
+    static byte[] extInvVersionTrust = Base64.decode("MIIBmjCCAQMCBgFd2RZiPjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTMwMDU1MDRaFw0xNzA4MTMwMTI1MDlaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPn6zObnrjGPUb0ozc3MCOOcHwtQABZmUHtB1jxRXWwYXKo+iTms2wJjDS5fhz2UmUptsbdFwPdvT2t7K8cpaZBcovC3jLvEAMmjO+nU3FQrdopZ6MhBjpgIezAvJ9LUhrYctqUJzfViqtLl0dL+YRjaVdfCz5z0iZn4rv2VSf3QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAHtS9RjMIRzeEpH9MKIaMLR7wVb55MYW7E2CVuIbsHmT+KyDBFsYbAylgc76cH1b8F53ECygS9jCpzfKtO61WVPPlUhsL13i2XbzCtj8DSPXaW5pgvpwClQZ+dpGFz8D/MYLSdjTdls8dbhJ5O08ckSKcrIGHcF90oeepVXOmiTw");
+    static byte[] extInvVersionCA = Base64.decode(    "MIICKDCCAZGgAwIBAgIGAV3ZFmI/MA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMzAwNTUwNFoXDTE3MDgxMzAxMjUwOVowETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChlaZhX9/eHmtfravHzs/E0g6ZhWTmD9aNNvuuz/GCBF9AMS6QQCGVhEzxESn0gLzs1bM/9M/EaylHS3Ecvi6QYdkrTKRDj38FDzrDhiPlM3TxY0XuUQ3Py590k8yZDcuEeVEQeoUx83qOnO7o/cL+vECfMj9ImYFFgY5sMcKkVQIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFAfTyJtmNkinVjfd7/2Giy6krDTpoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3ZFmI+MB0GA1UdDgQWBBQkMq+wajXvKQaJtSdpvDJn77bU9zASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQAqmKtykmixemAvppo2tTmekLsL93+/DMR+oz1iK2rjhqYzEF1/pM9VUyG+Ni1924U8tzGbXv2lL3MiToRSyjO50HHfnE7PfOvNiTUj73PTn27tPl03eWO3CtsOTGxtE2vpNyXyFXm4SFZlSicOXE0o/kUrNGVYvnjs/jjcNlPiHQ==");
+    static byte[] extInvVersionEE = Base64.decode(   "MIICJjCCAY+gAwIBBQIGAV3ZFmJAMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTMwMDU1MDRaFw0xNzA4MTMwMTI1MDlaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6PXdCOvE33+mMcal/rC+I7zdJqcc6OBhn+Lyku29TRcYplMA5mkh7WkjLtRYBUAzHukN/GXb1Mo+dFkvCnKO/l4gLWyVuf23rL6iELt8X1KVJdJlrDElCmTgl6lA0Omq7QhNrsv5Vdk7mK2mbJzl0bj4fcu5dc23nQXEskmGrZsCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBQkMq+wajXvKQaJtSdpvDJn77bU96EXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2RZiPzAdBgNVHQ4EFgQU3Nw/DFxNqK1fRhc/W8W4o3mkCHQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAjMTiKgLC2Kb5+elvfD/+CM8pNeLt5Y43sMSTpgIrebdWPA2hyvjvW/upsIYIquGrymOYBU/K0abQlkNUbBHpQCQMPQ6iPXuhTQj/P7rt7McLl6OXV/DQqgF+39y0xWAzoZbgMKrQaSr9oRmEVt6xzLM92JS67w8Xgbh39PGBfEg=");
+
+    static byte[] extInvExtTrust = Base64.decode("MIIBmjCCAQMCBgFd2SFqKjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTMwMTA3MDdaFw0xNzA4MTMwMTM3MTJaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEY3toxiphhoeoTd79/Uznb1YyKjYgxtXkYVQLZ+Q76bJFQftVVcUHw25/A/2qgSc8XPflGRpn82Qn/B7s3fxEglgeY0ekdYjea5+jZSJj70p1QcC60yH1NKGxE0ASBuv/22IoHhdu5dOTmiWegikKUXblBD1wAxbbvOcXFs2x/wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJPG9wt9edpTaCc0z03xGNF/M6x5cLx5eLgZaBFt+FO3S1xWIVby+iU8Hw2mzHOc58Fghw1jEwLaslQYadx9667NedGu7dYyY318h+VhaDppQqkhJiQl5Q8aTvVNt60fDEVLjvB7E6Z+CafVGR1jNrXxLDe6zVf/BZJK7QrkTKh4");
+    static byte[] extInvExtCA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3ZIWorMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMzAxMDcwN1oXDTE3MDgxMzAxMzcxMlowETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJKySmanEENpLJdPwpM/v0I7H0bW9ZlIxRpiL+/Z4uvF3j0r0O42Tm+dW8Ub42DzHcQ8pK/n/k2Wb4Jf7cP8+TGTAne3bgC24USW131XUZxaunGt4tCqZ0RNWpmBQUcUM0lgntDSfcvyv3QFB+nwLc93GYij9l3FaeUcHkwFiKsQIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFLnC9UF+JqEqboFH84ab9dEAkwEBoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3ZIWoqMB0GA1UdDgQWBBQkr/0UP1MKPGQH7bkRNctHMsVQsjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQCZxLwkAPif1H2P398MHK3NLf3mrmLsP41ZphdHnSLNROlY9PdO5I/dfhElzVXW2oxecIIKbOQsjZe0FOSGvZHEhLftQmOdfGc5QfGf5w9CSFCCBe5vHdMjglRLVhNB51jz6DB7Dp0MjFDgkQI4lBHaiMVkE+HUZjNLwBddHH58Sw==");
+    static byte[] extInvExtEE = Base64.decode("MIICNjCCAZ+gAwIBAgIGAV3ZIWosMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTMwMTA3MDdaFw0xNzA4MTMwMTM3MTJaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAj6WOoo8xHLLo+CT0l288xZDK3OsF64lPfNVkFnrRI65Ywl89M19nNF5Q24hF1FS6getO5oU+BhvRqft1/De22SME9SzKqs3G6uMxACKrMqgni1QBEOC/DdZ5Uaxh2s4lEgxbN0PQZIarAgLtAIgzRM4CrvofxFMwQy/neUuWmeMCAwEAAaOBmDCBlTBABgNVHSMEOTA3gBQkr/0UP1MKPGQH7bkRNctHMsVQsqEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2SFqKzAdBgNVHQ4EFgQU/yuQXlvqXJQsbqB6whCPu5bwFCAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4GBABYUGar9s7wlM3Qlnja7uc7U8FqU+xH4e8/Jk64ku7DdwXelEbKo/FTFAzh464aiFP4eMDOH7YThXyTruPudEAvYyWY7eaEgRqA2MmL0uWHSrN+HR9aBeqrMCJK/E2e1egvk2whJHMimhDUFJ3cIPsFhazMvLTnVgWGMjOqQtuP+");
+
+    private void checkInvalidCertPath()
+        throws Exception
+    {
+        checkInvalidPath(extInvTrust, extInvCA, extInvEE, "version 1 certificate contains extra data");
+        checkInvalidPath(extInvV2Trust, extInvV2CA, extInvV2EE, "version 2 certificate cannot contain extensions");
+        checkInvalidPath(extInvVersionTrust, extInvVersionCA, extInvVersionEE, "version number not recognised");
+        checkInvalidPath(extInvExtTrust, extInvExtCA, extInvExtEE, "repeated extension found: 2.5.29.15");
+    }
+
+    private void checkInvalidPath(byte[] root, byte[] inter, byte[] ee, String expected)
+        throws Exception
+    {
+        X509Certificate rootCert = new X509CertificateObject(X509CertificateStructure.getInstance(root));
+        X509Certificate interCert = new X509CertificateObject(X509CertificateStructure.getInstance(inter));
+        X509Certificate finalCert = new X509CertificateObject(X509CertificateStructure.getInstance(ee));
+        List list = new ArrayList();
+        list.add(rootCert);
+        list.add(interCert);
+        list.add(finalCert);
+
+        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+        Date validDate = new Date(rootCert.getNotBefore().getTime() + 60 * 1000);
+        //validating path
+        List certchain = new ArrayList();
+        certchain.add(finalCert);
+        certchain.add(interCert);
+        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
+        Set trust = new HashSet();
+        trust.add(new TrustAnchor(rootCert, null));
+
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
+        PKIXParameters param = new PKIXParameters(trust);
+        param.addCertStore(store);
+        param.setDate(validDate);
+        param.setRevocationEnabled(false);
+
+        try
+        {
+            PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+            fail("valid path passed");
+        }
+        catch (CertPathValidatorException e)
+        {
+            isTrue(e.getMessage().equals(expected));
+        }
+
+        // check that our cert factory also rejects - the EE is always the invalid one
+        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
+
+        try
+        {
+            cf.generateCertificate(new ByteArrayInputStream(ee));
+        }
+        catch (CertificateException e)
+        {
+            isTrue(e.getMessage().equals("parsing issue: " + expected));
+        }
     }
 
     public String getName()
@@ -457,21 +610,20 @@
     }
 
     public static void main(
-        String[]    args)
+        String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
 
         runTest(new CertPathValidatorTest());
     }
 
-
     private static class MyChecker
-       extends PKIXCertPathChecker
+        extends PKIXCertPathChecker
     {
         private static int count;
 
         public void init(boolean forward)
-        throws CertPathValidatorException
+            throws CertPathValidatorException
         {
             //To change body of implemented methods use File | Settings | File Templates.
         }
@@ -487,14 +639,872 @@
         }
 
         public void check(Certificate cert, Collection unresolvedCritExts)
-        throws CertPathValidatorException
+            throws CertPathValidatorException
         {
             count++;
         }
 
         public int getCount()
         {
-           return count;
+            return count;
+        }
+    }
+
+    public static class X509CertificateObject
+        extends X509Certificate
+    {
+        static final String CERTIFICATE_POLICIES = Extension.certificatePolicies.getId();
+        static final String POLICY_MAPPINGS = Extension.policyMappings.getId();
+        static final String INHIBIT_ANY_POLICY = Extension.inhibitAnyPolicy.getId();
+        static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
+        static final String FRESHEST_CRL = Extension.freshestCRL.getId();
+        static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId();
+        static final String POLICY_CONSTRAINTS = Extension.policyConstraints.getId();
+        static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId();
+        static final String CRL_DISTRIBUTION_POINTS = Extension.cRLDistributionPoints.getId();
+        static final String SUBJECT_ALTERNATIVE_NAME = Extension.subjectAlternativeName.getId();
+        static final String NAME_CONSTRAINTS = Extension.nameConstraints.getId();
+        static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId();
+        static final String KEY_USAGE = Extension.keyUsage.getId();
+        static final String CRL_NUMBER = Extension.cRLNumber.getId();
+        static final String ANY_POLICY = "2.5.29.32.0";
+
+        private org.bouncycastle.asn1.x509.X509CertificateStructure c;
+        private BasicConstraints basicConstraints;
+        private boolean[] keyUsage;
+        private boolean hashValueSet;
+        private int hashValue;
+
+        public X509CertificateObject(
+            org.bouncycastle.asn1.x509.X509CertificateStructure c)
+            throws CertificateParsingException
+        {
+            this.c = c;
+
+            try
+            {
+                byte[] bytes = this.getExtensionBytes("2.5.29.19");
+
+                if (bytes != null)
+                {
+                    basicConstraints = BasicConstraints.getInstance(ASN1Primitive.fromByteArray(bytes));
+                }
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
+            }
+
+            try
+            {
+                byte[] bytes = this.getExtensionBytes("2.5.29.15");
+                if (bytes != null)
+                {
+                    ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+
+                    bytes = bits.getBytes();
+                    int length = (bytes.length * 8) - bits.getPadBits();
+
+                    keyUsage = new boolean[(length < 9) ? 9 : length];
+
+                    for (int i = 0; i != length; i++)
+                    {
+                        keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+                    }
+                }
+                else
+                {
+                    keyUsage = null;
+                }
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+            }
+        }
+
+        public void checkValidity()
+            throws CertificateExpiredException, CertificateNotYetValidException
+        {
+            this.checkValidity(new Date());
+        }
+
+        public void checkValidity(
+            Date date)
+            throws CertificateExpiredException, CertificateNotYetValidException
+        {
+            if (date.getTime() > this.getNotAfter().getTime())  // for other VM compatibility
+            {
+                throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
+            }
+
+            if (date.getTime() < this.getNotBefore().getTime())
+            {
+                throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
+            }
+        }
+
+        public int getVersion()
+        {
+            return c.getVersion();
+        }
+
+        public BigInteger getSerialNumber()
+        {
+            return c.getSerialNumber().getValue();
+        }
+
+        public Principal getIssuerDN()
+        {
+            try
+            {
+                return new X509Principal(X500Name.getInstance(c.getIssuer().getEncoded()));
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
+        }
+
+        public X500Principal getIssuerX500Principal()
+        {
+            try
+            {
+                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+                ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+                aOut.writeObject(c.getIssuer());
+
+                return new X500Principal(bOut.toByteArray());
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("can't encode issuer DN");
+            }
+        }
+
+        public Principal getSubjectDN()
+        {
+            return new X509Principal(X500Name.getInstance(c.getSubject().toASN1Primitive()));
+        }
+
+        public X500Principal getSubjectX500Principal()
+        {
+            try
+            {
+                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+                ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+                aOut.writeObject(c.getSubject());
+
+                return new X500Principal(bOut.toByteArray());
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("can't encode issuer DN");
+            }
+        }
+
+        public Date getNotBefore()
+        {
+            return c.getStartDate().getDate();
+        }
+
+        public Date getNotAfter()
+        {
+            return c.getEndDate().getDate();
+        }
+
+        public byte[] getTBSCertificate()
+            throws CertificateEncodingException
+        {
+            try
+            {
+                return c.getTBSCertificate().getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                throw new CertificateEncodingException(e.toString());
+            }
+        }
+
+        public byte[] getSignature()
+        {
+            return c.getSignature().getOctets();
+        }
+
+        /**
+         * return a more "meaningful" representation for the signature algorithm used in
+         * the certficate.
+         */
+        public String getSigAlgName()
+        {
+            Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
+
+            if (prov != null)
+            {
+                String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+
+                if (algName != null)
+                {
+                    return algName;
+                }
+            }
+
+            Provider[] provs = Security.getProviders();
+
+            //
+            // search every provider looking for a real algorithm
+            //
+            for (int i = 0; i != provs.length; i++)
+            {
+                String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+                if (algName != null)
+                {
+                    return algName;
+                }
+            }
+
+            return this.getSigAlgOID();
+        }
+
+        /**
+         * return the object identifier for the signature.
+         */
+        public String getSigAlgOID()
+        {
+            return c.getSignatureAlgorithm().getAlgorithm().getId();
+        }
+
+        /**
+         * return the signature parameters, or null if there aren't any.
+         */
+        public byte[] getSigAlgParams()
+        {
+            if (c.getSignatureAlgorithm().getParameters() != null)
+            {
+                try
+                {
+                    return c.getSignatureAlgorithm().getParameters().toASN1Primitive().getEncoded(ASN1Encoding.DER);
+                }
+                catch (IOException e)
+                {
+                    return null;
+                }
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public boolean[] getIssuerUniqueID()
+        {
+            DERBitString id = c.getTBSCertificate().getIssuerUniqueId();
+
+            if (id != null)
+            {
+                byte[] bytes = id.getBytes();
+                boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+                for (int i = 0; i != boolId.length; i++)
+                {
+                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+                }
+
+                return boolId;
+            }
+
+            return null;
+        }
+
+        public boolean[] getSubjectUniqueID()
+        {
+            DERBitString id = c.getTBSCertificate().getSubjectUniqueId();
+
+            if (id != null)
+            {
+                byte[] bytes = id.getBytes();
+                boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+                for (int i = 0; i != boolId.length; i++)
+                {
+                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+                }
+
+                return boolId;
+            }
+
+            return null;
+        }
+
+        public boolean[] getKeyUsage()
+        {
+            return keyUsage;
+        }
+
+        public List getExtendedKeyUsage()
+            throws CertificateParsingException
+        {
+            byte[] bytes = this.getExtensionBytes("2.5.29.37");
+
+            if (bytes != null)
+            {
+                try
+                {
+                    ASN1InputStream dIn = new ASN1InputStream(bytes);
+                    ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+                    List list = new ArrayList();
+
+                    for (int i = 0; i != seq.size(); i++)
+                    {
+                        list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId());
+                    }
+
+                    return Collections.unmodifiableList(list);
+                }
+                catch (Exception e)
+                {
+                    throw new CertificateParsingException("error processing extended key usage extension");
+                }
+            }
+
+            return null;
+        }
+
+        public int getBasicConstraints()
+        {
+            if (basicConstraints != null)
+            {
+                if (basicConstraints.isCA())
+                {
+                    if (basicConstraints.getPathLenConstraint() == null)
+                    {
+                        return Integer.MAX_VALUE;
+                    }
+                    else
+                    {
+                        return basicConstraints.getPathLenConstraint().intValue();
+                    }
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+
+            return -1;
+        }
+
+        public Collection getSubjectAlternativeNames()
+            throws CertificateParsingException
+        {
+            return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId()));
+        }
+
+        public Collection getIssuerAlternativeNames()
+            throws CertificateParsingException
+        {
+            return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId()));
+        }
+
+        public Set getCriticalExtensionOIDs()
+        {
+            if (this.getVersion() == 3)
+            {
+                Set set = new HashSet();
+                X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+                if (extensions != null)
+                {
+                    Enumeration e = extensions.oids();
+
+                    while (e.hasMoreElements())
+                    {
+                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+                        X509Extension ext = extensions.getExtension(oid);
+
+                        if (ext.isCritical())
+                        {
+                            set.add(oid.getId());
+                        }
+                    }
+
+                    return set;
+                }
+            }
+
+            return null;
+        }
+
+        private byte[] getExtensionBytes(String oid)
+        {
+            X509Extensions exts = c.getTBSCertificate().getExtensions();
+
+            if (exts != null)
+            {
+                X509Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+                if (ext != null)
+                {
+                    return ext.getValue().getOctets();
+                }
+            }
+
+            return null;
+        }
+
+        public byte[] getExtensionValue(String oid)
+        {
+            X509Extensions exts = c.getTBSCertificate().getExtensions();
+
+            if (exts != null)
+            {
+                X509Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+                if (ext != null)
+                {
+                    try
+                    {
+                        return ext.getValue().getEncoded();
+                    }
+                    catch (Exception e)
+                    {
+                        throw new IllegalStateException("error parsing " + e.toString());
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        public Set getNonCriticalExtensionOIDs()
+        {
+            if (this.getVersion() == 3)
+            {
+                Set set = new HashSet();
+                X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+                if (extensions != null)
+                {
+                    Enumeration e = extensions.oids();
+
+                    while (e.hasMoreElements())
+                    {
+                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+                        X509Extension ext = extensions.getExtension(oid);
+
+                        if (!ext.isCritical())
+                        {
+                            set.add(oid.getId());
+                        }
+                    }
+
+                    return set;
+                }
+            }
+
+            return null;
+        }
+
+        public boolean hasUnsupportedCriticalExtension()
+        {
+            if (this.getVersion() == 3)
+            {
+                X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+                if (extensions != null)
+                {
+                    Enumeration e = extensions.oids();
+
+                    while (e.hasMoreElements())
+                    {
+                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+                        String oidId = oid.getId();
+
+                        if (oidId.equals(KEY_USAGE)
+                            || oidId.equals(CERTIFICATE_POLICIES)
+                            || oidId.equals(POLICY_MAPPINGS)
+                            || oidId.equals(INHIBIT_ANY_POLICY)
+                            || oidId.equals(CRL_DISTRIBUTION_POINTS)
+                            || oidId.equals(ISSUING_DISTRIBUTION_POINT)
+                            || oidId.equals(DELTA_CRL_INDICATOR)
+                            || oidId.equals(POLICY_CONSTRAINTS)
+                            || oidId.equals(BASIC_CONSTRAINTS)
+                            || oidId.equals(SUBJECT_ALTERNATIVE_NAME)
+                            || oidId.equals(NAME_CONSTRAINTS))
+                        {
+                            continue;
+                        }
+
+                        X509Extension ext = extensions.getExtension(oid);
+
+                        if (ext.isCritical())
+                        {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        public PublicKey getPublicKey()
+        {
+            try
+            {
+                return BouncyCastleProvider.getPublicKey(c.getSubjectPublicKeyInfo());
+            }
+            catch (IOException e)
+            {
+                return null;   // should never happen...
+            }
+        }
+
+        public byte[] getEncoded()
+            throws CertificateEncodingException
+        {
+            try
+            {
+                return c.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                throw new CertificateEncodingException(e.toString());
+            }
+        }
+
+        public boolean equals(
+            Object o)
+        {
+            if (o == this)
+            {
+                return true;
+            }
+
+            if (!(o instanceof Certificate))
+            {
+                return false;
+            }
+
+            Certificate other = (Certificate)o;
+
+            try
+            {
+                byte[] b1 = this.getEncoded();
+                byte[] b2 = other.getEncoded();
+
+                return Arrays.areEqual(b1, b2);
+            }
+            catch (CertificateEncodingException e)
+            {
+                return false;
+            }
+        }
+
+        public synchronized int hashCode()
+        {
+            if (!hashValueSet)
+            {
+                hashValue = calculateHashCode();
+                hashValueSet = true;
+            }
+
+            return hashValue;
+        }
+
+        private int calculateHashCode()
+        {
+            try
+            {
+                int hashCode = 0;
+                byte[] certData = this.getEncoded();
+                for (int i = 1; i < certData.length; i++)
+                {
+                    hashCode += certData[i] * i;
+                }
+                return hashCode;
+            }
+            catch (CertificateEncodingException e)
+            {
+                return 0;
+            }
+        }
+
+        public String toString()
+        {
+            StringBuffer buf = new StringBuffer();
+            String nl = Strings.lineSeparator();
+
+            buf.append("  [0]         Version: ").append(this.getVersion()).append(nl);
+            buf.append("         SerialNumber: ").append(this.getSerialNumber()).append(nl);
+            buf.append("             IssuerDN: ").append(this.getIssuerDN()).append(nl);
+            buf.append("           Start Date: ").append(this.getNotBefore()).append(nl);
+            buf.append("           Final Date: ").append(this.getNotAfter()).append(nl);
+            buf.append("            SubjectDN: ").append(this.getSubjectDN()).append(nl);
+            buf.append("           Public Key: ").append(this.getPublicKey()).append(nl);
+            buf.append("  Signature Algorithm: ").append(this.getSigAlgName()).append(nl);
+
+            byte[] sig = this.getSignature();
+
+            buf.append("            Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
+            for (int i = 20; i < sig.length; i += 20)
+            {
+                if (i < sig.length - 20)
+                {
+                    buf.append("                       ").append(new String(Hex.encode(sig, i, 20))).append(nl);
+                }
+                else
+                {
+                    buf.append("                       ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+                }
+            }
+
+            X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+            if (extensions != null)
+            {
+                Enumeration e = extensions.oids();
+
+                if (e.hasMoreElements())
+                {
+                    buf.append("       Extensions: \n");
+                }
+
+                while (e.hasMoreElements())
+                {
+                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+                    X509Extension ext = extensions.getExtension(oid);
+
+                    if (ext.getValue() != null)
+                    {
+                        byte[] octs = ext.getValue().getOctets();
+                        ASN1InputStream dIn = new ASN1InputStream(octs);
+                        buf.append("                       critical(").append(ext.isCritical()).append(") ");
+                        try
+                        {
+                            if (oid.equals(Extension.basicConstraints))
+                            {
+                                buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl);
+                            }
+                            else if (oid.equals(Extension.keyUsage))
+                            {
+                                buf.append(KeyUsage.getInstance(dIn.readObject())).append(nl);
+                            }
+                            else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
+                            {
+                                buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+                            }
+                            else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
+                            {
+                                buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+                            }
+                            else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
+                            {
+                                buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+                            }
+                            else
+                            {
+                                buf.append(oid.getId());
+                                buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+                                //buf.append(" value = ").append("*****").append(nl);
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+                            buf.append(oid.getId());
+                            //     buf.append(" value = ").append(new String(Hex.encode(ext.getExtnValue().getOctets()))).append(nl);
+                            buf.append(" value = ").append("*****").append(nl);
+                        }
+                    }
+                    else
+                    {
+                        buf.append(nl);
+                    }
+                }
+            }
+
+            return buf.toString();
+        }
+
+        public final void verify(
+            PublicKey key)
+            throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException
+        {
+            Signature signature;
+            String sigName = "SHA256withRSA";
+
+            try
+            {
+                signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME);
+            }
+            catch (Exception e)
+            {
+                signature = Signature.getInstance(sigName);
+            }
+
+            checkSignature(key, signature);
+        }
+
+        public final void verify(
+            PublicKey key,
+            String sigProvider)
+            throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException
+        {
+            String sigName = "SHA256withRSA";
+            Signature signature;
+
+            if (sigProvider != null)
+            {
+                signature = Signature.getInstance(sigName, sigProvider);
+            }
+            else
+            {
+                signature = Signature.getInstance(sigName);
+            }
+
+            checkSignature(key, signature);
+        }
+
+        public final void verify(
+            PublicKey key,
+            Provider sigProvider)
+            throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, SignatureException
+        {
+            String sigName = "SHA256withRSA";
+            Signature signature;
+
+            if (sigProvider != null)
+            {
+                signature = Signature.getInstance(sigName, sigProvider);
+            }
+            else
+            {
+                signature = Signature.getInstance(sigName);
+            }
+
+            checkSignature(key, signature);
+        }
+
+        private void checkSignature(
+            PublicKey key,
+            Signature signature)
+            throws CertificateException, NoSuchAlgorithmException,
+            SignatureException, InvalidKeyException
+        {
+            if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature()))
+            {
+                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+            }
+
+            ASN1Encodable params = c.getSignatureAlgorithm().getParameters();
+
+            signature.initVerify(key);
+
+            signature.update(this.getTBSCertificate());
+
+            if (!signature.verify(this.getSignature()))
+            {
+                throw new SignatureException("certificate does not verify with supplied key");
+            }
+        }
+
+        private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
+        {
+            if (!id1.getAlgorithm().equals(id2.getAlgorithm()))
+            {
+                return false;
+            }
+
+            if (id1.getParameters() == null)
+            {
+                if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            if (id2.getParameters() == null)
+            {
+                if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            return id1.getParameters().equals(id2.getParameters());
+        }
+
+        private static Collection getAlternativeNames(byte[] extVal)
+            throws CertificateParsingException
+        {
+            if (extVal == null)
+            {
+                return null;
+            }
+            try
+            {
+                Collection temp = new ArrayList();
+                Enumeration it = ASN1Sequence.getInstance(extVal).getObjects();
+                while (it.hasMoreElements())
+                {
+                    GeneralName genName = GeneralName.getInstance(it.nextElement());
+                    List list = new ArrayList();
+                    list.add(Integers.valueOf(genName.getTagNo()));
+                    switch (genName.getTagNo())
+                    {
+                    case GeneralName.ediPartyName:
+                    case GeneralName.x400Address:
+                    case GeneralName.otherName:
+                        list.add(genName.getEncoded());
+                        break;
+                    case GeneralName.directoryName:
+                        list.add(X500Name.getInstance(RFC4519Style.INSTANCE, genName.getName()).toString());
+                        break;
+                    case GeneralName.dNSName:
+                    case GeneralName.rfc822Name:
+                    case GeneralName.uniformResourceIdentifier:
+                        list.add(((ASN1String)genName.getName()).getString());
+                        break;
+                    case GeneralName.registeredID:
+                        list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId());
+                        break;
+                    case GeneralName.iPAddress:
+                        byte[] addrBytes = DEROctetString.getInstance(genName.getName()).getOctets();
+                        final String addr;
+                        try
+                        {
+                            addr = InetAddress.getByAddress(addrBytes).getHostAddress();
+                        }
+                        catch (UnknownHostException e)
+                        {
+                            continue;
+                        }
+                        list.add(addr);
+                        break;
+                    default:
+                        throw new IOException("Bad tag number: " + genName.getTagNo());
+                    }
+
+                    temp.add(Collections.unmodifiableList(list));
+                }
+                if (temp.size() == 0)
+                {
+                    return null;
+                }
+                return Collections.unmodifiableCollection(temp);
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException(e.getMessage());
+            }
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertTest.java
index bd70012..9f7b0a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CertTest.java
@@ -48,308 +48,308 @@
     //
     // server.crt
     //
-    byte[]  cert1 = Base64.decode(
-           "MIIDXjCCAsegAwIBAgIBBzANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
-         + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
-         + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
-         + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
-         + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU2MjFaFw0wMTA2"
-         + "MDIwNzU2MjFaMIG4MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
-         + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
-         + "dGQxFzAVBgNVBAsTDldlYnNlcnZlciBUZWFtMR0wGwYDVQQDExR3d3cyLmNvbm5l"
-         + "Y3Q0LmNvbS5hdTEoMCYGCSqGSIb3DQEJARYZd2VibWFzdGVyQGNvbm5lY3Q0LmNv"
-         + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArvDxclKAhyv7Q/Wmr2re"
-         + "Gw4XL9Cnh9e+6VgWy2AWNy/MVeXdlxzd7QAuc1eOWQkGQEiLPy5XQtTY+sBUJ3AO"
-         + "Rvd2fEVJIcjf29ey7bYua9J/vz5MG2KYo9/WCHIwqD9mmG9g0xLcfwq/s8ZJBswE"
-         + "7sb85VU+h94PTvsWOsWuKaECAwEAAaN3MHUwJAYDVR0RBB0wG4EZd2VibWFzdGVy"
-         + "QGNvbm5lY3Q0LmNvbS5hdTA6BglghkgBhvhCAQ0ELRYrbW9kX3NzbCBnZW5lcmF0"
-         + "ZWQgY3VzdG9tIHNlcnZlciBjZXJ0aWZpY2F0ZTARBglghkgBhvhCAQEEBAMCBkAw"
-         + "DQYJKoZIhvcNAQEEBQADgYEAotccfKpwSsIxM1Hae8DR7M/Rw8dg/RqOWx45HNVL"
-         + "iBS4/3N/TO195yeQKbfmzbAA2jbPVvIvGgTxPgO1MP4ZgvgRhasaa0qCJCkWvpM4"
-         + "yQf33vOiYQbpv4rTwzU8AmRlBG45WdjyNIigGV+oRc61aKCTnLq7zB8N3z1TF/bF"
-         + "5/8=");
+    byte[] cert1 = Base64.decode(
+        "MIIDXjCCAsegAwIBAgIBBzANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
+            + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
+            + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
+            + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
+            + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU2MjFaFw0wMTA2"
+            + "MDIwNzU2MjFaMIG4MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
+            + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
+            + "dGQxFzAVBgNVBAsTDldlYnNlcnZlciBUZWFtMR0wGwYDVQQDExR3d3cyLmNvbm5l"
+            + "Y3Q0LmNvbS5hdTEoMCYGCSqGSIb3DQEJARYZd2VibWFzdGVyQGNvbm5lY3Q0LmNv"
+            + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArvDxclKAhyv7Q/Wmr2re"
+            + "Gw4XL9Cnh9e+6VgWy2AWNy/MVeXdlxzd7QAuc1eOWQkGQEiLPy5XQtTY+sBUJ3AO"
+            + "Rvd2fEVJIcjf29ey7bYua9J/vz5MG2KYo9/WCHIwqD9mmG9g0xLcfwq/s8ZJBswE"
+            + "7sb85VU+h94PTvsWOsWuKaECAwEAAaN3MHUwJAYDVR0RBB0wG4EZd2VibWFzdGVy"
+            + "QGNvbm5lY3Q0LmNvbS5hdTA6BglghkgBhvhCAQ0ELRYrbW9kX3NzbCBnZW5lcmF0"
+            + "ZWQgY3VzdG9tIHNlcnZlciBjZXJ0aWZpY2F0ZTARBglghkgBhvhCAQEEBAMCBkAw"
+            + "DQYJKoZIhvcNAQEEBQADgYEAotccfKpwSsIxM1Hae8DR7M/Rw8dg/RqOWx45HNVL"
+            + "iBS4/3N/TO195yeQKbfmzbAA2jbPVvIvGgTxPgO1MP4ZgvgRhasaa0qCJCkWvpM4"
+            + "yQf33vOiYQbpv4rTwzU8AmRlBG45WdjyNIigGV+oRc61aKCTnLq7zB8N3z1TF/bF"
+            + "5/8=");
 
     //
     // ca.crt
     //
-    byte[]  cert2 = Base64.decode(
-           "MIIDbDCCAtWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
-         + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
-         + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
-         + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
-         + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU1MzNaFw0wMTA2"
-         + "MDIwNzU1MzNaMIG3MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
-         + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
-         + "dGQxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0eTEVMBMGA1UEAxMMQ29u"
-         + "bmVjdCA0IENBMSgwJgYJKoZIhvcNAQkBFhl3ZWJtYXN0ZXJAY29ubmVjdDQuY29t"
-         + "LmF1MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgs5ptNG6Qv1ZpCDuUNGmv"
-         + "rhjqMDPd3ri8JzZNRiiFlBA4e6/ReaO1U8ASewDeQMH6i9R6degFdQRLngbuJP0s"
-         + "xcEE+SksEWNvygfzLwV9J/q+TQDyJYK52utb++lS0b48A1KPLwEsyL6kOAgelbur"
-         + "ukwxowprKUIV7Knf1ajetQIDAQABo4GFMIGCMCQGA1UdEQQdMBuBGXdlYm1hc3Rl"
-         + "ckBjb25uZWN0NC5jb20uYXUwDwYDVR0TBAgwBgEB/wIBADA2BglghkgBhvhCAQ0E"
-         + "KRYnbW9kX3NzbCBnZW5lcmF0ZWQgY3VzdG9tIENBIGNlcnRpZmljYXRlMBEGCWCG"
-         + "SAGG+EIBAQQEAwICBDANBgkqhkiG9w0BAQQFAAOBgQCsGvfdghH8pPhlwm1r3pQk"
-         + "msnLAVIBb01EhbXm2861iXZfWqGQjrGAaA0ZpXNk9oo110yxoqEoSJSzniZa7Xtz"
-         + "soTwNUpE0SLHvWf/SlKdFWlzXA+vOZbzEv4UmjeelekTm7lc01EEa5QRVzOxHFtQ"
-         + "DhkaJ8VqOMajkQFma2r9iA==");
+    byte[] cert2 = Base64.decode(
+        "MIIDbDCCAtWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBtzELMAkGA1UEBhMCQVUx"
+            + "ETAPBgNVBAgTCFZpY3RvcmlhMRgwFgYDVQQHEw9Tb3V0aCBNZWxib3VybmUxGjAY"
+            + "BgNVBAoTEUNvbm5lY3QgNCBQdHkgTHRkMR4wHAYDVQQLExVDZXJ0aWZpY2F0ZSBB"
+            + "dXRob3JpdHkxFTATBgNVBAMTDENvbm5lY3QgNCBDQTEoMCYGCSqGSIb3DQEJARYZ"
+            + "d2VibWFzdGVyQGNvbm5lY3Q0LmNvbS5hdTAeFw0wMDA2MDIwNzU1MzNaFw0wMTA2"
+            + "MDIwNzU1MzNaMIG3MQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExGDAW"
+            + "BgNVBAcTD1NvdXRoIE1lbGJvdXJuZTEaMBgGA1UEChMRQ29ubmVjdCA0IFB0eSBM"
+            + "dGQxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0eTEVMBMGA1UEAxMMQ29u"
+            + "bmVjdCA0IENBMSgwJgYJKoZIhvcNAQkBFhl3ZWJtYXN0ZXJAY29ubmVjdDQuY29t"
+            + "LmF1MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgs5ptNG6Qv1ZpCDuUNGmv"
+            + "rhjqMDPd3ri8JzZNRiiFlBA4e6/ReaO1U8ASewDeQMH6i9R6degFdQRLngbuJP0s"
+            + "xcEE+SksEWNvygfzLwV9J/q+TQDyJYK52utb++lS0b48A1KPLwEsyL6kOAgelbur"
+            + "ukwxowprKUIV7Knf1ajetQIDAQABo4GFMIGCMCQGA1UdEQQdMBuBGXdlYm1hc3Rl"
+            + "ckBjb25uZWN0NC5jb20uYXUwDwYDVR0TBAgwBgEB/wIBADA2BglghkgBhvhCAQ0E"
+            + "KRYnbW9kX3NzbCBnZW5lcmF0ZWQgY3VzdG9tIENBIGNlcnRpZmljYXRlMBEGCWCG"
+            + "SAGG+EIBAQQEAwICBDANBgkqhkiG9w0BAQQFAAOBgQCsGvfdghH8pPhlwm1r3pQk"
+            + "msnLAVIBb01EhbXm2861iXZfWqGQjrGAaA0ZpXNk9oo110yxoqEoSJSzniZa7Xtz"
+            + "soTwNUpE0SLHvWf/SlKdFWlzXA+vOZbzEv4UmjeelekTm7lc01EEa5QRVzOxHFtQ"
+            + "DhkaJ8VqOMajkQFma2r9iA==");
 
     //
     // testx509.pem
     //
-    byte[]  cert3 = Base64.decode(
-           "MIIBWzCCAQYCARgwDQYJKoZIhvcNAQEEBQAwODELMAkGA1UEBhMCQVUxDDAKBgNV"
-         + "BAgTA1FMRDEbMBkGA1UEAxMSU1NMZWF5L3JzYSB0ZXN0IENBMB4XDTk1MDYxOTIz"
-         + "MzMxMloXDTk1MDcxNzIzMzMxMlowOjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA1FM"
-         + "RDEdMBsGA1UEAxMUU1NMZWF5L3JzYSB0ZXN0IGNlcnQwXDANBgkqhkiG9w0BAQEF"
-         + "AANLADBIAkEAqtt6qS5GTxVxGZYWa0/4u+IwHf7p2LNZbcPBp9/OfIcYAXBQn8hO"
-         + "/Re1uwLKXdCjIoaGs4DLdG88rkzfyK5dPQIDAQABMAwGCCqGSIb3DQIFBQADQQAE"
-         + "Wc7EcF8po2/ZO6kNCwK/ICH6DobgLekA5lSLr5EvuioZniZp5lFzAw4+YzPQ7XKJ"
-         + "zl9HYIMxATFyqSiD9jsx");
+    byte[] cert3 = Base64.decode(
+        "MIIBWzCCAQYCARgwDQYJKoZIhvcNAQEEBQAwODELMAkGA1UEBhMCQVUxDDAKBgNV"
+            + "BAgTA1FMRDEbMBkGA1UEAxMSU1NMZWF5L3JzYSB0ZXN0IENBMB4XDTk1MDYxOTIz"
+            + "MzMxMloXDTk1MDcxNzIzMzMxMlowOjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA1FM"
+            + "RDEdMBsGA1UEAxMUU1NMZWF5L3JzYSB0ZXN0IGNlcnQwXDANBgkqhkiG9w0BAQEF"
+            + "AANLADBIAkEAqtt6qS5GTxVxGZYWa0/4u+IwHf7p2LNZbcPBp9/OfIcYAXBQn8hO"
+            + "/Re1uwLKXdCjIoaGs4DLdG88rkzfyK5dPQIDAQABMAwGCCqGSIb3DQIFBQADQQAE"
+            + "Wc7EcF8po2/ZO6kNCwK/ICH6DobgLekA5lSLr5EvuioZniZp5lFzAw4+YzPQ7XKJ"
+            + "zl9HYIMxATFyqSiD9jsx");
 
     //
     // v3-cert1.pem
     //
-    byte[]  cert4 = Base64.decode(
-           "MIICjTCCAfigAwIBAgIEMaYgRzALBgkqhkiG9w0BAQQwRTELMAkGA1UEBhMCVVMx"
-         + "NjA0BgNVBAoTLU5hdGlvbmFsIEFlcm9uYXV0aWNzIGFuZCBTcGFjZSBBZG1pbmlz"
-         + "dHJhdGlvbjAmFxE5NjA1MjgxMzQ5MDUrMDgwMBcROTgwNTI4MTM0OTA1KzA4MDAw"
-         + "ZzELMAkGA1UEBhMCVVMxNjA0BgNVBAoTLU5hdGlvbmFsIEFlcm9uYXV0aWNzIGFu"
-         + "ZCBTcGFjZSBBZG1pbmlzdHJhdGlvbjEgMAkGA1UEBRMCMTYwEwYDVQQDEwxTdGV2"
-         + "ZSBTY2hvY2gwWDALBgkqhkiG9w0BAQEDSQAwRgJBALrAwyYdgxmzNP/ts0Uyf6Bp"
-         + "miJYktU/w4NG67ULaN4B5CnEz7k57s9o3YY3LecETgQ5iQHmkwlYDTL2fTgVfw0C"
-         + "AQOjgaswgagwZAYDVR0ZAQH/BFowWDBWMFQxCzAJBgNVBAYTAlVTMTYwNAYDVQQK"
-         + "Ey1OYXRpb25hbCBBZXJvbmF1dGljcyBhbmQgU3BhY2UgQWRtaW5pc3RyYXRpb24x"
-         + "DTALBgNVBAMTBENSTDEwFwYDVR0BAQH/BA0wC4AJODMyOTcwODEwMBgGA1UdAgQR"
-         + "MA8ECTgzMjk3MDgyM4ACBSAwDQYDVR0KBAYwBAMCBkAwCwYJKoZIhvcNAQEEA4GB"
-         + "AH2y1VCEw/A4zaXzSYZJTTUi3uawbbFiS2yxHvgf28+8Js0OHXk1H1w2d6qOHH21"
-         + "X82tZXd/0JtG0g1T9usFFBDvYK8O0ebgz/P5ELJnBL2+atObEuJy1ZZ0pBDWINR3"
-         + "WkDNLCGiTkCKp0F5EWIrVDwh54NNevkCQRZita+z4IBO");
+    byte[] cert4 = Base64.decode(
+        "MIICjTCCAfigAwIBAgIEMaYgRzALBgkqhkiG9w0BAQQwRTELMAkGA1UEBhMCVVMx"
+            + "NjA0BgNVBAoTLU5hdGlvbmFsIEFlcm9uYXV0aWNzIGFuZCBTcGFjZSBBZG1pbmlz"
+            + "dHJhdGlvbjAmFxE5NjA1MjgxMzQ5MDUrMDgwMBcROTgwNTI4MTM0OTA1KzA4MDAw"
+            + "ZzELMAkGA1UEBhMCVVMxNjA0BgNVBAoTLU5hdGlvbmFsIEFlcm9uYXV0aWNzIGFu"
+            + "ZCBTcGFjZSBBZG1pbmlzdHJhdGlvbjEgMAkGA1UEBRMCMTYwEwYDVQQDEwxTdGV2"
+            + "ZSBTY2hvY2gwWDALBgkqhkiG9w0BAQEDSQAwRgJBALrAwyYdgxmzNP/ts0Uyf6Bp"
+            + "miJYktU/w4NG67ULaN4B5CnEz7k57s9o3YY3LecETgQ5iQHmkwlYDTL2fTgVfw0C"
+            + "AQOjgaswgagwZAYDVR0ZAQH/BFowWDBWMFQxCzAJBgNVBAYTAlVTMTYwNAYDVQQK"
+            + "Ey1OYXRpb25hbCBBZXJvbmF1dGljcyBhbmQgU3BhY2UgQWRtaW5pc3RyYXRpb24x"
+            + "DTALBgNVBAMTBENSTDEwFwYDVR0BAQH/BA0wC4AJODMyOTcwODEwMBgGA1UdAgQR"
+            + "MA8ECTgzMjk3MDgyM4ACBSAwDQYDVR0KBAYwBAMCBkAwCwYJKoZIhvcNAQEEA4GB"
+            + "AH2y1VCEw/A4zaXzSYZJTTUi3uawbbFiS2yxHvgf28+8Js0OHXk1H1w2d6qOHH21"
+            + "X82tZXd/0JtG0g1T9usFFBDvYK8O0ebgz/P5ELJnBL2+atObEuJy1ZZ0pBDWINR3"
+            + "WkDNLCGiTkCKp0F5EWIrVDwh54NNevkCQRZita+z4IBO");
 
     //
     // v3-cert2.pem
     //
-    byte[]  cert5 = Base64.decode(
-           "MIICiTCCAfKgAwIBAgIEMeZfHzANBgkqhkiG9w0BAQQFADB9MQswCQYDVQQGEwJD"
-         + "YTEPMA0GA1UEBxMGTmVwZWFuMR4wHAYDVQQLExVObyBMaWFiaWxpdHkgQWNjZXB0"
-         + "ZWQxHzAdBgNVBAoTFkZvciBEZW1vIFB1cnBvc2VzIE9ubHkxHDAaBgNVBAMTE0Vu"
-         + "dHJ1c3QgRGVtbyBXZWIgQ0EwHhcNOTYwNzEyMTQyMDE1WhcNOTYxMDEyMTQyMDE1"
-         + "WjB0MSQwIgYJKoZIhvcNAQkBExVjb29rZUBpc3NsLmF0bC5ocC5jb20xCzAJBgNV"
-         + "BAYTAlVTMScwJQYDVQQLEx5IZXdsZXR0IFBhY2thcmQgQ29tcGFueSAoSVNTTCkx"
-         + "FjAUBgNVBAMTDVBhdWwgQS4gQ29va2UwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA"
-         + "6ceSq9a9AU6g+zBwaL/yVmW1/9EE8s5you1mgjHnj0wAILuoB3L6rm6jmFRy7QZT"
-         + "G43IhVZdDua4e+5/n1ZslwIDAQABo2MwYTARBglghkgBhvhCAQEEBAMCB4AwTAYJ"
-         + "YIZIAYb4QgENBD8WPVRoaXMgY2VydGlmaWNhdGUgaXMgb25seSBpbnRlbmRlZCBm"
-         + "b3IgZGVtb25zdHJhdGlvbiBwdXJwb3Nlcy4wDQYJKoZIhvcNAQEEBQADgYEAi8qc"
-         + "F3zfFqy1sV8NhjwLVwOKuSfhR/Z8mbIEUeSTlnH3QbYt3HWZQ+vXI8mvtZoBc2Fz"
-         + "lexKeIkAZXCesqGbs6z6nCt16P6tmdfbZF3I3AWzLquPcOXjPf4HgstkyvVBn0Ap"
-         + "jAFN418KF/Cx4qyHB4cjdvLrRjjQLnb2+ibo7QU=");
+    byte[] cert5 = Base64.decode(
+        "MIICiTCCAfKgAwIBAgIEMeZfHzANBgkqhkiG9w0BAQQFADB9MQswCQYDVQQGEwJD"
+            + "YTEPMA0GA1UEBxMGTmVwZWFuMR4wHAYDVQQLExVObyBMaWFiaWxpdHkgQWNjZXB0"
+            + "ZWQxHzAdBgNVBAoTFkZvciBEZW1vIFB1cnBvc2VzIE9ubHkxHDAaBgNVBAMTE0Vu"
+            + "dHJ1c3QgRGVtbyBXZWIgQ0EwHhcNOTYwNzEyMTQyMDE1WhcNOTYxMDEyMTQyMDE1"
+            + "WjB0MSQwIgYJKoZIhvcNAQkBExVjb29rZUBpc3NsLmF0bC5ocC5jb20xCzAJBgNV"
+            + "BAYTAlVTMScwJQYDVQQLEx5IZXdsZXR0IFBhY2thcmQgQ29tcGFueSAoSVNTTCkx"
+            + "FjAUBgNVBAMTDVBhdWwgQS4gQ29va2UwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA"
+            + "6ceSq9a9AU6g+zBwaL/yVmW1/9EE8s5you1mgjHnj0wAILuoB3L6rm6jmFRy7QZT"
+            + "G43IhVZdDua4e+5/n1ZslwIDAQABo2MwYTARBglghkgBhvhCAQEEBAMCB4AwTAYJ"
+            + "YIZIAYb4QgENBD8WPVRoaXMgY2VydGlmaWNhdGUgaXMgb25seSBpbnRlbmRlZCBm"
+            + "b3IgZGVtb25zdHJhdGlvbiBwdXJwb3Nlcy4wDQYJKoZIhvcNAQEEBQADgYEAi8qc"
+            + "F3zfFqy1sV8NhjwLVwOKuSfhR/Z8mbIEUeSTlnH3QbYt3HWZQ+vXI8mvtZoBc2Fz"
+            + "lexKeIkAZXCesqGbs6z6nCt16P6tmdfbZF3I3AWzLquPcOXjPf4HgstkyvVBn0Ap"
+            + "jAFN418KF/Cx4qyHB4cjdvLrRjjQLnb2+ibo7QU=");
 
     //
     // pem encoded pkcs7
     //
-    byte[]  cert6 = Base64.decode(
-          "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIJbzCCAj0w"
-        + "ggGmAhEAzbp/VvDf5LxU/iKss3KqVTANBgkqhkiG9w0BAQIFADBfMQswCQYDVQQGEwJVUzEXMBUG"
-        + "A1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2Vy"
-        + "dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNOTYwMTI5MDAwMDAwWhcNMjgwODAxMjM1OTU5WjBfMQsw"
-        + "CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVi"
-        + "bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwgZ8wDQYJKoZIhvcNAQEBBQADgY0A"
-        + "MIGJAoGBAOUZv22jVmEtmUhx9mfeuY3rt56GgAqRDvo4Ja9GiILlc6igmyRdDR/MZW4MsNBWhBiH"
-        + "mgabEKFz37RYOWtuwfYV1aioP6oSBo0xrH+wNNePNGeICc0UEeJORVZpH3gCgNrcR5EpuzbJY1zF"
-        + "4Ncth3uhtzKwezC6Ki8xqu6jZ9rbAgMBAAEwDQYJKoZIhvcNAQECBQADgYEATD+4i8Zo3+5DMw5d"
-        + "6abLB4RNejP/khv0Nq3YlSI2aBFsfELM85wuxAc/FLAPT/+Qknb54rxK6Y/NoIAK98Up8YIiXbix"
-        + "3YEjo3slFUYweRb46gVLlH8dwhzI47f0EEA8E8NfH1PoSOSGtHuhNbB7Jbq4046rPzidADQAmPPR"
-        + "cZQwggMuMIICl6ADAgECAhEA0nYujRQMPX2yqCVdr+4NdTANBgkqhkiG9w0BAQIFADBfMQswCQYD"
-        + "VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVibGlj"
-        + "IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNOTgwNTEyMDAwMDAwWhcNMDgwNTEy"
-        + "MjM1OTU5WjCBzDEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy"
-        + "dXN0IE5ldHdvcmsxRjBEBgNVBAsTPXd3dy52ZXJpc2lnbi5jb20vcmVwb3NpdG9yeS9SUEEgSW5j"
-        + "b3JwLiBCeSBSZWYuLExJQUIuTFREKGMpOTgxSDBGBgNVBAMTP1ZlcmlTaWduIENsYXNzIDEgQ0Eg"
-        + "SW5kaXZpZHVhbCBTdWJzY3JpYmVyLVBlcnNvbmEgTm90IFZhbGlkYXRlZDCBnzANBgkqhkiG9w0B"
-        + "AQEFAAOBjQAwgYkCgYEAu1pEigQWu1X9A3qKLZRPFXg2uA1Ksm+cVL+86HcqnbnwaLuV2TFBcHqB"
-        + "S7lIE1YtxwjhhEKrwKKSq0RcqkLwgg4C6S/7wju7vsknCl22sDZCM7VuVIhPh0q/Gdr5FegPh7Yc"
-        + "48zGmo5/aiSS4/zgZbqnsX7vyds3ashKyAkG5JkCAwEAAaN8MHowEQYJYIZIAYb4QgEBBAQDAgEG"
-        + "MEcGA1UdIARAMD4wPAYLYIZIAYb4RQEHAQEwLTArBggrBgEFBQcCARYfd3d3LnZlcmlzaWduLmNv"
-        + "bS9yZXBvc2l0b3J5L1JQQTAPBgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0B"
-        + "AQIFAAOBgQCIuDc73dqUNwCtqp/hgQFxHpJqbS/28Z3TymQ43BuYDAeGW4UVag+5SYWklfEXfWe0"
-        + "fy0s3ZpCnsM+tI6q5QsG3vJWKvozx74Z11NMw73I4xe1pElCY+zCphcPXVgaSTyQXFWjZSAA/Rgg"
-        + "5V+CprGoksVYasGNAzzrw80FopCubjCCA/gwggNhoAMCAQICEBbbn/1G1zppD6KsP01bwywwDQYJ"
-        + "KoZIhvcNAQEEBQAwgcwxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln"
-        + "biBUcnVzdCBOZXR3b3JrMUYwRAYDVQQLEz13d3cudmVyaXNpZ24uY29tL3JlcG9zaXRvcnkvUlBB"
-        + "IEluY29ycC4gQnkgUmVmLixMSUFCLkxURChjKTk4MUgwRgYDVQQDEz9WZXJpU2lnbiBDbGFzcyAx"
-        + "IENBIEluZGl2aWR1YWwgU3Vic2NyaWJlci1QZXJzb25hIE5vdCBWYWxpZGF0ZWQwHhcNMDAxMDAy"
-        + "MDAwMDAwWhcNMDAxMjAxMjM1OTU5WjCCAQcxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYD"
-        + "VQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMUYwRAYDVQQLEz13d3cudmVyaXNpZ24uY29tL3Jl"
-        + "cG9zaXRvcnkvUlBBIEluY29ycC4gYnkgUmVmLixMSUFCLkxURChjKTk4MR4wHAYDVQQLExVQZXJz"
-        + "b25hIE5vdCBWYWxpZGF0ZWQxJzAlBgNVBAsTHkRpZ2l0YWwgSUQgQ2xhc3MgMSAtIE1pY3Jvc29m"
-        + "dDETMBEGA1UEAxQKRGF2aWQgUnlhbjElMCMGCSqGSIb3DQEJARYWZGF2aWRAbGl2ZW1lZGlhLmNv"
-        + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqxBsdeNmSvFqhMNwhQgNzM8mdjX9eSXb"
-        + "DawpHtQHjmh0AKJSa3IwUY0VIsyZHuXWktO/CgaMBVPt6OVf/n0R2sQigMP6Y+PhEiS0vCJBL9aK"
-        + "0+pOo2qXrjVBmq+XuCyPTnc+BOSrU26tJsX0P9BYorwySiEGxGanBNATdVL4NdUCAwEAAaOBnDCB"
-        + "mTAJBgNVHRMEAjAAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQgwKjAoBggrBgEFBQcCARYcaHR0"
-        + "cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTARBglghkgBhvhCAQEEBAMCB4AwMwYDVR0fBCwwKjAo"
-        + "oCagJIYiaHR0cDovL2NybC52ZXJpc2lnbi5jb20vY2xhc3MxLmNybDANBgkqhkiG9w0BAQQFAAOB"
-        + "gQBC8yIIdVGpFTf8/YiL14cMzcmL0nIRm4kGR3U59z7UtcXlfNXXJ8MyaeI/BnXwG/gD5OKYqW6R"
-        + "yca9vZOxf1uoTBl82gInk865ED3Tej6msCqFzZffnSUQvOIeqLxxDlqYRQ6PmW2nAnZeyjcnbI5Y"
-        + "syQSM2fmo7n6qJFP+GbFezGCAkUwggJBAgEBMIHhMIHMMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5j"
-        + "LjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazFGMEQGA1UECxM9d3d3LnZlcmlzaWdu"
-        + "LmNvbS9yZXBvc2l0b3J5L1JQQSBJbmNvcnAuIEJ5IFJlZi4sTElBQi5MVEQoYyk5ODFIMEYGA1UE"
-        + "AxM/VmVyaVNpZ24gQ2xhc3MgMSBDQSBJbmRpdmlkdWFsIFN1YnNjcmliZXItUGVyc29uYSBOb3Qg"
-        + "VmFsaWRhdGVkAhAW25/9Rtc6aQ+irD9NW8MsMAkGBSsOAwIaBQCggbowGAYJKoZIhvcNAQkDMQsG"
-        + "CSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMDAxMDAyMTczNTE4WjAjBgkqhkiG9w0BCQQxFgQU"
-        + "gZjSaBEY2oxGvlQUIMnxSXhivK8wWwYJKoZIhvcNAQkPMU4wTDAKBggqhkiG9w0DBzAOBggqhkiG"
-        + "9w0DAgICAIAwDQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwBwYFKw4DAh0w"
-        + "DQYJKoZIhvcNAQEBBQAEgYAzk+PU91/ZFfoiuKOECjxEh9fDYE2jfDCheBIgh5gdcCo+sS1WQs8O"
-        + "HreQ9Nop/JdJv1DQMBK6weNBBDoP0EEkRm1XCC144XhXZC82jBZohYmi2WvDbbC//YN58kRMYMyy"
-        + "srrfn4Z9I+6kTriGXkrpGk9Q0LSGjmG2BIsqiF0dvwAAAAAAAA==");
+    byte[] cert6 = Base64.decode(
+        "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIJbzCCAj0w"
+            + "ggGmAhEAzbp/VvDf5LxU/iKss3KqVTANBgkqhkiG9w0BAQIFADBfMQswCQYDVQQGEwJVUzEXMBUG"
+            + "A1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2Vy"
+            + "dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNOTYwMTI5MDAwMDAwWhcNMjgwODAxMjM1OTU5WjBfMQsw"
+            + "CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVi"
+            + "bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwgZ8wDQYJKoZIhvcNAQEBBQADgY0A"
+            + "MIGJAoGBAOUZv22jVmEtmUhx9mfeuY3rt56GgAqRDvo4Ja9GiILlc6igmyRdDR/MZW4MsNBWhBiH"
+            + "mgabEKFz37RYOWtuwfYV1aioP6oSBo0xrH+wNNePNGeICc0UEeJORVZpH3gCgNrcR5EpuzbJY1zF"
+            + "4Ncth3uhtzKwezC6Ki8xqu6jZ9rbAgMBAAEwDQYJKoZIhvcNAQECBQADgYEATD+4i8Zo3+5DMw5d"
+            + "6abLB4RNejP/khv0Nq3YlSI2aBFsfELM85wuxAc/FLAPT/+Qknb54rxK6Y/NoIAK98Up8YIiXbix"
+            + "3YEjo3slFUYweRb46gVLlH8dwhzI47f0EEA8E8NfH1PoSOSGtHuhNbB7Jbq4046rPzidADQAmPPR"
+            + "cZQwggMuMIICl6ADAgECAhEA0nYujRQMPX2yqCVdr+4NdTANBgkqhkiG9w0BAQIFADBfMQswCQYD"
+            + "VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDEgUHVibGlj"
+            + "IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNOTgwNTEyMDAwMDAwWhcNMDgwNTEy"
+            + "MjM1OTU5WjCBzDEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy"
+            + "dXN0IE5ldHdvcmsxRjBEBgNVBAsTPXd3dy52ZXJpc2lnbi5jb20vcmVwb3NpdG9yeS9SUEEgSW5j"
+            + "b3JwLiBCeSBSZWYuLExJQUIuTFREKGMpOTgxSDBGBgNVBAMTP1ZlcmlTaWduIENsYXNzIDEgQ0Eg"
+            + "SW5kaXZpZHVhbCBTdWJzY3JpYmVyLVBlcnNvbmEgTm90IFZhbGlkYXRlZDCBnzANBgkqhkiG9w0B"
+            + "AQEFAAOBjQAwgYkCgYEAu1pEigQWu1X9A3qKLZRPFXg2uA1Ksm+cVL+86HcqnbnwaLuV2TFBcHqB"
+            + "S7lIE1YtxwjhhEKrwKKSq0RcqkLwgg4C6S/7wju7vsknCl22sDZCM7VuVIhPh0q/Gdr5FegPh7Yc"
+            + "48zGmo5/aiSS4/zgZbqnsX7vyds3ashKyAkG5JkCAwEAAaN8MHowEQYJYIZIAYb4QgEBBAQDAgEG"
+            + "MEcGA1UdIARAMD4wPAYLYIZIAYb4RQEHAQEwLTArBggrBgEFBQcCARYfd3d3LnZlcmlzaWduLmNv"
+            + "bS9yZXBvc2l0b3J5L1JQQTAPBgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0B"
+            + "AQIFAAOBgQCIuDc73dqUNwCtqp/hgQFxHpJqbS/28Z3TymQ43BuYDAeGW4UVag+5SYWklfEXfWe0"
+            + "fy0s3ZpCnsM+tI6q5QsG3vJWKvozx74Z11NMw73I4xe1pElCY+zCphcPXVgaSTyQXFWjZSAA/Rgg"
+            + "5V+CprGoksVYasGNAzzrw80FopCubjCCA/gwggNhoAMCAQICEBbbn/1G1zppD6KsP01bwywwDQYJ"
+            + "KoZIhvcNAQEEBQAwgcwxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln"
+            + "biBUcnVzdCBOZXR3b3JrMUYwRAYDVQQLEz13d3cudmVyaXNpZ24uY29tL3JlcG9zaXRvcnkvUlBB"
+            + "IEluY29ycC4gQnkgUmVmLixMSUFCLkxURChjKTk4MUgwRgYDVQQDEz9WZXJpU2lnbiBDbGFzcyAx"
+            + "IENBIEluZGl2aWR1YWwgU3Vic2NyaWJlci1QZXJzb25hIE5vdCBWYWxpZGF0ZWQwHhcNMDAxMDAy"
+            + "MDAwMDAwWhcNMDAxMjAxMjM1OTU5WjCCAQcxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYD"
+            + "VQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMUYwRAYDVQQLEz13d3cudmVyaXNpZ24uY29tL3Jl"
+            + "cG9zaXRvcnkvUlBBIEluY29ycC4gYnkgUmVmLixMSUFCLkxURChjKTk4MR4wHAYDVQQLExVQZXJz"
+            + "b25hIE5vdCBWYWxpZGF0ZWQxJzAlBgNVBAsTHkRpZ2l0YWwgSUQgQ2xhc3MgMSAtIE1pY3Jvc29m"
+            + "dDETMBEGA1UEAxQKRGF2aWQgUnlhbjElMCMGCSqGSIb3DQEJARYWZGF2aWRAbGl2ZW1lZGlhLmNv"
+            + "bS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqxBsdeNmSvFqhMNwhQgNzM8mdjX9eSXb"
+            + "DawpHtQHjmh0AKJSa3IwUY0VIsyZHuXWktO/CgaMBVPt6OVf/n0R2sQigMP6Y+PhEiS0vCJBL9aK"
+            + "0+pOo2qXrjVBmq+XuCyPTnc+BOSrU26tJsX0P9BYorwySiEGxGanBNATdVL4NdUCAwEAAaOBnDCB"
+            + "mTAJBgNVHRMEAjAAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQgwKjAoBggrBgEFBQcCARYcaHR0"
+            + "cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTARBglghkgBhvhCAQEEBAMCB4AwMwYDVR0fBCwwKjAo"
+            + "oCagJIYiaHR0cDovL2NybC52ZXJpc2lnbi5jb20vY2xhc3MxLmNybDANBgkqhkiG9w0BAQQFAAOB"
+            + "gQBC8yIIdVGpFTf8/YiL14cMzcmL0nIRm4kGR3U59z7UtcXlfNXXJ8MyaeI/BnXwG/gD5OKYqW6R"
+            + "yca9vZOxf1uoTBl82gInk865ED3Tej6msCqFzZffnSUQvOIeqLxxDlqYRQ6PmW2nAnZeyjcnbI5Y"
+            + "syQSM2fmo7n6qJFP+GbFezGCAkUwggJBAgEBMIHhMIHMMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5j"
+            + "LjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazFGMEQGA1UECxM9d3d3LnZlcmlzaWdu"
+            + "LmNvbS9yZXBvc2l0b3J5L1JQQSBJbmNvcnAuIEJ5IFJlZi4sTElBQi5MVEQoYyk5ODFIMEYGA1UE"
+            + "AxM/VmVyaVNpZ24gQ2xhc3MgMSBDQSBJbmRpdmlkdWFsIFN1YnNjcmliZXItUGVyc29uYSBOb3Qg"
+            + "VmFsaWRhdGVkAhAW25/9Rtc6aQ+irD9NW8MsMAkGBSsOAwIaBQCggbowGAYJKoZIhvcNAQkDMQsG"
+            + "CSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMDAxMDAyMTczNTE4WjAjBgkqhkiG9w0BCQQxFgQU"
+            + "gZjSaBEY2oxGvlQUIMnxSXhivK8wWwYJKoZIhvcNAQkPMU4wTDAKBggqhkiG9w0DBzAOBggqhkiG"
+            + "9w0DAgICAIAwDQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwBwYFKw4DAh0w"
+            + "DQYJKoZIhvcNAQEBBQAEgYAzk+PU91/ZFfoiuKOECjxEh9fDYE2jfDCheBIgh5gdcCo+sS1WQs8O"
+            + "HreQ9Nop/JdJv1DQMBK6weNBBDoP0EEkRm1XCC144XhXZC82jBZohYmi2WvDbbC//YN58kRMYMyy"
+            + "srrfn4Z9I+6kTriGXkrpGk9Q0LSGjmG2BIsqiF0dvwAAAAAAAA==");
 
     //
     // dsaWithSHA1 cert
     //
-    byte[]  cert7 = Base64.decode(
-          "MIIEXAYJKoZIhvcNAQcCoIIETTCCBEkCAQExCzAJBgUrDgMCGgUAMAsGCSqG"
-        + "SIb3DQEHAaCCAsMwggK/MIIB4AIBADCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7"
-        + "d8miwTMN55CUSmo3TO8WGCxgY61TX5k+7NU4XPf1TULjw3GobwaJX13kquPh"
-        + "fVXk+gVy46n4Iw3hAhUBSe/QF4BUj+pJOF9ROBM4u+FEWA8CQQD4mSJbrABj"
-        + "TUWrlnAte8pS22Tq4/FPO7jHSqjijUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/z"
-        + "m8Q12PFp/PjOhh+nMA4xDDAKBgNVBAMTA0lEMzAeFw05NzEwMDEwMDAwMDBa"
-        + "Fw0zODAxMDEwMDAwMDBaMA4xDDAKBgNVBAMTA0lEMzCB8DCBpwYFKw4DAhsw"
-        + "gZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxgY61TX5k+7NU4XPf1TULj"
-        + "w3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/QF4BUj+pJOF9ROBM4u+FE"
-        + "WA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jHSqjijUHfXKTrHL1OEqV3"
-        + "SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nA0QAAkEAkYkXLYMtGVGWj9OnzjPn"
-        + "sB9sefSRPrVegZJCZbpW+Iv0/1RP1u04pHG9vtRpIQLjzUiWvLMU9EKQTThc"
-        + "eNMmWDCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxg"
-        + "Y61TX5k+7NU4XPf1TULjw3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/Q"
-        + "F4BUj+pJOF9ROBM4u+FEWA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jH"
-        + "SqjijUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nAy8AMCwC"
-        + "FBY3dBSdeprGcqpr6wr3xbG+6WW+AhRMm/facKJNxkT3iKgJbp7R8Xd3QTGC"
-        + "AWEwggFdAgEBMBMwDjEMMAoGA1UEAxMDSUQzAgEAMAkGBSsOAwIaBQCgXTAY"
-        + "BgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0wMjA1"
-        + "MjQyMzEzMDdaMCMGCSqGSIb3DQEJBDEWBBS4WMsoJhf7CVbZYCFcjoTRzPkJ"
-        + "xjCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxgY61T"
-        + "X5k+7NU4XPf1TULjw3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/QF4BU"
-        + "j+pJOF9ROBM4u+FEWA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jHSqji"
-        + "jUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nBC8wLQIVALID"
-        + "dt+MHwawrDrwsO1Z6sXBaaJsAhRaKssrpevmLkbygKPV07XiAKBG02Zvb2Jh"
-        + "cg==");
+    byte[] cert7 = Base64.decode(
+        "MIIEXAYJKoZIhvcNAQcCoIIETTCCBEkCAQExCzAJBgUrDgMCGgUAMAsGCSqG"
+            + "SIb3DQEHAaCCAsMwggK/MIIB4AIBADCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7"
+            + "d8miwTMN55CUSmo3TO8WGCxgY61TX5k+7NU4XPf1TULjw3GobwaJX13kquPh"
+            + "fVXk+gVy46n4Iw3hAhUBSe/QF4BUj+pJOF9ROBM4u+FEWA8CQQD4mSJbrABj"
+            + "TUWrlnAte8pS22Tq4/FPO7jHSqjijUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/z"
+            + "m8Q12PFp/PjOhh+nMA4xDDAKBgNVBAMTA0lEMzAeFw05NzEwMDEwMDAwMDBa"
+            + "Fw0zODAxMDEwMDAwMDBaMA4xDDAKBgNVBAMTA0lEMzCB8DCBpwYFKw4DAhsw"
+            + "gZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxgY61TX5k+7NU4XPf1TULj"
+            + "w3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/QF4BUj+pJOF9ROBM4u+FE"
+            + "WA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jHSqjijUHfXKTrHL1OEqV3"
+            + "SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nA0QAAkEAkYkXLYMtGVGWj9OnzjPn"
+            + "sB9sefSRPrVegZJCZbpW+Iv0/1RP1u04pHG9vtRpIQLjzUiWvLMU9EKQTThc"
+            + "eNMmWDCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxg"
+            + "Y61TX5k+7NU4XPf1TULjw3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/Q"
+            + "F4BUj+pJOF9ROBM4u+FEWA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jH"
+            + "SqjijUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nAy8AMCwC"
+            + "FBY3dBSdeprGcqpr6wr3xbG+6WW+AhRMm/facKJNxkT3iKgJbp7R8Xd3QTGC"
+            + "AWEwggFdAgEBMBMwDjEMMAoGA1UEAxMDSUQzAgEAMAkGBSsOAwIaBQCgXTAY"
+            + "BgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0wMjA1"
+            + "MjQyMzEzMDdaMCMGCSqGSIb3DQEJBDEWBBS4WMsoJhf7CVbZYCFcjoTRzPkJ"
+            + "xjCBpwYFKw4DAhswgZ0CQQEkJRHP+mN7d8miwTMN55CUSmo3TO8WGCxgY61T"
+            + "X5k+7NU4XPf1TULjw3GobwaJX13kquPhfVXk+gVy46n4Iw3hAhUBSe/QF4BU"
+            + "j+pJOF9ROBM4u+FEWA8CQQD4mSJbrABjTUWrlnAte8pS22Tq4/FPO7jHSqji"
+            + "jUHfXKTrHL1OEqV3SVWcFy5j/cqBgX/zm8Q12PFp/PjOhh+nBC8wLQIVALID"
+            + "dt+MHwawrDrwsO1Z6sXBaaJsAhRaKssrpevmLkbygKPV07XiAKBG02Zvb2Jh"
+            + "cg==");
 
     //
     // testcrl.pem
     //
-    byte[]  crl1 = Base64.decode(
+    byte[] crl1 = Base64.decode(
         "MIICjTCCAfowDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoT"
-        + "F1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVy"
-        + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw05NTA1MDIwMjEyMjZaFw05NTA2MDEw"
-        + "MDAxNDlaMIIBaDAWAgUCQQAABBcNOTUwMjAxMTcyNDI2WjAWAgUCQQAACRcNOTUw"
-        + "MjEwMDIxNjM5WjAWAgUCQQAADxcNOTUwMjI0MDAxMjQ5WjAWAgUCQQAADBcNOTUw"
-        + "MjI1MDA0NjQ0WjAWAgUCQQAAGxcNOTUwMzEzMTg0MDQ5WjAWAgUCQQAAFhcNOTUw"
-        + "MzE1MTkxNjU0WjAWAgUCQQAAGhcNOTUwMzE1MTk0MDQxWjAWAgUCQQAAHxcNOTUw"
-        + "MzI0MTk0NDMzWjAWAgUCcgAABRcNOTUwMzI5MjAwNzExWjAWAgUCcgAAERcNOTUw"
-        + "MzMwMDIzNDI2WjAWAgUCQQAAIBcNOTUwNDA3MDExMzIxWjAWAgUCcgAAHhcNOTUw"
-        + "NDA4MDAwMjU5WjAWAgUCcgAAQRcNOTUwNDI4MTcxNzI0WjAWAgUCcgAAOBcNOTUw"
-        + "NDI4MTcyNzIxWjAWAgUCcgAATBcNOTUwNTAyMDIxMjI2WjANBgkqhkiG9w0BAQIF"
-        + "AAN+AHqOEJXSDejYy0UwxxrH/9+N2z5xu/if0J6qQmK92W0hW158wpJg+ovV3+wQ"
-        + "wvIEPRL2rocL0tKfAsVq1IawSJzSNgxG0lrcla3MrJBnZ4GaZDu4FutZh72MR3Gt"
-        + "JaAL3iTJHJD55kK2D/VoyY1djlsPuNh6AEgdVwFAyp0v");
+            + "F1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVy"
+            + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw05NTA1MDIwMjEyMjZaFw05NTA2MDEw"
+            + "MDAxNDlaMIIBaDAWAgUCQQAABBcNOTUwMjAxMTcyNDI2WjAWAgUCQQAACRcNOTUw"
+            + "MjEwMDIxNjM5WjAWAgUCQQAADxcNOTUwMjI0MDAxMjQ5WjAWAgUCQQAADBcNOTUw"
+            + "MjI1MDA0NjQ0WjAWAgUCQQAAGxcNOTUwMzEzMTg0MDQ5WjAWAgUCQQAAFhcNOTUw"
+            + "MzE1MTkxNjU0WjAWAgUCQQAAGhcNOTUwMzE1MTk0MDQxWjAWAgUCQQAAHxcNOTUw"
+            + "MzI0MTk0NDMzWjAWAgUCcgAABRcNOTUwMzI5MjAwNzExWjAWAgUCcgAAERcNOTUw"
+            + "MzMwMDIzNDI2WjAWAgUCQQAAIBcNOTUwNDA3MDExMzIxWjAWAgUCcgAAHhcNOTUw"
+            + "NDA4MDAwMjU5WjAWAgUCcgAAQRcNOTUwNDI4MTcxNzI0WjAWAgUCcgAAOBcNOTUw"
+            + "NDI4MTcyNzIxWjAWAgUCcgAATBcNOTUwNTAyMDIxMjI2WjANBgkqhkiG9w0BAQIF"
+            + "AAN+AHqOEJXSDejYy0UwxxrH/9+N2z5xu/if0J6qQmK92W0hW158wpJg+ovV3+wQ"
+            + "wvIEPRL2rocL0tKfAsVq1IawSJzSNgxG0lrcla3MrJBnZ4GaZDu4FutZh72MR3Gt"
+            + "JaAL3iTJHJD55kK2D/VoyY1djlsPuNh6AEgdVwFAyp0v");
 
     //
     // ecdsa cert with extra octet string.
     //
-    byte[]  oldEcdsa = Base64.decode(
-          "MIICljCCAkCgAwIBAgIBATALBgcqhkjOPQQBBQAwgY8xCzAJBgNVBAYTAkFVMSgwJ"
-        + "gYDVQQKEx9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIwEAYDVQQHEw"
-        + "lNZWxib3VybmUxETAPBgNVBAgTCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWV"
-        + "kYmFjay1jcnlwdG9AYm91bmN5Y2FzdGxlLm9yZzAeFw0wMTEyMDcwMTAwMDRaFw0w"
-        + "MTEyMDcwMTAxNDRaMIGPMQswCQYDVQQGEwJBVTEoMCYGA1UEChMfVGhlIExlZ2lvb"
-        + "iBvZiB0aGUgQm91bmN5IENhc3RsZTESMBAGA1UEBxMJTWVsYm91cm5lMREwDwYDVQ"
-        + "QIEwhWaWN0b3JpYTEvMC0GCSqGSIb3DQEJARYgZmVlZGJhY2stY3J5cHRvQGJvdW5"
-        + "jeWNhc3RsZS5vcmcwgeQwgb0GByqGSM49AgEwgbECAQEwKQYHKoZIzj0BAQIef///"
-        + "////////////f///////gAAAAAAAf///////MEAEHn///////////////3///////"
-        + "4AAAAAAAH///////AQeawFsO9zxiUHQ1lSSFHXKcanbL7J9HTd5YYXClCwKBB8CD/"
-        + "qWPNyogWzMM7hkK+35BcPTWFc9Pyf7vTs8uaqvAh5///////////////9///+eXpq"
-        + "fXZBx+9FSJoiQnQsDIgAEHwJbbcU7xholSP+w9nFHLebJUhqdLSU05lq/y9X+DHAw"
-        + "CwYHKoZIzj0EAQUAA0MAMEACHnz6t4UNoVROp74ma4XNDjjGcjaqiIWPZLK8Bdw3G"
-        + "QIeLZ4j3a6ividZl344UH+UPUE7xJxlYGuy7ejTsqRR");
+    byte[] oldEcdsa = Base64.decode(
+        "MIICljCCAkCgAwIBAgIBATALBgcqhkjOPQQBBQAwgY8xCzAJBgNVBAYTAkFVMSgwJ"
+            + "gYDVQQKEx9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIwEAYDVQQHEw"
+            + "lNZWxib3VybmUxETAPBgNVBAgTCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWV"
+            + "kYmFjay1jcnlwdG9AYm91bmN5Y2FzdGxlLm9yZzAeFw0wMTEyMDcwMTAwMDRaFw0w"
+            + "MTEyMDcwMTAxNDRaMIGPMQswCQYDVQQGEwJBVTEoMCYGA1UEChMfVGhlIExlZ2lvb"
+            + "iBvZiB0aGUgQm91bmN5IENhc3RsZTESMBAGA1UEBxMJTWVsYm91cm5lMREwDwYDVQ"
+            + "QIEwhWaWN0b3JpYTEvMC0GCSqGSIb3DQEJARYgZmVlZGJhY2stY3J5cHRvQGJvdW5"
+            + "jeWNhc3RsZS5vcmcwgeQwgb0GByqGSM49AgEwgbECAQEwKQYHKoZIzj0BAQIef///"
+            + "////////////f///////gAAAAAAAf///////MEAEHn///////////////3///////"
+            + "4AAAAAAAH///////AQeawFsO9zxiUHQ1lSSFHXKcanbL7J9HTd5YYXClCwKBB8CD/"
+            + "qWPNyogWzMM7hkK+35BcPTWFc9Pyf7vTs8uaqvAh5///////////////9///+eXpq"
+            + "fXZBx+9FSJoiQnQsDIgAEHwJbbcU7xholSP+w9nFHLebJUhqdLSU05lq/y9X+DHAw"
+            + "CwYHKoZIzj0EAQUAA0MAMEACHnz6t4UNoVROp74ma4XNDjjGcjaqiIWPZLK8Bdw3G"
+            + "QIeLZ4j3a6ividZl344UH+UPUE7xJxlYGuy7ejTsqRR");
 
-    byte[]  uncompressedPtEC = Base64.decode(
-          "MIIDKzCCAsGgAwIBAgICA+kwCwYHKoZIzj0EAQUAMGYxCzAJBgNVBAYTAkpQ"
-        + "MRUwEwYDVQQKEwxuaXRlY2guYWMuanAxDjAMBgNVBAsTBWFpbGFiMQ8wDQYD"
-        + "VQQDEwZ0ZXN0Y2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RjYUBsb2NhbGhvc3Qw"
-        + "HhcNMDExMDEzMTE1MzE3WhcNMjAxMjEyMTE1MzE3WjBmMQswCQYDVQQGEwJK"
-        + "UDEVMBMGA1UEChMMbml0ZWNoLmFjLmpwMQ4wDAYDVQQLEwVhaWxhYjEPMA0G"
-        + "A1UEAxMGdGVzdGNhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0Y2FAbG9jYWxob3N0"
-        + "MIIBczCCARsGByqGSM49AgEwggEOAgEBMDMGByqGSM49AQECKEdYWnajFmnZ"
-        + "tzrukK2XWdle2v+GsD9l1ZiR6g7ozQDbhFH/bBiMDQcwVAQoJ5EQKrI54/CT"
-        + "xOQ2pMsd/fsXD+EX8YREd8bKHWiLz8lIVdD5cBNeVwQoMKSc6HfI7vKZp8Q2"
-        + "zWgIFOarx1GQoWJbMcSt188xsl30ncJuJT2OoARRBAqJ4fD+q6hbqgNSjTQ7"
-        + "htle1KO3eiaZgcJ8rrnyN8P+5A8+5K+H9aQ/NbBR4Gs7yto5PXIUZEUgodHA"
-        + "TZMSAcSq5ZYt4KbnSYaLY0TtH9CqAigEwZ+hglbT21B7ZTzYX2xj0x+qooJD"
-        + "hVTLtIPaYJK2HrMPxTw6/zfrAgEPA1IABAnvfFcFDgD/JicwBGn6vR3N8MIn"
-        + "mptZf/mnJ1y649uCF60zOgdwIyI7pVSxBFsJ7ohqXEHW0x7LrGVkdSEiipiH"
-        + "LYslqh3xrqbAgPbl93GUo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB"
-        + "/wQEAwIBxjAdBgNVHQ4EFgQUAEo62Xm9H6DcsE0zUDTza4BRG90wCwYHKoZI"
-        + "zj0EAQUAA1cAMFQCKAQsCHHSNOqfJXLgt3bg5+k49hIBGVr/bfG0B9JU3rNt"
-        + "Ycl9Y2zfRPUCKAK2ccOQXByAWfsasDu8zKHxkZv7LVDTFjAIffz3HaCQeVhD"
-        + "z+fauEg=");
+    byte[] uncompressedPtEC = Base64.decode(
+        "MIIDKzCCAsGgAwIBAgICA+kwCwYHKoZIzj0EAQUAMGYxCzAJBgNVBAYTAkpQ"
+            + "MRUwEwYDVQQKEwxuaXRlY2guYWMuanAxDjAMBgNVBAsTBWFpbGFiMQ8wDQYD"
+            + "VQQDEwZ0ZXN0Y2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RjYUBsb2NhbGhvc3Qw"
+            + "HhcNMDExMDEzMTE1MzE3WhcNMjAxMjEyMTE1MzE3WjBmMQswCQYDVQQGEwJK"
+            + "UDEVMBMGA1UEChMMbml0ZWNoLmFjLmpwMQ4wDAYDVQQLEwVhaWxhYjEPMA0G"
+            + "A1UEAxMGdGVzdGNhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0Y2FAbG9jYWxob3N0"
+            + "MIIBczCCARsGByqGSM49AgEwggEOAgEBMDMGByqGSM49AQECKEdYWnajFmnZ"
+            + "tzrukK2XWdle2v+GsD9l1ZiR6g7ozQDbhFH/bBiMDQcwVAQoJ5EQKrI54/CT"
+            + "xOQ2pMsd/fsXD+EX8YREd8bKHWiLz8lIVdD5cBNeVwQoMKSc6HfI7vKZp8Q2"
+            + "zWgIFOarx1GQoWJbMcSt188xsl30ncJuJT2OoARRBAqJ4fD+q6hbqgNSjTQ7"
+            + "htle1KO3eiaZgcJ8rrnyN8P+5A8+5K+H9aQ/NbBR4Gs7yto5PXIUZEUgodHA"
+            + "TZMSAcSq5ZYt4KbnSYaLY0TtH9CqAigEwZ+hglbT21B7ZTzYX2xj0x+qooJD"
+            + "hVTLtIPaYJK2HrMPxTw6/zfrAgEPA1IABAnvfFcFDgD/JicwBGn6vR3N8MIn"
+            + "mptZf/mnJ1y649uCF60zOgdwIyI7pVSxBFsJ7ohqXEHW0x7LrGVkdSEiipiH"
+            + "LYslqh3xrqbAgPbl93GUo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB"
+            + "/wQEAwIBxjAdBgNVHQ4EFgQUAEo62Xm9H6DcsE0zUDTza4BRG90wCwYHKoZI"
+            + "zj0EAQUAA1cAMFQCKAQsCHHSNOqfJXLgt3bg5+k49hIBGVr/bfG0B9JU3rNt"
+            + "Ycl9Y2zfRPUCKAK2ccOQXByAWfsasDu8zKHxkZv7LVDTFjAIffz3HaCQeVhD"
+            + "z+fauEg=");
 
-    byte[]  keyUsage = Base64.decode(
-          "MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE"
-        + "BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50"
-        + "cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs"
-        + "aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp"
-        + "bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0"
-        + "aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa"
-        + "MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV"
-        + "BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw"
-        + "LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50"
-        + "cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50"
-        + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL"
-        + "ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv"
-        + "x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV"
-        + "iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173"
-        + "iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw"
-        + "ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50"
-        + "cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff"
-        + "SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE"
-        + "CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
-        + "cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD"
-        + "VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D"
-        + "bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx"
-        + "MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW"
-        + "/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG"
-        + "A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI"
-        + "hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ"
-        + "OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU"
-        + "ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE"
-        + "PHayXOw=");
+    byte[] keyUsage = Base64.decode(
+        "MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE"
+            + "BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50"
+            + "cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs"
+            + "aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp"
+            + "bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0"
+            + "aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa"
+            + "MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV"
+            + "BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw"
+            + "LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50"
+            + "cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50"
+            + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL"
+            + "ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv"
+            + "x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV"
+            + "iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173"
+            + "iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw"
+            + "ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50"
+            + "cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff"
+            + "SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE"
+            + "CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
+            + "cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD"
+            + "VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D"
+            + "bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx"
+            + "MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW"
+            + "/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG"
+            + "A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI"
+            + "hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ"
+            + "OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU"
+            + "ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE"
+            + "PHayXOw=");
 
     byte[] nameCert = Base64.decode(
-            "MIIEFjCCA3+gAwIBAgIEdS8BozANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJE"+
-            "RTERMA8GA1UEChQIREFURVYgZUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRQ0Eg"+
-            "REFURVYgRDAzIDE6UE4wIhgPMjAwMTA1MTAxMDIyNDhaGA8yMDA0MDUwOTEwMjI0"+
-            "OFowgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIFAZCYXllcm4xEjAQBgNVBAcUCU7I"+
-            "dXJuYmVyZzERMA8GA1UEChQIREFURVYgZUcxHTAbBgNVBAUTFDAwMDAwMDAwMDA4"+
-            "OTU3NDM2MDAxMR4wHAYDVQQDFBVEaWV0bWFyIFNlbmdlbmxlaXRuZXIwgaEwDQYJ"+
-            "KoZIhvcNAQEBBQADgY8AMIGLAoGBAJLI/LJLKaHoMk8fBECW/od8u5erZi6jI8Ug"+
-            "C0a/LZyQUO/R20vWJs6GrClQtXB+AtfiBSnyZOSYzOdfDI8yEKPEv8qSuUPpOHps"+
-            "uNCFdLZF1vavVYGEEWs2+y+uuPmg8q1oPRyRmUZ+x9HrDvCXJraaDfTEd9olmB/Z"+
-            "AuC/PqpjAgUAwAAAAaOCAcYwggHCMAwGA1UdEwEB/wQCMAAwDwYDVR0PAQH/BAUD"+
-            "AwdAADAxBgNVHSAEKjAoMCYGBSskCAEBMB0wGwYIKwYBBQUHAgEWD3d3dy56cy5k"+
-            "YXRldi5kZTApBgNVHREEIjAggR5kaWV0bWFyLnNlbmdlbmxlaXRuZXJAZGF0ZXYu"+
-            "ZGUwgYQGA1UdIwR9MHuhc6RxMG8xCzAJBgNVBAYTAkRFMT0wOwYDVQQKFDRSZWd1"+
-            "bGllcnVuZ3NiZWjIb3JkZSBmyHVyIFRlbGVrb21tdW5pa2F0aW9uIHVuZCBQb3N0"+
-            "MSEwDAYHAoIGAQoHFBMBMTARBgNVBAMUCjVSLUNBIDE6UE6CBACm8LkwDgYHAoIG"+
-            "AQoMAAQDAQEAMEcGA1UdHwRAMD4wPKAUoBKGEHd3dy5jcmwuZGF0ZXYuZGWiJKQi"+
-            "MCAxCzAJBgNVBAYTAkRFMREwDwYDVQQKFAhEQVRFViBlRzAWBgUrJAgDBAQNMAsT"+
-            "A0VVUgIBBQIBATAdBgNVHQ4EFgQUfv6xFP0xk7027folhy+ziZvBJiwwLAYIKwYB"+
-            "BQUHAQEEIDAeMBwGCCsGAQUFBzABhhB3d3cuZGlyLmRhdGV2LmRlMA0GCSqGSIb3"+
-            "DQEBBQUAA4GBAEOVX6uQxbgtKzdgbTi6YLffMftFr2mmNwch7qzpM5gxcynzgVkg"+
-            "pnQcDNlm5AIbS6pO8jTCLfCd5TZ5biQksBErqmesIl3QD+VqtB+RNghxectZ3VEs"+
+        "MIIEFjCCA3+gAwIBAgIEdS8BozANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJE" +
+            "RTERMA8GA1UEChQIREFURVYgZUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRQ0Eg" +
+            "REFURVYgRDAzIDE6UE4wIhgPMjAwMTA1MTAxMDIyNDhaGA8yMDA0MDUwOTEwMjI0" +
+            "OFowgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIFAZCYXllcm4xEjAQBgNVBAcUCU7I" +
+            "dXJuYmVyZzERMA8GA1UEChQIREFURVYgZUcxHTAbBgNVBAUTFDAwMDAwMDAwMDA4" +
+            "OTU3NDM2MDAxMR4wHAYDVQQDFBVEaWV0bWFyIFNlbmdlbmxlaXRuZXIwgaEwDQYJ" +
+            "KoZIhvcNAQEBBQADgY8AMIGLAoGBAJLI/LJLKaHoMk8fBECW/od8u5erZi6jI8Ug" +
+            "C0a/LZyQUO/R20vWJs6GrClQtXB+AtfiBSnyZOSYzOdfDI8yEKPEv8qSuUPpOHps" +
+            "uNCFdLZF1vavVYGEEWs2+y+uuPmg8q1oPRyRmUZ+x9HrDvCXJraaDfTEd9olmB/Z" +
+            "AuC/PqpjAgUAwAAAAaOCAcYwggHCMAwGA1UdEwEB/wQCMAAwDwYDVR0PAQH/BAUD" +
+            "AwdAADAxBgNVHSAEKjAoMCYGBSskCAEBMB0wGwYIKwYBBQUHAgEWD3d3dy56cy5k" +
+            "YXRldi5kZTApBgNVHREEIjAggR5kaWV0bWFyLnNlbmdlbmxlaXRuZXJAZGF0ZXYu" +
+            "ZGUwgYQGA1UdIwR9MHuhc6RxMG8xCzAJBgNVBAYTAkRFMT0wOwYDVQQKFDRSZWd1" +
+            "bGllcnVuZ3NiZWjIb3JkZSBmyHVyIFRlbGVrb21tdW5pa2F0aW9uIHVuZCBQb3N0" +
+            "MSEwDAYHAoIGAQoHFBMBMTARBgNVBAMUCjVSLUNBIDE6UE6CBACm8LkwDgYHAoIG" +
+            "AQoMAAQDAQEAMEcGA1UdHwRAMD4wPKAUoBKGEHd3dy5jcmwuZGF0ZXYuZGWiJKQi" +
+            "MCAxCzAJBgNVBAYTAkRFMREwDwYDVQQKFAhEQVRFViBlRzAWBgUrJAgDBAQNMAsT" +
+            "A0VVUgIBBQIBATAdBgNVHQ4EFgQUfv6xFP0xk7027folhy+ziZvBJiwwLAYIKwYB" +
+            "BQUHAQEEIDAeMBwGCCsGAQUFBzABhhB3d3cuZGlyLmRhdGV2LmRlMA0GCSqGSIb3" +
+            "DQEBBQUAA4GBAEOVX6uQxbgtKzdgbTi6YLffMftFr2mmNwch7qzpM5gxcynzgVkg" +
+            "pnQcDNlm5AIbS6pO8jTCLfCd5TZ5biQksBErqmesIl3QD+VqtB+RNghxectZ3VEs" +
             "nCUtcE7tJ8O14qwCb3TxS9dvIUFiVi4DjbxX46TdcTbTaK8/qr6AIf+l");
-    
+
     byte[] probSelfSignedCert = Base64.decode(
-              "MIICxTCCAi6gAwIBAgIQAQAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQUFADBF"
+        "MIICxTCCAi6gAwIBAgIQAQAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQUFADBF"
             + "MScwJQYDVQQKEx4gRElSRUNUSU9OIEdFTkVSQUxFIERFUyBJTVBPVFMxGjAYBgNV"
             + "BAMTESBBQyBNSU5FRkkgQiBURVNUMB4XDTA0MDUwNzEyMDAwMFoXDTE0MDUwNzEy"
             + "MDAwMFowRTEnMCUGA1UEChMeIERJUkVDVElPTiBHRU5FUkFMRSBERVMgSU1QT1RT"
@@ -364,10 +364,10 @@
             + "AQEFBQADgYEAmToHJWjd3+4zknfsP09H6uMbolHNGG0zTS2lrLKpzcmkQfjhQpT9"
             + "LUTBvfs1jdjo9fGmQLvOG+Sm51Rbjglb8bcikVI5gLbclOlvqLkm77otjl4U4Z2/"
             + "Y0vP14Aov3Sn3k+17EfReYUZI4liuB95ncobC4e8ZM++LjQcIM0s+Vs=");
-    
-    
+
+
     byte[] gost34102001base = Base64.decode(
-              "MIIB1DCCAYECEEjpVKXP6Wn1yVz3VeeDQa8wCgYGKoUDAgIDBQAwbTEfMB0G"
+        "MIIB1DCCAYECEEjpVKXP6Wn1yVz3VeeDQa8wCgYGKoUDAgIDBQAwbTEfMB0G"
             + "A1UEAwwWR29zdFIzNDEwLTIwMDEgZXhhbXBsZTESMBAGA1UECgwJQ3J5cHRv"
             + "UHJvMQswCQYDVQQGEwJSVTEpMCcGCSqGSIb3DQEJARYaR29zdFIzNDEwLTIw"
             + "MDFAZXhhbXBsZS5jb20wHhcNMDUwMjAzMTUxNjQ2WhcNMTUwMjAzMTUxNjQ2"
@@ -378,9 +378,9 @@
             + "V4/rzBa5lYgD0Jp8ha4P5I3qprt+VsfLsN8PZrzK6hpgMAoGBiqFAwICAwUA"
             + "A0EAHw5dw/aw/OiNvHyOE65kvyo4Hp0sfz3csM6UUkp10VO247ofNJK3tsLb"
             + "HOLjUaqzefrlGb11WpHYrvWFg+FcLA==");
-    
+
     byte[] gost341094base = Base64.decode(
-              "MIICDzCCAbwCEBcxKsIb0ghYvAQeUjfQdFAwCgYGKoUDAgIEBQAwaTEdMBsG"
+        "MIICDzCCAbwCEBcxKsIb0ghYvAQeUjfQdFAwCgYGKoUDAgIEBQAwaTEdMBsG"
             + "A1UEAwwUR29zdFIzNDEwLTk0IGV4YW1wbGUxEjAQBgNVBAoMCUNyeXB0b1By"
             + "bzELMAkGA1UEBhMCUlUxJzAlBgkqhkiG9w0BCQEWGEdvc3RSMzQxMC05NEBl"
             + "eGFtcGxlLmNvbTAeFw0wNTAyMDMxNTE2NTFaFw0xNTAyMDMxNTE2NTFaMGkx"
@@ -392,9 +392,9 @@
             + "G/TliYoJgmjTXHemD7aQEBON4z58nJHWrA0ILD54wbXCtrcaqCqLRYGTMjJ2"
             + "+nswCgYGKoUDAgIEBQADQQBxKNhOmjgz/i5CEgLOyKyz9pFGkDcaymsWYQWV"
             + "v7CZ0pTM8IzMzkUBW3GHsUjCFpanFZDfg2zuN+3kT+694n9B");
-    
+
     byte[] gost341094A = Base64.decode(
-            "MIICSDCCAfWgAwIBAgIBATAKBgYqhQMCAgQFADCBgTEXMBUGA1UEAxMOZGVmYXVsdDM0MTAtOTQx"
+        "MIICSDCCAfWgAwIBAgIBATAKBgYqhQMCAgQFADCBgTEXMBUGA1UEAxMOZGVmYXVsdDM0MTAtOTQx"
             + "DTALBgNVBAoTBERpZ3QxDzANBgNVBAsTBkNyeXB0bzEOMAwGA1UEBxMFWS1vbGExDDAKBgNVBAgT"
             + "A01FTDELMAkGA1UEBhMCcnUxGzAZBgkqhkiG9w0BCQEWDHRlc3RAdGVzdC5ydTAeFw0wNTAzMjkx"
             + "MzExNTdaFw0wNjAzMjkxMzExNTdaMIGBMRcwFQYDVQQDEw5kZWZhdWx0MzQxMC05NDENMAsGA1UE"
@@ -405,22 +405,22 @@
             + "+hLPKbS561WpvB2XSTgbV+pqqXR3j6j30STmybelEV3RdS2Now8wDTALBgNVHQ8EBAMCB4AwCgYG"
             + "KoUDAgIEBQADQQBCFy7xWRXtNVXflKvDs0pBdBuPzjCMeZAXVxK8vUxsxxKu76d9CsvhgIFknFRi"
             + "wWTPiZenvNoJ4R1uzeX+vREm");
-    
+
     byte[] gost341094B = Base64.decode(
-            "MIICSDCCAfWgAwIBAgIBATAKBgYqhQMCAgQFADCBgTEXMBUGA1UEAxMOcGFyYW0xLTM0MTAtOTQx"
-            +  "DTALBgNVBAoTBERpZ3QxDzANBgNVBAsTBkNyeXB0bzEOMAwGA1UEBxMFWS1PbGExDDAKBgNVBAgT"
-            +  "A01lbDELMAkGA1UEBhMCcnUxGzAZBgkqhkiG9w0BCQEWDHRlc3RAdGVzdC5ydTAeFw0wNTAzMjkx"
-            +  "MzEzNTZaFw0wNjAzMjkxMzEzNTZaMIGBMRcwFQYDVQQDEw5wYXJhbTEtMzQxMC05NDENMAsGA1UE"
-            +  "ChMERGlndDEPMA0GA1UECxMGQ3J5cHRvMQ4wDAYDVQQHEwVZLU9sYTEMMAoGA1UECBMDTWVsMQsw"
-            +  "CQYDVQQGEwJydTEbMBkGCSqGSIb3DQEJARYMdGVzdEB0ZXN0LnJ1MIGlMBwGBiqFAwICFDASBgcq"
-            +  "hQMCAiADBgcqhQMCAh4BA4GEAASBgEa+AAcZmijWs1M9x5Pn9efE8D9ztG1NMoIt0/hNZNqln3+j"
-            +  "lMZjyqPt+kTLIjtmvz9BRDmIDk6FZz+4LhG2OTL7yGpWfrMxMRr56nxomTN9aLWRqbyWmn3brz9Y"
-            +  "AUD3ifnwjjIuW7UM84JNlDTOdxx0XRUfLQIPMCXe9cO02Xskow8wDTALBgNVHQ8EBAMCB4AwCgYG"
-            +  "KoUDAgIEBQADQQBzFcnuYc/639OTW+L5Ecjw9KxGr+dwex7lsS9S1BUgKa3m1d5c+cqI0B2XUFi5"
-            +  "4iaHHJG0dCyjtQYLJr0OZjRw");
-    
+        "MIICSDCCAfWgAwIBAgIBATAKBgYqhQMCAgQFADCBgTEXMBUGA1UEAxMOcGFyYW0xLTM0MTAtOTQx"
+            + "DTALBgNVBAoTBERpZ3QxDzANBgNVBAsTBkNyeXB0bzEOMAwGA1UEBxMFWS1PbGExDDAKBgNVBAgT"
+            + "A01lbDELMAkGA1UEBhMCcnUxGzAZBgkqhkiG9w0BCQEWDHRlc3RAdGVzdC5ydTAeFw0wNTAzMjkx"
+            + "MzEzNTZaFw0wNjAzMjkxMzEzNTZaMIGBMRcwFQYDVQQDEw5wYXJhbTEtMzQxMC05NDENMAsGA1UE"
+            + "ChMERGlndDEPMA0GA1UECxMGQ3J5cHRvMQ4wDAYDVQQHEwVZLU9sYTEMMAoGA1UECBMDTWVsMQsw"
+            + "CQYDVQQGEwJydTEbMBkGCSqGSIb3DQEJARYMdGVzdEB0ZXN0LnJ1MIGlMBwGBiqFAwICFDASBgcq"
+            + "hQMCAiADBgcqhQMCAh4BA4GEAASBgEa+AAcZmijWs1M9x5Pn9efE8D9ztG1NMoIt0/hNZNqln3+j"
+            + "lMZjyqPt+kTLIjtmvz9BRDmIDk6FZz+4LhG2OTL7yGpWfrMxMRr56nxomTN9aLWRqbyWmn3brz9Y"
+            + "AUD3ifnwjjIuW7UM84JNlDTOdxx0XRUfLQIPMCXe9cO02Xskow8wDTALBgNVHQ8EBAMCB4AwCgYG"
+            + "KoUDAgIEBQADQQBzFcnuYc/639OTW+L5Ecjw9KxGr+dwex7lsS9S1BUgKa3m1d5c+cqI0B2XUFi5"
+            + "4iaHHJG0dCyjtQYLJr0OZjRw");
+
     byte[] gost34102001A = Base64.decode(
-            "MIICCzCCAbigAwIBAgIBATAKBgYqhQMCAgMFADCBhDEaMBgGA1UEAxMRZGVmYXVsdC0zNDEwLTIw"
+        "MIICCzCCAbigAwIBAgIBATAKBgYqhQMCAgMFADCBhDEaMBgGA1UEAxMRZGVmYXVsdC0zNDEwLTIw"
             + "MDExDTALBgNVBAoTBERpZ3QxDzANBgNVBAsTBkNyeXB0bzEOMAwGA1UEBxMFWS1PbGExDDAKBgNV"
             + "BAgTA01lbDELMAkGA1UEBhMCcnUxGzAZBgkqhkiG9w0BCQEWDHRlc3RAdGVzdC5ydTAeFw0wNTAz"
             + "MjkxMzE4MzFaFw0wNjAzMjkxMzE4MzFaMIGEMRowGAYDVQQDExFkZWZhdWx0LTM0MTAtMjAwMTEN"
@@ -430,9 +430,9 @@
             + "ned0KTg7TJreRUc+VX7vca4hLQaZ1o/TxVtfEApK/O6jDzANMAsGA1UdDwQEAwIHgDAKBgYqhQMC"
             + "AgMFAANBAN8y2b6HuIdkD3aWujpfQbS1VIA/7hro4vLgDhjgVmev/PLzFB8oTh3gKhExpDo82IEs"
             + "ZftGNsbbyp1NFg7zda0=");
-    
+
     byte[] gostCA1 = Base64.decode(
-            "MIIDNDCCAuGgAwIBAgIQZLcKDcWcQopF+jp4p9jylDAKBgYqhQMCAgQFADBm"
+        "MIIDNDCCAuGgAwIBAgIQZLcKDcWcQopF+jp4p9jylDAKBgYqhQMCAgQFADBm"
             + "MQswCQYDVQQGEwJSVTEPMA0GA1UEBxMGTW9zY293MRcwFQYDVQQKEw5PT08g"
             + "Q3J5cHRvLVBybzEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxFzAVBgNVBAMTDkNQ"
             + "IENTUCBUZXN0IENBMB4XDTAyMDYwOTE1NTIyM1oXDTA5MDYwOTE1NTkyOVow"
@@ -451,9 +451,9 @@
             + "LmNybC8wEgYJKwYBBAGCNxUBBAUCAwMAAzAKBgYqhQMCAgQFAANBAIJi7ni7"
             + "9rwMR5rRGTFftt2k70GbqyUEfkZYOzrgdOoKiB4IIsIstyBX0/ne6GsL9Xan"
             + "G2IN96RB7KrowEHeW+k=");
-    
+
     byte[] gostCA2 = Base64.decode(
-            "MIIC2DCCAoWgAwIBAgIQe9ZCugm42pRKNcHD8466zTAKBgYqhQMCAgMFADB+"
+        "MIIC2DCCAoWgAwIBAgIQe9ZCugm42pRKNcHD8466zTAKBgYqhQMCAgMFADB+"
             + "MRowGAYJKoZIhvcNAQkBFgtzYmFAZGlndC5ydTELMAkGA1UEBhMCUlUxDDAK"
             + "BgNVBAgTA01FTDEUMBIGA1UEBxMLWW9zaGthci1PbGExDTALBgNVBAoTBERp"
             + "Z3QxDzANBgNVBAsTBkNyeXB0bzEPMA0GA1UEAxMGc2JhLUNBMB4XDTA0MDgw"
@@ -471,204 +471,8 @@
             + "q8EYl6iJqXCuR+ozRmH7hPAP3c4KqYSC38TClCgBloLapx/3/WdatctFJW/L"
             + "mcTovpq088927shE");
 
-    byte[] inDirectCrl = Base64.decode(
-            "MIIdXjCCHMcCAQEwDQYJKoZIhvcNAQEFBQAwdDELMAkGA1UEBhMCREUxHDAaBgNV"
-            +"BAoUE0RldXRzY2hlIFRlbGVrb20gQUcxFzAVBgNVBAsUDlQtVGVsZVNlYyBUZXN0"
-            +"MS4wDAYHAoIGAQoHFBMBMTAeBgNVBAMUF1QtVGVsZVNlYyBUZXN0IERJUiA4OlBO"
-            +"Fw0wNjA4MDQwODQ1MTRaFw0wNjA4MDQxNDQ1MTRaMIIbfzB+AgQvrj/pFw0wMzA3"
-            +"MjIwNTQxMjhaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP+oXDTAzMDcyMjA1NDEyOFowZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/5xcNMDQwNDA1MTMxODE3WjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNDpQTjB+AgQvrj/oFw0wNDA0"
-            +"MDUxMzE4MTdaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP+UXDTAzMDExMzExMTgxMVowZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/5hcNMDMwMTEzMTExODExWjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNDpQTjB+AgQvrj/jFw0wMzAx"
-            +"MTMxMTI2NTZaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP+QXDTAzMDExMzExMjY1NlowZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/4hcNMDQwNzEzMDc1ODM4WjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNDpQTjB+AgQvrj/eFw0wMzAy"
-            +"MTcwNjMzMjVaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP98XDTAzMDIxNzA2MzMyNVowZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/0xcNMDMwMjE3MDYzMzI1WjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNDpQTjB+AgQvrj/dFw0wMzAx"
-            +"MTMxMTI4MTRaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP9cXDTAzMDExMzExMjcwN1owZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/2BcNMDMwMTEzMTEyNzA3WjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNDpQTjB+AgQvrj/VFw0wMzA0"
-            +"MzAxMjI3NTNaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYD"
-            +"VQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMU"
-            +"EVNpZ0cgVGVzdCBDQSA0OlBOMH4CBC+uP9YXDTAzMDQzMDEyMjc1M1owZzBlBgNV"
-            +"HR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDQ6"
-            +"UE4wfgIEL64/xhcNMDMwMjEyMTM0NTQwWjBnMGUGA1UdHQEB/wRbMFmkVzBVMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKC"
-            +"BgEKBxQTATEwGAYDVQQDFBFUVEMgVGVzdCBDQSAxMTpQTjCBkAIEL64/xRcNMDMw"
-            +"MjEyMTM0NTQwWjB5MHcGA1UdHQEB/wRtMGukaTBnMQswCQYDVQQGEwJERTEcMBoG"
-            +"A1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEQMA4GA1UECxQHVGVsZVNlYzEoMAwG"
-            +"BwKCBgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0EgNTpQTjB+AgQvrj/CFw0w"
-            +"MzAyMTIxMzA5MTZaMGcwZQYDVR0dAQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRww"
-            +"GgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNV"
-            +"BAMUEVRUQyBUZXN0IENBIDExOlBOMIGQAgQvrj/BFw0wMzAyMTIxMzA4NDBaMHkw"
-            +"dwYDVR0dAQH/BG0wa6RpMGcxCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2No"
-            +"ZSBUZWxla29tIEFHMRAwDgYDVQQLFAdUZWxlU2VjMSgwDAYHAoIGAQoHFBMBMTAY"
-            +"BgNVBAMUEVNpZ0cgVGVzdCBDQSA1OlBOMH4CBC+uP74XDTAzMDIxNzA2MzcyNVow"
-            +"ZzBlBgNVHR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRz"
-            +"Y2hlIFRlbGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRVFRDIFRlc3Qg"
-            +"Q0EgMTE6UE4wgZACBC+uP70XDTAzMDIxNzA2MzcyNVoweTB3BgNVHR0BAf8EbTBr"
-            +"pGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcx"
-            +"EDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBU"
-            +"ZXN0IENBIDU6UE4wgZACBC+uP7AXDTAzMDIxMjEzMDg1OVoweTB3BgNVHR0BAf8E"
-            +"bTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20g"
-            +"QUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2ln"
-            +"RyBUZXN0IENBIDU6UE4wgZACBC+uP68XDTAzMDIxNzA2MzcyNVoweTB3BgNVHR0B"
-            +"Af8EbTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVr"
-            +"b20gQUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQR"
-            +"U2lnRyBUZXN0IENBIDU6UE4wfgIEL64/kxcNMDMwNDEwMDUyNjI4WjBnMGUGA1Ud"
-            +"HQEB/wRbMFmkVzBVMQswCQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVs"
-            +"ZWtvbSBBRzEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFUVEMgVGVzdCBDQSAxMTpQ"
-            +"TjCBkAIEL64/khcNMDMwNDEwMDUyNjI4WjB5MHcGA1UdHQEB/wRtMGukaTBnMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEQMA4GA1UE"
-            +"CxQHVGVsZVNlYzEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFTaWdHIFRlc3QgQ0Eg"
-            +"NTpQTjB+AgQvrj8/Fw0wMzAyMjYxMTA0NDRaMGcwZQYDVR0dAQH/BFswWaRXMFUx"
-            +"CzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMSgwDAYH"
-            +"AoIGAQoHFBMBMTAYBgNVBAMUEVRUQyBUZXN0IENBIDExOlBOMIGQAgQvrj8+Fw0w"
-            +"MzAyMjYxMTA0NDRaMHkwdwYDVR0dAQH/BG0wa6RpMGcxCzAJBgNVBAYTAkRFMRww"
-            +"GgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMRAwDgYDVQQLFAdUZWxlU2VjMSgw"
-            +"DAYHAoIGAQoHFBMBMTAYBgNVBAMUEVNpZ0cgVGVzdCBDQSA1OlBOMH4CBC+uPs0X"
-            +"DTAzMDUyMDA1MjczNlowZzBlBgNVHR0BAf8EWzBZpFcwVTELMAkGA1UEBhMCREUx"
-            +"HDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcxKDAMBgcCggYBCgcUEwExMBgG"
-            +"A1UEAxQRVFRDIFRlc3QgQ0EgMTE6UE4wgZACBC+uPswXDTAzMDUyMDA1MjczNlow"
-            +"eTB3BgNVHR0BAf8EbTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRz"
-            +"Y2hlIFRlbGVrb20gQUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwEx"
-            +"MBgGA1UEAxQRU2lnRyBUZXN0IENBIDY6UE4wfgIEL64+PBcNMDMwNjE3MTAzNDE2"
-            +"WjBnMGUGA1UdHQEB/wRbMFmkVzBVMQswCQYDVQQGEwJERTEcMBoGA1UEChQTRGV1"
-            +"dHNjaGUgVGVsZWtvbSBBRzEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFUVEMgVGVz"
-            +"dCBDQSAxMTpQTjCBkAIEL64+OxcNMDMwNjE3MTAzNDE2WjB5MHcGA1UdHQEB/wRt"
-            +"MGukaTBnMQswCQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBB"
-            +"RzEQMA4GA1UECxQHVGVsZVNlYzEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFTaWdH"
-            +"IFRlc3QgQ0EgNjpQTjCBkAIEL64+OhcNMDMwNjE3MTAzNDE2WjB5MHcGA1UdHQEB"
-            +"/wRtMGukaTBnMQswCQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtv"
-            +"bSBBRzEQMA4GA1UECxQHVGVsZVNlYzEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFT"
-            +"aWdHIFRlc3QgQ0EgNjpQTjB+AgQvrj45Fw0wMzA2MTcxMzAxMDBaMGcwZQYDVR0d"
-            +"AQH/BFswWaRXMFUxCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxl"
-            +"a29tIEFHMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEVRUQyBUZXN0IENBIDExOlBO"
-            +"MIGQAgQvrj44Fw0wMzA2MTcxMzAxMDBaMHkwdwYDVR0dAQH/BG0wa6RpMGcxCzAJ"
-            +"BgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMRAwDgYDVQQL"
-            +"FAdUZWxlU2VjMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEVNpZ0cgVGVzdCBDQSA2"
-            +"OlBOMIGQAgQvrj43Fw0wMzA2MTcxMzAxMDBaMHkwdwYDVR0dAQH/BG0wa6RpMGcx"
-            +"CzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMRAwDgYD"
-            +"VQQLFAdUZWxlU2VjMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEVNpZ0cgVGVzdCBD"
-            +"QSA2OlBOMIGQAgQvrj42Fw0wMzA2MTcxMzAxMDBaMHkwdwYDVR0dAQH/BG0wa6Rp"
-            +"MGcxCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFHMRAw"
-            +"DgYDVQQLFAdUZWxlU2VjMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEVNpZ0cgVGVz"
-            +"dCBDQSA2OlBOMIGQAgQvrj4zFw0wMzA2MTcxMDM3NDlaMHkwdwYDVR0dAQH/BG0w"
-            +"a6RpMGcxCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFH"
-            +"MRAwDgYDVQQLFAdUZWxlU2VjMSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEVNpZ0cg"
-            +"VGVzdCBDQSA2OlBOMH4CBC+uPjEXDTAzMDYxNzEwNDI1OFowZzBlBgNVHR0BAf8E"
-            +"WzBZpFcwVTELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20g"
-            +"QUcxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRVFRDIFRlc3QgQ0EgMTE6UE4wgZAC"
-            +"BC+uPjAXDTAzMDYxNzEwNDI1OFoweTB3BgNVHR0BAf8EbTBrpGkwZzELMAkGA1UE"
-            +"BhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcxEDAOBgNVBAsUB1Rl"
-            +"bGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDY6UE4w"
-            +"gZACBC+uPakXDTAzMTAyMjExMzIyNFoweTB3BgNVHR0BAf8EbTBrpGkwZzELMAkG"
-            +"A1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcxEDAOBgNVBAsU"
-            +"B1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENBIDY6"
-            +"UE4wgZACBC+uPLIXDTA1MDMxMTA2NDQyNFoweTB3BgNVHR0BAf8EbTBrpGkwZzEL"
-            +"MAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcxEDAOBgNV"
-            +"BAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0IENB"
-            +"IDY6UE4wgZACBC+uPKsXDTA0MDQwMjA3NTQ1M1oweTB3BgNVHR0BAf8EbTBrpGkw"
-            +"ZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcxEDAO"
-            +"BgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBUZXN0"
-            +"IENBIDY6UE4wgZACBC+uOugXDTA1MDEyNzEyMDMyNFoweTB3BgNVHR0BAf8EbTBr"
-            +"pGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20gQUcx"
-            +"EDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2lnRyBU"
-            +"ZXN0IENBIDY6UE4wgZACBC+uOr4XDTA1MDIxNjA3NTcxNloweTB3BgNVHR0BAf8E"
-            +"bTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVrb20g"
-            +"QUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQRU2ln"
-            +"RyBUZXN0IENBIDY6UE4wgZACBC+uOqcXDTA1MDMxMDA1NTkzNVoweTB3BgNVHR0B"
-            +"Af8EbTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRlbGVr"
-            +"b20gQUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UEAxQR"
-            +"U2lnRyBUZXN0IENBIDY6UE4wgZACBC+uOjwXDTA1MDUxMTEwNDk0NloweTB3BgNV"
-            +"HR0BAf8EbTBrpGkwZzELMAkGA1UEBhMCREUxHDAaBgNVBAoUE0RldXRzY2hlIFRl"
-            +"bGVrb20gQUcxEDAOBgNVBAsUB1RlbGVTZWMxKDAMBgcCggYBCgcUEwExMBgGA1UE"
-            +"AxQRU2lnRyBUZXN0IENBIDY6UE4wgaoCBC+sbdUXDTA1MTExMTEwMDMyMVowgZIw"
-            +"gY8GA1UdHQEB/wSBhDCBgaR/MH0xCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0"
-            +"c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLFBZQcm9kdWt0emVudHJ1bSBUZWxlU2Vj"
-            +"MS8wDAYHAoIGAQoHFBMBMTAfBgNVBAMUGFRlbGVTZWMgUEtTIFNpZ0cgQ0EgMTpQ"
-            +"TjCBlQIEL64uaBcNMDYwMTIzMTAyNTU1WjB+MHwGA1UdHQEB/wRyMHCkbjBsMQsw"
-            +"CQYDVQQGEwJERTEcMBoGA1UEChQTRGV1dHNjaGUgVGVsZWtvbSBBRzEWMBQGA1UE"
-            +"CxQNWmVudHJhbGUgQm9ubjEnMAwGBwKCBgEKBxQTATEwFwYDVQQDFBBUVEMgVGVz"
-            +"dCBDQSA5OlBOMIGVAgQvribHFw0wNjA4MDEwOTQ4NDRaMH4wfAYDVR0dAQH/BHIw"
-            +"cKRuMGwxCzAJBgNVBAYTAkRFMRwwGgYDVQQKFBNEZXV0c2NoZSBUZWxla29tIEFH"
-            +"MRYwFAYDVQQLFA1aZW50cmFsZSBCb25uMScwDAYHAoIGAQoHFBMBMTAXBgNVBAMU"
-            +"EFRUQyBUZXN0IENBIDk6UE6ggZswgZgwCwYDVR0UBAQCAhEMMB8GA1UdIwQYMBaA"
-            +"FANbyNumDI9545HwlCF26NuOJC45MA8GA1UdHAEB/wQFMAOEAf8wVwYDVR0SBFAw"
-            +"ToZMbGRhcDovL3Brc2xkYXAudHR0Yy5kZS9vdT1ULVRlbGVTZWMgVGVzdCBESVIg"
-            +"ODpQTixvPURldXRzY2hlIFRlbGVrb20gQUcsYz1kZTANBgkqhkiG9w0BAQUFAAOB"
-            +"gQBewL5gLFHpeOWO07Vk3Gg7pRDuAlvaovBH4coCyCWpk5jEhUfFSYEDuaQB7do4"
-            +"IlJmeTHvkI0PIZWJ7bwQ2PVdipPWDx0NVwS/Cz5jUKiS3BbAmZQZOueiKLFpQq3A"
-            +"b8aOHA7WHU4078/1lM+bgeu33Ln1CGykEbmSjA/oKPi/JA==");
-    
-    byte[] directCRL = Base64.decode(
-            "MIIGXTCCBckCAQEwCgYGKyQDAwECBQAwdDELMAkGA1UEBhMCREUxHDAaBgNVBAoU"
-            +"E0RldXRzY2hlIFRlbGVrb20gQUcxFzAVBgNVBAsUDlQtVGVsZVNlYyBUZXN0MS4w"
-            +"DAYHAoIGAQoHFBMBMTAeBgNVBAMUF1QtVGVsZVNlYyBUZXN0IERJUiA4OlBOFw0w"
-            +"NjA4MDQwODQ1MTRaFw0wNjA4MDQxNDQ1MTRaMIIElTAVAgQvrj/pFw0wMzA3MjIw"
-            +"NTQxMjhaMBUCBC+uP+oXDTAzMDcyMjA1NDEyOFowFQIEL64/5xcNMDQwNDA1MTMx"
-            +"ODE3WjAVAgQvrj/oFw0wNDA0MDUxMzE4MTdaMBUCBC+uP+UXDTAzMDExMzExMTgx"
-            +"MVowFQIEL64/5hcNMDMwMTEzMTExODExWjAVAgQvrj/jFw0wMzAxMTMxMTI2NTZa"
-            +"MBUCBC+uP+QXDTAzMDExMzExMjY1NlowFQIEL64/4hcNMDQwNzEzMDc1ODM4WjAV"
-            +"AgQvrj/eFw0wMzAyMTcwNjMzMjVaMBUCBC+uP98XDTAzMDIxNzA2MzMyNVowFQIE"
-            +"L64/0xcNMDMwMjE3MDYzMzI1WjAVAgQvrj/dFw0wMzAxMTMxMTI4MTRaMBUCBC+u"
-            +"P9cXDTAzMDExMzExMjcwN1owFQIEL64/2BcNMDMwMTEzMTEyNzA3WjAVAgQvrj/V"
-            +"Fw0wMzA0MzAxMjI3NTNaMBUCBC+uP9YXDTAzMDQzMDEyMjc1M1owFQIEL64/xhcN"
-            +"MDMwMjEyMTM0NTQwWjAVAgQvrj/FFw0wMzAyMTIxMzQ1NDBaMBUCBC+uP8IXDTAz"
-            +"MDIxMjEzMDkxNlowFQIEL64/wRcNMDMwMjEyMTMwODQwWjAVAgQvrj++Fw0wMzAy"
-            +"MTcwNjM3MjVaMBUCBC+uP70XDTAzMDIxNzA2MzcyNVowFQIEL64/sBcNMDMwMjEy"
-            +"MTMwODU5WjAVAgQvrj+vFw0wMzAyMTcwNjM3MjVaMBUCBC+uP5MXDTAzMDQxMDA1"
-            +"MjYyOFowFQIEL64/khcNMDMwNDEwMDUyNjI4WjAVAgQvrj8/Fw0wMzAyMjYxMTA0"
-            +"NDRaMBUCBC+uPz4XDTAzMDIyNjExMDQ0NFowFQIEL64+zRcNMDMwNTIwMDUyNzM2"
-            +"WjAVAgQvrj7MFw0wMzA1MjAwNTI3MzZaMBUCBC+uPjwXDTAzMDYxNzEwMzQxNlow"
-            +"FQIEL64+OxcNMDMwNjE3MTAzNDE2WjAVAgQvrj46Fw0wMzA2MTcxMDM0MTZaMBUC"
-            +"BC+uPjkXDTAzMDYxNzEzMDEwMFowFQIEL64+OBcNMDMwNjE3MTMwMTAwWjAVAgQv"
-            +"rj43Fw0wMzA2MTcxMzAxMDBaMBUCBC+uPjYXDTAzMDYxNzEzMDEwMFowFQIEL64+"
-            +"MxcNMDMwNjE3MTAzNzQ5WjAVAgQvrj4xFw0wMzA2MTcxMDQyNThaMBUCBC+uPjAX"
-            +"DTAzMDYxNzEwNDI1OFowFQIEL649qRcNMDMxMDIyMTEzMjI0WjAVAgQvrjyyFw0w"
-            +"NTAzMTEwNjQ0MjRaMBUCBC+uPKsXDTA0MDQwMjA3NTQ1M1owFQIEL6466BcNMDUw"
-            +"MTI3MTIwMzI0WjAVAgQvrjq+Fw0wNTAyMTYwNzU3MTZaMBUCBC+uOqcXDTA1MDMx"
-            +"MDA1NTkzNVowFQIEL646PBcNMDUwNTExMTA0OTQ2WjAVAgQvrG3VFw0wNTExMTEx"
-            +"MDAzMjFaMBUCBC+uLmgXDTA2MDEyMzEwMjU1NVowFQIEL64mxxcNMDYwODAxMDk0"
-            +"ODQ0WqCBijCBhzALBgNVHRQEBAICEQwwHwYDVR0jBBgwFoAUA1vI26YMj3njkfCU"
-            +"IXbo244kLjkwVwYDVR0SBFAwToZMbGRhcDovL3Brc2xkYXAudHR0Yy5kZS9vdT1U"
-            +"LVRlbGVTZWMgVGVzdCBESVIgODpQTixvPURldXRzY2hlIFRlbGVrb20gQUcsYz1k"
-            +"ZTAKBgYrJAMDAQIFAAOBgQArj4eMlbAwuA2aS5O4UUUHQMKKdK/dtZi60+LJMiMY"
-            +"ojrMIf4+ZCkgm1Ca0Cd5T15MJxVHhh167Ehn/Hd48pdnAP6Dfz/6LeqkIHGWMHR+"
-            +"z6TXpwWB+P4BdUec1ztz04LypsznrHcLRa91ixg9TZCb1MrOG+InNhleRs1ImXk8"
-            +"MQ==");
-
     private final byte[] pkcs7CrlProblem = Base64.decode(
-              "MIIwSAYJKoZIhvcNAQcCoIIwOTCCMDUCAQExCzAJBgUrDgMCGgUAMAsGCSqG"
+        "MIIwSAYJKoZIhvcNAQcCoIIwOTCCMDUCAQExCzAJBgUrDgMCGgUAMAsGCSqG"
             + "SIb3DQEHAaCCEsAwggP4MIIC4KADAgECAgF1MA0GCSqGSIb3DQEBBQUAMEUx"
             + "CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR4wHAYDVQQD"
             + "ExVHZW9UcnVzdCBDQSBmb3IgQWRvYmUwHhcNMDQxMjAyMjEyNTM5WhcNMDYx"
@@ -954,7 +758,7 @@
             + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==");
 
     private final byte[] emptyDNCert = Base64.decode(
-              "MIICfTCCAeagAwIBAgIBajANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJVUzEMMAoGA1UEChMD"
+        "MIICfTCCAeagAwIBAgIBajANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJVUzEMMAoGA1UEChMD"
             + "Q0RXMQkwBwYDVQQLEwAxCTAHBgNVBAcTADEJMAcGA1UECBMAMRowGAYDVQQDExFUZW1wbGFyIFRl"
             + "c3QgMTAyNDEiMCAGCSqGSIb3DQEJARYTdGVtcGxhcnRlc3RAY2R3LmNvbTAeFw0wNjA1MjIwNTAw"
             + "MDBaFw0xMDA1MjIwNTAwMDBaMHwxCzAJBgNVBAYTAlVTMQwwCgYDVQQKEwNDRFcxCTAHBgNVBAsT"
@@ -981,7 +785,7 @@
             "g1DjB8zy5DEjiULIc+HeIveF81W9lOxGkZxnrFjXBSqnjLeFKgF1hffXOAP7zUM=");
 
     private final byte[] gostRFC4491_2001 = Base64.decode(
-            "MIIB0DCCAX8CECv1xh7CEb0Xx9zUYma0LiEwCAYGKoUDAgIDMG0xHzAdBgNVBAMM" +
+        "MIIB0DCCAX8CECv1xh7CEb0Xx9zUYma0LiEwCAYGKoUDAgIDMG0xHzAdBgNVBAMM" +
             "Fkdvc3RSMzQxMC0yMDAxIGV4YW1wbGUxEjAQBgNVBAoMCUNyeXB0b1BybzELMAkG" +
             "A1UEBhMCUlUxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDAxQGV4YW1wbGUu" +
             "Y29tMB4XDTA1MDgxNjE0MTgyMFoXDTE1MDgxNjE0MTgyMFowbTEfMB0GA1UEAwwW" +
@@ -991,9 +795,9 @@
             "CM0TjDGJLP3lBQN6Q1z0bSsP508yfleP68wWuZWIA9CafIWuD+SN6qa7flbHy7Df" +
             "D2a8yuoaYDAIBgYqhQMCAgMDQQA8L8kJRLcnqeyn1en7U23Sw6pkfEQu3u0xFkVP" +
             "vFQ/3cHeF26NG+xxtZPz3TaTVXdoiYkXYiD02rEx1bUcM97i");
-    
+
     private final byte[] uaczo1 = Base64.decode(
-            "MIIFWzCCBNegAwIBAgIUMAR1He8seK4BAAAAAQAAAAEAAAAwDQYLKoYkAgEBAQED" +
+        "MIIFWzCCBNegAwIBAgIUMAR1He8seK4BAAAAAQAAAAEAAAAwDQYLKoYkAgEBAQED" +
             "AQEwgfoxPzA9BgNVBAoMNtCc0ZbQvdGW0YHRgtC10YDRgdGC0LLQviDRjtGB0YLQ" +
             "uNGG0ZbRlyDQo9C60YDQsNGX0L3QuDExMC8GA1UECwwo0JDQtNC80ZbQvdGW0YHR" +
             "gtGA0LDRgtC+0YAg0IbQotChINCm0JfQnjFJMEcGA1UEAwxA0KbQtdC90YLRgNCw" +
@@ -1022,9 +826,9 @@
             "DQYLKoYkAgEBAQEDAQEDbwAEbPF4bx7drDxzzYABhB33Y0MQ+/N5FuPl7faVx/es" +
             "V5n5DXg5TzZovzZeICB5JHPLcbdeCq6aGwvXsgybt34zqf7LKmfq0rFNYfXJVWFH" +
             "4Tg5sPA+fCQ+T0O35VN873BLgTGz7bnHH9o8bnjwMA==");
-    
+
     private final byte[] uaczo2 = Base64.decode(
-            "MIIEvTCCBDmgAwIBAgIDAYhwMA0GCyqGJAIBAQEBAwEBMIIBHjELMAkGA1UEBhMC" +
+        "MIIEvTCCBDmgAwIBAgIDAYhwMA0GCyqGJAIBAQEBAwEBMIIBHjELMAkGA1UEBhMC" +
             "VUExKDAmBgNVBAgMH9Ca0LjRl9Cy0YHRjNC60LAg0L7QsdC70LDRgdGC0YwxETAP" +
             "BgNVBAcMCNCa0LjRl9CyMUkwRwYDVQQKDEDQptC10L3RgtGA0LDQu9GM0L3QuNC5" +
             "INC30LDRgdCy0ZbQtNGH0YPQstCw0LvRjNC90LjQuSDQvtGA0LPQsNC9MTUwMwYD" +
@@ -1050,9 +854,9 @@
             "A28ABGx8QNaWcy0admsBt6iB0Vi+kAargzsQuoc/BThskYdxGNftLvYDPYxkEM2N" +
             "GQ+9f1RJgCSNVRj3NhWoHhkqcL5R3gxAHie+a+zMqsX0258hGdT3MXkm0Syn/cNo" +
             "sga4XzzvnVaas9vsPKMrZTQ=");
-    
+
     private final byte[] uaczo3 = Base64.decode(
-            "MIIEtTCCBDGgAwIBAgIDAYisMA0GCyqGJAIBAQEBAwEBMIIBGjELMAkGA1UEBhMC" +
+        "MIIEtTCCBDGgAwIBAgIDAYisMA0GCyqGJAIBAQEBAwEBMIIBGjELMAkGA1UEBhMC" +
             "VUExKDAmBgNVBAgMH9Ca0LjRl9Cy0YHRjNC60LAg0L7QsdC70LDRgdGC0YwxETAP" +
             "BgNVBAcMCNCa0LjRl9CyMUkwRwYDVQQKDEDQptC10L3RgtGA0LDQu9GM0L3QuNC5" +
             "INC30LDRgdCy0ZbQtNGH0YPQstCw0LvRjNC90LjQuSDQvtGA0LPQsNC9MTEwLwYD" +
@@ -1078,9 +882,9 @@
             "ajEgdBh5mPUZefcLY56AIRWqmsJsWuZuUbCa5oQXRH5iCRa4PSvs8v6zHAKKlMgK" +
             "gaoY6jywqmwiMlylbSgo/A0HKdCFnUUl7S8yjE4054MSSIjb2R0c2pmqmwtU25JB" +
             "/MkNbe77Uzka");
-    
+
     private final byte[] uaczo4 = Base64.decode(
-            "MIIEKzCCA6egAwIBAgIBATANBgsqhiQCAQEBAQMBATCBzDFJMEcGA1UECwxA0KbQ" +
+        "MIIEKzCCA6egAwIBAgIBATANBgsqhiQCAQEBAQMBATCBzDFJMEcGA1UECwxA0KbQ" +
             "tdC90YLRgNCw0LvRjNC90LjQuSDQt9Cw0YHQstGW0LTRh9GD0LLQsNC70YzQvdC4" +
             "0Lkg0L7RgNCz0LDQvTE1MDMGA1UEAwws0KPQutGA0LDRl9C90LAsINCm0JfQniAv" +
             "IFVrcmFpbmUsIENlbnRyYWwgQ0ExCzAJBgNVBAYTAlVBMREwDwYDVQQHDAjQmtC4" +
@@ -1106,28 +910,28 @@
 
     private final byte[] sha3Cert = Base64.decode(
         "MIID8jCCAqagAwIBAgIICfBykpzUT+IwQQYJKoZIhvcNAQEKMDSgDzANBglg"
-      + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
-      + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
-      + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
-      + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
-      + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
-      + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
-      + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
-      + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
-      + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
-      + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
-      + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
-      + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
-      + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
-      + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
-      + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
-      + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
-      + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
-      + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
-      + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
-      + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
-      + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
-      + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
+            + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
+            + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
+            + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
+            + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
+            + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
+            + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
+            + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
+            + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
+            + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
+            + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
+            + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
+            + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
+            + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
+            + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
+            + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
+            + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
+            + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
+            + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
+            + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
+            + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
+            + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
+            + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
 
     private final String ecPemCert =
         "-----BEGIN CERTIFICATE-----\n" +
@@ -1145,59 +949,91 @@
             "-----END CERTIFICATE-----";
 
     private final String pemPKCS7 =
-        "-----BEGIN PKCS7-----\n"+
-        "MIIJDAYJKoZIhvcNAQcCoIII/TCCCPkCAQExADALBgkqhkiG9w0BBwGgggjfMIIF\n"+
-        "wTCCBKmgAwIBAgIJ+pQ4odKc8AABMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNVBAYT\n"+
-        "AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQHEwlTYW4gTWF0ZW8xFzAV\n"+
-        "BgNVBAoTDkdlbml1cy5jb20gSW5jMQ8wDQYDVQQLEwZOZXRPcHMwHhcNMTQwMTI4\n"+
-        "MjE0MjE0WhcNMjQwMTI2MjE0MjE0WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMK\n"+
-        "Q2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIE1hdGVvMRcwFQYDVQQKEw5HZW5pdXMu\n"+
-        "Y29tIEluYzEPMA0GA1UECxMGTmV0T3BzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"+
-        "MIIBCgKCAQEArfmkkDffJP6ODl13KnTaB8cwvB4anWw8+bGa8y9N7wPx7RWZWFMr\n"+
-        "fOac01p2fhq+oUIw3/uxRcDAQBQx0ZFLx3OFMuQkTpFbzHeSctsXi1Kk28pn4K3B\n"+
-        "K2CModRh8ir/qdhu0PG4SsXdyN8uT8H6bitmH4vpLaAMMi6aa1M6Ygio8a37UCQQ\n"+
-        "7fw2P7YVR61BsyqwsM/eYtgd2LqrObLwkkOvxTwpZPWDftHI4ucz1rgNnD9q0H3g\n"+
-        "kyGyGq9NBkBHJ25+CkMe+1q/eh4Xt2kt2ML4q5YZmQEwHm1eIR3/uGlb1+bueRMd\n"+
-        "hrueth/FsUiKPJ0gzmsxzQefgcLnctIx3wIDAQABo4ICfDCCAngwCQYDVR0TBAIw\n"+
-        "ADALBgNVHQ8EBAMCBeAwHQYDVR0OBBYEFJ/uU/wudzNDSI/SWkNNTXNLq2EIMIGS\n"+
-        "BgNVHSMEgYowgYeAFJ/uU/wudzNDSI/SWkNNTXNLq2EIoWSkYjBgMQswCQYDVQQG\n"+
-        "EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIE1hdGVvMRcw\n"+
-        "FQYDVQQKEw5HZW5pdXMuY29tIEluYzEPMA0GA1UECxMGTmV0T3Bzggn6lDih0pzw\n"+
-        "AAEwHgYJYIZIAYb4QgENBBEWD1guNTA5IFVuaXQgVGVzdDCBwwYDVR0RBIG7MIG4\n"+
-        "oA4GAyoDBKAHDAV0ZXN0MYEQeDUwOUBleGFtcGxlLmNvbYIQeDUwOS5leGFtcGxl\n"+
-        "LmNvbaRQME4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1Bd2Vzb21lIER1ZGVzMRcw\n"+
-        "FQYDVQQLFA7DnGJlciBGcsOuZW5kczEOMAwGA1UEAxQF4oiGxpKGJWh0dHA6Ly93\n"+
-        "d3cuZXhhbXBsZS5jb20vP3E9YXdlc29tZW5lc3OHBMCoAAGIAyoDBDCBwwYDVR0S\n"+
-        "BIG7MIG4oA4GAyoDBKAHDAV0ZXN0MYEQeDUwOUBleGFtcGxlLmNvbYIQeDUwOS5l\n"+
-        "eGFtcGxlLmNvbaRQME4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1Bd2Vzb21lIER1\n"+
-        "ZGVzMRcwFQYDVQQLFA7DnGJlciBGcsOuZW5kczEOMAwGA1UEAxQF4oiGxpKGJWh0\n"+
-        "dHA6Ly93d3cuZXhhbXBsZS5jb20vP3E9YXdlc29tZW5lc3OHBMCoAAGIAyoDBDAN\n"+
-        "BgkqhkiG9w0BAQUFAAOCAQEAQK5jBzTq2lX1GpVD9RHxtTHJn/WkYOpMJYJruw8j\n"+
-        "HGfQwAkhlL9AqWgodTruoTnXgZbA7F3S8hx9gmUbHVjVeBvxZnGEJ8g7So1erFKv\n"+
-        "yQD1Ajtn7+uGXw6s0Dvde2ZVzV05pRk9ybg7kxKNXvVbKS3kyd6XoA27H5CSmzDu\n"+
-        "8cwHQkN4mJlwAiNCwMarpN4m4X0rQ+g1Ncfq+4sRjFLd8VVCbCpzD8UMBOVTpxxj\n"+
-        "kSyRPJZ7Db8SY0H2vcTUj2Yyog1RQ+RA/xp7Fgw+leEiveIE23Dq62hCHq6rU5Vj\n"+
-        "6L/LlLiKZ17lZT4z0fJ0lukPUpmVTynALKsKNm57+fOfnzCCAxYwggLWoAMCAQIC\n"+
-        "CQDcaK5WyhbztjAJBgcqhkjOOAQDMGAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD\n"+
-        "YWxpZm9ybmlhMRIwEAYDVQQHEwlTYW4gTWF0ZW8xFzAVBgNVBAoTDkdlbml1cy5j\n"+
-        "b20gSW5jMQ8wDQYDVQQLEwZOZXRPcHMwHhcNMTQwMTI4MjE0MjE1WhcNMTQwMjI3\n"+
-        "MjE0MjE1WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAG\n"+
-        "A1UEBxMJU2FuIE1hdGVvMRcwFQYDVQQKEw5HZW5pdXMuY29tIEluYzEPMA0GA1UE\n"+
-        "CxMGTmV0T3BzMIIBtzCCASsGByqGSM44BAEwggEeAoGBAIiv42coWuyVXpYoyEwf\n"+
-        "7uevd4ILhylFuvKH5tRWRcZENuxPOmXfr3L43PCdbnJhXMg3RkkWgjaE7lBk5evx\n"+
-        "LKH6rU2a1GnGmoY34OIvVvGL3xi96uYTTvLIX3+6NXaAlNppIBSHXcYx8cMdtYIn\n"+
-        "3J6JGSHEPo36ay4rDZbfS1frAhUAxF6k+/9T00QMolE41R+6ytzrawkCgYA4soyt\n"+
-        "rrIrQq6gwm2HanT8coIChr3/Et8rMamj7gS1yT9kH8HNGf217XtE3f/LUZZWUkBq\n"+
-        "3PNOuxhprNmvSAdQ7ZzhWfRvOFHKaH/DtKvLeEC5I00DfYSI64/V869Jy7lnyY7M\n"+
-        "h7ShLIwOlwnBDIL5oluircfXTr20a/Jv9pS1AAOBhQACgYEAhg6lELBZAIHVkjm7\n"+
-        "bwVJ5G/ka+KCjXxWXo+BCbqo0LqfrKQoQwUcwDzuKdqWxYbyUd0cl5/9fX59/RT/\n"+
-        "9ULklGy+dTyUSc/hj85PCXLYly3G6WECiN29TK0QLhEMZfi+iSm3YxNX3rxvmrHb\n"+
-        "bfO2SMef4r6ujv9KscDg0zQ4AgajGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgA3\n"+
-        "MAkGByqGSM44BAMDLwAwLAIUVcYZ1LNv22fDBiajZ99FpTn05SMCFCgMXzUGLdPy\n"+
-        "gY460q7tGpuydry+oQAxAA==\n"+
-        "-----END PKCS7-----\n";
+        "-----BEGIN PKCS7-----\n" +
+            "MIIJDAYJKoZIhvcNAQcCoIII/TCCCPkCAQExADALBgkqhkiG9w0BBwGgggjfMIIF\n" +
+            "wTCCBKmgAwIBAgIJ+pQ4odKc8AABMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNVBAYT\n" +
+            "AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQHEwlTYW4gTWF0ZW8xFzAV\n" +
+            "BgNVBAoTDkdlbml1cy5jb20gSW5jMQ8wDQYDVQQLEwZOZXRPcHMwHhcNMTQwMTI4\n" +
+            "MjE0MjE0WhcNMjQwMTI2MjE0MjE0WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMK\n" +
+            "Q2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIE1hdGVvMRcwFQYDVQQKEw5HZW5pdXMu\n" +
+            "Y29tIEluYzEPMA0GA1UECxMGTmV0T3BzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+            "MIIBCgKCAQEArfmkkDffJP6ODl13KnTaB8cwvB4anWw8+bGa8y9N7wPx7RWZWFMr\n" +
+            "fOac01p2fhq+oUIw3/uxRcDAQBQx0ZFLx3OFMuQkTpFbzHeSctsXi1Kk28pn4K3B\n" +
+            "K2CModRh8ir/qdhu0PG4SsXdyN8uT8H6bitmH4vpLaAMMi6aa1M6Ygio8a37UCQQ\n" +
+            "7fw2P7YVR61BsyqwsM/eYtgd2LqrObLwkkOvxTwpZPWDftHI4ucz1rgNnD9q0H3g\n" +
+            "kyGyGq9NBkBHJ25+CkMe+1q/eh4Xt2kt2ML4q5YZmQEwHm1eIR3/uGlb1+bueRMd\n" +
+            "hrueth/FsUiKPJ0gzmsxzQefgcLnctIx3wIDAQABo4ICfDCCAngwCQYDVR0TBAIw\n" +
+            "ADALBgNVHQ8EBAMCBeAwHQYDVR0OBBYEFJ/uU/wudzNDSI/SWkNNTXNLq2EIMIGS\n" +
+            "BgNVHSMEgYowgYeAFJ/uU/wudzNDSI/SWkNNTXNLq2EIoWSkYjBgMQswCQYDVQQG\n" +
+            "EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIE1hdGVvMRcw\n" +
+            "FQYDVQQKEw5HZW5pdXMuY29tIEluYzEPMA0GA1UECxMGTmV0T3Bzggn6lDih0pzw\n" +
+            "AAEwHgYJYIZIAYb4QgENBBEWD1guNTA5IFVuaXQgVGVzdDCBwwYDVR0RBIG7MIG4\n" +
+            "oA4GAyoDBKAHDAV0ZXN0MYEQeDUwOUBleGFtcGxlLmNvbYIQeDUwOS5leGFtcGxl\n" +
+            "LmNvbaRQME4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1Bd2Vzb21lIER1ZGVzMRcw\n" +
+            "FQYDVQQLFA7DnGJlciBGcsOuZW5kczEOMAwGA1UEAxQF4oiGxpKGJWh0dHA6Ly93\n" +
+            "d3cuZXhhbXBsZS5jb20vP3E9YXdlc29tZW5lc3OHBMCoAAGIAyoDBDCBwwYDVR0S\n" +
+            "BIG7MIG4oA4GAyoDBKAHDAV0ZXN0MYEQeDUwOUBleGFtcGxlLmNvbYIQeDUwOS5l\n" +
+            "eGFtcGxlLmNvbaRQME4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1Bd2Vzb21lIER1\n" +
+            "ZGVzMRcwFQYDVQQLFA7DnGJlciBGcsOuZW5kczEOMAwGA1UEAxQF4oiGxpKGJWh0\n" +
+            "dHA6Ly93d3cuZXhhbXBsZS5jb20vP3E9YXdlc29tZW5lc3OHBMCoAAGIAyoDBDAN\n" +
+            "BgkqhkiG9w0BAQUFAAOCAQEAQK5jBzTq2lX1GpVD9RHxtTHJn/WkYOpMJYJruw8j\n" +
+            "HGfQwAkhlL9AqWgodTruoTnXgZbA7F3S8hx9gmUbHVjVeBvxZnGEJ8g7So1erFKv\n" +
+            "yQD1Ajtn7+uGXw6s0Dvde2ZVzV05pRk9ybg7kxKNXvVbKS3kyd6XoA27H5CSmzDu\n" +
+            "8cwHQkN4mJlwAiNCwMarpN4m4X0rQ+g1Ncfq+4sRjFLd8VVCbCpzD8UMBOVTpxxj\n" +
+            "kSyRPJZ7Db8SY0H2vcTUj2Yyog1RQ+RA/xp7Fgw+leEiveIE23Dq62hCHq6rU5Vj\n" +
+            "6L/LlLiKZ17lZT4z0fJ0lukPUpmVTynALKsKNm57+fOfnzCCAxYwggLWoAMCAQIC\n" +
+            "CQDcaK5WyhbztjAJBgcqhkjOOAQDMGAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD\n" +
+            "YWxpZm9ybmlhMRIwEAYDVQQHEwlTYW4gTWF0ZW8xFzAVBgNVBAoTDkdlbml1cy5j\n" +
+            "b20gSW5jMQ8wDQYDVQQLEwZOZXRPcHMwHhcNMTQwMTI4MjE0MjE1WhcNMTQwMjI3\n" +
+            "MjE0MjE1WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAG\n" +
+            "A1UEBxMJU2FuIE1hdGVvMRcwFQYDVQQKEw5HZW5pdXMuY29tIEluYzEPMA0GA1UE\n" +
+            "CxMGTmV0T3BzMIIBtzCCASsGByqGSM44BAEwggEeAoGBAIiv42coWuyVXpYoyEwf\n" +
+            "7uevd4ILhylFuvKH5tRWRcZENuxPOmXfr3L43PCdbnJhXMg3RkkWgjaE7lBk5evx\n" +
+            "LKH6rU2a1GnGmoY34OIvVvGL3xi96uYTTvLIX3+6NXaAlNppIBSHXcYx8cMdtYIn\n" +
+            "3J6JGSHEPo36ay4rDZbfS1frAhUAxF6k+/9T00QMolE41R+6ytzrawkCgYA4soyt\n" +
+            "rrIrQq6gwm2HanT8coIChr3/Et8rMamj7gS1yT9kH8HNGf217XtE3f/LUZZWUkBq\n" +
+            "3PNOuxhprNmvSAdQ7ZzhWfRvOFHKaH/DtKvLeEC5I00DfYSI64/V869Jy7lnyY7M\n" +
+            "h7ShLIwOlwnBDIL5oluircfXTr20a/Jv9pS1AAOBhQACgYEAhg6lELBZAIHVkjm7\n" +
+            "bwVJ5G/ka+KCjXxWXo+BCbqo0LqfrKQoQwUcwDzuKdqWxYbyUd0cl5/9fX59/RT/\n" +
+            "9ULklGy+dTyUSc/hj85PCXLYly3G6WECiN29TK0QLhEMZfi+iSm3YxNX3rxvmrHb\n" +
+            "bfO2SMef4r6ujv9KscDg0zQ4AgajGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgA3\n" +
+            "MAkGByqGSM44BAMDLwAwLAIUVcYZ1LNv22fDBiajZ99FpTn05SMCFCgMXzUGLdPy\n" +
+            "gY460q7tGpuydry+oQAxAA==\n" +
+            "-----END PKCS7-----\n";
 
     private static byte[] sm_root = Base64.decode(
+        "MIICDTCCAbGgAwIBAgIEHPP3OTAMBggqgRzPVQGDdQUAMFoxCzAJBgNVBAYTAkNO"
+            + "MTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp"
+            + "dHkxGTAXBgNVBAMMEENGQ0EgRVYgU00yIFJPT1QwHhcNMTIwODA4MDMwNjMwWhcN"
+            + "MjkxMjMxMDMwNjMwWjBaMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmlu"
+            + "YW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRkwFwYDVQQDDBBDRkNBIEVW"
+            + "IFNNMiBST09UMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/9xokvYPq1PPsvqh"
+            + "wzc1OvhRJyqfm+FeefW522OMUJeSqmaYRcwAaEC1IH03etyYEOD4R4HQG+ovJr4z"
+            + "PLZzUqNjMGEwHwYDVR0jBBgwFoAUXxyJKUK15hS66W6X7kBqaMo9lLgwDwYDVR0T"
+            + "AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFF8ciSlCteYUuulu"
+            + "l+5AamjKPZS4MAwGCCqBHM9VAYN1BQADSAAwRQIgbE/XnzWMMQItSfz/LH6CyNz1"
+            + "OxFwvI6WcgcqGGUdCiMCIQDRFwF7M4Cvo7KqGMNuSiByFNUX9PJYXByjxqPart9U"
+            + "tw==");
+
+    private static byte[] sm_sign = Base64.decode(
+        "MIICzTCCAnKgAwIBAgIGAIgmba9KMAwGCCqBHM9VAYN1BQAwWjELMAkGA1UEBhMC"
+            + "Q04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv"
+            + "cml0eTEZMBcGA1UEAwwQQ0ZDQSBFViBTTTIgUk9PVDAeFw0xMjA4MDgwNTU2Mjda"
+            + "Fw0yOTEyMjkwNTU2MjdaMFkxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBG"
+            + "aW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGDAWBgNVBAMMD0NGQ0Eg"
+            + "RVYgU00yIE9DQTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABG6sjKVpVukkQpY1"
+            + "nokr6wmp44hwkVnzmdXvuBbq/VtwB/8V+awkIfpz4THaSjPGzSGh+hwFcka0NCFK"
+            + "TQ7y8rqjggEhMIIBHTA4BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6"
+            + "Ly9vY3NwLmNmY2EuY29tLmNuL29jc3AwHwYDVR0jBBgwFoAUXxyJKUK15hS66W6X"
+            + "7kBqaMo9lLgwDwYDVR0TAQH/BAUwAwEB/zBEBgNVHSAEPTA7MDkGBFUdIAAwMTAv"
+            + "BggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xMi5odG0w"
+            + "OgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5jZmNhLmNvbS5jbi9ldnJjYS9T"
+            + "TTIvY3JsMS5jcmwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTDJFfnVXuTdN3s"
+            + "yIha7AIm0b8vWTAMBggqgRzPVQGDdQUAA0cAMEQCIBgrKO75mE5lfONElZZzkAWh"
+            + "eb0R0ai6/J7nj7SCZ3jJAiAE2dKJIv9ROkN17bhZpXsVrOtyYULW7YzQePqnNN58"
+            + "MA==");
+
+    private static byte[] sm_root1 = Base64.decode(
         "MIICwzCCAmmgAwIBAgIIIBQGIgAAAAMwCgYIKoEcz1UBg3UwgdgxCzAJBgNVBAYT" +
             "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
             "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
@@ -1214,7 +1050,7 @@
             "BjAKBggqgRzPVQGDdQNIADBFAiEAy9NkogihHCj9Jx0ZiHdkMyCHF0wHWX58KZco" +
             "CW5mjbgCIC9cAyuVV91ygLWk14PDuIAPFWKm6rJPXbiZL6KzwHQA");
 
-    private static byte[] sm_ca = Base64.decode(
+    private static byte[] sm_ca1 = Base64.decode(
          "MIIC/TCCAqKgAwIBAgIIIBYDKQETeGQwCgYIKoEcz1UBg3UwgccxCzAJBgNVBAYT" +
              "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
              "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
@@ -1233,7 +1069,7 @@
              "oSdFZLZDB4uDlOIqU9wCIQDXmE1iiCsWi1RmdoY+/ics2ZlY8vyHWBJnZ+XFy1Jb" +
              "fA==");
 
-    private static byte[] sm_sign = Base64.decode(
+    private static byte[] sm_sign1 = Base64.decode(
         "MIID9zCCA5ygAwIBAgIIIBcEJwKSCCMwCgYIKoEcz1UBg3UwgccxCzAJBgNVBAYT" +
         "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
         "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
@@ -1257,7 +1093,17 @@
         "tXfmoyePz1pmv0CWPBgEP1EfDeS6FPitAiEAjHJYq7ryHKULqpRg6ph9r+xUDoWd" +
         "0TPMOQ9jj4XJPO4=");
 
-    private PublicKey dudPublicKey = new PublicKey() 
+    private byte[] x25519Cert = Base64.decode(
+        "MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX" +
+            "N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD" +
+            "DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj" +
+            "ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg" +
+            "BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v" +
+            "/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg" +
+            "w1AH9efZBw=="
+    );
+
+    private PublicKey dudPublicKey = new PublicKey()
     {
         public String getAlgorithm()
         {
@@ -1275,54 +1121,54 @@
         }
 
     };
-    
+
     public String getName()
     {
         return "CertTest";
     }
 
     public void checkCertificate(
-        int     id,
-        byte[]  bytes)
+        int id,
+        byte[] bytes)
     {
-        ByteArrayInputStream    bIn;
-        String                  dump = "";
+        ByteArrayInputStream bIn;
+        String dump = "";
 
         try
         {
             bIn = new ByteArrayInputStream(bytes);
 
-            CertificateFactory  fact = CertificateFactory.getInstance("X.509", "BC");
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
 
             Certificate cert = fact.generateCertificate(bIn);
 
-            PublicKey    k = cert.getPublicKey();
+            PublicKey k = cert.getPublicKey();
 
-            // System.out.println(cert);
+//            System.out.println(cert);
         }
         catch (Exception e)
         {
-            fail(dump + Strings.lineSeparator() + getName() + ": "+ id + " failed - exception " + e.toString(), e);
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
         }
 
     }
 
     public void checkNameCertificate(
-        int     id,
-        byte[]  bytes)
+        int id,
+        byte[] bytes)
     {
-        ByteArrayInputStream    bIn;
-        String                  dump = "";
+        ByteArrayInputStream bIn;
+        String dump = "";
 
         try
         {
             bIn = new ByteArrayInputStream(bytes);
 
-            CertificateFactory  fact = CertificateFactory.getInstance("X.509", "BC");
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
 
             X509Certificate cert = (X509Certificate)fact.generateCertificate(bIn);
 
-            PublicKey    k = cert.getPublicKey();
+            PublicKey k = cert.getPublicKey();
             if (!cert.getIssuerDN().toString().equals("C=DE,O=DATEV eG,0.2.262.1.10.7.20=1+CN=CA DATEV D03 1:PN"))
             {
                 fail(id + " failed - name test.");
@@ -1331,27 +1177,27 @@
         }
         catch (Exception e)
         {
-            fail(dump + Strings.lineSeparator() + getName() + ": "+ id + " failed - exception " + e.toString(), e);
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
         }
 
     }
 
     public void checkKeyUsage(
-        int     id,
-        byte[]  bytes)
+        int id,
+        byte[] bytes)
     {
-        ByteArrayInputStream    bIn;
-        String                  dump = "";
+        ByteArrayInputStream bIn;
+        String dump = "";
 
         try
         {
             bIn = new ByteArrayInputStream(bytes);
 
-            CertificateFactory  fact = CertificateFactory.getInstance("X.509", "BC");
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
 
             X509Certificate cert = (X509Certificate)fact.generateCertificate(bIn);
 
-            PublicKey    k = cert.getPublicKey();
+            PublicKey k = cert.getPublicKey();
 
             if (cert.getKeyUsage()[7])
             {
@@ -1362,29 +1208,28 @@
         }
         catch (Exception e)
         {
-            fail(dump + Strings.lineSeparator() + getName() + ": "+ id + " failed - exception " + e.toString(), e);
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
         }
 
     }
 
-
     public void checkSelfSignedCertificate(
-        int     id,
-        byte[]  bytes,
-        String  sigAlgName)
+        int id,
+        byte[] bytes,
+        String sigAlgName)
     {
-        ByteArrayInputStream    bIn;
-        String                  dump = "";
+        ByteArrayInputStream bIn;
+        String dump = "";
 
         try
         {
             bIn = new ByteArrayInputStream(bytes);
 
-            CertificateFactory  fact = CertificateFactory.getInstance("X.509", "BC");
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
 
             Certificate cert = fact.generateCertificate(bIn);
 
-            PublicKey    k = cert.getPublicKey();
+            PublicKey k = cert.getPublicKey();
 
             cert.verify(k);
             if (sigAlgName != null && !sigAlgName.equals(((X509Certificate)cert).getSigAlgName()))
@@ -1400,23 +1245,58 @@
         }
         catch (Exception e)
         {
-            fail(dump + Strings.lineSeparator() + getName() + ": "+ id + " failed - exception " + e.toString(), e);
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
         }
 
     }
-    
-    private void checkCRL(
-        int     id,
-        byte[]  bytes)
+
+    public void checkCertificateSignedBy(
+        int id,
+        byte[] certBytes,
+        byte[] signingCertBytes)
     {
-        ByteArrayInputStream    bIn;
-        String                  dump = "";
+        ByteArrayInputStream bIn;
+        String dump = "";
+
+        try
+        {
+            bIn = new ByteArrayInputStream(certBytes);
+
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
+
+            Certificate cert = fact.generateCertificate(bIn);
+
+            bIn = new ByteArrayInputStream(signingCertBytes);
+
+            PublicKey k = fact.generateCertificate(bIn).getPublicKey();
+
+            cert.verify(k);
+
+            // System.out.println(cert);
+        }
+        catch (TestFailedException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
+        }
+
+    }
+
+    private void checkCRL(
+        int id,
+        byte[] bytes)
+    {
+        ByteArrayInputStream bIn;
+        String dump = "";
 
         try
         {
             bIn = new ByteArrayInputStream(bytes);
 
-            CertificateFactory  fact = CertificateFactory.getInstance("X.509", "BC");
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
 
             CRL cert = fact.generateCRL(bIn);
 
@@ -1424,32 +1304,32 @@
         }
         catch (Exception e)
         {
-            fail(dump + Strings.lineSeparator() + getName() + ": "+ id + " failed - exception " + e.toString(), e);
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
         }
 
     }
 
-    private void testForgedSignature() 
+    private void testForgedSignature()
         throws Exception
     {
         String cert = "MIIBsDCCAVoCAQYwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV"
-                    + "BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD"
-                    + "VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw0wNjA5MTEyMzU4NTVa"
-                    + "Fw0wNjEwMTEyMzU4NTVaMGMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpRdWVlbnNs"
-                    + "YW5kMRowGAYDVQQKExFDcnlwdFNvZnQgUHR5IEx0ZDEjMCEGA1UEAxMaU2VydmVy"
-                    + "IHRlc3QgY2VydCAoNTEyIGJpdCkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAn7PD"
-                    + "hCeV/xIxUg8V70YRxK2A5jZbD92A12GN4PxyRQk0/lVmRUNMaJdq/qigpd9feP/u"
-                    + "12S4PwTLb/8q/v657QIDAQABMA0GCSqGSIb3DQEBBQUAA0EAbynCRIlUQgaqyNgU"
-                    + "DF6P14yRKUtX8akOP2TwStaSiVf/akYqfLFm3UGka5XbPj4rifrZ0/sOoZEEBvHQ"
-                    + "e20sRA==";
-        
+            + "BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD"
+            + "VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw0wNjA5MTEyMzU4NTVa"
+            + "Fw0wNjEwMTEyMzU4NTVaMGMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpRdWVlbnNs"
+            + "YW5kMRowGAYDVQQKExFDcnlwdFNvZnQgUHR5IEx0ZDEjMCEGA1UEAxMaU2VydmVy"
+            + "IHRlc3QgY2VydCAoNTEyIGJpdCkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAn7PD"
+            + "hCeV/xIxUg8V70YRxK2A5jZbD92A12GN4PxyRQk0/lVmRUNMaJdq/qigpd9feP/u"
+            + "12S4PwTLb/8q/v657QIDAQABMA0GCSqGSIb3DQEBBQUAA0EAbynCRIlUQgaqyNgU"
+            + "DF6P14yRKUtX8akOP2TwStaSiVf/akYqfLFm3UGka5XbPj4rifrZ0/sOoZEEBvHQ"
+            + "e20sRA==";
+
         CertificateFactory certFact = CertificateFactory.getInstance("X.509", "BC");
-        
+
         X509Certificate x509 = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(Base64.decode(cert)));
         try
         {
             x509.verify(x509.getPublicKey());
-            
+
             fail("forged RSA signature passed");
         }
         catch (Exception e)
@@ -1629,18 +1509,18 @@
         throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException
     {
         RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
-            new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
-            new BigInteger("010001",16));
+            new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137", 16),
+            new BigInteger("010001", 16));
 
         RSAPrivateCrtKeySpec privKeySpec = new RSAPrivateCrtKeySpec(
-            new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
-            new BigInteger("010001",16),
-            new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325",16),
-            new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443",16),
-            new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd",16),
-            new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979",16),
-            new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729",16),
-            new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d",16));
+            new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137", 16),
+            new BigInteger("010001", 16),
+            new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325", 16),
+            new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443", 16),
+            new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd", 16),
+            new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979", 16),
+            new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729", 16),
+            new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d", 16));
 
         KeyFactory fact = KeyFactory.getInstance("RSA", "BC");
 
@@ -1648,7 +1528,7 @@
     }
 
     private void rfc4491Test()
-       throws Exception
+        throws Exception
     {
         CertificateFactory certFact = CertificateFactory.getInstance("X.509", "BC");
 
@@ -1773,7 +1653,7 @@
 
         isTrue("certs size <cr><nl>", certs1.size() == certs2.size());
 
-        for (Iterator it = certs1.iterator(); it.hasNext();)
+        for (Iterator it = certs1.iterator(); it.hasNext(); )
         {
             certs2.remove(it.next());
         }
@@ -1818,7 +1698,7 @@
 
         isTrue("certs size <nl>", certs1.size() == certs2.size());
 
-        for (Iterator it = certs1.iterator(); it.hasNext();)
+        for (Iterator it = certs1.iterator(); it.hasNext(); )
         {
             certs2.remove(it.next());
         }
@@ -1839,6 +1719,7 @@
         checkCertificate(6, oldEcdsa);
         checkCertificate(7, cert7);
         checkCertificate(8, sm_sign);
+        checkCertificate(9, x25519Cert);
 
         checkComparison(cert1);
 
@@ -1871,7 +1752,12 @@
         }
 
         checkSelfSignedCertificate(23, sha3Cert, "SHA3-256withRSAandMGF1");
+        checkSelfSignedCertificate(24, sm_root, "SM3WITHSM2");
 
+        checkCertificateSignedBy(1, sm_sign, sm_root);
+        checkCertificateSignedBy(2, sm_ca1, sm_root1);
+        checkCertificateSignedBy(3, sm_sign1, sm_root1);
+        
         checkCRL(1, crl1);
 
         pemTest();
@@ -1890,7 +1776,7 @@
     }
 
     public static void main(
-        String[]    args)
+        String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
index 6182dfe..39e37a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
@@ -2,6 +2,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.PrivateKey;
@@ -18,6 +19,7 @@
 import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 import org.bouncycastle.util.test.TestFailedException;
@@ -193,6 +195,13 @@
         {
             fail("" + name + " failed short buffer decryption - " + e.toString());
         }
+
+        // mode test
+        if (name.indexOf('/') < 0)
+        {
+            Cipher.getInstance(name + "/NONE/NoPadding");
+            Cipher.getInstance(name + "/ECB/NoPadding");    // very old school
+        }
     }
 
 
@@ -232,6 +241,19 @@
             out.init(Cipher.DECRYPT_MODE, key);
         }
 
+        if (iv != null)
+        {
+            isTrue(Arrays.areEqual(iv, in.getIV()));
+            isTrue(Arrays.areEqual(iv, out.getIV()));
+       
+            AlgorithmParameters algParams = in.getParameters();
+
+            isTrue(Arrays.areEqual(iv, ((IvParameterSpec)algParams.getParameterSpec(IvParameterSpec.class)).getIV()));
+
+            algParams = out.getParameters();
+            isTrue(Arrays.areEqual(iv, ((IvParameterSpec)algParams.getParameterSpec(IvParameterSpec.class)).getIV()));
+        }
+        
         byte[] enc = in.doFinal(plainText);
         if (!areEqual(enc, cipherText))
         {
@@ -268,7 +290,7 @@
                     (byte)137, (byte)138, (byte)140, (byte)143 };
 
             byte[] keyBytes;
-            if (name.equals("HC256") || name.equals("XSalsa20") || name.equals("ChaCha7539"))
+            if (name.equals("HC256") || name.equals("XSalsa20") || name.equals("ChaCha7539") || name.equals("ChaCha20"))
             {
                 keyBytes = key256;
             }
@@ -389,6 +411,9 @@
         runTest("ChaCha7539");
         testException("ChaCha7539");
         testAlgorithm("ChaCha7539", CHA7539K, CHA7539IV, CHA7539IN, CHA7539OUT);
+        runTest("ChaCha20");
+        testException("ChaCha20");
+        testAlgorithm("ChaCha20", CHA7539K, CHA7539IV, CHA7539IN, CHA7539OUT);
         runTest("HC128");
         testException("HC128");
         testAlgorithm("HC128", HCK128A, HCIV, HCIN, HC128A);
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
index 4be5a10..295e14b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
@@ -8,6 +8,7 @@
 import java.security.AlgorithmParameterGenerator;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.KeyPair;
@@ -21,6 +22,7 @@
 import java.security.spec.ECFieldFp;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 import java.security.spec.PKCS8EncodedKeySpec;
@@ -36,14 +38,22 @@
 import javax.crypto.spec.DHPrivateKeySpec;
 import javax.crypto.spec.DHPublicKeySpec;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.agreement.DHStandardGroups;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
+import org.bouncycastle.jcajce.spec.DHUParameterSpec;
+import org.bouncycastle.jcajce.spec.MQVParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.jce.ECNamedCurveTable;
 import org.bouncycastle.jce.ECPointUtil;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
@@ -270,6 +280,19 @@
         {
             fail(size + " bit 3-way test failed (c and b differ)");
         }
+
+        KeyAgreement noKdf = KeyAgreement.getInstance("DH", "BC");
+        
+
+        try
+        {
+            noKdf.init(aPair.getPrivate(), new UserKeyingMaterialSpec(new byte[20]));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isTrue("no KDF specified for UserKeyingMaterialSpec".equals(e.getMessage()));
+        }
     }
 
     private void testTwoParty(String algName, int size, int privateValueSize, KeyPairGenerator keyGen)
@@ -621,6 +644,52 @@
         }
     }
 
+    private void testECDH(String algorithm, String curveName, ASN1ObjectIdentifier algorithmOid, String cipher, int keyLen)
+        throws Exception
+    {
+        ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(curveName);
+        KeyPairGenerator g = KeyPairGenerator.getInstance("EC", "BC");
+
+        g.initialize(parameterSpec);
+
+        //
+        // a side
+        //
+        KeyPair aKeyPair = g.generateKeyPair();
+
+        KeyAgreement aKeyAgree = KeyAgreement.getInstance(algorithm, "BC");
+
+        aKeyAgree.init(aKeyPair.getPrivate());
+
+        //
+        // b side
+        //
+        KeyPair bKeyPair = g.generateKeyPair();
+
+        KeyAgreement bKeyAgree = KeyAgreement.getInstance(algorithmOid.getId(), "BC");
+
+        bKeyAgree.init(bKeyPair.getPrivate());
+
+        //
+        // agreement
+        //
+        aKeyAgree.doPhase(bKeyPair.getPublic(), true);
+        bKeyAgree.doPhase(aKeyPair.getPublic(), true);
+
+        SecretKey k1 = aKeyAgree.generateSecret(cipher);
+        SecretKey k2 = bKeyAgree.generateSecret(cipher + "[" + keyLen + "]");  // explicit key-len
+
+        if (!k1.equals(k2))
+        {
+            fail(algorithm + " 2-way test failed");
+        }
+
+        if (k1.getEncoded().length != keyLen / 8)
+        {
+            fail("key for " + cipher + " the wrong size expected " + keyLen / 8 + " got " + k1.getEncoded().length);
+        }
+    }
+
     private void testECDH(String algorithm)
         throws Exception
     {
@@ -709,6 +778,274 @@
         }
     }
 
+    private void testMinSpecValue()
+        throws Exception
+    {
+        BigInteger p = new BigInteger("16560215747140417249215968347342080587", 16);
+        BigInteger g = new BigInteger("1234567890", 16);
+
+        DHParameterSpec serverParam = new DHParameterSpec(p, g);
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH", "BC");
+
+        try
+        {
+            keyGen.initialize(serverParam, new SecureRandom());
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isTrue("unsafe p value so small specific l required".equals(e.getMessage()));
+        }
+
+    }
+
+    /*
+     COUNT = 14
+     XstatCAVS = 95fc47b3c755935a5babce2738d70557a43ded59c2ef8926a04e402cdb0c97b8
+     YstatCAVS = 0b7faaefb56ea62937c2210c44feca95037d4cedfe01abd61cd8085ea195547e29aa2162951a73f9acf0f79de9da1ed587a1374d7b9c25a3ac4848a857edf28d4d6b80142871cd3fa984d673945a6ae69fbd0bc559a68330e7ba8556189216fe5d25abd8f1857baea7ab42fbdc3bb482272eca02fd0095c6b53c8d9ffb3ec172d97a3a1bde096178e2aaa3f084717f3e4530f58906f8f509533cead788b4efbb69ed78b91109965018b9a094612e60b1af470ec03565729c38e6d131eebac3483e7fdb24a7c85fd9bd362404956b928d6e1762b91b13825f7769b6e9426936c03f0328d9325f6fdd4af0b53ab1bc4201fedc5961c7f2c3a9668aa90ed2c4bb5d
+     XephemCAVS = 6044f14699de46fe870688b27d5644a78da6e0758f2c999cc5e6d80a69220e2e
+     YephemCAVS = 4a8d5a1a12cc0aeeb409e07dbffa052d289c4cb49d6550d8483fe063eee9d9faa4e918fe4daa858d535c4ed5cd270d96315db210e20b4446db4f460238b9187accc65b6d43e53b3c85eec3053c8bd675ef34dca5f6189f2233ecf0b1eda0995460ccdded4e31bf3170f9ec3941d010bbd1d7a0e017f0d43c0dd1d6435f8523babfa6599120f3cbf718e755cee86189459bcd20f52d2a0ca04bdff38e26197c211fcb64cc3d7d3f2f28ee4f7eb9dbdc84a420442b8481bfa3218f0d40c00abaedff682e7d66f6e891642bdea3e2d9c6240b768376abc50343cc69ab08b0a12cc4c6f1508444fd662c4825bd6da99eaab40ff5547aae539450062ce70b9722091b
+     XstatIUT = 133a7729c7f1c1872438738edfa44d4cf44d3356d47b73b62eab45853ebdc66d
+     YstatIUT = 21e25ee916b7b56a82f9e7622e909bef000997c44434e1149fa30cb1571500be5e61bab977d9ace85ba62a21719199b9a9747e3bcc0fa729a69c17f080633e6c1426db891721ae74b9752effe8a4b9749f8c7d8edd1f4356bab994304d3fde8223de38436a1a7ffb70371d25cf4c75df7f58cd833837318c1c2213f9a058655905d752fb637d3d7f780c3ee4a788120040424199bc99d96f3c3e56a2a9fe8d6d93e60a91b6f61a1cf0559bc68a1e33716a54fdbd2895c0d9d1f7da2cb936ff0c1bc7c60380d9cf4eaa8595366ed86a72cbd964d1e4309b2dd6efad1e944cdb92752ebe13d2e65772295fb13cd9f11d5b89253e4cc109b76d53306a6534be2641
+     XephemIUT = 412a15e0866572a825219d3eaf9a4d6c0ed855180e5bdabc90f6d1a2354c3964
+     YephemIUT = 8e235a5e20d0d1d431eb832a4309de239403a68217a595d30b2e7fd677ad5eb7a2a3cc5fb0793fe466169d8acac366a20de3863adc542a4fc6dd9dcb59126dfd0336b2c7736d26e87ad4fd84d6240e149f50ffcdaa81b60ca04a26f6335e1c41e49f183bf3a7a39ffe6bf2654874399e07d9a52fb34d08a7246929649171f6e7ceceb19016b83093a9a795245ae348346f9aa8f06380cb2b3cc9176e63e107734e23ead912e408c3085b6ba361cb66cf5b25ed03fdc6893646ea3cddd770fcb51d762a8f549b600044946c362f4dda85288fbc4499e022e2b705b4f1151d5206932da92b36c6b121e3a55a2edca4b42407021f4ea3f4748f21a36d722c086cf6
+     CCMNonce = 7def4d439a9b7a6c5700bb9168
+     OI = a1b2c3d4e543415653696412daed24199775845035176e67b0ace1b413e0
+     CAVSTag = df851c60d5336269c68e42cc0d3b6ea5
+     Z = 051f570adc0c2e26f946153f31784409102f5bd9edc2cdc466b14196b7489d2b157847fb7a13bfe89edc9712b2a161be360936802dc2c1158f0a84a2175671a4f46ed6fbadc4238244a217ed21a35e01b966b100daad49e2390e0c11525280b2ecc60ffad1e73ad12aa49e28fd9dfbf7d90ad75514c48a4c05f7bd8482929c68cc62e86424019462b1e2ef6a7a16507577ab144a89dafe57b9b0889d7afda25e62022f69220f0fb32046d0aa478bde5914177aeb4f359e790a6f9fac367f431b4e32acb8616f040c77cd99c1a666d4569c06b62faa4925f9c6f6525fe074cac972aead654c87dcc772b96992202afff62c82cc501b821bf0fd851942f0797dc98be4bdf193bc6d0d95d40146b5dad610bd4123413369686b460018918c493854a14558b302f6bc3d10109cbb549dc624448246e41a32842b1962a3b884b2eb8546f2bb51d30ceb80ae7a631f2f2fb820c7f149d5e53e2ec3d62f1ff5c6cb07f845de1b31be0e1d31143476a22952406c4fa37029b1e4d2107f5efb9df9e04ec2a4d9def274f934a0e34e22003f2142185c1f79d6058f612b1315acff738e94a18a08be36a3b327ae3e28e1c9aa96fe99cbe4fdeb0df92ff133e94929d6d50fad4d5bffe54454832125212c30dad53109e114413f954f02cfa39fcc0ef574074df2f1d6f4fcb9d99dfcbcc252ee42980f1a483508379434e1ef72358f39bb5725
+     MacData = 4b435f325f56434156536964a1b2c3d4e54a8d5a1a12cc0aeeb409e07dbffa052d289c4cb49d6550d8483fe063eee9d9faa4e918fe4daa858d535c4ed5cd270d96315db210e20b4446db4f460238b9187accc65b6d43e53b3c85eec3053c8bd675ef34dca5f6189f2233ecf0b1eda0995460ccdded4e31bf3170f9ec3941d010bbd1d7a0e017f0d43c0dd1d6435f8523babfa6599120f3cbf718e755cee86189459bcd20f52d2a0ca04bdff38e26197c211fcb64cc3d7d3f2f28ee4f7eb9dbdc84a420442b8481bfa3218f0d40c00abaedff682e7d66f6e891642bdea3e2d9c6240b768376abc50343cc69ab08b0a12cc4c6f1508444fd662c4825bd6da99eaab40ff5547aae539450062ce70b9722091b8e235a5e20d0d1d431eb832a4309de239403a68217a595d30b2e7fd677ad5eb7a2a3cc5fb0793fe466169d8acac366a20de3863adc542a4fc6dd9dcb59126dfd0336b2c7736d26e87ad4fd84d6240e149f50ffcdaa81b60ca04a26f6335e1c41e49f183bf3a7a39ffe6bf2654874399e07d9a52fb34d08a7246929649171f6e7ceceb19016b83093a9a795245ae348346f9aa8f06380cb2b3cc9176e63e107734e23ead912e408c3085b6ba361cb66cf5b25ed03fdc6893646ea3cddd770fcb51d762a8f549b600044946c362f4dda85288fbc4499e022e2b705b4f1151d5206932da92b36c6b121e3a55a2edca4b42407021f4ea3f4748f21a36d722c086cf6
+     DKM = 24a246e6cbaae19e4e8bffbe3167fbbc
+     Result = P (10 - Z value should have leading 0 nibble )
+     */
+    private void testDHUnifiedTestVector1()
+        throws Exception
+    {
+        // Test Vector from NIST sample data
+        KeyFactory dhKeyFact = KeyFactory.getInstance("DH", "BC");
+
+        DHParameterSpec dhSpec = new DHParameterSpec(
+            new BigInteger("9a076bb269abfff57c72073053190a2008c3067fdcd9712ec00ee55c8fbf22af7c454dc5f10ae224d1e29fcccb3855a2509b082b934a353c21dfa5d1212f29d24866f022873d1f0b76373d47bb345e7e74f0ffc27e7c6c149282cb68a66705412995ed7a650a784f15107ed14244563b10f61d3f998b1466c9a3dd7c48a1b92d236b99b912a25f1c5279640c29714ce2123d222a6c9775223be80c5a4e9392db9ae45027110b75703c42d53fbfc1484e84cb70cabdcdcdc55066e5c03ce13ad0d7fa3af6f49101d454d5b3b77ce4c8db5772a427af7e351cdad3d7d278f52c3f57fc9274fc101c66d829871435ea2fc1f43f0e0d556a80dba9ab4e57c7b4b5a7", 16),
+            // Q = fdd88b09ff0c6c6c334a598059c1b55396dab2de01af2e8d06481fd5cd506c71
+            new BigInteger("167f9631c8aba192976a396b9df4bca5e54d1c1400eab4bdea27b1ca957211733d847026d2b3e3ea9b4c14d13b6e59f40c0df0c80bdecafb7ac414de2f920642c60d63406d2cc999ad149d24216b08a3952b50a50a088ab747de04bb4fd26899f7052970cfd0f65002cc0639bea634ba5ac2d98170b3a1b3ab5295e9395990b57fbdaf117662a9430da6b74d4e52d3969ce385b2fb61c11febd93867f1062084ca0a62c0de17b1e7265545198355e026818c037c43535de8f0d5cf0159501bcd35a4ba8fe92041a92e85fae03a051dfb3199d9764d17a3b8968eaf32e666ae867d1d0e6178ab31985b665e3178c36565e685046cb1d0611a25b0d559cd31f818", 16));
+
+        KeyPair U1 = new KeyPair(
+            dhKeyFact.generatePublic(new DHPublicKeySpec(
+                new BigInteger("0b7faaefb56ea62937c2210c44feca95037d4cedfe01abd61cd8085ea195547e29aa2162951a73f9acf0f79de9da1ed587a1374d7b9c25a3ac4848a857edf28d4d6b80142871cd3fa984d673945a6ae69fbd0bc559a68330e7ba8556189216fe5d25abd8f1857baea7ab42fbdc3bb482272eca02fd0095c6b53c8d9ffb3ec172d97a3a1bde096178e2aaa3f084717f3e4530f58906f8f509533cead788b4efbb69ed78b91109965018b9a094612e60b1af470ec03565729c38e6d131eebac3483e7fdb24a7c85fd9bd362404956b928d6e1762b91b13825f7769b6e9426936c03f0328d9325f6fdd4af0b53ab1bc4201fedc5961c7f2c3a9668aa90ed2c4bb5d", 16),
+                dhSpec.getP(), dhSpec.getG())),
+            dhKeyFact.generatePrivate(new DHPrivateKeySpec(
+                new BigInteger("95fc47b3c755935a5babce2738d70557a43ded59c2ef8926a04e402cdb0c97b8", 16),
+                dhSpec.getP(), dhSpec.getG())));
+
+        KeyPair U2 = new KeyPair(
+            dhKeyFact.generatePublic(new DHPublicKeySpec(
+                new BigInteger("4a8d5a1a12cc0aeeb409e07dbffa052d289c4cb49d6550d8483fe063eee9d9faa4e918fe4daa858d535c4ed5cd270d96315db210e20b4446db4f460238b9187accc65b6d43e53b3c85eec3053c8bd675ef34dca5f6189f2233ecf0b1eda0995460ccdded4e31bf3170f9ec3941d010bbd1d7a0e017f0d43c0dd1d6435f8523babfa6599120f3cbf718e755cee86189459bcd20f52d2a0ca04bdff38e26197c211fcb64cc3d7d3f2f28ee4f7eb9dbdc84a420442b8481bfa3218f0d40c00abaedff682e7d66f6e891642bdea3e2d9c6240b768376abc50343cc69ab08b0a12cc4c6f1508444fd662c4825bd6da99eaab40ff5547aae539450062ce70b9722091b", 16),
+                dhSpec.getP(), dhSpec.getG())),
+            dhKeyFact.generatePrivate(new DHPrivateKeySpec(
+                new BigInteger("6044f14699de46fe870688b27d5644a78da6e0758f2c999cc5e6d80a69220e2e", 16),
+                dhSpec.getP(), dhSpec.getG())));
+
+        KeyPair V1 = new KeyPair(
+            dhKeyFact.generatePublic(new DHPublicKeySpec(
+                new BigInteger("21e25ee916b7b56a82f9e7622e909bef000997c44434e1149fa30cb1571500be5e61bab977d9ace85ba62a21719199b9a9747e3bcc0fa729a69c17f080633e6c1426db891721ae74b9752effe8a4b9749f8c7d8edd1f4356bab994304d3fde8223de38436a1a7ffb70371d25cf4c75df7f58cd833837318c1c2213f9a058655905d752fb637d3d7f780c3ee4a788120040424199bc99d96f3c3e56a2a9fe8d6d93e60a91b6f61a1cf0559bc68a1e33716a54fdbd2895c0d9d1f7da2cb936ff0c1bc7c60380d9cf4eaa8595366ed86a72cbd964d1e4309b2dd6efad1e944cdb92752ebe13d2e65772295fb13cd9f11d5b89253e4cc109b76d53306a6534be2641", 16),
+                dhSpec.getP(), dhSpec.getG())),
+            dhKeyFact.generatePrivate(new DHPrivateKeySpec(
+                new BigInteger("133a7729c7f1c1872438738edfa44d4cf44d3356d47b73b62eab45853ebdc66d", 16),
+                dhSpec.getP(), dhSpec.getG())));
+
+        KeyPair V2 = new KeyPair(
+            dhKeyFact.generatePublic(new DHPublicKeySpec(
+                new BigInteger("8e235a5e20d0d1d431eb832a4309de239403a68217a595d30b2e7fd677ad5eb7a2a3cc5fb0793fe466169d8acac366a20de3863adc542a4fc6dd9dcb59126dfd0336b2c7736d26e87ad4fd84d6240e149f50ffcdaa81b60ca04a26f6335e1c41e49f183bf3a7a39ffe6bf2654874399e07d9a52fb34d08a7246929649171f6e7ceceb19016b83093a9a795245ae348346f9aa8f06380cb2b3cc9176e63e107734e23ead912e408c3085b6ba361cb66cf5b25ed03fdc6893646ea3cddd770fcb51d762a8f549b600044946c362f4dda85288fbc4499e022e2b705b4f1151d5206932da92b36c6b121e3a55a2edca4b42407021f4ea3f4748f21a36d722c086cf6", 16),
+                dhSpec.getP(), dhSpec.getG())),
+            dhKeyFact.generatePrivate(new DHPrivateKeySpec(
+                new BigInteger("412a15e0866572a825219d3eaf9a4d6c0ed855180e5bdabc90f6d1a2354c3964", 16),
+                dhSpec.getP(), dhSpec.getG())));
+
+        byte[] x = calculateUnifiedAgreement("DHUwithSHA256CKDF", "AES[128]", U1, U2, V1, V2,
+            Hex.decode("a1b2c3d4e543415653696412daed24199775845035176e67b0ace1b413e0"));
+
+        if (x == null
+            || !areEqual(Hex.decode("24a246e6cbaae19e4e8bffbe3167fbbc"), x))
+        {
+            fail("DH unified Test Vector #1 agreement failed, got: " + Hex.toHexString(x));
+        }
+    }
+
+    private void testECUnifiedTestVector1()
+        throws Exception
+    {
+        // Test Vector from NIST sample data
+
+        ECNamedCurveParameterSpec namedSpec = ECNamedCurveTable.getParameterSpec("P-224");
+        KeyFactory ecKeyFact = KeyFactory.getInstance("EC", "BC");
+
+        EllipticCurve ecCurve = new EllipticCurve(
+            new ECFieldFp(namedSpec.getCurve().getField().getCharacteristic()),
+            namedSpec.getCurve().getA().toBigInteger(), namedSpec.getCurve().getB().toBigInteger());
+        ECParameterSpec ecSpec = new ECParameterSpec(ecCurve,
+            new ECPoint(namedSpec.getG().getAffineXCoord().toBigInteger(), namedSpec.getG().getAffineYCoord().toBigInteger()),
+            namedSpec.getN(), namedSpec.getH().intValue());
+        
+        KeyPair U1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("040784e946ef1fae0cfe127042a310a018ba639d3f6b41f265904f0a7b21b7953efe638b45e6c0c0d34a883a510ce836d143d831daa9ce8a12")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("86d1735ca357890aeec8eccb4859275151356ecee9f1b2effb76b092", 16), ecSpec)));
+
+        KeyPair U2 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("04b33713dc0d56215be26ee6c5e60ad36d12e02e78529ae3ff07873c6b39598bda41c1cf86ee3981f40e102333c15fef214bda034291c1aca6")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("764010b3137ef8d34a3552955ada572a4fa1bb1f5289f27c1bf18344", 16), ecSpec)));
+
+        KeyPair V1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("0484c22d9575d09e280613c8758467f84869c6eede4f6c1b644517d6a72c4fc5c68fa12b4c259032fc5949c630259948fca38fb3342d9cb0a8")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("e37964e391f5058fb43435352a9913438a1ec10831f755273285230a", 16), ecSpec)));
+
+        KeyPair V2 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("044b917e9ce693b277c8095e535ea81c2dea089446a8c55438eda750fb6170c85b86390481fff2dff94b7dff3e42d35ff623921cb558967b48")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("ab40d67f59ba7265d8ad33ade8f704d13a7ba2298b69172a7cd02515", 16), ecSpec)));
+
+        byte[] x = calculateUnifiedAgreement("ECCDHUwithSHA224CKDF", "AES[128]", U1, U2, V1, V2,
+            Hex.decode("a1b2c3d4e54341565369643dba868da77897b6552f6f767ad873b232aa4a810a91863ec3dc86db53359a772dd76933"));
+
+        if (x == null
+            || !areEqual(Hex.decode("63b7ba5699927cb08e058b76af7fc0b0"), x))
+        {
+            fail("EC unified Test Vector #1 agreement failed, got: " + Hex.toHexString(x));
+        }
+    }
+
+    private void testECUnifiedTestVector2()
+        throws Exception
+    {
+        // Test Vector from NIST sample data
+
+        ECNamedCurveParameterSpec namedSpec = ECNamedCurveTable.getParameterSpec("P-256");
+        KeyFactory ecKeyFact = KeyFactory.getInstance("EC", "BC");
+
+        EllipticCurve ecCurve = new EllipticCurve(
+            new ECFieldFp(namedSpec.getCurve().getField().getCharacteristic()),
+            namedSpec.getCurve().getA().toBigInteger(), namedSpec.getCurve().getB().toBigInteger());
+        ECParameterSpec ecSpec = new ECParameterSpec(ecCurve,
+            new ECPoint(namedSpec.getG().getAffineXCoord().toBigInteger(), namedSpec.getG().getAffineYCoord().toBigInteger()),
+            namedSpec.getN(), namedSpec.getH().intValue());
+
+        KeyPair U1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("047581b35964a983414ebdd56f4ebb1ddcad10881b200666a51ae41306e1ecf1db368468a5e8a65ca10ccea526472c8982db68316c468800e171c11f4ee694fce4")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("2eb7ef76d4936123b6f13035045aedf45c1c7731f35d529d25941926b5bb38bb", 16), ecSpec)));
+
+        KeyPair U2 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("045b1e4cdeb0728333c0a51631b1a75269e4878d10732f4cb94d600483db4bd9ee625c374592c3db7e9f8b4f2c91a0098a158bc37b922e4243bd9cbdefe67d6ab0")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("78acde388a022261767e6b3dd6dd016c53b70a084260ec87d395aec761c082de", 16), ecSpec)));
+
+        KeyPair V1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("04e4916d616803ff1bd9569f35b7d06f792f19c1fb4e6fa916d686c027a17d8dffd570193d8e101624ac2ea0bcb762d5613f05452670f09af66ef70861fb528868")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("9c85898640a1b1de8ce7f557492dc1460530b9e17afaaf742eb953bb644e9c5a", 16), ecSpec)));
+
+        KeyPair V2 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("04d1cd23c29d0fc865c316d44a1fd5adb6605ee47c9ddfec3a9b0a5e532d52704e74ff5d149aeb50856fefb38d5907b6dbb580fe6dc166bcfcbee4eb376d77e95c")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("d6e11d5d3b85b201b8f4c12dadfad3000e267961a806a0658a2b859d44389599", 16), ecSpec)));
+
+        byte[] x = calculateUnifiedAgreement("ECCDHUwithSHA256CKDF", "AES[128]",
+            U1, U2, V1, V2, Hex.decode("a1b2c3d4e54341565369649018558dc958160b4b1d240d06ea07c6f321a752496c1a3ff45cbb4b43507c6fe1997d1d"));
+
+        if (x == null
+            || !areEqual(Hex.decode("221d252072d6f85b8298eab6fc38634e"), x))
+        {
+            fail("EC unified Test Vector #2 agreement failed");
+        }
+    }
+
+    private void testECUnifiedTestVector3()
+        throws Exception
+    {
+        // Test Vector from NIST sample data - One pass unified.
+
+        ECNamedCurveParameterSpec namedSpec = ECNamedCurveTable.getParameterSpec("P-224");
+        KeyFactory ecKeyFact = KeyFactory.getInstance("EC", "BC");
+
+        EllipticCurve ecCurve = new EllipticCurve(
+            new ECFieldFp(namedSpec.getCurve().getField().getCharacteristic()),
+            namedSpec.getCurve().getA().toBigInteger(), namedSpec.getCurve().getB().toBigInteger());
+        ECParameterSpec ecSpec = new ECParameterSpec(ecCurve,
+            new ECPoint(namedSpec.getG().getAffineXCoord().toBigInteger(), namedSpec.getG().getAffineYCoord().toBigInteger()),
+            namedSpec.getN(), namedSpec.getH().intValue());
+
+        KeyPair U1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("04030f136fa7fef90d185655ed1c6d46bacdb82001714e682cc80ca6b2d7c62e2f2e19d11755dba4aafd7e1ee5fda3e5f4d0af9a3ad773c38a")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("6fc464c741f52b2a2e4cde35673b87fdd0f52caf4e716230b11570ba", 16), ecSpec)));
+
+        KeyPair V1 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("048f87f5f8a632c9a3348ea85b596c01c12ca29ca71583dcdc27ff9766351416a707b95fae67d56be5119b460a446b6a02db20a13bbc8ed13b")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("f5cb57a08a6949d3f2c2cc02e7c2252cecb3ebb8b3572943ceb407c7", 16), ecSpec)));
+
+        KeyPair V2 = new KeyPair(
+            ecKeyFact.generatePublic(new ECPublicKeySpec(
+                ECPointUtil.decodePoint(ecCurve, Hex.decode("046fcc7d01f905b279e9413645d24cc30d293b98b0ea7bfe87124e4951eba04a74817f596a67c0bfe3b4f4cee99537a2ac1c6470dd006be8ca")), ecSpec)),
+            ecKeyFact.generatePrivate(new ECPrivateKeySpec(
+                new BigInteger("505b6f372725e293cda07bf0dd14dabe2faf0edaa5ab1c7d187a6138", 16), ecSpec)));
+
+        byte[] x = calculateUnifiedAgreement("ECCDHUwithSHA224CKDF", "AES[128]", U1, U1, V1, V2,
+            Hex.decode("a1b2c3d4e5434156536964b62d3197031c27af0e3b45228a8768efcc0b39a375f8f61852f8765b80c067eed4e4db30"));
+
+        if (x == null
+            || !areEqual(Hex.decode("0c96fa268b89cf664392621ad5e174a6"), x))
+        {
+            fail("EC unified Test Vector #3 agreement failed, got: " + Hex.toHexString(x));
+        }
+    }
+
+    private byte[] calculateUnifiedAgreement(
+        String alg,
+        String keyAlg,
+        KeyPair U1,
+        KeyPair U2,
+        KeyPair V1,
+        KeyPair V2,
+        byte[] oi)
+        throws Exception
+    {
+        KeyAgreement u = KeyAgreement.getInstance(alg, "BC");
+
+        u.init(U1.getPrivate(), new DHUParameterSpec(U2, V2.getPublic(), oi));
+
+        u.doPhase(V1.getPublic(), true);
+
+        SecretKey uk = u.generateSecret(keyAlg);
+        byte[] ux = uk.getEncoded();
+
+        KeyAgreement v = KeyAgreement.getInstance(alg, "BC");
+
+        v.init(V1.getPrivate(), new DHUParameterSpec(V2, U2.getPublic(), oi));
+
+        v.doPhase(U1.getPublic(), true);
+
+        SecretKey vk = v.generateSecret(keyAlg);
+        byte[] vx = vk.getEncoded();
+
+        if (areEqual(ux, vx))
+        {
+            return ux;
+        }
+
+        return null;
+    }
+
     private void testExceptions()
         throws Exception
     {
@@ -717,6 +1054,7 @@
             KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", "BC");
 
             aKeyAgree.generateSecret("DES");
+            fail("no exception");
         }
         catch (IllegalStateException e)
         {
@@ -747,11 +1085,23 @@
         }
         catch (java.security.spec.InvalidKeySpecException e)
         {
-            isTrue("wrong message", "invalid KeySpec: point not on curve".equals(e.getMessage()));
+            isTrue("wrong message: " + e.getMessage(), "invalid KeySpec: Point not on curve".equals(e.getMessage()));
         }
         catch (java.security.InvalidKeyException e)
         {
-            isTrue("wrong message", "calculation failed: Invalid point".equals(e.getMessage()));
+            isTrue("wrong message: " + e.getMessage(), "calculation failed: Invalid point".equals(e.getMessage()));
+        }
+
+        agreement = KeyAgreement.getInstance("ECDH", "BC");
+
+        try
+        {
+            agreement.init(kp.getPrivate(), new UserKeyingMaterialSpec(new byte[20]));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isTrue("no KDF specified for UserKeyingMaterialSpec".equals(e.getMessage()));
         }
     }
 
@@ -890,10 +1240,10 @@
             new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4", 16),
             384);
 
-        DHParameterSpec dhSpec768 = new DHParameterSpec(
-            new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45ee3688c11a8c56ab127a3daf", 16),
-            new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d3668445771f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a7064f316933a346d3f529252", 16),
-            384);
+        DHParameterSpec dhSpec640 = new DHParameterSpec(
+            new BigInteger("c3d5a7f9a1cd7330099cebb60194f5176793a1cf13cd429f37bcbf1a7ddd53893ffdf1228af760c4a448e459d9cbab8302cc8cfc3368db01972108587c72a0f8b512ede0c99a3bef16cda0de529c8be7", 16),
+            new BigInteger("c066a53c43a55e3474e20de07d14a574f6f1febe0b55e4c49bf72b0c712e02a51b03f379f485884bfd1f53819347b69401b9292196092a635320313ec6ee5ee5a5eac7ab9c57f2631a71452feeab3ef", 16),
+            320);
 
         DHParameterSpec dhSpec1024 = new DHParameterSpec(
             new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7", 16),
@@ -907,21 +1257,21 @@
             fail("config mismatch");
         }
 
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768) != null)
+        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640) != null)
         {
             fail("config found when none expected");
         }
 
-        prov.setParameter(ConfigurableProvider.DH_DEFAULT_PARAMS, new DHParameterSpec[]{dhSpec512, dhSpec768, dhSpec1024});
+        prov.setParameter(ConfigurableProvider.DH_DEFAULT_PARAMS, new DHParameterSpec[]{dhSpec512, dhSpec640, dhSpec1024});
 
         if (!dhSpec512.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(512)))
         {
             fail("512 config mismatch");
         }
 
-        if (!dhSpec768.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768)))
+        if (!dhSpec640.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640)))
         {
-            fail("768 config mismatch");
+            fail("640 config mismatch");
         }
 
         if (!dhSpec1024.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(1024)))
@@ -930,15 +1280,10 @@
         }
 
         prov.setParameter(ConfigurableProvider.DH_DEFAULT_PARAMS, null);
-
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(512) != null)
+        
+        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640) != null)
         {
-            fail("config found for 512 when none expected");
-        }
-
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768) != null)
-        {
-            fail("config found for 768 when none expected");
+            fail("config found for 640 when none expected");
         }
 
         prov.setParameter(ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS, dhSpec512);
@@ -948,21 +1293,21 @@
             fail("config mismatch");
         }
 
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768) != null)
+        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640) != null)
         {
             fail("config found when none expected");
         }
 
-        prov.setParameter(ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS, new DHParameterSpec[]{dhSpec512, dhSpec768, dhSpec1024});
+        prov.setParameter(ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS, new DHParameterSpec[]{dhSpec512, dhSpec640, dhSpec1024});
 
         if (!dhSpec512.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(512)))
         {
             fail("512 config mismatch");
         }
 
-        if (!dhSpec768.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768)))
+        if (!dhSpec640.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640)))
         {
-            fail("768 config mismatch");
+            fail("640 config mismatch");
         }
 
         if (!dhSpec1024.equals(BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(1024)))
@@ -972,14 +1317,9 @@
 
         prov.setParameter(ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS, null);
 
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(512) != null)
+        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(640) != null)
         {
-            fail("config found for 512 when none expected");
-        }
-
-        if (BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(768) != null)
-        {
-            fail("config found for 768 when none expected");
+            fail("config found for 640 when none expected");
         }
     }
 
@@ -1086,10 +1426,104 @@
         }
     }
 
+    private void testGenerateUsingStandardGroup()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH", "BC");
+        DHDomainParameterSpec mySpec = new DHDomainParameterSpec(DHStandardGroups.rfc7919_ffdhe2048);
+        kpGen.initialize(mySpec, new SecureRandom());
+        KeyPair kp = kpGen.generateKeyPair();
+
+        /* Obtain encoded keys */
+        PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded());
+        X509EncodedKeySpec x509 = new X509EncodedKeySpec(kp.getPublic().getEncoded());
+    }
+
+    private KeyPair generateDHKeyPair()
+        throws GeneralSecurityException
+    {
+        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DH", "BC");
+
+        keyPairGen.initialize(2048);
+
+        return keyPairGen.generateKeyPair();
+    }
+
+    private SecretKey mqvGenerateAESKey(
+        PrivateKey aPriv, PublicKey aPubEph, PrivateKey aPrivEph, PublicKey bPub, PublicKey bPubEph, byte[] keyMaterial)
+        throws GeneralSecurityException
+    {
+        KeyAgreement agreement = KeyAgreement.getInstance("MQVwithSHA256KDF", "BC");
+
+        agreement.init(aPriv, new MQVParameterSpec(aPubEph, aPrivEph, bPubEph, keyMaterial));
+
+        agreement.doPhase(bPub, true);
+
+        return agreement.generateSecret("AES");
+    }
+
+    private void mqvTest()
+        throws Exception
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        // Generate the key pairs for party A and party B
+        KeyPair aKpS = generateDHKeyPair();
+        KeyPair aKpE = generateDHKeyPair();    // A's ephemeral pair
+        KeyPair bKpS = generateDHKeyPair();
+        KeyPair bKpE = generateDHKeyPair();    // B's ephemeral pair
+
+        // key agreement generating an AES key
+        byte[] keyMaterial = Strings.toByteArray("For an AES key");
+
+        SecretKey aKey = mqvGenerateAESKey(
+            aKpS.getPrivate(),
+            aKpE.getPublic(), aKpE.getPrivate(),
+            bKpS.getPublic(), bKpE.getPublic(), keyMaterial);
+        SecretKey bKey = mqvGenerateAESKey(
+            bKpS.getPrivate(),
+            bKpE.getPublic(), bKpE.getPrivate(),
+            aKpS.getPublic(), aKpE.getPublic(), keyMaterial);
+
+        // compare the two return values.
+        isTrue(Arrays.areEqual(aKey.getEncoded(), bKey.getEncoded()));
+
+        // check with encoding
+        KeyFactory kFact = KeyFactory.getInstance("DH", "BC");
+
+        aKey = mqvGenerateAESKey(
+            kFact.generatePrivate(new PKCS8EncodedKeySpec(aKpS.getPrivate().getEncoded())),
+            aKpE.getPublic(), aKpE.getPrivate(),
+            bKpS.getPublic(), kFact.generatePublic(new X509EncodedKeySpec(bKpE.getPublic().getEncoded())), keyMaterial);
+        bKey = mqvGenerateAESKey(
+            bKpS.getPrivate(),
+            bKpE.getPublic(), kFact.generatePrivate(new PKCS8EncodedKeySpec(bKpE.getPrivate().getEncoded())),
+            aKpS.getPublic(), aKpE.getPublic(), keyMaterial);
+
+        // compare the two return values.
+        isTrue(Arrays.areEqual(aKey.getEncoded(), bKey.getEncoded()));
+    }
+
+    private void generalKeyTest()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+
+        int[] keySizes = new int[]{512, 768, 1024, 2048};
+        for (int i = 0; i != keySizes.length; i++)
+        {
+            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH", "BC");
+            keyPairGenerator.initialize(keySizes[i], random);
+            keyPairGenerator.generateKeyPair();
+        }
+    }
+
     public void performTest()
         throws Exception
     {
+        generalKeyTest();
         testDefault(64, g512, p512);
+        mqvTest();
 
         testEnc();
         testGP("DH", 512, 0, g512, p512);
@@ -1112,7 +1546,12 @@
         testECDH("ECDH", "Curve25519", "DESEDE", 192);
         testECDH("ECDH", "Curve25519", "DES", 64);
         testECDH("ECDHwithSHA1KDF", "Curve25519", "AES", 256);
-        testECDH("ECDHwithSHA1KDF", "Curve25519", "DESEDE", 192);
+        testECDH("ECKAEGWITHSHA1KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, "DESEDE", 192);
+        testECDH("ECKAEGWITHSHA224KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, "DESEDE", 192);
+        testECDH("ECKAEGWITHSHA256KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, "DESEDE", 192);
+        testECDH("ECKAEGWITHSHA384KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384,"AES", 256);
+        testECDH("ECKAEGWITHSHA512KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512,"DESEDE", 192);
+        testECDH("ECKAEGWITHRIPEMD160KDF", "secp256r1", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, "AES", 256);
 
         testExceptions();
         testDESAndDESede(g768, p768);
@@ -1120,6 +1559,15 @@
         testSmallSecret();
         testConfig();
         testSubgroupConfinement();
+
+        testECUnifiedTestVector1();
+        testECUnifiedTestVector2();
+        testECUnifiedTestVector3();
+
+        testDHUnifiedTestVector1();
+
+        testMinSpecValue();
+        testGenerateUsingStandardGroup();
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DRBGTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DRBGTest.java
new file mode 100644
index 0000000..f17f484
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DRBGTest.java
@@ -0,0 +1,37 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.SecureRandom;
+import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * This test needs to be run with -Djava.security.debug=provider
+ */
+public class DRBGTest
+    extends SimpleTest
+{
+    public DRBGTest()
+    {
+    }
+    
+    public String getName()
+    {
+        return "DRBG";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        SecureRandom.getInstance("DEFAULT", "BC");
+    }
+
+    public static void main(
+        String[]    args)
+    {
+        runTest(new DRBGTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSATest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSATest.java
index 6d7b858..8d0f46b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSATest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSATest.java
@@ -41,6 +41,7 @@
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.DSAParameters;
@@ -450,26 +451,19 @@
 
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
 
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve curve = new ECCurve.Fp(
-                new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-                new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-                new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-        
-        ECParameterSpec spec = new ECParameterSpec(
-                curve,
-                curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-                new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-        
-        
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
+
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
-                new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
-                spec);
+            new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+            spec);
         
         ECPublicKeySpec pubKey = new ECPublicKeySpec(
-                curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
-                spec);
+            curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+            spec);
         
         Signature           sgr = Signature.getInstance("ECDSA", "BC");
         KeyFactory          f = KeyFactory.getInstance("ECDSA", "BC");
@@ -513,24 +507,17 @@
     private void testNONEwithECDSA239bitPrime()
         throws Exception
     {
-        ECCurve curve = new ECCurve.Fp(
-                new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-                new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-                new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-                curve,
-                curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-                new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
-                new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
-                spec);
+            new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+            spec);
 
         ECPublicKeySpec pubKey = new ECPublicKeySpec(
-                curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
-                spec);
+            curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+            spec);
 
         Signature           sgr = Signature.getInstance("NONEwithECDSA", "BC");
         KeyFactory          f = KeyFactory.getInstance("ECDSA", "BC");
@@ -773,28 +760,20 @@
     
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
 
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve curve = new ECCurve.F2m(
-            239, // m
-            36, // k
-            new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
-            new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16)); // b
-    
-        ECParameterSpec params = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
-            new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783"), // n
-            BigInteger.valueOf(4)); // h
-    
+        X9ECParameters x9 = ECNamedCurveTable.getByName("c2tnb239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec params = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
+
         ECPrivateKeySpec priKeySpec = new ECPrivateKeySpec(
             new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
             params);
-        
+
         ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(
             curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
             params);
-    
+
         Signature   sgr = Signature.getInstance("ECDSA", "BC");
         KeyFactory  f = KeyFactory.getInstance("ECDSA", "BC");
         PrivateKey  sKey = f.generatePrivate(priKeySpec);
@@ -838,19 +817,11 @@
     {
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
 
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve curve = new ECCurve.F2m(
-            239, // m
-            36, // k
-            new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
-            new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16)); // b
-
-        ECParameterSpec params = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
-            new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783"), // n
-            BigInteger.valueOf(4)); // h
+        X9ECParameters x9 = ECNamedCurveTable.getByName("c2tnb239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec params = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec priKeySpec = new ECPrivateKeySpec(
             new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
@@ -978,15 +949,9 @@
         s = Signature.getInstance("ECDSA", "BC");
         g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         g.initialize(ecSpec, new SecureRandom());
 
@@ -1067,18 +1032,10 @@
         s = Signature.getInstance("ECDSA", "BC");
         g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        curve = new ECCurve.F2m(
-                239, // m
-                36, // k
-                new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
-                new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16)); // b
-        
-        ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
-            new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783"), // n
-            BigInteger.valueOf(4)); // h
-        
+        x9 = ECNamedCurveTable.getByName("c2tnb239v1");
+        curve = x9.getCurve();
+        ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
+
         g.initialize(ecSpec, new SecureRandom());
 
         p = g.generateKeyPair();
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU4145Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU4145Test.java
index 7a7baa8..a07b919 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU4145Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU4145Test.java
@@ -2,6 +2,7 @@
 
 import java.io.IOException;
 import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -10,6 +11,7 @@
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
 
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -38,7 +40,7 @@
         DSTU4145Test();
         generationTest();
         //parametersTest();
-
+        generateFromCurveTest();
     }
 
     public static void main(String[] args)
@@ -104,6 +106,27 @@
         }
     }
 
+    private void generateFromCurveTest()
+        throws Exception
+    {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSTU4145", "BC");
+
+        for (int i = 0; i != 10; i++)
+        {
+            keyGen.initialize(new ECGenParameterSpec("1.2.804.2.1.1.1.1.3.1.1.2." + i));
+        }
+
+        try
+        {
+            keyGen.initialize(new ECGenParameterSpec("1.2.804.2.1.1.1.1.3.1.1.2." + 10));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isTrue("unknown curve name: 1.2.804.2.1.1.1.1.3.1.1.2.10".equals(e.getMessage()));
+        }
+    }
+
     private void DSTU4145Test()
         throws Exception
     {
@@ -187,5 +210,4 @@
 
         return sig;
     }
-
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU7624Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU7624Test.java
new file mode 100644
index 0000000..9827adc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DSTU7624Test.java
@@ -0,0 +1,359 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * basic test class for DSTU7624
+ */
+public class DSTU7624Test
+    extends BaseBlockCipherTest
+{
+    public DSTU7624Test()
+    {
+        super("DSTU7624");
+    }
+
+    public void test(
+        String name,
+        byte[] keyBytes,
+        byte[] input,
+        byte[] output)
+        throws Exception
+    {
+        Key key;
+        Cipher in, out;
+        CipherInputStream cIn;
+        CipherOutputStream cOut;
+        ByteArrayInputStream bIn;
+        ByteArrayOutputStream bOut;
+
+        key = new SecretKeySpec(keyBytes, name);
+
+        in = Cipher.getInstance(name + "/ECB/NoPadding", "BC");
+        out = Cipher.getInstance(name + "/ECB/NoPadding", "BC");
+
+        try
+        {
+            out.init(Cipher.ENCRYPT_MODE, key);
+        }
+        catch (Exception e)
+        {
+            fail("DSTU7624 failed initialisation - " + e.toString(), e);
+        }
+
+        try
+        {
+            in.init(Cipher.DECRYPT_MODE, key);
+        }
+        catch (Exception e)
+        {
+            fail("DSTU7624 failed initialisation - " + e.toString(), e);
+        }
+
+        //
+        // encryption pass
+        //
+        bOut = new ByteArrayOutputStream();
+
+        cOut = new CipherOutputStream(bOut, out);
+
+        try
+        {
+            for (int i = 0; i != input.length / 2; i++)
+            {
+                cOut.write(input[i]);
+            }
+            cOut.write(input, input.length / 2, input.length - input.length / 2);
+            cOut.close();
+        }
+        catch (IOException e)
+        {
+            fail("DSTU7624 failed encryption - " + e.toString(), e);
+        }
+
+        byte[] bytes;
+
+        bytes = bOut.toByteArray();
+
+        if (!areEqual(bytes, output))
+        {
+            fail("DSTU7624 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
+        }
+
+        //
+        // decryption pass
+        //
+        bIn = new ByteArrayInputStream(bytes);
+
+        cIn = new CipherInputStream(bIn, in);
+
+        try
+        {
+            DataInputStream dIn = new DataInputStream(cIn);
+
+            bytes = new byte[input.length];
+
+            for (int i = 0; i != input.length / 2; i++)
+            {
+                bytes[i] = (byte)dIn.read();
+            }
+            dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2);
+        }
+        catch (Exception e)
+        {
+            fail("DSTU7624 failed encryption - " + e.toString(), e);
+        }
+
+        if (!areEqual(bytes, input))
+        {
+            fail("DSTU7624 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes)));
+        }
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        test("DSTU7624", Hex.decode("000102030405060708090A0B0C0D0E0F"), Hex.decode("101112131415161718191A1B1C1D1E1F"), Hex.decode("81BF1C7D779BAC20E1C9EA39B4D2AD06"));
+        test("DSTU7624", Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), Hex.decode("202122232425262728292A2B2C2D2E2F"), Hex.decode("58EC3E091000158A1148F7166F334F14"));
+
+        test("DSTU7624-128", Hex.decode("000102030405060708090A0B0C0D0E0F"), Hex.decode("101112131415161718191A1B1C1D1E1F"), Hex.decode("81BF1C7D779BAC20E1C9EA39B4D2AD06"));
+        test("DSTU7624-128", Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), Hex.decode("202122232425262728292A2B2C2D2E2F"), Hex.decode("58EC3E091000158A1148F7166F334F14"));
+
+        test("DSTU7624-256", Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), Hex.decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"), Hex.decode("F66E3D570EC92135AEDAE323DCBD2A8CA03963EC206A0D5A88385C24617FD92C"));
+        test("DSTU7624-256", Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"), Hex.decode("606990E9E6B7B67A4BD6D893D72268B78E02C83C3CD7E102FD2E74A8FDFE5DD9"));
+
+        test("DSTU7624-512", Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"), Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F"), Hex.decode("4A26E31B811C356AA61DD6CA0596231A67BA8354AA47F3A13E1DEEC320EB56B895D0F417175BAB662FD6F134BB15C86CCB906A26856EFEB7C5BC6472940DD9D9"));
+
+        byte[] kek1 = Hex.decode("000102030405060708090A0B0C0D0E0F");
+        byte[] in1 = Hex.decode("101112131415161718191A1B1C1D1E1F");
+        byte[] out1 = Hex.decode("1DC91DC6E52575F6DBED25ADDA95A1B6AD3E15056E489738972C199FB9EE2913");
+
+        wrapTest(1, "DSTU7624Wrap", kek1, in1, out1);
+
+        String[] oids = {
+
+            UAObjectIdentifiers.dstu7624ecb_128.getId(),
+            UAObjectIdentifiers.dstu7624ecb_256.getId(),
+            UAObjectIdentifiers.dstu7624ecb_512.getId(),
+
+            UAObjectIdentifiers.dstu7624cbc_128.getId(),
+            UAObjectIdentifiers.dstu7624cbc_256.getId(),
+            UAObjectIdentifiers.dstu7624cbc_512.getId(),
+
+            UAObjectIdentifiers.dstu7624ofb_128.getId(),
+            UAObjectIdentifiers.dstu7624ofb_256.getId(),
+            UAObjectIdentifiers.dstu7624ofb_512.getId(),
+
+            UAObjectIdentifiers.dstu7624cfb_128.getId(),
+            UAObjectIdentifiers.dstu7624cfb_256.getId(),
+            UAObjectIdentifiers.dstu7624cfb_512.getId(),
+
+            UAObjectIdentifiers.dstu7624ctr_128.getId(),
+            UAObjectIdentifiers.dstu7624ctr_256.getId(),
+            UAObjectIdentifiers.dstu7624ctr_512.getId(),
+
+            UAObjectIdentifiers.dstu7624ccm_128.getId(),
+            UAObjectIdentifiers.dstu7624ccm_256.getId(),
+            UAObjectIdentifiers.dstu7624ccm_512.getId(),
+        };
+
+        String[] names = {
+            "DSTU7624-128/ECB/PKCS7Padding",
+            "DSTU7624-256/ECB/PKCS7Padding",
+            "DSTU7624-512/ECB/PKCS7Padding",
+            "DSTU7624-128/CBC/PKCS7Padding",
+            "DSTU7624-256/CBC/PKCS7Padding",
+            "DSTU7624-512/CBC/PKCS7Padding",
+            "DSTU7624-128/OFB/NoPadding",
+            "DSTU7624-256/OFB/NoPadding",
+            "DSTU7624-512/OFB/NoPadding",
+            "DSTU7624-128/CFB/NoPadding",
+            "DSTU7624-256/CFB/NoPadding",
+            "DSTU7624-512/CFB/NoPadding",
+            "DSTU7624-128/CTR/NoPadding",
+            "DSTU7624-256/CTR/NoPadding",
+            "DSTU7624-512/CTR/NoPadding",
+            "DSTU7624-128/CCM/NoPadding",
+            "DSTU7624-256/CCM/NoPadding",
+            "DSTU7624-512/CCM/NoPadding",
+        };
+
+        int[] keyBlockLengths = {
+            16,
+            32,
+            64,
+            16,
+            32,
+            64,
+            16,
+            32,
+            64,
+            16,
+            32,
+            64,
+            16,
+            32,
+            64,
+            16,
+            32,
+            64,
+        };
+
+        oidTest(oids, names, keyBlockLengths);
+
+        wrapOidTest(UAObjectIdentifiers.dstu7624kw_128, "DSTU7624Wrap", 16);
+
+        wrapOidTest(UAObjectIdentifiers.dstu7624kw_256, "DSTU7624-256Wrap", 32);
+
+        wrapOidTest(UAObjectIdentifiers.dstu7624kw_512, "DSTU7624-512Wrap", 64);
+
+        macOidTest(UAObjectIdentifiers.dstu7624gmac_128, "DSTU7624GMAC", 16);
+
+        macOidTest(UAObjectIdentifiers.dstu7624gmac_128, "DSTU7624-128GMAC", 16);
+
+        macOidTest(UAObjectIdentifiers.dstu7624gmac_256, "DSTU7624-256GMAC", 32);
+
+        macOidTest(UAObjectIdentifiers.dstu7624gmac_512, "DSTU7624-512GMAC", 64);
+    }
+
+    protected void wrapOidTest(ASN1ObjectIdentifier oid, String name, int blockLength)
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+
+        byte[] data = new byte[blockLength];
+
+        random.nextBytes(data);
+
+        Cipher c1 = Cipher.getInstance(oid.getId(), "BC");
+        Cipher c2 = Cipher.getInstance(name, "BC");
+        KeyGenerator kg = KeyGenerator.getInstance(oid.getId(), "BC");
+
+        SecretKey k = kg.generateKey();
+
+        c1.init(Cipher.WRAP_MODE, k);
+        c2.init(Cipher.UNWRAP_MODE, k);
+
+        Key wKey = c2.unwrap(c1.wrap(new SecretKeySpec(data, algorithm)), algorithm, Cipher.SECRET_KEY);
+
+        if (!areEqual(data, wKey.getEncoded()))
+        {
+            fail("failed wrap OID test");
+        }
+
+        if (k.getEncoded().length != blockLength)
+        {
+            fail("failed key length test");
+        }
+    }
+
+    protected void macOidTest(ASN1ObjectIdentifier oid, String name, int blockLength)
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+
+        byte[] data = new byte[blockLength];
+
+        random.nextBytes(data);
+
+        Mac m1 = Mac.getInstance(oid.getId(), "BC");
+        Mac m2 = Mac.getInstance(name, "BC");
+        KeyGenerator kg = KeyGenerator.getInstance(oid.getId(), "BC");
+
+        SecretKey k = kg.generateKey();
+
+        m1.init(k, new IvParameterSpec(new byte[blockLength]));
+        m2.init(k, new IvParameterSpec(new byte[blockLength]));
+
+        m1.update(data);
+
+        m2.update(data);
+
+        byte[] mac = m1.doFinal();
+
+        if (mac.length != blockLength)
+        {
+            fail("mac wrong size");
+        }
+        if (!areEqual(mac, m2.doFinal()))
+        {
+            fail("failed mac OID test");
+        }
+
+        if (k.getEncoded().length != blockLength)
+        {
+            fail("failed key length test");
+        }
+    }
+
+    private void oidTest(String[] oids, String[] names, int[] keyBlockLengths)
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+
+        for (int i = 0; i != oids.length; i++)
+        {
+            byte[] data = new byte[keyBlockLengths[i]];
+
+            random.nextBytes(data);
+
+            IvParameterSpec ivSpec = new IvParameterSpec(new byte[keyBlockLengths[i]]);
+            Cipher c1 = Cipher.getInstance(oids[i], "BC");
+            Cipher c2 = Cipher.getInstance(names[i], "BC");
+            KeyGenerator kg = KeyGenerator.getInstance(oids[i], "BC");
+
+            SecretKey k = kg.generateKey();
+
+            if (names[i].indexOf("/ECB/") > 0)
+            {
+                c1.init(Cipher.ENCRYPT_MODE, k);
+                c2.init(Cipher.DECRYPT_MODE, k);
+            }
+            else
+            {
+                c1.init(Cipher.ENCRYPT_MODE, k, ivSpec);
+                c2.init(Cipher.DECRYPT_MODE, k, ivSpec);
+            }
+
+            byte[] result = c2.doFinal(c1.doFinal(data));
+
+            if (!areEqual(data, result))
+            {
+                fail("failed OID test: " + names[i]);
+            }
+
+            if (k.getEncoded().length != keyBlockLengths[i])
+            {
+                fail("failed key length test");
+            }
+        }
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new DSTU7624Test());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
index 151b01d..e9b4a58 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
@@ -3,8 +3,12 @@
 import java.security.MessageDigest;
 import java.security.Security;
 
+import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
@@ -27,17 +31,25 @@
         { "SHA-512/224", "4634270F707B6A54DAAE7530460842E20E37ED265CEEE9A43E8924AA" },
         { "SHA-512/256", "53048E2681941EF99B2E29B76B4C7DABE4C2D0C634FC6D46E0E2F13107E7AF23" },
         { "RIPEMD128", "c14a12199c66e4ba84636b0f69144c77" },
+        { TeleTrusTObjectIdentifiers.ripemd128.getId(), "c14a12199c66e4ba84636b0f69144c77" },
         { "RIPEMD160", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" },
+        { TeleTrusTObjectIdentifiers.ripemd160.getId(),  "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" },
         { "RIPEMD256", "afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65" },
+        { TeleTrusTObjectIdentifiers.ripemd256.getId(), "afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65" },
         { "RIPEMD320", "de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d" },
         { "Tiger", "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93" },
         { "GOST3411", "b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c" },
         { "WHIRLPOOL", "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" },
+        { ISOIECObjectIdentifiers.whirlpool.getId(), "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" },
         { "SM3", "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" },
         { "SHA3-224", "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf" },
         { "SHA3-256", "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" },
         { "SHA3-384", "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25" },
         { "SHA3-512", "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0" },
+        { NISTObjectIdentifiers.id_sha3_224.getId(), "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf" },
+        { NISTObjectIdentifiers.id_sha3_256.getId(), "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" },
+        { NISTObjectIdentifiers.id_sha3_384.getId(), "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25" },
+        { NISTObjectIdentifiers.id_sha3_512.getId(), "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0" },
         { "KECCAK-224", "c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8" },
         { "KECCAK-256", "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45" },
         { "KECCAK-288", "20ff13d217d5789fa7fc9e0e9a2ee627363ec28171d0b6c52bbd2f240554dbc94289f4d6" },
@@ -51,10 +63,24 @@
         { MiscObjectIdentifiers.id_blake2b256.getId(), "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319" },
         { MiscObjectIdentifiers.id_blake2b384.getId(), "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4" },
         { MiscObjectIdentifiers.id_blake2b512.getId(), "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923" },
+        { "BLAKE2S-128", "aa4938119b1dc7b87cbad0ffd200d0ae" },
+        { "BLAKE2S-160", "5ae3b99be29b01834c3b508521ede60438f8de17" },
+        { "BLAKE2S-224", "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55" },
+        { "BLAKE2S-256", "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982" },
+        { MiscObjectIdentifiers.id_blake2s128.getId(), "aa4938119b1dc7b87cbad0ffd200d0ae" },
+        { MiscObjectIdentifiers.id_blake2s160.getId(), "5ae3b99be29b01834c3b508521ede60438f8de17" },
+        { MiscObjectIdentifiers.id_blake2s224.getId(), "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55" },
+        { MiscObjectIdentifiers.id_blake2s256.getId(), "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982" },
         { "GOST3411-2012-256", "4e2919cf137ed41ec4fb6270c61826cc4fffb660341e0af3688cd0626d23b481" },
         { RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.getId(), "4e2919cf137ed41ec4fb6270c61826cc4fffb660341e0af3688cd0626d23b481" },
         { "GOST3411-2012-512", "28156e28317da7c98f4fe2bed6b542d0dab85bb224445fcedaf75d46e26d7eb8d5997f3e0915dd6b7f0aab08d9c8beb0d8c64bae2ab8b3c8c6bc53b3bf0db728" },
         { RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId(), "28156e28317da7c98f4fe2bed6b542d0dab85bb224445fcedaf75d46e26d7eb8d5997f3e0915dd6b7f0aab08d9c8beb0d8c64bae2ab8b3c8c6bc53b3bf0db728" },
+        { "DSTU7564-256", "0bd1b36109f1318411a0517315aa46b8839df06622a278676f5487996c9cfc04" },
+        { UAObjectIdentifiers.dstu7564digest_256.getId(), "0bd1b36109f1318411a0517315aa46b8839df06622a278676f5487996c9cfc04" },
+        { "DSTU7564-384", "72945012b0820c3132846ddc90da511f80bb7b70abd0cb1ab8df785d600c187b9d0ac567e8b6f76fde8a0b417a2ebf88" },
+        { UAObjectIdentifiers.dstu7564digest_384.getId(), "72945012b0820c3132846ddc90da511f80bb7b70abd0cb1ab8df785d600c187b9d0ac567e8b6f76fde8a0b417a2ebf88" },
+        { "DSTU7564-512", "9e5be7daf7b68b49d2ecbd04c7a5b3af72945012b0820c3132846ddc90da511f80bb7b70abd0cb1ab8df785d600c187b9d0ac567e8b6f76fde8a0b417a2ebf88" },
+        { UAObjectIdentifiers.dstu7564digest_512.getId(), "9e5be7daf7b68b49d2ecbd04c7a5b3af72945012b0820c3132846ddc90da511f80bb7b70abd0cb1ab8df785d600c187b9d0ac567e8b6f76fde8a0b417a2ebf88" },
     };
     
     public String getName()
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
index 7b74b52..b37e301 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
@@ -58,11 +58,13 @@
 import org.bouncycastle.jce.ECNamedCurveTable;
 import org.bouncycastle.jce.ECPointUtil;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.FixedSecureRandom;
 import org.bouncycastle.util.test.SimpleTest;
@@ -71,6 +73,10 @@
 public class ECDSA5Test
     extends SimpleTest
 {
+    private static final byte[] namedPubKey = Base64.decode(
+        "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJMeqHZzm+saHt1m3a4u5BIqgSznd8LNvoeS93zzE9Ll31/AMaveAj" +
+            "JqWxGdyCwnqmM5m3IFCZV3abKVGNpnuQwhIOPMm1355YX1JeEy/ifCx7lYe1o8Xs/Ajqz8cJB3j");
+
     byte[] k1 = Hex.decode("d5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
     byte[] k2 = Hex.decode("345e8d05c075c3a508df729a1685690e68fcfb8c8117847e89063bca1f85d968fd281540b6e13bd1af989a1fbf17e06462bf511f9d0b140fb48ac1b1baa5bded");
 
@@ -164,6 +170,23 @@
         }
     }
 
+    public void testNamedCurveInKeyFactory()
+        throws Exception
+    {
+        KeyFactory kfBc = KeyFactory.getInstance("EC", "BC");
+        BigInteger x = new BigInteger("24c7aa1d9ce6fac687b759b76b8bb9048aa04b39ddf0b36fa1e4bddf3cc4f4b977d7f00c6af7808c9a96c467720b09ea", 16);
+        BigInteger y = new BigInteger("98ce66dc8142655dda6ca5463699ee43084838f326d77e79617d49784cbf89f0b1ee561ed68f17b3f023ab3f1c241de3", 16);
+        String curveName = "secp384r1";
+        ECPoint point = new ECPoint(x, y);
+
+        AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", "BC");
+        parameters.init(new ECGenParameterSpec(curveName));
+        ECParameterSpec ecParamSpec = parameters.getParameterSpec(ECParameterSpec.class);
+        PublicKey pubKey = kfBc.generatePublic(new ECPublicKeySpec(point, ecParamSpec));
+
+        isTrue(Arrays.areEqual(namedPubKey, pubKey.getEncoded()));
+    }
+
     private void decodeTest()
     {
         EllipticCurve curve = new EllipticCurve(
@@ -258,6 +281,49 @@
         }
     }
 
+    private void testSM2()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("ECDSA", "BC");
+
+        kpGen.initialize(new ECGenParameterSpec("sm2p256v1"));
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        kpGen.initialize(new ECNamedCurveGenParameterSpec("sm2p256v1"));
+
+        kp = kpGen.generateKeyPair();
+    }
+
+    private void testNonsense()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("ECDSA", "BC");
+
+        try
+        {
+            kpGen.initialize(new ECGenParameterSpec("no_such_curve"));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("unknown curve name: no_such_curve", e.getMessage());
+        }
+        KeyPair kp = kpGen.generateKeyPair();
+
+        try
+        {
+            kpGen.initialize(new ECNamedCurveGenParameterSpec("1.2.3.4.5"));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("unknown curve OID: 1.2.3.4.5", e.getMessage());
+        }
+
+        kp = kpGen.generateKeyPair();
+    }
+
     // test BSI algorithm support.
     private void testBSI()
         throws Exception
@@ -286,6 +352,15 @@
             BSIObjectIdentifiers.ecdsa_plain_SHA512.getId(), BSIObjectIdentifiers.ecdsa_plain_RIPEMD160.getId()};
 
         testBsiAlgorithms(kp, data, plainAlgs, plainOids);
+
+        kpGen = KeyPairGenerator.getInstance("ECDSA", "BC");
+
+        kpGen.initialize(new ECGenParameterSpec(SECObjectIdentifiers.secp521r1.getId()));
+
+        kp = kpGen.generateKeyPair();
+
+        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(SECObjectIdentifiers.secp521r1.getId());
+        testBsiSigSize(kp, spec.getN(), "SHA224WITHPLAIN-ECDSA");
     }
 
     private void testBsiAlgorithms(KeyPair kp, byte[] data, String[] algs, String[] oids)
@@ -313,6 +388,32 @@
         }
     }
 
+    private void testBsiSigSize(KeyPair kp, BigInteger order, String alg)
+        throws Exception
+    {
+        for (int i = 0; i != 20; i++)
+        {
+            Signature sig1 = Signature.getInstance(alg, "BC");
+            Signature sig2 = Signature.getInstance(alg, "BC");
+
+            sig1.initSign(kp.getPrivate());
+
+            sig1.update(new byte[]{(byte)i});
+
+            byte[] sig = sig1.sign();
+            
+            isTrue(sig.length == (2 * ((order.bitLength() + 7) / 8)));
+            sig2.initVerify(kp.getPublic());
+
+            sig2.update(new byte[]{(byte)i});
+
+            if (!sig2.verify(sig))
+            {
+                fail("BSI CVC signature failed: " + alg);
+            }
+        }
+    }
+    
     /**
      * X9.62 - 1998,<br>
      * J.2.1, Page 100, ECDSA over the field F2m<br>
@@ -505,7 +606,7 @@
         PublicKey pubKey = ECKeyUtil.publicToExplicitParameters(pair.getPublic(), "BC");
 
         SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pubKey.getEncoded()));
-        X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters());
+        X962Parameters params = X962Parameters.getInstance(info.getAlgorithm().getParameters());
 
         if (params.isNamedCurve() || params.isImplicitlyCA())
         {
@@ -519,7 +620,7 @@
 
         PrivateKey privKey = ECKeyUtil.privateToExplicitParameters(pair.getPrivate(), "BC");
         PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privKey.getEncoded()));
-        params = X962Parameters.getInstance(privInfo.getAlgorithmId().getParameters());
+        params = X962Parameters.getInstance(privInfo.getPrivateKeyAlgorithm().getParameters());
 
         if (params.isNamedCurve() || params.isImplicitlyCA())
         {
@@ -1123,6 +1224,9 @@
         testMQVwithHMACOnePass();
         testAlgorithmParameters();
         testModified();
+        testSM2();
+        testNonsense();
+        testNamedCurveInKeyFactory();
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECNRTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECNRTest.java
index 98b1058..d84c2f7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECNRTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECNRTest.java
@@ -13,6 +13,8 @@
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
@@ -47,18 +49,11 @@
 
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
         
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-        
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
             new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
@@ -91,18 +86,11 @@
 
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("dcc5d1f1020906df2782360d36b2de7a17ece37d503784af", 16));
         
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q (or p)
-            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", 16));  // b
-        
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("03188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012")), // G
-            new BigInteger("6277101735386680763835789423176059013767194773182842284081")); // n
-        
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime192v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
             new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
@@ -135,18 +123,11 @@
 
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("cdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 16));
         
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-        
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
-        
+        X9ECParameters x9 = ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/EdECTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/EdECTest.java
new file mode 100644
index 0000000..a73fd2f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/EdECTest.java
@@ -0,0 +1,628 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.crypto.KeyAgreement;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.jcajce.spec.DHUParameterSpec;
+import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import org.bouncycastle.jcajce.spec.XDHParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class EdECTest
+    extends SimpleTest
+{
+    private static final byte[] pubEnc = Base64.decode(
+        "MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=");
+
+    private static final byte[] privEnc = Base64.decode(
+        "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC");
+
+    private static final byte[] privWithPubEnc = Base64.decode(
+        "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC" +
+            "oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB" +
+            "Z9w7lshQhqowtrbLDFw4rXAxZuE=");
+
+    public static final byte[] x25519Cert = Base64.decode(
+        "MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX" +
+            "N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD" +
+            "DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj" +
+            "ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg" +
+            "BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v" +
+            "/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg" +
+            "w1AH9efZBw==");
+
+    public String getName()
+    {
+        return "EdEC";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        KeyFactory kFact = KeyFactory.getInstance("EdDSA", "BC");
+
+        PublicKey pub = kFact.generatePublic(new X509EncodedKeySpec(pubEnc));
+
+        isTrue("pub failed", areEqual(pubEnc, pub.getEncoded()));
+
+        serializationTest("ref pub", pub);
+
+        PrivateKey priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(privEnc));
+
+        isTrue("priv failed", areEqual(privEnc, priv.getEncoded()));
+
+        priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(privWithPubEnc));
+
+        isTrue("priv with pub failed", areEqual(privWithPubEnc, priv.getEncoded()));
+
+        serializationTest("ref priv", priv);
+
+        Signature sig = Signature.getInstance("EDDSA", "BC");
+
+        Certificate x25519Cert = Certificate.getInstance(EdECTest.x25519Cert);
+
+        sig.initVerify(pub);
+
+        sig.update(x25519Cert.getTBSCertificate().getEncoded());
+
+        isTrue(sig.verify(x25519Cert.getSignature().getBytes()));
+
+        x448AgreementTest();
+        x25519AgreementTest();
+        ed448SignatureTest();
+        ed25519SignatureTest();
+        x448withCKDFTest();
+        x25519withCKDFTest();
+        x448withKDFTest();
+        x25519withKDFTest();
+        x448UwithKDFTest();
+        x25519UwithKDFTest();
+
+        xdhGeneratorTest();
+        eddsaGeneratorTest();
+
+        keyTest("X448");
+        keyTest("X25519");
+        keyTest("Ed448");
+        keyTest("Ed25519");
+
+        keyFactoryTest("X448", EdECObjectIdentifiers.id_X448);
+        keyFactoryTest("X25519", EdECObjectIdentifiers.id_X25519);
+        keyFactoryTest("Ed448", EdECObjectIdentifiers.id_Ed448);
+        keyFactoryTest("Ed25519", EdECObjectIdentifiers.id_Ed25519);
+    }
+
+    private void keyFactoryTest(String algorithm, ASN1ObjectIdentifier algOid)
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC");
+        KeyFactory kFact = KeyFactory.getInstance((algorithm.startsWith("X") ? "XDH" : "EdDSA"), "BC");
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        Set<String> alts = new HashSet<String>();
+
+        alts.add("X448");
+        alts.add("X25519");
+        alts.add("Ed448");
+        alts.add("Ed25519");
+
+        alts.remove(algorithm);
+
+        PrivateKey k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        checkEquals(algorithm, kp.getPrivate(), k1);
+
+        PublicKey k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        checkEquals(algorithm, kp.getPublic(), k2);
+
+        for (Iterator<String> it = alts.iterator(); it.hasNext(); )
+        {
+            String altAlg = (String)it.next();
+
+            kFact = KeyFactory.getInstance(altAlg, "BC");
+
+            try
+            {
+                k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+                fail("no exception");
+            }
+            catch (InvalidKeySpecException e)
+            {
+                isEquals("encoded key spec not recognized: algorithm identifier " + algOid.getId() + " in key not recognized", e.getMessage());
+            }
+
+            try
+            {
+                k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+                fail("no exception");
+            }
+            catch (InvalidKeySpecException e)
+            {
+                isEquals("encoded key spec not recognized: algorithm identifier " + algOid.getId() + " in key not recognized", e.getMessage());
+            }
+        }
+    }
+
+    private void keyTest(String algorithm)
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC");
+
+        KeyFactory kFact = KeyFactory.getInstance(algorithm, "BC");
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        PrivateKey k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        checkEquals(algorithm, kp.getPrivate(), k1);
+
+        PublicKey k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        checkEquals(algorithm, kp.getPublic(), k2);
+
+        serializationTest(algorithm, kp.getPublic());
+        serializationTest(algorithm, kp.getPrivate());
+
+        String pubString = kp.getPublic().toString();
+        String privString = kp.getPrivate().toString();
+
+        isTrue(pubString.startsWith(algorithm + " Public Key ["));
+        isTrue(privString.startsWith(algorithm + " Private Key ["));
+        isTrue(privString.substring((algorithm + " Private Key [").length())
+            .equals(pubString.substring((algorithm + " Public Key [").length())));
+    }
+
+    private void xdhGeneratorTest()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XDH", "BC");
+
+        kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X448));
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        isTrue("X448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(new ECGenParameterSpec(XDHParameterSpec.X448));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(448);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen = KeyPairGenerator.getInstance("XDH", "BC");
+        
+        kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X25519));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(new ECGenParameterSpec(XDHParameterSpec.X25519));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(256);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(255);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("X25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen = KeyPairGenerator.getInstance("XDH", "BC");
+
+        try
+        {
+            kpGen.generateKeyPair();
+            fail("no exception");
+        }
+        catch (IllegalStateException e)
+        {
+            isEquals("generator not correctly initialized", e.getMessage());
+        }
+
+        try
+        {
+            kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("parameterSpec for wrong curve type", e.getMessage());
+        }
+
+        try
+        {
+            kpGen.initialize(1024);
+            fail("no exception");
+        }
+        catch (InvalidParameterException e)
+        {
+            isEquals("unknown key size", e.getMessage());
+        }
+        
+        try
+        {
+            kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("parameterSpec for wrong curve type", e.getMessage());
+        }
+
+        try
+        {
+            new XDHParameterSpec(EdDSAParameterSpec.Ed448);
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("unrecognized curve name: Ed448", e.getMessage());
+        }
+    }
+
+    private void eddsaGeneratorTest()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EdDSA", "BC");
+
+        kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448));
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        isTrue("Ed448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(448);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed448".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen = KeyPairGenerator.getInstance("EdDSA", "BC");
+
+        kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed25519));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(new ECGenParameterSpec(EdDSAParameterSpec.Ed25519));
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(256);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen.initialize(255);
+
+        kp = kpGen.generateKeyPair();
+
+        isTrue("Ed25519".equals(kp.getPublic().getAlgorithm()));
+
+        kpGen = KeyPairGenerator.getInstance("EdDSA", "BC");
+
+        try
+        {
+            kpGen.generateKeyPair();
+            fail("no exception");
+        }
+        catch (IllegalStateException e)
+        {
+            isEquals("generator not correctly initialized", e.getMessage());
+        }
+
+        try
+        {
+            kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X448));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("parameterSpec for wrong curve type", e.getMessage());
+        }
+
+        try
+        {
+            kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X25519));
+            fail("no exception");
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            isEquals("parameterSpec for wrong curve type", e.getMessage());
+        }
+
+        try
+        {
+            kpGen.initialize(1024);
+            fail("no exception");
+        }
+        catch (InvalidParameterException e)
+        {
+            isEquals("unknown key size", e.getMessage());
+        }
+
+        try
+        {
+            new EdDSAParameterSpec(XDHParameterSpec.X448);
+        }
+        catch (IllegalArgumentException e)
+        {
+            isEquals("unrecognized curve name: X448", e.getMessage());
+        }
+    }
+
+    private void checkEquals(String algorithm, Key ka, Key kb)
+    {
+        isEquals(algorithm + " check equals", ka, kb);
+        isEquals(algorithm + " check hashCode", ka.hashCode(), kb.hashCode());
+    }
+
+    private void serializationTest(String algorithm, Key key)
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(key);
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        Key rk = (Key)oIn.readObject();
+
+        checkEquals(algorithm, key, rk);
+    }
+
+    private void x448AgreementTest()
+        throws Exception
+    {
+        agreementTest("X448");
+    }
+
+    private void x25519AgreementTest()
+        throws Exception
+    {
+        agreementTest("X25519");
+    }
+
+    private void x448withCKDFTest()
+        throws Exception
+    {
+        agreementTest("X448withSHA256CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+        agreementTest("X448withSHA384CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+        agreementTest("X448withSHA512CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+    }
+
+    private void x25519withCKDFTest()
+        throws Exception
+    {
+        agreementTest("X25519withSHA256CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+        agreementTest("X25519withSHA384CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+        agreementTest("X25519withSHA512CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+    }
+
+    private void x448withKDFTest()
+        throws Exception
+    {
+        agreementTest("X448withSHA512KDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+    }
+
+    private void x25519withKDFTest()
+        throws Exception
+    {
+        agreementTest("X25519withSHA256KDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed")));
+    }
+
+    private void ed448SignatureTest()
+        throws Exception
+    {
+        signatureTest("Ed448");
+    }
+
+    private void ed25519SignatureTest()
+        throws Exception
+    {
+        signatureTest("Ed25519");
+    }
+
+    private void agreementTest(String algorithm)
+        throws Exception
+    {
+        agreementTest(algorithm, null);
+    }
+
+    private void agreementTest(String algorithm, AlgorithmParameterSpec spec)
+        throws Exception
+    {
+        KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm, "BC");
+
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance(
+            algorithm.startsWith("X448") ? "X448" : "X25519", "BC");
+
+        KeyPair kp1 = kpGen.generateKeyPair();
+        KeyPair kp2 = kpGen.generateKeyPair();
+
+        keyAgreement.init(kp1.getPrivate());
+
+        keyAgreement.doPhase(kp2.getPublic(), true);
+
+        byte[] sec1 = keyAgreement.generateSecret();
+
+        keyAgreement.init(kp2.getPrivate());
+
+        keyAgreement.doPhase(kp1.getPublic(), true);
+
+        byte[] sec2 = keyAgreement.generateSecret();
+
+        isTrue(areEqual(sec1, sec2));
+
+        if (spec != null)
+        {
+            keyAgreement.init(kp1.getPrivate(), spec);
+
+            keyAgreement.doPhase(kp2.getPublic(), true);
+
+            byte[] sec3 = keyAgreement.generateSecret();
+
+            keyAgreement.init(kp2.getPrivate(), spec);
+
+            keyAgreement.doPhase(kp1.getPublic(), true);
+
+            byte[] sec4 = keyAgreement.generateSecret();
+
+            isTrue(areEqual(sec3, sec4));
+            isTrue(!areEqual(sec1, sec4));
+        }
+    }
+
+    private void x448UwithKDFTest()
+        throws Exception
+    {
+        unifiedAgreementTest("X448UwithSHA512KDF");
+    }
+
+    private void x25519UwithKDFTest()
+        throws Exception
+    {
+        unifiedAgreementTest("X25519UwithSHA256KDF");
+    }
+
+    private void unifiedAgreementTest(String algorithm)
+        throws Exception
+    {
+        KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm, "BC");
+
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance(
+            algorithm.startsWith("X448") ? "X448" : "X25519", "BC");
+
+        KeyPair aKp1 = kpGen.generateKeyPair();
+        KeyPair aKp2 = kpGen.generateKeyPair();
+
+        KeyPair bKp1 = kpGen.generateKeyPair();
+        KeyPair bKp2 = kpGen.generateKeyPair();
+
+        keyAgreement.init(aKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("beeffeed")));
+
+        keyAgreement.doPhase(bKp1.getPublic(), true);
+
+        byte[] sec1 = keyAgreement.generateSecret();
+
+        keyAgreement.init(bKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("beeffeed")));
+
+        keyAgreement.doPhase(aKp1.getPublic(), true);
+
+        byte[] sec2 = keyAgreement.generateSecret();
+
+        isTrue(areEqual(sec1, sec2));
+
+        keyAgreement.init(bKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("feed")));
+
+        keyAgreement.doPhase(aKp1.getPublic(), true);
+
+        byte[] sec3 = keyAgreement.generateSecret();
+
+        isTrue(!areEqual(sec1, sec3));
+    }
+
+    private void signatureTest(String algorithm)
+        throws Exception
+    {
+        byte[] msg = Strings.toByteArray("Hello, world!");
+        Signature signature = Signature.getInstance(algorithm, "BC");
+
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC");
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        signature.initSign(kp.getPrivate());
+
+        signature.update(msg);
+
+        byte[] sig = signature.sign();
+
+        signature.initVerify(kp.getPublic());
+
+        signature.update(msg);
+
+        isTrue(signature.verify(sig));
+
+        // try with random - should be ignored
+
+        signature.initSign(kp.getPrivate(), new SecureRandom());
+
+        signature.update(msg);
+
+        sig = signature.sign();
+
+        signature.initVerify(kp.getPublic());
+
+        signature.update(msg);
+
+        isTrue(signature.verify(sig));
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new EdECTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ElGamalTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ElGamalTest.java
index e2d24e6..06f6930 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ElGamalTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ElGamalTest.java
@@ -24,6 +24,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -128,11 +129,13 @@
         
         outLen += c1.doFinal(in, 2, in.length - 2, out1, outLen);
 
+        out2 = new byte[c2.getOutputSize(out1.length)];
+
         outLen = c2.update(out1, 0, 2, out2, 0);
         
         outLen += c2.doFinal(out1, 2, out1.length - 2, out2, outLen);
 
-        if (!areEqual(in, out2))
+        if (!areEqual(in, Arrays.copyOfRange(out2, 0, outLen)))
         {
             fail(size + " encrypt with update test failed");
         }
@@ -376,11 +379,13 @@
 
         outLen += c1.doFinal(in, 2, in.length - 2, out1, outLen);
 
+        out2 = new byte[c2.getOutputSize(out1.length)];
+
         outLen = c2.update(out1, 0, 2, out2, 0);
 
         outLen += c2.doFinal(out1, 2, out1.length - 2, out2, outLen);
 
-        if (!areEqual(in, out2))
+        if (!areEqual(in, Arrays.copyOfRange(out2, 0, outLen)))
         {
             fail(size + " encrypt with update test failed");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
index d95b761..7323698 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
@@ -246,7 +246,7 @@
 
             if (!Arrays.areEqual(Hex.decode("1b69996e"), mac.doFinal(Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20"))))
             {
-                fail("mac test falied.");
+                fail("mac test failed.");
             }
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410KeyPairTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410KeyPairTest.java
new file mode 100644
index 0000000..7dda095
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410KeyPairTest.java
@@ -0,0 +1,179 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class GOST3410KeyPairTest
+    extends SimpleTest
+{
+    private void gost2012MismatchTest()
+        throws Exception
+    {
+        KeyPairGenerator keyPair = KeyPairGenerator.getInstance(
+            "ECGOST3410-2012", "BC");
+
+        keyPair.initialize(new ECGenParameterSpec("Tc26-Gost-3410-12-512-paramSetA"));
+
+        KeyPair kp = keyPair.generateKeyPair();
+
+        testWrong256(kp);
+
+        keyPair = KeyPairGenerator.getInstance(
+            "ECGOST3410-2012", "BC");
+
+        keyPair.initialize(new ECGenParameterSpec("Tc26-Gost-3410-12-512-paramSetB"));
+
+        kp = keyPair.generateKeyPair();
+
+        testWrong256(kp);
+
+        keyPair = KeyPairGenerator.getInstance(
+            "ECGOST3410-2012", "BC");
+
+        keyPair.initialize(new ECGenParameterSpec("Tc26-Gost-3410-12-512-paramSetC"));
+
+        kp = keyPair.generateKeyPair();
+
+        testWrong256(kp);
+
+        keyPair = KeyPairGenerator.getInstance(
+            "ECGOST3410-2012", "BC");
+
+        keyPair.initialize(new ECGenParameterSpec("Tc26-Gost-3410-12-256-paramSetA"));
+
+        kp = keyPair.generateKeyPair();
+
+        testWrong512(kp);
+    }
+
+    private void testWrong512(KeyPair kp)
+        throws NoSuchAlgorithmException, NoSuchProviderException
+    {
+        Signature sig;
+        sig = Signature.getInstance("ECGOST3410-2012-512", "BC");
+
+        try
+        {
+            sig.initSign(kp.getPrivate());
+
+            fail("no exception");
+        }
+        catch (InvalidKeyException e)
+        {
+            isEquals("key too weak for ECGOST-2012-512", e.getMessage());
+        }
+
+        try
+        {
+            sig.initVerify(kp.getPublic());
+            fail("no exception");
+        }
+        catch (InvalidKeyException e)
+        {
+            isEquals("key too weak for ECGOST-2012-512", e.getMessage());
+        }
+    }
+
+    private void testWrong256(KeyPair kp)
+        throws NoSuchAlgorithmException, NoSuchProviderException
+    {
+        Signature sig = Signature.getInstance("ECGOST3410-2012-256", "BC");
+
+        try
+        {
+            sig.initSign(kp.getPrivate());
+            fail("no exception");
+        }
+        catch (InvalidKeyException e)
+        {
+            isEquals("key out of range for ECGOST-2012-256", e.getMessage());
+        }
+
+        try
+        {
+            sig.initVerify(kp.getPublic());
+            fail("no exception");
+        }
+        catch (InvalidKeyException e)
+        {
+            isEquals("key out of range for ECGOST-2012-256", e.getMessage());
+        }
+    }
+
+    private BigInteger[] decode(
+        byte[] encoding)
+    {
+        byte[] r = new byte[32];
+        byte[] s = new byte[32];
+
+        System.arraycopy(encoding, 0, s, 0, 32);
+
+        System.arraycopy(encoding, 32, r, 0, 32);
+
+        BigInteger[] sig = new BigInteger[2];
+
+        sig[0] = new BigInteger(1, r);
+        sig[1] = new BigInteger(1, s);
+
+        return sig;
+    }
+
+    private Object serializeDeserialize(Object o)
+        throws Exception
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(o);
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        return oIn.readObject();
+    }
+
+    public String getName()
+    {
+        return "GOST3410/ECGOST3410/ECGOST3410 2012";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        gost2012MismatchTest();
+    }
+
+    protected byte[] toByteArray(String input)
+    {
+        byte[] bytes = new byte[input.length()];
+
+        for (int i = 0; i != bytes.length; i++)
+        {
+            bytes[i] = (byte)input.charAt(i);
+        }
+
+        return bytes;
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new GOST3410KeyPairTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410Test.java
index a7cc7a2..e9e2d17 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3410Test.java
@@ -29,6 +29,17 @@
 import java.util.Date;
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECGOST3410_2012Signer;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.interfaces.ECPrivateKey;
 import org.bouncycastle.jce.interfaces.ECPublicKey;
@@ -41,40 +52,53 @@
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
 import org.bouncycastle.jce.spec.ECPublicKeySpec;
 import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.test.SimpleTest;
 import org.bouncycastle.util.test.TestRandomBigInteger;
 import org.bouncycastle.x509.X509V3CertificateGenerator;
 
+//import java.security.spec.ECGenParameterSpec;
+
 public class GOST3410Test
     extends SimpleTest
 {
+    private static byte[] ecgostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+    private static byte[] ecgost2012_256Key = Base64.decode("MGgwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgNDAARAuSmiubNU4NMTvYsWb59uIa0dvNvikSyafTFTvHYhfoEeyVj5qeCoED1AjraW3Q44EZdNZaS5exAUIHuK5Bhd/Q==");
+    private static byte[] ecgost2012_256Sig = Base64.decode("CNUdC6ny8sryzNcwGy7MG3DUbcU+3RgJNPWb3WVtAwUcbaFKPgL0TERfDM4Vsurwx0POt+PZCTxjaiaoY0UxkQ==");
+
+    private static byte[] ecgost2012_512Key = Base64.decode("MIGqMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUDBwEBAgMDgYQABIGAhiwvUj3M58X6KQfFmqvQhka/JxigdS6hy6rqoYZec0pAwPKFNJ+AUl70zvNR/GDLB2DNBGryofKFXJk1l8aZCHM6cpuSzJbD7y728U/rclJ4GVDAbb4ktq4UmiYaJ7JZcc/CSL0qoj7w69sY7rWZm/T2o+hb1cM1jVq5/u5zYqo=");
+    private static byte[] ecgost2012_512Sig = Base64.decode("uX4splTTDpH6T04tnElszTSmj+aTAl2LV7JxP+1xRRGoQ0ET2+QniOW+6WIOZzCZxEo75fZfx1jRHa7Eo99KfQNzHqmiN7G1Ch9pHQ7eMMwaLVurmWEFpZqBH4k5XfHTSPIa8mUmCn6808xMNy1VfwppbaJwRjtyW0h/CqeDTr8=");
+
     private void ecGOST3410Test()
         throws Exception
     {
-        
+
         BigInteger r = new BigInteger("29700980915817952874371204983938256990422752107994319651632687982059210933395");
         BigInteger s = new BigInteger("46959264877825372965922731380059061821746083849389763294914877353246631700866");
 
         byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("53854137677348463731403841147996619241504003434302020712960838528893196233395"));
 
-        SecureRandom    k = new TestRandomBigInteger(kData);
+        SecureRandom k = new TestRandomBigInteger(kData);
 
-        BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); //p
+        BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041");
+        BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619");
 
         ECCurve curve = new ECCurve.Fp(
-            mod_p, // p
+            mod_p,
             new BigInteger("7"), // a
-            new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414")); // b
+            new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414"), // b
+            mod_q, ECConstants.ONE);
 
         ECParameterSpec spec = new ECParameterSpec(
             curve,
             curve.createPoint(
                 new BigInteger("2"), // x
                 new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
-            new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619")); // q
+            mod_q, ECConstants.ONE);
 
         ECPrivateKeySpec priKey = new ECPrivateKeySpec(
             new BigInteger("55441196065363246126355624130324183196576709222340016572108097750006097525544"), // d
@@ -86,18 +110,18 @@
                 new BigInteger("17614944419213781543809391949654080031942662045363639260709847859438286763994")), // y
             spec);
 
-        Signature           sgr = Signature.getInstance("ECGOST3410", "BC");
-        KeyFactory          f = KeyFactory.getInstance("ECGOST3410", "BC");
-        PrivateKey          sKey = f.generatePrivate(priKey);
-        PublicKey           vKey = f.generatePublic(pubKey);
+        Signature sgr = Signature.getInstance("ECGOST3410", "BC");
+        KeyFactory f = KeyFactory.getInstance("ECGOST3410", "BC");
+        PrivateKey sKey = f.generatePrivate(priKey);
+        PublicKey vKey = f.generatePublic(pubKey);
 
         sgr.initSign(sKey, k);
 
-        byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' };
+        byte[] message = new byte[]{(byte)'a', (byte)'b', (byte)'c'};
 
         sgr.update(message);
 
-        byte[]  sigBytes = sgr.sign();
+        byte[] sigBytes = sgr.sign();
 
         sgr.initVerify(vKey);
 
@@ -108,45 +132,45 @@
             fail("ECGOST3410 verification failed");
         }
 
-        BigInteger[]  sig = decode(sigBytes);
+        BigInteger[] sig = decode(sigBytes);
 
         if (!r.equals(sig[0]))
         {
             fail(
-                  ": r component wrong." + Strings.lineSeparator()
-                + " expecting: " + r + Strings.lineSeparator()
-                + " got      : " + sig[0]);
+                ": r component wrong." + Strings.lineSeparator()
+                    + " expecting: " + r + Strings.lineSeparator()
+                    + " got      : " + sig[0]);
         }
 
         if (!s.equals(sig[1]))
         {
             fail(
-                  ": s component wrong." + Strings.lineSeparator()
-                + " expecting: " + s + Strings.lineSeparator()
-                + " got      : " + sig[1]);
+                ": s component wrong." + Strings.lineSeparator()
+                    + " expecting: " + s + Strings.lineSeparator()
+                    + " got      : " + sig[1]);
         }
     }
 
     private void generationTest()
         throws Exception
     {
-        Signature             s = Signature.getInstance("GOST3410", "BC");
-        KeyPairGenerator      g = KeyPairGenerator.getInstance("GOST3410", "BC");
-        byte[]                data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
+        Signature s = Signature.getInstance("GOST3410", "BC");
+        KeyPairGenerator g = KeyPairGenerator.getInstance("GOST3410", "BC");
+        byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
         GOST3410ParameterSpec gost3410P = new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId());
 
         g.initialize(gost3410P, new SecureRandom());
 
         KeyPair p = g.generateKeyPair();
 
-        PrivateKey  sKey = p.getPrivate();
-        PublicKey   vKey = p.getPublic();
+        PrivateKey sKey = p.getPrivate();
+        PublicKey vKey = p.getPublic();
 
         s.initSign(sKey);
 
         s.update(data);
 
-        byte[]  sigBytes = s.sign();
+        byte[] sigBytes = s.sign();
 
         s = Signature.getInstance("GOST3410", "BC");
 
@@ -192,8 +216,8 @@
         //
         KeyFactory f = KeyFactory.getInstance("GOST3410", "BC");
 
-        X509EncodedKeySpec  x509s = new X509EncodedKeySpec(vKey.getEncoded());
-        GOST3410PublicKey   k1 = (GOST3410PublicKey)f.generatePublic(x509s);
+        X509EncodedKeySpec x509s = new X509EncodedKeySpec(vKey.getEncoded());
+        GOST3410PublicKey k1 = (GOST3410PublicKey)f.generatePublic(x509s);
 
         if (!k1.getY().equals(((GOST3410PublicKey)vKey).getY()))
         {
@@ -205,8 +229,8 @@
             fail("public parameters not decoded properly");
         }
 
-        PKCS8EncodedKeySpec  pkcs8 = new PKCS8EncodedKeySpec(sKey.getEncoded());
-        GOST3410PrivateKey   k2 = (GOST3410PrivateKey)f.generatePrivate(pkcs8);
+        PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(sKey.getEncoded());
+        GOST3410PrivateKey k2 = (GOST3410PrivateKey)f.generatePrivate(pkcs8);
 
         if (!k2.getX().equals(((GOST3410PrivateKey)sKey).getX()))
         {
@@ -358,6 +382,367 @@
         checkEquals(eck1, vKey);
     }
 
+
+    private void ecGOST34102012256Test()
+        throws Exception
+    {
+
+        BigInteger r = new BigInteger("29700980915817952874371204983938256990422752107994319651632687982059210933395");
+        BigInteger s = new BigInteger("574973400270084654178925310019147038455227042649098563933718999175515839552");
+
+        BigInteger e = new BigInteger("20798893674476452017134061561508270130637142515379653289952617252661468872421");
+
+        byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("53854137677348463731403841147996619241504003434302020712960838528893196233395"));
+        SecureRandom k = new TestRandomBigInteger(kData);
+
+        BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041");
+        BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619");
+
+        ECCurve curve = new ECCurve.Fp(
+            mod_p,
+            new BigInteger("7"), // a
+            new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414"), // b
+            mod_q, ECConstants.ONE);
+
+        ECParameterSpec spec = new ECParameterSpec(
+            curve,
+            curve.createPoint(
+                new BigInteger("2"), // x
+                new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
+            mod_q, ECConstants.ONE);
+
+        ECPrivateKeySpec priKey = new ECPrivateKeySpec(
+            new BigInteger("55441196065363246126355624130324183196576709222340016572108097750006097525544"), // d
+            spec);
+
+        ECPublicKeySpec pubKey = new ECPublicKeySpec(
+            curve.createPoint(
+                new BigInteger("57520216126176808443631405023338071176630104906313632182896741342206604859403"), // x
+                new BigInteger("17614944419213781543809391949654080031942662045363639260709847859438286763994")), // y
+            spec);
+
+        KeyFactory f = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+        PrivateKey sKey = f.generatePrivate(priKey);
+        PublicKey vKey = f.generatePublic(pubKey);
+
+        ECGOST3410_2012Signer signer = new ECGOST3410_2012Signer();
+        CipherParameters param = ECUtil.generatePrivateKeyParameter(sKey);
+
+        signer.init(true, new ParametersWithRandom(param, k));
+
+        byte[] rev = e.toByteArray();
+        byte[] message = new byte[rev.length];
+        for (int i = 0; i != rev.length; i++)
+        {
+            message[i] = rev[rev.length - 1 - i];
+        }
+        BigInteger[] sig = signer.generateSignature(message);
+
+        ECPublicKey ecPublicKey = (ECPublicKey)vKey;
+        param = new ECPublicKeyParameters(
+            ecPublicKey.getQ(),
+            new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN()));
+        signer.init(false, param);
+
+        if (!signer.verifySignature(message, sig[0], sig[1]))
+        {
+            fail("ECGOST3410 2012 verification failed");
+        }
+
+        if (!r.equals(sig[0]))
+        {
+            fail(
+                ": r component wrong." + Strings.lineSeparator()
+                    + " expecting: " + r + Strings.lineSeparator()
+                    + " got      : " + sig[0]);
+        }
+
+        if (!s.equals(sig[1]))
+        {
+            fail(
+                ": s component wrong." + Strings.lineSeparator()
+                    + " expecting: " + s + Strings.lineSeparator()
+                    + " got      : " + sig[1]);
+        }
+
+        KeyPairGenerator g = KeyPairGenerator.getInstance("ECGOST3410-2012", "BC");
+
+        g.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-256-paramSetA"), new SecureRandom());
+
+        KeyPair p = g.generateKeyPair();
+
+        signatureGost12Test("ECGOST3410-2012-256", 64, p);
+        encodedGost12Test(p);
+
+
+        g.initialize(new org.bouncycastle.jcajce.spec.GOST3410ParameterSpec("Tc26-Gost-3410-12-512-paramSetA"), new SecureRandom());
+
+        p = g.generateKeyPair();
+
+        signatureGost12Test("ECGOST3410-2012-512", 128, p);
+        encodedGost12Test(p);
+    }
+
+    private void ecGOST2012NameCurveGenerationTest()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("ECGOST3410-2012", "BC");
+
+        kpGen.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-256-paramSetA"));
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        AlgorithmIdentifier expectedAlgId = new AlgorithmIdentifier(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256,
+                    new GOST3410PublicKeyAlgParameters(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256));
+
+        checkKeyPairAlgId(kp, expectedAlgId);
+
+        kpGen.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-512-paramSetA"));
+
+        kp = kpGen.generateKeyPair();
+
+        expectedAlgId = new AlgorithmIdentifier(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512,
+                    new GOST3410PublicKeyAlgParameters(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512));
+
+        checkKeyPairAlgId(kp, expectedAlgId);
+
+        kpGen.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-512-paramSetB"));
+
+        kp = kpGen.generateKeyPair();
+
+        expectedAlgId = new AlgorithmIdentifier(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512,
+                    new GOST3410PublicKeyAlgParameters(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512));
+
+        checkKeyPairAlgId(kp, expectedAlgId);
+
+        kpGen.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-512-paramSetC"));
+
+        kp = kpGen.generateKeyPair();
+
+        expectedAlgId = new AlgorithmIdentifier(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512,
+                    new GOST3410PublicKeyAlgParameters(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512));
+
+        checkKeyPairAlgId(kp, expectedAlgId);
+    }
+
+    private void checkKeyPairAlgId(KeyPair kp, AlgorithmIdentifier expectedAlgId)
+    {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded());
+
+        AlgorithmIdentifier algId = info.getAlgorithm();
+
+        isEquals(expectedAlgId, algId);
+
+        PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded());
+
+        algId = privInfo.getPrivateKeyAlgorithm();
+
+        isEquals(expectedAlgId, algId);
+    }
+
+    private void ecGOST34102012512Test()
+        throws Exception
+    {
+
+        BigInteger r = new BigInteger("2489204477031349265072864643032147753667451319282131444027498637357611092810221795101871412928823716805959828708330284243653453085322004442442534151761462");
+        BigInteger s = new BigInteger("864523221707669519038849297382936917075023735848431579919598799313385180564748877195639672460179421760770893278030956807690115822709903853682831835159370");
+
+        BigInteger e = new BigInteger("2897963881682868575562827278553865049173745197871825199562947419041388950970536661109553499954248733088719748844538964641281654463513296973827706272045964");
+
+        byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("359E7F4B1410FEACC570456C6801496946312120B39D019D455986E364F365886748ED7A44B3E794434006011842286212273A6D14CF70EA3AF71BB1AE679F1", 16));
+        SecureRandom k = new TestRandomBigInteger(kData);
+
+        BigInteger mod_p = new BigInteger("3623986102229003635907788753683874306021320925534678605086546150450856166624002482588482022271496854025090823603058735163734263822371964987228582907372403");
+        BigInteger mod_q = new BigInteger("3623986102229003635907788753683874306021320925534678605086546150450856166623969164898305032863068499961404079437936585455865192212970734808812618120619743");
+
+        ECCurve curve = new ECCurve.Fp(
+            mod_p,
+            new BigInteger("7"), // a
+            new BigInteger("1518655069210828534508950034714043154928747527740206436194018823352809982443793732829756914785974674866041605397883677596626326413990136959047435811826396"), // b
+            mod_q, ECConstants.ONE);
+
+        ECParameterSpec spec = new ECParameterSpec(
+            curve,
+            curve.createPoint(
+                new BigInteger("1928356944067022849399309401243137598997786635459507974357075491307766592685835441065557681003184874819658004903212332884252335830250729527632383493573274"), // x
+                new BigInteger("2288728693371972859970012155529478416353562327329506180314497425931102860301572814141997072271708807066593850650334152381857347798885864807605098724013854")), // y
+            mod_q, ECConstants.ONE);
+
+        ECPrivateKeySpec priKey = new ECPrivateKeySpec(
+            new BigInteger("610081804136373098219538153239847583006845519069531562982388135354890606301782255383608393423372379057665527595116827307025046458837440766121180466875860"), // d
+            spec);
+
+        ECPublicKeySpec pubKey = new ECPublicKeySpec(
+            curve.createPoint(
+                new BigInteger("909546853002536596556690768669830310006929272546556281596372965370312498563182320436892870052842808608262832456858223580713780290717986855863433431150561"), // x
+                new BigInteger("2921457203374425620632449734248415455640700823559488705164895837509539134297327397380287741428246088626609329139441895016863758984106326600572476822372076")), // y
+            spec);
+
+        KeyFactory f = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+        PrivateKey sKey = f.generatePrivate(priKey);
+        PublicKey vKey = f.generatePublic(pubKey);
+
+
+        ECGOST3410_2012Signer signer = new ECGOST3410_2012Signer();
+        CipherParameters param = ECUtil.generatePrivateKeyParameter(sKey);
+
+        signer.init(true, new ParametersWithRandom(param, k));
+
+        byte[] rev = e.toByteArray();
+        byte[] message = new byte[rev.length];
+        for (int i = 0; i != rev.length; i++)
+        {
+            message[i] = rev[rev.length - 1 - i];
+        }
+        BigInteger[] sig = signer.generateSignature(message);
+
+        ECPublicKey ecPublicKey = (ECPublicKey)vKey;
+        param = new ECPublicKeyParameters(
+            ecPublicKey.getQ(),
+            new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN()));
+        signer.init(false, param);
+
+        if (!signer.verifySignature(message, sig[0], sig[1]))
+        {
+            fail("ECGOST3410 2012 verification failed");
+        }
+
+        if (!r.equals(sig[0]))
+        {
+            fail(
+                ": r component wrong." + Strings.lineSeparator()
+                    + " expecting: " + r + Strings.lineSeparator()
+                    + " got      : " + sig[0]);
+        }
+
+        if (!s.equals(sig[1]))
+        {
+            fail(
+                ": s component wrong." + Strings.lineSeparator()
+                    + " expecting: " + s + Strings.lineSeparator()
+                    + " got      : " + sig[1]);
+        }
+
+
+    }
+
+    private void ecGOST2012VerifyTest(String signatureAlg, byte[] data, byte[] pubEnc, byte[] sig)
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+
+        PublicKey vKey = keyFact.generatePublic(new X509EncodedKeySpec(pubEnc));
+
+        Signature s = Signature.getInstance(signatureAlg, "BC");
+
+        s.initVerify(vKey);
+
+        s.update(data);
+
+        if (!s.verify(sig))
+        {
+            fail(signatureAlg + " verification failed");
+        }
+    }
+
+    private void signatureGost12Test(String signatureAlg, int expectedSignLen, KeyPair p)
+        throws Exception
+    {
+        byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+
+        PrivateKey sKey = p.getPrivate();
+        PublicKey vKey = p.getPublic();
+        Signature s = Signature.getInstance(signatureAlg, "BC");
+        s.initSign(sKey);
+
+        s.update(data);
+
+        byte[] sigBytes = s.sign();
+
+        if (sigBytes.length != expectedSignLen)
+        {
+            fail(signatureAlg + " signature failed");
+        }
+
+        s = Signature.getInstance(signatureAlg, "BC");
+
+        s.initVerify(vKey);
+
+        s.update(data);
+
+        if (!s.verify(sigBytes))
+        {
+            fail(signatureAlg + " verification failed");
+        }
+
+    }
+
+    private void encodedGost12Test(KeyPair p)
+        throws Exception
+    {
+        PrivateKey sKey = p.getPrivate();
+        PublicKey vKey = p.getPublic();
+
+        KeyFactory f = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+        X509EncodedKeySpec x509s = new X509EncodedKeySpec(vKey.getEncoded());
+        ECPublicKey eck1 = (ECPublicKey)f.generatePublic(x509s);
+
+        if (!eck1.getQ().equals(((ECPublicKey)vKey).getQ()))
+        {
+            fail("public number not decoded properly");
+        }
+
+        if (!eck1.getParameters().equals(((ECPublicKey)vKey).getParameters()))
+        {
+            fail("public parameters not decoded properly");
+        }
+
+        PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(sKey.getEncoded());
+        ECPrivateKey eck2 = (ECPrivateKey)f.generatePrivate(pkcs8);
+
+        if (!eck2.getD().equals(((ECPrivateKey)sKey).getD()))
+        {
+            fail("private number not decoded properly");
+        }
+
+        if (!eck2.getParameters().equals(((ECPrivateKey)sKey).getParameters()))
+        {
+            fail("private number not decoded properly");
+        }
+
+        eck2 = (ECPrivateKey)serializeDeserialize(sKey);
+        if (!eck2.getD().equals(((ECPrivateKey)sKey).getD()))
+        {
+            fail("private number not decoded properly");
+        }
+
+        if (!eck2.getParameters().equals(((ECPrivateKey)sKey).getParameters()))
+        {
+            fail("private number not decoded properly");
+        }
+
+        checkEquals(eck2, sKey);
+
+        if (!(eck2 instanceof PKCS12BagAttributeCarrier))
+        {
+            fail("private key not implementing PKCS12 attribute carrier");
+        }
+
+        eck1 = (ECPublicKey)serializeDeserialize(vKey);
+
+        if (!eck1.getQ().equals(((ECPublicKey)vKey).getQ()))
+        {
+            fail("public number not decoded properly");
+        }
+
+        if (!eck1.getParameters().equals(((ECPublicKey)vKey).getParameters()))
+        {
+            fail("public parameters not decoded properly");
+        }
+
+        checkEquals(eck1, vKey);
+    }
+
     private void keyStoreTest(PrivateKey sKey, PublicKey vKey)
         throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, UnrecoverableKeyException
     {
@@ -383,7 +768,7 @@
 
         X509Certificate cert = certGen.generate(sKey, "BC");
 
-        ks.setKeyEntry("gost",sKey, "gost".toCharArray(), new Certificate[] { cert });
+        ks.setKeyEntry("gost", sKey, "gost".toCharArray(), new Certificate[]{cert});
 
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
@@ -431,21 +816,21 @@
 
         GOST3410ParameterSpec gost3410P = new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_B.getId());
 
-        KeyPairGenerator    g = KeyPairGenerator.getInstance("GOST3410", "BC");
+        KeyPairGenerator g = KeyPairGenerator.getInstance("GOST3410", "BC");
         g.initialize(gost3410P, new SecureRandom());
         KeyPair p = g.generateKeyPair();
 
-        PrivateKey  sKey = p.getPrivate();
-        PublicKey   vKey = p.getPublic();
+        PrivateKey sKey = p.getPrivate();
+        PublicKey vKey = p.getPublic();
 
-        Signature           s = Signature.getInstance("GOST3410", "BC");
-        byte[]              data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
+        Signature s = Signature.getInstance("GOST3410", "BC");
+        byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
 
         s.initSign(sKey);
 
         s.update(data);
 
-        byte[]  sigBytes = s.sign();
+        byte[] sigBytes = s.sign();
 
         s = Signature.getInstance("GOST3410", "BC");
 
@@ -462,7 +847,7 @@
     }
 
     private BigInteger[] decode(
-        byte[]  encoding)
+        byte[] encoding)
     {
         byte[] r = new byte[32];
         byte[] s = new byte[32];
@@ -471,7 +856,7 @@
 
         System.arraycopy(encoding, 32, r, 0, 32);
 
-        BigInteger[]            sig = new BigInteger[2];
+        BigInteger[] sig = new BigInteger[2];
 
         sig[0] = new BigInteger(1, r);
         sig[1] = new BigInteger(1, s);
@@ -495,19 +880,41 @@
 
     public String getName()
     {
-        return "GOST3410/ECGOST3410";
+        return "GOST3410/ECGOST3410/ECGOST3410 2012";
     }
 
     public void performTest()
         throws Exception
     {
         ecGOST3410Test();
+
+        if (Security.getProvider("BC").containsKey("KeyFactory.ECGOST3410-2012"))
+        {
+            ecGOST34102012256Test();
+            ecGOST34102012512Test();
+            ecGOST2012NameCurveGenerationTest();
+            ecGOST2012VerifyTest("ECGOST3410-2012-256", ecgostData, ecgost2012_256Key, ecgost2012_256Sig);
+            ecGOST2012VerifyTest("ECGOST3410-2012-512", ecgostData, ecgost2012_512Key, ecgost2012_512Sig);
+        }
+        
         generationTest();
         parametersTest();
     }
 
+    protected byte[] toByteArray(String input)
+    {
+        byte[] bytes = new byte[input.length()];
+
+        for (int i = 0; i != bytes.length; i++)
+        {
+            bytes[i] = (byte)input.charAt(i);
+        }
+
+        return bytes;
+    }
+
     public static void main(
-        String[]    args)
+        String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3412Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3412Test.java
new file mode 100644
index 0000000..cbb199e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST3412Test.java
@@ -0,0 +1,205 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.Mac;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * basic test class for the GOST28147 cipher
+ */
+public class GOST3412Test
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "GOST3412";
+    }
+
+    public void testECB(
+        byte[] keyBytes,
+        byte[] input,
+        byte[] output)
+        throws Exception
+    {
+        Key key;
+        Cipher in, out;
+        CipherInputStream cIn;
+        CipherOutputStream cOut;
+        ByteArrayInputStream bIn;
+        ByteArrayOutputStream bOut;
+
+        key = new SecretKeySpec(keyBytes, "GOST3412-2015");
+
+        in = Cipher.getInstance("GOST3412-2015/ECB/NoPadding", "BC");
+        out = Cipher.getInstance("GOST3412-2015/ECB/NoPadding", "BC");
+        out.init(Cipher.ENCRYPT_MODE, key);
+        in.init(Cipher.DECRYPT_MODE, key);
+
+        //
+        // encryption pass
+        //
+        bOut = new ByteArrayOutputStream();
+
+        cOut = new CipherOutputStream(bOut, out);
+
+        for (int i = 0; i != input.length / 2; i++)
+        {
+            cOut.write(input[i]);
+        }
+        cOut.write(input, input.length / 2, input.length - input.length / 2);
+        cOut.close();
+
+        byte[] bytes;
+
+        bytes = bOut.toByteArray();
+
+        if (!areEqual(bytes, output))
+        {
+            fail("GOST3412-2015 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
+        }
+
+        //
+        // decryption pass
+        //
+        bIn = new ByteArrayInputStream(bytes);
+
+        cIn = new CipherInputStream(bIn, in);
+
+        DataInputStream dIn = new DataInputStream(cIn);
+
+        bytes = new byte[input.length];
+
+        for (int i = 0; i != input.length / 2; i++)
+        {
+            bytes[i] = (byte)dIn.read();
+        }
+        dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2);
+
+        if (!areEqual(bytes, input))
+        {
+            fail("GOST3412-2015 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes)));
+        }
+    }
+
+    public void testCFB(
+        byte[] keyBytes,
+        byte[] iv,
+        byte[] input,
+        byte[] output)
+        throws Exception
+    {
+        Key key;
+        Cipher in, out;
+        CipherInputStream cIn;
+        CipherOutputStream cOut;
+        ByteArrayInputStream bIn;
+        ByteArrayOutputStream bOut;
+
+        key = new SecretKeySpec(keyBytes, "GOST3412-2015");
+
+        in = Cipher.getInstance("GOST3412-2015/CFB8/NoPadding", "BC");
+        out = Cipher.getInstance("GOST3412-2015/CFB8/NoPadding", "BC");
+
+        out.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+        in.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+
+        //
+        // encryption pass
+        //
+        bOut = new ByteArrayOutputStream();
+
+        cOut = new CipherOutputStream(bOut, out);
+
+        for (int i = 0; i != input.length / 2; i++)
+        {
+            cOut.write(input[i]);
+        }
+        cOut.write(input, input.length / 2, input.length - input.length / 2);
+        cOut.close();
+
+        byte[] bytes;
+
+        bytes = bOut.toByteArray();
+
+        if (!areEqual(bytes, output))
+        {
+            fail("GOST3412-2015 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
+        }
+
+        //
+        // decryption pass
+        //
+        bIn = new ByteArrayInputStream(bytes);
+
+        cIn = new CipherInputStream(bIn, in);
+
+        DataInputStream dIn = new DataInputStream(cIn);
+
+        bytes = new byte[input.length];
+
+        for (int i = 0; i != input.length / 2; i++)
+        {
+            bytes[i] = (byte)dIn.read();
+        }
+        dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2);
+
+        if (!areEqual(bytes, input))
+        {
+            fail("GOST3412-2015 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes)));
+        }
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        testECB(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef"),
+            Hex.decode("1122334455667700ffeeddccbbaa9988"), Hex.decode("7f679d90bebc24305a468d42b9d4edcd"));
+
+        testCFB(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef"),
+             Hex.decode("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819"),
+             Hex.decode("1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011"), Hex.decode("819b19c5867e61f1cf1b16f664f66e46ed8fcb82b1110b1e7ec03bfa6611f2eabd7a32363691cbdc3bbe403bc80552d822c2cdf483981cd71d5595453d7f057d"));
+
+        byte[][] inputs = new byte[][]{
+            Hex.decode("1122334455667700ffeeddccbbaa9988"),
+            Hex.decode("00112233445566778899aabbcceeff0a"),
+            Hex.decode("112233445566778899aabbcceeff0a00"),
+            Hex.decode("2233445566778899aabbcceeff0a0011"),
+        };
+
+        Mac mac = Mac.getInstance("GOST3412MAC", "BC");
+
+        mac.init(new SecretKeySpec(Hex.decode("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef"), "GOST3412MAC"));
+
+        for (int i = 0; i != inputs.length; i++)
+        {
+            mac.update(inputs[i]);
+        }
+
+        if (!Arrays.areEqual(Hex.decode("336f4d296059fbe34ddeb35b37749c67"), mac.doFinal()))
+        {
+            fail("mac test failed.");
+        }
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new GOST3412Test());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/HMacTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/HMacTest.java
index ff1f2d5..4df0c56 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/HMacTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/HMacTest.java
@@ -17,6 +17,7 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
@@ -59,6 +60,10 @@
     static byte[] outputGost2012_256 = Hex.decode("f03422dfa37a507ca126ce01b8eba6b7fdda8f8a60dd8f2703e3a372120b8294");
     static byte[] outputGost2012_512 = Hex.decode("86b6a06bfa9f1974aff6ccd7fa3f835f0bd850395d6084efc47b9dda861a2cdf0dcaf959160733d5269f6567966dd7a9f932a77cd6f080012cd476f1c2cc31bb");
 
+    static byte[] outputDSTU7564_256 = Hex.decode("98ac67aa21eaf6e8666fb748d66cfc15d5d66f5194c87fffa647e406d3375cdb");
+    static byte[] outputDSTU7564_384 = Hex.decode("4e46a87e70fcd2ccfb4433a8eaec68991a96b11085c5d5484db71af51bac469c03f76e1f721843c8e8667708fe41a48d");
+    static byte[] outputDSTU7564_512 = Hex.decode("5b7acf633a7551b8410fa66a60c74a494e46a87e70fcd2ccfb4433a8eaec68991a96b11085c5d5484db71af51bac469c03f76e1f721843c8e8667708fe41a48d");
+
     public HMacTest()
     {
     }
@@ -213,6 +218,10 @@
         testHMac("HMac-GOST3411-2012-256", 256, outputGost2012_256);
         testHMac("HMac-GOST3411-2012-512", 512, outputGost2012_512);
 
+        testHMac("HMac-DSTU7564-256", 256, outputDSTU7564_256);
+        testHMac("HMac-DSTU7564-384", 384, outputDSTU7564_384);
+        testHMac("HMac-DSTU7564-512", 512, outputDSTU7564_512);
+
         testHMac("HMac/SHA1", output1);
         testHMac("HMac/MD5", outputMD5);
         testHMac("HMac/MD4", outputMD4);
@@ -254,6 +263,10 @@
         testHMac(RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_256.getId(), 256, outputGost2012_256);
         testHMac(RosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_512.getId(), 512, outputGost2012_512);
 
+        testHMac(UAObjectIdentifiers.dstu7564mac_256.getId(), 256, outputDSTU7564_256);
+        testHMac(UAObjectIdentifiers.dstu7564mac_384.getId(), 384, outputDSTU7564_384);
+        testHMac(UAObjectIdentifiers.dstu7564mac_512.getId(), 512, outputDSTU7564_512);
+
         // test for compatibility with broken HMac.
         testHMac("OldHMacSHA384", outputOld384);
         testHMac("OldHMacSHA512", outputOld512);
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/IESTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/IESTest.java
index 177ec2e..60c071f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/IESTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/IESTest.java
@@ -12,11 +12,12 @@
 import javax.crypto.Cipher;
 import javax.crypto.spec.DHParameterSpec;
 
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.IEKeySpec;
 import org.bouncycastle.jce.spec.IESParameterSpec;
-import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
@@ -41,17 +42,10 @@
     public void performTest()
         throws Exception
     {
-        KeyPairGenerator    g = KeyPairGenerator.getInstance("ECIES", "BC");
+        KeyPairGenerator g = KeyPairGenerator.getInstance("ECIES", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-                new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-                new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-                new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-                curve,
-                curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-                new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECParameterSpec ecSpec = new ECParameterSpec(x9.getCurve(), x9.getG(),x9.getN(), x9.getH());
 
         g.initialize(ecSpec, new SecureRandom());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
index eb7c0dd..5d67fa8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
@@ -17,6 +17,8 @@
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jce.ECPointUtil;
 import org.bouncycastle.jce.interfaces.ECPrivateKey;
@@ -25,7 +27,6 @@
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
 import org.bouncycastle.jce.spec.ECPublicKeySpec;
-import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.FixedSecureRandom;
 import org.bouncycastle.util.test.SimpleTest;
@@ -56,15 +57,8 @@
     {
         KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECParameterSpec ecSpec = new ECParameterSpec(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
 
         ConfigurableProvider config = (ConfigurableProvider)Security.getProvider("BC");
 
@@ -90,15 +84,8 @@
     {
         KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECParameterSpec ecSpec = new ECParameterSpec(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
 
         ConfigurableProvider config = (ConfigurableProvider)Security.getProvider("BC");
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeyStoreTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeyStoreTest.java
index 4dc6247..9c660a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeyStoreTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeyStoreTest.java
@@ -24,12 +24,13 @@
 import java.util.Hashtable;
 import java.util.Vector;
 
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 import org.bouncycastle.x509.X509V3CertificateGenerator;
 
@@ -76,15 +77,9 @@
         String  storeName)
         throws Exception
     {
-        ECCurve curve = new ECCurve.Fp(
-                                new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-                                new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-                                new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-                                curve,
-                                curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-                                new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         KeyPairGenerator    g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/NamedCurveTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/NamedCurveTest.java
index bf660be..4cd326e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/NamedCurveTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/NamedCurveTest.java
@@ -219,11 +219,33 @@
     {
         ECGenParameterSpec     ecSpec = new ECGenParameterSpec(name);
 
-        KeyPairGenerator    g = KeyPairGenerator.getInstance("ECGOST3410", "BC");
+        KeyPairGenerator    g;
+        Signature           sgr;
+        String              keyAlgorithm;
+
+        if (name.startsWith("Tc26-Gost-3410"))
+        {
+            keyAlgorithm = "ECGOST3410-2012";
+            if (name.indexOf("256") > 0)
+            {
+                sgr = Signature.getInstance("ECGOST3410-2012-256", "BC");
+            }
+            else
+            {
+                sgr = Signature.getInstance("ECGOST3410-2012-512", "BC");
+            }
+        }
+        else
+        {
+            keyAlgorithm = "ECGOST3410";
+
+            sgr = Signature.getInstance("ECGOST3410", "BC");
+        }
+
+        g = KeyPairGenerator.getInstance(keyAlgorithm, "BC");
 
         g.initialize(ecSpec, new SecureRandom());
 
-        Signature sgr = Signature.getInstance("ECGOST3410", "BC");
         KeyPair   pair = g.generateKeyPair();
         PrivateKey sKey = pair.getPrivate();
         PublicKey vKey = pair.getPublic();
@@ -249,7 +271,7 @@
         // public key encoding test
         //
         byte[]              pubEnc = vKey.getEncoded();
-        KeyFactory          keyFac = KeyFactory.getInstance("ECGOST3410", "BC");
+        KeyFactory          keyFac = KeyFactory.getInstance(keyAlgorithm, "BC");
         X509EncodedKeySpec  pubX509 = new X509EncodedKeySpec(pubEnc);
         ECPublicKey         pubKey = (ECPublicKey)keyFac.generatePublic(pubX509);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java
new file mode 100644
index 0000000..76d8789
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java
@@ -0,0 +1,211 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.StringReader;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec;
+import org.bouncycastle.jce.spec.OpenSSHPublicKeySpec;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class OpenSSHSpecTests
+    extends SimpleTest
+{
+    private static final SecureRandom secureRandom = new SecureRandom();
+
+    public void testEncodingRSA()
+        throws Exception
+    {
+        byte[] rawPub = Base64.decode("AAAAB3NzaC1yc2EAAAADAQABAAAAgQDvh2BophdIp8ojwGZQR0FQ/awowXnV24nAPm+/na8MOUrdySNhOnlek4LAZl82/+Eu2t21XD6hQUiHKAj6XaNFBthTuss7Cz/tA348DLEMHD9wUtT0FXVmsxqN4BfusunbcULxxVWG2z8FvqeaGgc/Unkp9y7/kyf54pPUCBcClw==");
+        byte[] rawPriv = new PemReader(new StringReader("-----BEGIN RSA PRIVATE KEY-----\n" +
+            "MIICXgIBAAKBgQDvh2BophdIp8ojwGZQR0FQ/awowXnV24nAPm+/na8MOUrdySNh\n" +
+            "Onlek4LAZl82/+Eu2t21XD6hQUiHKAj6XaNFBthTuss7Cz/tA348DLEMHD9wUtT0\n" +
+            "FXVmsxqN4BfusunbcULxxVWG2z8FvqeaGgc/Unkp9y7/kyf54pPUCBcClwIDAQAB\n" +
+            "AoGBAOMXYEoXHgAeREE9CkOWKtDUkEJbnF0rNSB0kZIDt5BJSTeYmNh3jdYi2FX9\n" +
+            "OMx2MFIx4v0tJZvQvyiUxl5IJJ9ZJsYUWF+6VbcTVwYYfdVzZzP2TNyGmF9/ADZW\n" +
+            "wBehqP04uRlYjt94kqb4HoOKF3gJ3LC4uW9xcEltTBeHWCfhAkEA/2biF5St9/Ya\n" +
+            "540E4zu/FKPsxLSaT8LWCo9+X7IqIzlBQCB4GjM+nZeTm7eZOkfAFZoxwfiNde/9\n" +
+            "qleXXf6B2QJBAPAW+jDBC3QF4/g8n9cDxm/A3ICmcOFSychLSrydk9ZyRPbTRyQC\n" +
+            "YlC2mf/pCrO/yO7h189BXyQ3PXOEhnujce8CQQD7gDy0K90EiH0F94AQpA0OLj5B\n" +
+            "lfc/BAXycEtpwPBtrzvqAg9C/aNzXIgmly10jqNAoo7NDA2BTcrlq0uLa8xBAkBl\n" +
+            "7Hs+I1XnZXDIO4Rn1VRysN9rRj15ipnbDAuoUwUl7tDUMBFteg2e0kZCW/6NHIgC\n" +
+            "0aG6fLgVOdY+qi4lYtfFAkEAqqiBgEgSrDmnJLTm6j/Pv1mBA6b9bJbjOqomrDtr\n" +
+            "AWTXe+/kSCv/jYYdpNA/tDgAwEmtkWWEie6+SwJB5cXXqg==\n" +
+            "-----END RSA PRIVATE KEY-----\n")).readPemObject().getContent();
+
+
+        OpenSSHPublicKeySpec pubSpec = new OpenSSHPublicKeySpec(rawPub);
+        OpenSSHPrivateKeySpec privSpec = new OpenSSHPrivateKeySpec(rawPriv);
+
+        isEquals("Pk type", pubSpec.getType(), "ssh-rsa");
+        isEquals("Spec Type", privSpec.getFormat(), "ASN.1");
+
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+
+        originalMessage[0] |= 1;
+
+        KeyFactory kpf = KeyFactory.getInstance("RSA", "BC");
+
+        PublicKey pk = kpf.generatePublic(pubSpec);
+        PrivateKey prk = kpf.generatePrivate(privSpec);
+
+        OpenSSHPublicKeySpec rcPublicKeySpec = (OpenSSHPublicKeySpec)kpf.getKeySpec(pk, OpenSSHPublicKeySpec.class);
+        OpenSSHPrivateKeySpec rcPrivateSpec = (OpenSSHPrivateKeySpec)kpf.getKeySpec(prk, OpenSSHPrivateKeySpec.class);
+
+        isEquals("Pk type", rcPublicKeySpec.getType(), "ssh-rsa");
+        isEquals("Spec Type", rcPrivateSpec.getFormat(), "ASN.1");
+
+        isTrue("RSAPublic key not same", Arrays.areEqual(rawPub, rcPublicKeySpec.getEncoded()));
+        isTrue("RSAPrivate key not same", Arrays.areEqual(rawPriv, rcPrivateSpec.getEncoded()));
+
+    }
+
+    public void testEncodingDSA()
+        throws Exception
+    {
+        byte[] rawPub = Base64.decode("AAAAB3NzaC1kc3MAAACBAJBB5+S4kZZYZLswaQ/zm3GM7YWmHsumwo/Xxu+z6Cg2l5PUoiBBZ4ET9EhhQuL2ja/zrCMCi0ZwiSRuSp36ayPrHLbNJb3VdOuJg8xExRa6F3YfVZfcTPUEKh6FU72fI31HrQmi4rpyHnWxL/iDX496ZG2Hdq6UkPISQpQwj4TtAAAAFQCP9TXcVahR/2rpfEhvdXR0PfhbRwAAAIBdXzAVqoOtb9zog6lNF1cGS1S06W9W/clvuwq2xF1s3bkoI/xUbFSc0IAPsGl2kcB61PAZqcop50lgpvYzt8cq/tbqz3ypq1dCQ0xdmJHj975QsRFax+w6xQ0kgpBhwcS2EOizKb+C+tRzndGpcDSoSMuVXp9i4wn5pJSTZxAYFQAAAIEAhQZc687zYxrEDR/1q6m4hw5GFxuVvLsC+bSHtMF0c11Qy4IPg7mBeP7K5Kq4WyJPtmZhuc5Bb12bJQR6qgd1uLn692fe1UK2kM6eWXBzhlzZ54BslfSKHGNN4qH+ln3Zaf/4rpKE7fvoinkrgkOZmj0PMx9D6wlpHKkXMUxeXtc=");
+        byte[] rawPriv = new PemReader(new StringReader("-----BEGIN DSA PRIVATE KEY-----\n" +
+            "MIIBuwIBAAKBgQCQQefkuJGWWGS7MGkP85txjO2Fph7LpsKP18bvs+goNpeT1KIg\n" +
+            "QWeBE/RIYULi9o2v86wjAotGcIkkbkqd+msj6xy2zSW91XTriYPMRMUWuhd2H1WX\n" +
+            "3Ez1BCoehVO9nyN9R60JouK6ch51sS/4g1+PemRth3aulJDyEkKUMI+E7QIVAI/1\n" +
+            "NdxVqFH/aul8SG91dHQ9+FtHAoGAXV8wFaqDrW/c6IOpTRdXBktUtOlvVv3Jb7sK\n" +
+            "tsRdbN25KCP8VGxUnNCAD7BpdpHAetTwGanKKedJYKb2M7fHKv7W6s98qatXQkNM\n" +
+            "XZiR4/e+ULERWsfsOsUNJIKQYcHEthDosym/gvrUc53RqXA0qEjLlV6fYuMJ+aSU\n" +
+            "k2cQGBUCgYEAhQZc687zYxrEDR/1q6m4hw5GFxuVvLsC+bSHtMF0c11Qy4IPg7mB\n" +
+            "eP7K5Kq4WyJPtmZhuc5Bb12bJQR6qgd1uLn692fe1UK2kM6eWXBzhlzZ54BslfSK\n" +
+            "HGNN4qH+ln3Zaf/4rpKE7fvoinkrgkOZmj0PMx9D6wlpHKkXMUxeXtcCFELnLOJ8\n" +
+            "D0akSCUFY/iDLo/KnOIH\n" +
+            "-----END DSA PRIVATE KEY-----\n")).readPemObject().getContent();
+
+
+        OpenSSHPublicKeySpec pubSpec = new OpenSSHPublicKeySpec(rawPub);
+        OpenSSHPrivateKeySpec privSpec = new OpenSSHPrivateKeySpec(rawPriv);
+
+        isEquals("Pk type", pubSpec.getType(), "ssh-dss");
+        isEquals("Spec Type", privSpec.getFormat(), "ASN.1");
+
+
+        byte[] originalMessage = new byte[10];
+        secureRandom.nextBytes(originalMessage);
+
+
+        originalMessage[0] |= 1;
+
+        KeyFactory kpf = KeyFactory.getInstance("DSA", "BC");
+
+        PublicKey pk = kpf.generatePublic(pubSpec);
+        PrivateKey prk = kpf.generatePrivate(privSpec);
+
+        OpenSSHPublicKeySpec dsaPublicKeySpec = (OpenSSHPublicKeySpec)kpf.getKeySpec(pk, OpenSSHPublicKeySpec.class);
+        OpenSSHPrivateKeySpec dsaPrivateSpec = (OpenSSHPrivateKeySpec)kpf.getKeySpec(prk, OpenSSHPrivateKeySpec.class);
+
+        isEquals("Pk type", dsaPublicKeySpec.getType(), "ssh-dss");
+        isEquals("Spec Type", dsaPrivateSpec.getFormat(), "ASN.1");
+
+        isTrue("DSA Public key not same", Arrays.areEqual(rawPub, dsaPublicKeySpec.getEncoded()));
+        isTrue("DSA Private key not same", Arrays.areEqual(rawPriv, dsaPrivateSpec.getEncoded()));
+
+    }
+
+    private void testEncodingECDSA()
+        throws Exception
+    {
+        byte[] rawPub = Base64.decode("AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHq5qxGqnh93Gpbj2w1Avx1UwBl6z5bZC3Viog1yNHDZYcV6Da4YQ3i0/hN7xY7sUy9dNF6g16tJSYXQQ4tvO3g=");
+        byte[] rawPriv = new PemReader(new StringReader("-----BEGIN EC PRIVATE KEY-----\n" +
+            "MHcCAQEEIHeg/+m02j6nr4bO8ubfbzhs0fqOjiuIoWbvGnVg+FmpoAoGCCqGSM49\n" +
+            "AwEHoUQDQgAEermrEaqeH3caluPbDUC/HVTAGXrPltkLdWKiDXI0cNlhxXoNrhhD\n" +
+            "eLT+E3vFjuxTL100XqDXq0lJhdBDi287eA==\n" +
+            "-----END EC PRIVATE KEY-----\n")).readPemObject().getContent();
+
+        OpenSSHPublicKeySpec pubSpec = new OpenSSHPublicKeySpec(rawPub);
+        OpenSSHPrivateKeySpec privSpec = new OpenSSHPrivateKeySpec(rawPriv);
+
+        isEquals("ecdsa-sha2-nistp256", pubSpec.getType());
+        isEquals("Spec Type", privSpec.getFormat(), "ASN.1");
+
+        KeyFactory kpf = KeyFactory.getInstance("EC", "BC");
+
+        PublicKey pk = kpf.generatePublic(pubSpec);
+        PrivateKey prk = kpf.generatePrivate(privSpec);
+
+        OpenSSHPublicKeySpec ecdsaPublicKeySpec = (OpenSSHPublicKeySpec)kpf.getKeySpec(pk, OpenSSHPublicKeySpec.class);
+        OpenSSHPrivateKeySpec ecdsaPrivateSpec = (OpenSSHPrivateKeySpec)kpf.getKeySpec(prk, OpenSSHPrivateKeySpec.class);
+
+        isEquals("Spec Type", ecdsaPrivateSpec.getFormat(), "ASN.1");
+
+        isTrue("ECPublic key not same", Arrays.areEqual(rawPub, ecdsaPublicKeySpec.getEncoded()));
+        isTrue("ECPrivate key not same", Arrays.areEqual(rawPriv, ecdsaPrivateSpec.getEncoded()));
+
+        isEquals("ecdsa-sha2-nistp256", ecdsaPublicKeySpec.getType());
+    }
+
+    public void testED25519()
+        throws Exception
+    {
+        byte[] rawPub = Base64.decode("AAAAC3NzaC1lZDI1NTE5AAAAIM4CaV7WQcy0lht0hclgXf4Olyvzvv2fnUvQ3J8IYsWF");
+        byte[] rawPriv = new PemReader(new StringReader("-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" +
+            "QyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQAAAKBTr4PvU6+D\n" +
+            "7wAAAAtzc2gtZWQyNTUxOQAAACDOAmle1kHMtJYbdIXJYF3+Dpcr8779n51L0NyfCGLFhQ\n" +
+            "AAAED4BTHeR3YD7CFQqusztfL5K+YSD4mRGLBwb7jHiXxIJM4CaV7WQcy0lht0hclgXf4O\n" +
+            "lyvzvv2fnUvQ3J8IYsWFAAAAG21lZ2Fud29vZHNAdHljaGUtMzI2NS5sb2NhbAEC\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n")).readPemObject().getContent();
+
+        OpenSSHPublicKeySpec pubSpec = new OpenSSHPublicKeySpec(rawPub);
+        OpenSSHPrivateKeySpec privSpec = new OpenSSHPrivateKeySpec(rawPriv);
+
+        isEquals("Pk type", pubSpec.getType(), "ssh-ed25519");
+        isEquals("Spec Type", privSpec.getFormat(), "OpenSSH");
+
+        KeyFactory kpf = KeyFactory.getInstance("ED25519", "BC");
+
+        PublicKey pk = kpf.generatePublic(pubSpec);
+        PrivateKey prk = kpf.generatePrivate(privSpec);
+
+        OpenSSHPublicKeySpec edDsaPublicKeySpec = (OpenSSHPublicKeySpec)kpf.getKeySpec(pk, OpenSSHPublicKeySpec.class);
+        OpenSSHPrivateKeySpec edDsaPrivateKeySpec = (OpenSSHPrivateKeySpec)kpf.getKeySpec(prk, OpenSSHPrivateKeySpec.class);
+
+        isEquals("Pk type", edDsaPublicKeySpec.getType(), "ssh-ed25519");
+        isEquals("Spec Type", edDsaPrivateKeySpec.getFormat(), "OpenSSH");
+
+
+        isTrue("EDPublic key not same", Arrays.areEqual(rawPub, edDsaPublicKeySpec.getEncoded()));
+
+        isTrue("EDPrivate key not same", Arrays.areEqual(
+            Hex.decode("6f70656e7373682d6b65792d763100000000046e6f6e65000000046e6f6e650000000000000001000000330000000b7373682d6564323535313900000020ce02695ed641ccb4961b7485c9605dfe0e972bf3befd9f9d4bd0dc9f0862c5850000008300ff00ff00ff00ff0000000b7373682d6564323535313900000020ce02695ed641ccb4961b7485c9605dfe0e972bf3befd9f9d4bd0dc9f0862c58500000040f80531de477603ec2150aaeb33b5f2f92be6120f899118b0706fb8c7897c4824ce02695ed641ccb4961b7485c9605dfe0e972bf3befd9f9d4bd0dc9f0862c58500000000"),
+            edDsaPrivateKeySpec.getEncoded()));
+
+        isEquals("ssh-ed25519", edDsaPublicKeySpec.getType());
+    }
+
+    public String getName()
+    {
+        return "OpenSSHSpec";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        testEncodingDSA();
+        testEncodingRSA();
+        testEncodingECDSA();
+        testED25519();
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new OpenSSHSpecTests());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS10CertRequestTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS10CertRequestTest.java
index 527e5ab..84303ee 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS10CertRequestTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS10CertRequestTest.java
@@ -27,6 +27,7 @@
 import org.bouncycastle.asn1.x509.X509Extension;
 import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -40,6 +41,7 @@
 import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
 import org.bouncycastle.jce.spec.ECPublicKeySpec;
+import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
@@ -222,15 +224,9 @@
     private void createECRequest(String algorithm, ASN1ObjectIdentifier algOid)
         throws Exception
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
@@ -496,15 +492,9 @@
         // elliptic curve openSSL
         KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         g.initialize(ecSpec, new SecureRandom());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
index 0a515e6..b8b1e74 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
@@ -2,6 +2,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.math.BigInteger;
 import java.security.Key;
 import java.security.KeyFactory;
@@ -25,16 +26,20 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1StreamParser;
 import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequenceParser;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.ContentInfo;
 import org.bouncycastle.asn1.pkcs.EncryptedData;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.MacData;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.Pfx;
 import org.bouncycastle.asn1.pkcs.SafeBag;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x500.X500NameBuilder;
 import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jcajce.PKCS12StoreParameter;
 import org.bouncycastle.jce.PKCS12Util;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
@@ -633,6 +638,27 @@
         {
             fail("key test failed in 2nd GOST store");
         }
+
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+        pkcs12.store(stream, "2".toCharArray());
+
+        // confirm mac details consistent
+        Pfx bag = Pfx.getInstance(stream.toByteArray());
+        MacData mData = bag.getMacData();
+
+        isEquals("mac alg not match", new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3411, DERNull.INSTANCE), mData.getMac().getAlgorithmId());
+        isEquals(2048, mData.getIterationCount().intValue());
+        isEquals(8, mData.getSalt().length);
+
+        //confirm key recovery
+        pkcs12 = KeyStore.getInstance("PKCS12", "BC");
+
+        pkcs12.load(new ByteArrayInputStream(stream.toByteArray()), "2".toCharArray());
+
+        PrivateKey pk2 = (PrivateKey)pkcs12.getKey("cp_exported", null);
+
+        isEquals(pk, pk2);
     }
 
     public void testPKCS12Store()
@@ -1370,6 +1396,90 @@
         }
     }
 
+    private void testIterationCount()
+        throws Exception
+    {
+        System.setProperty("org.bouncycastle.pkcs12.max_it_count", "10");
+
+        ByteArrayInputStream stream = new ByteArrayInputStream(pkcs12StorageIssue);
+        KeyStore store = KeyStore.getInstance("PKCS12", "BC");
+
+        try
+        {
+            store.load(stream, storagePassword);
+            fail("no exception");
+        }
+        catch (IOException e)
+        {
+            isTrue(e.getMessage().endsWith("iteration count 2000 greater than 10"));
+        }
+
+        System.clearProperty("org.bouncycastle.pkcs12.max_it_count");
+    }
+
+    private void testBCFKSLoad()
+        throws Exception
+    {
+        KeyStore k = KeyStore.getInstance("BCFKS", "BC");
+
+        try
+        {
+            k.load(new ByteArrayInputStream(pkcs12), passwd);
+        }
+        catch (IOException e)
+        {
+            isTrue("malformed sequence".equals(e.getMessage()));
+        }
+
+        KeyPair kp1 = TestUtils.generateRSAKeyPair();
+        KeyPair kp1ca = TestUtils.generateRSAKeyPair();
+        KeyPair kp1ee = TestUtils.generateRSAKeyPair();
+
+        X509Certificate kp1Root = TestUtils.generateRootCert(kp1, new X500Name("CN=KP1 ROOT"));
+        X509Certificate kp1CA = TestUtils.generateIntermediateCert(kp1ca.getPublic(), new X500Name("CN=KP1 CA"), kp1.getPrivate(), kp1Root);
+        X509Certificate kp1EE = TestUtils.generateEndEntityCert(kp1ee.getPublic(), new X500Name("CN=KP1 EE"), kp1ca.getPrivate(), kp1CA);
+
+        Certificate[] kp1Chain = new Certificate[] { kp1EE, kp1CA, kp1Root };
+
+        KeyPair kp2 = TestUtils.generateRSAKeyPair();
+        KeyPair kp2ca = TestUtils.generateRSAKeyPair();
+        KeyPair kp2ee = TestUtils.generateRSAKeyPair();
+
+        X509Certificate kp2Root = TestUtils.generateRootCert(kp2, new X500Name("CN=KP2 ROOT"));
+        X509Certificate kp2CA = TestUtils.generateIntermediateCert(kp2ca.getPublic(), new X500Name("CN=KP2 CA"), kp2.getPrivate(), kp1Root);
+        X509Certificate kp2EE = TestUtils.generateEndEntityCert(kp2ee.getPublic(), new X500Name("CN=KP2 EE"), kp2ca.getPrivate(), kp1CA);
+
+        Certificate[] kp2Chain = new Certificate[] { kp2EE, kp2CA, kp2Root };
+
+        KeyPair kp3 = TestUtils.generateRSAKeyPair();
+        X509Certificate kp3Root = TestUtils.generateRootCert(kp3, new X500Name("CN=KP3 ROOT"));
+
+        KeyStore keyStore = KeyStore.getInstance("BCFKS", "BC");
+
+        keyStore.load(null, null);
+
+        keyStore.setKeyEntry("kp1", kp1ee.getPrivate(), null, kp1Chain);
+        keyStore.setCertificateEntry("kp1root", kp1Root);
+        keyStore.setKeyEntry("kp2", kp1ee.getPrivate(), null, kp2Chain);
+
+        keyStore.setCertificateEntry("kp3root", kp3Root);
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        keyStore.store(bOut, "fred".toCharArray());
+
+        KeyStore k12 = KeyStore.getInstance("PKCS12", "BC");
+
+        try
+        {
+            k12.load(new ByteArrayInputStream(bOut.toByteArray()), "fred".toCharArray());
+        }
+        catch (IOException e)
+        {           
+            isTrue("illegal object in getInstance: org.bouncycastle.asn1.DLSequence".equals(e.getMessage()));
+        }
+    }
+
     public String getName()
     {
         return "PKCS12Store";
@@ -1378,9 +1488,11 @@
     public void performTest()
         throws Exception
     {
+        testIterationCount();
         testPKCS12Store();
         testGOSTStore();
         testChainCycle();
+        testBCFKSLoad();
 
         // converter tests
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PSSTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PSSTest.java
index a27863d..3618593 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PSSTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PSSTest.java
@@ -5,6 +5,7 @@
 import java.security.KeyFactory;
 import java.security.MessageDigest;
 import java.security.PrivateKey;
+import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
@@ -169,6 +170,70 @@
             fail("SHA256 signature verification failed");
         }
 
+        // set parameter after sig intialisation
+        s = Signature.getInstance("RSAPSS", "BC");
+
+        s.initVerify(pubKey);
+
+        s.setParameter(pss.getParameterSpec(PSSParameterSpec.class));
+
+        s.update(msg1a);
+        if (!s.verify(sig1b))
+        {
+            fail("SHA256 signature verification failed");
+        }
+
+        s = Signature.getInstance("RSASSA-PSS", "BC");
+
+        s.initSign(privKey);
+
+        s.setParameter(pss.getParameterSpec(PSSParameterSpec.class));
+
+        s.update(msg1a);
+
+        sig = s.sign();
+
+        s.initVerify(pubKey);
+
+        s.update(msg1a);
+
+        if (!s.verify(sig))
+        {
+            fail("SHA256 signature verification failed (setParameter)");
+        }
+
+        s = Signature.getInstance("RSASSA-PSS", "BC");
+
+        s.initSign(privKey);
+
+        s.setParameter(pss.getParameterSpec(PSSParameterSpec.class));
+
+        s.update(msg1a);
+
+        try
+        {
+            s.setParameter(pss.getParameterSpec(PSSParameterSpec.class));
+            fail("no exception - setParameter byte[]");
+        }
+        catch (ProviderException e)
+        {
+            isEquals("cannot call setParameter in the middle of update", e.getMessage());
+        }
+
+        s.initSign(privKey);
+        
+        s.update(msg1a[0]);
+
+        try
+        {
+            s.setParameter(pss.getParameterSpec(PSSParameterSpec.class));
+            fail("no exception - setParameter byte");
+        }
+        catch (ProviderException e)
+        {
+            isEquals("cannot call setParameter in the middle of update", e.getMessage());
+        }
+
         //
         // 512 test -with zero salt length
         //
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java
index 798f164..0a6d8a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java
@@ -50,7 +50,7 @@
             "211195296d9afc7b35a1223a79487c87",
             "f328857a1b653684e73760c804c55b1d",
             "21cd8adb23ca84eb4dbb12780595bf28",
-            "211195296d9afc7b35a1223a79487c87",
+            "c218102702d8a2ee5c9ef9000e91454d",
             "9bb04be6a1c314a9054ae3c94d3c941b",
             "db86de7b1fcae429753d68b1263d7ca0",
             "11918174f33a2f278fb86554da094112"};
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
index 5c91909..fbc690b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
@@ -78,9 +78,16 @@
         new Shacal2Test(),
         new DetDSATest(),
         new ThreefishTest(),
+        new SM2SignatureTest(),
         new SM4Test(),
         new TLSKDFTest(),
-        new BCFKSStoreTest()
+        new BCFKSStoreTest(),
+        new DSTU7624Test(),
+        new GOST3412Test(),
+        new GOST3410KeyPairTest(),
+        new EdECTest(),
+        new OpenSSHSpecTests(),
+        new SM2CipherTest()
     };
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2CipherTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2CipherTest.java
new file mode 100644
index 0000000..e2782ec
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2CipherTest.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.TestRandomBigInteger;
+
+public class SM2CipherTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "SM2Cipher";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        BigInteger SM2_ECC_P = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16);
+        BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
+        BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
+        BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+        BigInteger SM2_ECC_H = ECConstants.ONE;
+        BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
+        BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
+
+        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
+
+        ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
+        ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
+
+        ECParameterSpec aKeyGenParams = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), domainParams.getN(), domainParams.getH());
+
+        keyPairGenerator.initialize(aKeyGenParams, new TestRandomBigInteger("1649AB77A00637BD5E2EFE283FBF353534AA7F7CB89463F208DDBC2920BB0DA0", 16));
+
+        KeyPair aKp = keyPairGenerator.generateKeyPair();
+
+        Cipher sm2Engine = Cipher.getInstance("SM2", "BC");
+
+        byte[] m = Strings.toByteArray("encryption standard");
+
+        sm2Engine.init(Cipher.ENCRYPT_MODE, aKp.getPublic(), new TestRandomBigInteger("4C62EEFD6ECFC2B95B92FD6C3D9575148AFA17425546D49018E5388D49DD7B4F", 16));
+
+        byte[] enc = sm2Engine.doFinal(m);
+
+        isTrue("enc wrong", Arrays.areEqual(Hex.decode(
+            "04245C26 FB68B1DD DDB12C4B 6BF9F2B6 D5FE60A3 83B0D18D 1C4144AB F17F6252" +
+            "E776CB92 64C2A7E8 8E52B199 03FDC473 78F605E3 6811F5C0 7423A24B 84400F01" +
+            "B8650053 A89B41C4 18B0C3AA D00D886C 00286467 9C3D7360 C30156FA B7C80A02" +
+            "76712DA9 D8094A63 4B766D3A 285E0748 0653426D"), enc));
+
+        sm2Engine.init(Cipher.DECRYPT_MODE, aKp.getPrivate());
+
+        byte[] dec = sm2Engine.doFinal(enc);
+
+        isTrue("dec wrong", Arrays.areEqual(m, dec));
+        
+        testAlgorithm(aKp, "SM2", GMObjectIdentifiers.sm2encrypt_with_sm3);
+        testAlgorithm(aKp, "SM2withSM3", GMObjectIdentifiers.sm2encrypt_with_sm3);
+        testAlgorithm(aKp, "SM2withBlake2b", GMObjectIdentifiers.sm2encrypt_with_blake2b512);
+        testAlgorithm(aKp, "SM2withBlake2s", GMObjectIdentifiers.sm2encrypt_with_blake2s256);
+        testAlgorithm(aKp, "SM2withMD5", GMObjectIdentifiers.sm2encrypt_with_md5);
+        testAlgorithm(aKp, "SM2withRIPEMD160", GMObjectIdentifiers.sm2encrypt_with_rmd160);
+        testAlgorithm(aKp, "SM2withWhirlpool", GMObjectIdentifiers.sm2encrypt_with_whirlpool);
+        testAlgorithm(aKp, "SM2withSHA1", GMObjectIdentifiers.sm2encrypt_with_sha1);
+        testAlgorithm(aKp, "SM2withSHA224", GMObjectIdentifiers.sm2encrypt_with_sha224);
+        testAlgorithm(aKp, "SM2withSHA256", GMObjectIdentifiers.sm2encrypt_with_sha256);
+        testAlgorithm(aKp, "SM2withSHA384", GMObjectIdentifiers.sm2encrypt_with_sha384);
+        testAlgorithm(aKp, "SM2withSHA512", GMObjectIdentifiers.sm2encrypt_with_sha512);
+    }
+
+    private void testAlgorithm(KeyPair kp, String name, ASN1ObjectIdentifier oid)
+        throws Exception
+    {
+        Cipher sm2Engine1 = Cipher.getInstance(name, "BC");
+        Cipher sm2Engine2 = Cipher.getInstance(oid.getId(), "BC");
+        
+        byte[] m = Strings.toByteArray("encryption standard");
+
+        sm2Engine1.init(Cipher.ENCRYPT_MODE, kp.getPublic(), new TestRandomBigInteger("4C62EEFD6ECFC2B95B92FD6C3D9575148AFA17425546D49018E5388D49DD7B4F", 16));
+
+        byte[] enc = sm2Engine1.doFinal(m);
+
+        isTrue(enc.length == sm2Engine1.getOutputSize(m.length));
+
+        sm2Engine2.init(Cipher.DECRYPT_MODE, kp.getPrivate());
+
+        byte[] dec = sm2Engine2.doFinal(enc);
+
+        isTrue("dec wrong", Arrays.areEqual(m, dec));
+    }
+
+    public static void main(
+        String[]    args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new SM2CipherTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2SignatureTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2SignatureTest.java
new file mode 100644
index 0000000..b23feff
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM2SignatureTest.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Security;
+import java.security.Signature;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.TestRandomBigInteger;
+
+public class SM2SignatureTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "SM2";
+    }
+
+    private void doSignerTestFp()
+        throws Exception
+    {
+        BigInteger SM2_ECC_P = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16);
+        BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
+        BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
+        BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+        BigInteger SM2_ECC_H = ECConstants.ONE;
+        BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
+        BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
+
+        ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
+
+        ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
+
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
+
+        kpGen.initialize(new ECParameterSpec(curve, g, SM2_ECC_N), new TestRandomBigInteger("128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263", 16));
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        Signature signer = Signature.getInstance("SM3withSM2", "BC");
+
+        signer.setParameter(new SM2ParameterSpec(Strings.toByteArray("ALICE123@YAHOO.COM")));
+
+        // repetition test
+        final int times = 2;
+        String random = "";
+        for (int i = 0; i < times; i++) {
+            random += "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";
+        }
+        signer.initSign(kp.getPrivate(),
+                    new TestRandomBigInteger(random, 16));
+
+        byte[] msg = Strings.toByteArray("message digest");
+
+        Signature verifier = Signature.getInstance("SM3withSM2", "BC");
+
+        verifier.setParameter(new SM2ParameterSpec(Strings.toByteArray("ALICE123@YAHOO.COM")));
+
+        verifier.initVerify(kp.getPublic());
+
+        for (int i = 0; i < times; i++) {
+            signer.update(msg, 0, msg.length);
+
+            byte[] sig = signer.sign();
+
+            BigInteger[] rs = decode(sig);
+
+            isTrue("r wrong", rs[0].equals(new BigInteger("40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D1", 16)));
+            isTrue("s wrong", rs[1].equals(new BigInteger("6FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7", 16)));
+
+            verifier.update(msg, 0, msg.length);
+
+            isTrue("verification failed i=" + i, verifier.verify(sig));
+        }
+    }
+
+    private static BigInteger[] decode(byte[] sig)
+    {
+        ASN1Sequence s = ASN1Sequence.getInstance(sig);
+
+        return new BigInteger[] { ASN1Integer.getInstance(s.getObjectAt(0)).getValue(),
+            ASN1Integer.getInstance(s.getObjectAt(1)).getValue() };
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        doSignerTestFp();
+    }
+    
+    public static void main(
+        String[]    args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new SM2SignatureTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/TestUtils.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/TestUtils.java
index 31537b8..1bc8d0a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/TestUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/TestUtils.java
@@ -249,8 +249,8 @@
 
         crlGen.addCRLEntry(serialNumber, now, CRLReason.privilegeWithdrawn);
 
-        crlGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));
-        crlGen.addExtension(X509Extensions.CRLNumber, false, new CRLNumber(BigInteger.valueOf(1)));
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));
+        crlGen.addExtension(Extension.cRLNumber, false, new CRLNumber(BigInteger.valueOf(1)));
 
         return crlGen.generate(caKey, "BC");
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
index 36aa595..0be2ca9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
@@ -7,6 +7,7 @@
 import java.security.spec.ECPoint;
 import java.security.spec.EllipticCurve;
 
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.field.FiniteField;
@@ -46,21 +47,14 @@
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
-    
-    private static ECPoint convertPoint(
-        org.bouncycastle.math.ec.ECPoint  g)
-    {
-        g = g.normalize();
-        return new ECPoint(g.getAffineXCoord().toBigInteger(), g.getAffineYCoord().toBigInteger());
-    }
-    
+
     public ECNamedCurveSpec(
         String                              name,
         ECCurve                             curve,
         org.bouncycastle.math.ec.ECPoint    g,
         BigInteger                          n)
     {
-        super(convertCurve(curve, null), convertPoint(g), n, 1);
+        super(convertCurve(curve, null), EC5Util.convertPoint(g), n, 1);
 
         this.name = name;
     }
@@ -83,7 +77,7 @@
         BigInteger                          n,
         BigInteger                          h)
     {
-        super(convertCurve(curve, null), convertPoint(g), n, h.intValue());
+        super(convertCurve(curve, null), EC5Util.convertPoint(g), n, h.intValue());
 
         this.name = name;
     }
@@ -108,8 +102,8 @@
         BigInteger                          h,
         byte[]                              seed)
     {
-        super(convertCurve(curve, seed), convertPoint(g), n, h.intValue());
-        
+        super(convertCurve(curve, seed), EC5Util.convertPoint(g), n, h.intValue());
+
         this.name = name;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPrivateKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPrivateKeySpec.java
new file mode 100644
index 0000000..5b870e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPrivateKeySpec.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.jce.spec;
+
+import java.security.spec.EncodedKeySpec;
+
+import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
+
+/**
+ * OpenSSHPrivateKeySpec holds and encoded OpenSSH private key.
+ * The format of the key can be either ASN.1 or OpenSSH.
+ */
+public class OpenSSHPrivateKeySpec
+    extends EncodedKeySpec
+{
+    private final String format;
+
+    /**
+     * Accept an encoded key and determine the format.
+     * <p>
+     * The encoded key should be the Base64 decoded blob between the "---BEGIN and ---END" markers.
+     * This constructor will endeavour to find the OpenSSH format magic value. If it can not then it
+     * will default to ASN.1. It does not attempt to validate the ASN.1
+     * <p>
+     * Example:
+     * OpenSSHPrivateKeySpec privSpec = new OpenSSHPrivateKeySpec(rawPriv);
+     * <p>
+     * KeyFactory kpf = KeyFactory.getInstance("RSA", "BC");
+     * PrivateKey prk = kpf.generatePrivate(privSpec);
+     * <p>
+     * OpenSSHPrivateKeySpec rcPrivateSpec = kpf.getKeySpec(prk, OpenSSHPrivateKeySpec.class);
+     *
+     * @param encodedKey The encoded key.
+     */
+    public OpenSSHPrivateKeySpec(byte[] encodedKey)
+    {
+        super(encodedKey);
+
+        if  (encodedKey[0] == 0x30)   // DER SEQUENCE
+        {
+            format = "ASN.1";
+        }
+        else if (encodedKey[0] == 'o')
+        {
+            format = "OpenSSH";
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown byte encoding");
+        }
+    }
+
+    /**
+     * Return the format, either OpenSSH for the OpenSSH propriety format or ASN.1.
+     *
+     * @return the format OpenSSH or ASN.1
+     */
+    public String getFormat()
+    {
+        return format;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPublicKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPublicKeySpec.java
new file mode 100644
index 0000000..50fbde1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/OpenSSHPublicKeySpec.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.jce.spec;
+
+import java.security.spec.EncodedKeySpec;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Holds an OpenSSH encoded public key.
+ */
+public class OpenSSHPublicKeySpec
+    extends EncodedKeySpec
+{
+    private static final String[] allowedTypes = new String[]{"ssh-rsa", "ssh-ed25519", "ssh-dss"};
+    private final String type;
+
+
+    /**
+     * Construct and instance and determine the OpenSSH public key type.
+     * The current types are ssh-rsa, ssh-ed25519, ssh-dss and ecdsa-*
+     * <p>
+     * It does not validate the key beyond identifying the type.
+     *
+     * @param encodedKey
+     */
+    public OpenSSHPublicKeySpec(byte[] encodedKey)
+    {
+        super(encodedKey);
+
+        //
+        // The type is encoded at the start of the blob.
+        //
+        int pos = 0;
+        int i = (encodedKey[pos++] & 0xFF) << 24;
+        i |= (encodedKey[pos++] & 0xFF) << 16;
+        i |= (encodedKey[pos++] & 0xFF) << 8;
+        i |= (encodedKey[pos++] & 0xFF);
+
+        if ((pos + i) >= encodedKey.length)
+        {
+            throw new IllegalArgumentException("invalid public key blob: type field longer than blob");
+        }
+
+        this.type = Strings.fromByteArray(Arrays.copyOfRange(encodedKey, pos, pos + i));
+
+        if (type.startsWith("ecdsa"))
+        {
+            return; // These have a curve name and digest in them and can't be compared exactly.
+        }
+
+        for (int t = 0; t < allowedTypes.length; t++)
+        {
+            if (allowedTypes[t].equals(this.type))
+            {
+                return;
+            }
+        }
+
+        throw new IllegalArgumentException("unrecognised public key type " + type);
+
+    }
+
+    public String getFormat()
+    {
+        return "OpenSSH";
+    }
+
+    /**
+     * The type of OpenSSH public key.
+     *
+     * @return the type.
+     */
+    public String getType()
+    {
+        return type;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java
index d1f35c5..e0a5543 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java
@@ -19,8 +19,13 @@
          * Although the various multipliers ought not to produce invalid output under normal
          * circumstances, a final check here is advised to guard against fault attacks.
          */
-        return ECAlgorithms.validatePoint(result);
+        return checkResult(result);
     }
 
     protected abstract ECPoint multiplyPositive(ECPoint p, BigInteger k);
+
+    protected ECPoint checkResult(ECPoint p)
+    {
+        return ECAlgorithms.implCheckResult(p);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
index f8bf1eb..f0b1585 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
@@ -61,10 +61,10 @@
         ECEndomorphism endomorphism = c.getEndomorphism();
         if (endomorphism instanceof GLVEndomorphism)
         {
-            return validatePoint(implSumOfMultipliesGLV(imported, ks, (GLVEndomorphism)endomorphism));
+            return implCheckResult(implSumOfMultipliesGLV(imported, ks, (GLVEndomorphism)endomorphism));
         }
 
-        return validatePoint(implSumOfMultiplies(imported, ks));
+        return implCheckResult(implSumOfMultiplies(imported, ks));
     }
 
     public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a,
@@ -79,18 +79,18 @@
             ECCurve.AbstractF2m f2mCurve = (ECCurve.AbstractF2m)cp;
             if (f2mCurve.isKoblitz())
             {
-                return validatePoint(P.multiply(a).add(Q.multiply(b)));
+                return implCheckResult(P.multiply(a).add(Q.multiply(b)));
             }
         }
 
         ECEndomorphism endomorphism = cp.getEndomorphism();
         if (endomorphism instanceof GLVEndomorphism)
         {
-            return validatePoint(
+            return implCheckResult(
                 implSumOfMultipliesGLV(new ECPoint[]{ P, Q }, new BigInteger[]{ a, b }, (GLVEndomorphism)endomorphism));
         }
 
-        return validatePoint(implShamirsTrickWNaf(P, a, Q, b));
+        return implCheckResult(implShamirsTrickWNaf(P, a, Q, b));
     }
 
     /*
@@ -118,7 +118,7 @@
         ECCurve cp = P.getCurve();
         Q = importPoint(cp, Q);
 
-        return validatePoint(implShamirsTrickJsf(P, k, Q, l));
+        return implCheckResult(implShamirsTrickJsf(P, k, Q, l));
     }
 
     public static ECPoint importPoint(ECCurve c, ECPoint p)
@@ -211,7 +211,28 @@
     {
         if (!p.isValid())
         {
-            throw new IllegalArgumentException("Invalid point");
+            throw new IllegalStateException("Invalid point");
+        }
+
+        return p;
+    }
+
+    public static ECPoint cleanPoint(ECCurve c, ECPoint p)
+    {
+        ECCurve cp = p.getCurve();
+        if (!c.equals(cp))
+        {
+            throw new IllegalArgumentException("Point must be on the same curve");
+        }
+
+        return c.decodePoint(p.getEncoded(false));
+    }
+
+    static ECPoint implCheckResult(ECPoint p)
+    {
+        if (!p.isValidPartial())
+        {
+            throw new IllegalStateException("Invalid result");
         }
 
         return p;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
index 7f3197b..7c10c78 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
@@ -8,6 +8,7 @@
 import org.bouncycastle.math.ec.endo.GLVEndomorphism;
 import org.bouncycastle.math.field.FiniteField;
 import org.bouncycastle.math.field.FiniteFields;
+import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Integers;
 
@@ -173,15 +174,26 @@
     public PreCompInfo getPreCompInfo(ECPoint point, String name)
     {
         checkPoint(point);
+
+        Hashtable table;
         synchronized (point)
         {
-            Hashtable table = point.preCompTable;
-            return table == null ? null : (PreCompInfo)table.get(name);
+            table = point.preCompTable;
+        }
+
+        if (null == table)
+        {
+            return null;
+        }
+
+        synchronized (table)
+        {
+            return (PreCompInfo)table.get(name);
         }
     }
 
     /**
-     * Adds <code>PreCompInfo</code> for a point on this curve, under a given name. Used by
+     * Compute a <code>PreCompInfo</code> for a point on this curve, under a given name. Used by
      * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use
      * by subsequent multiplication.
      * 
@@ -189,20 +201,34 @@
      *            The <code>ECPoint</code> to store precomputations for.
      * @param name
      *            A <code>String</code> used to index precomputations of different types.
-     * @param preCompInfo
-     *            The values precomputed by the <code>ECMultiplier</code>.
+     * @param callback
+     *            Called to calculate the <code>PreCompInfo</code>.
      */
-    public void setPreCompInfo(ECPoint point, String name, PreCompInfo preCompInfo)
+    public PreCompInfo precompute(ECPoint point, String name, PreCompCallback callback)
     {
         checkPoint(point);
+
+        Hashtable table;
         synchronized (point)
         {
-            Hashtable table = point.preCompTable;
+            table = point.preCompTable;
             if (null == table)
             {
                 point.preCompTable = table = new Hashtable(4);
             }
-            table.put(name, preCompInfo);
+        }
+
+        synchronized (table)
+        {
+            PreCompInfo existing = (PreCompInfo)table.get(name);
+            PreCompInfo result = callback.precompute(existing);
+
+            if (result != existing)
+            {
+                table.put(name, result);
+            }
+
+            return result;
         }
     }
 
@@ -220,7 +246,7 @@
         // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
         p = p.normalize();
 
-        return validatePoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression);
+        return createPoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression);
     }
 
     /**
@@ -390,7 +416,7 @@
             BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
 
             p = decompressPoint(yTilde, X);
-            if (!p.satisfiesCofactor())
+            if (!p.implIsValid(true, true))
             {
                 throw new IllegalArgumentException("Invalid point");
             }
@@ -441,6 +467,61 @@
         return p;
     }
 
+    /**
+     * Create a cache-safe lookup table for the specified sequence of points. All the points MUST
+     * belong to this {@link ECCurve} instance, and MUST already be normalized.
+     */
+    public ECLookupTable createCacheSafeLookupTable(final ECPoint[] points, int off, final int len)
+    {
+        final int FE_BYTES = (getFieldSize() + 7) >>> 3;
+
+        final byte[] table = new byte[len * FE_BYTES * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                byte[] px = p.getRawXCoord().toBigInteger().toByteArray();
+                byte[] py = p.getRawYCoord().toBigInteger().toByteArray();
+
+                int pxStart = px.length > FE_BYTES ? 1 : 0, pxLen = px.length - pxStart;
+                int pyStart = py.length > FE_BYTES ? 1 : 0, pyLen = py.length - pyStart;
+
+                System.arraycopy(px, pxStart, table, pos + FE_BYTES - pxLen, pxLen); pos += FE_BYTES;
+                System.arraycopy(py, pyStart, table, pos + FE_BYTES - pyLen, pyLen); pos += FE_BYTES;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                byte[] x = new byte[FE_BYTES], y = new byte[FE_BYTES];
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_BYTES; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_BYTES + j] & MASK;
+                    }
+
+                    pos += (FE_BYTES * 2);
+                }
+
+                return createRawPoint(fromBigInteger(new BigInteger(1, x)), fromBigInteger(new BigInteger(1, y)), false);
+            }
+        };
+    }
+
     protected void checkPoint(ECPoint point)
     {
         if (null == point || (this != point.getCurve()))
@@ -542,6 +623,9 @@
         BigInteger q, r;
         ECPoint.Fp infinity;
 
+        /**
+         * @deprecated use constructor taking order/cofactor
+         */
         public Fp(BigInteger q, BigInteger a, BigInteger b)
         {
             this(q, a, b, null, null);
@@ -553,7 +637,7 @@
 
             this.q = q;
             this.r = ECFieldElement.Fp.calculateResidue(q);
-            this.infinity = new ECPoint.Fp(this, null, null);
+            this.infinity = new ECPoint.Fp(this, null, null, false);
 
             this.a = fromBigInteger(a);
             this.b = fromBigInteger(b);
@@ -562,6 +646,9 @@
             this.coord = FP_DEFAULT_COORDS;
         }
 
+        /**
+         * @deprecated use constructor taking order/cofactor
+         */
         protected Fp(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b)
         {
             this(q, r, a, b, null, null);
@@ -573,7 +660,7 @@
 
             this.q = q;
             this.r = r;
-            this.infinity = new ECPoint.Fp(this, null, null);
+            this.infinity = new ECPoint.Fp(this, null, null, false);
 
             this.a = a;
             this.b = b;
@@ -814,7 +901,7 @@
          * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
          *         <code>null</code> if no solution exists.
          */
-        private ECFieldElement solveQuadraticEquation(ECFieldElement beta)
+        protected ECFieldElement solveQuadraticEquation(ECFieldElement beta)
         {
             if (beta.isZero())
             {
@@ -928,6 +1015,7 @@
          * @param b The coefficient <code>b</code> in the Weierstrass equation
          * for non-supersingular elliptic curves over
          * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @deprecated use constructor taking order/cofactor
          */
         public F2m(
             int m,
@@ -985,6 +1073,7 @@
          * @param b The coefficient <code>b</code> in the Weierstrass equation
          * for non-supersingular elliptic curves over
          * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @deprecated use constructor taking order/cofactor
          */
         public F2m(
             int m,
@@ -1039,7 +1128,7 @@
             this.order = order;
             this.cofactor = cofactor;
 
-            this.infinity = new ECPoint.F2m(this, null, null);
+            this.infinity = new ECPoint.F2m(this, null, null, false);
             this.a = fromBigInteger(a);
             this.b = fromBigInteger(b);
             this.coord = F2M_DEFAULT_COORDS;
@@ -1056,7 +1145,7 @@
             this.order = order;
             this.cofactor = cofactor;
 
-            this.infinity = new ECPoint.F2m(this, null, null);
+            this.infinity = new ECPoint.F2m(this, null, null, false);
             this.a = a;
             this.b = b;
             this.coord = F2M_DEFAULT_COORDS;
@@ -1145,20 +1234,50 @@
             return k3;
         }
 
-        /**
-         * @deprecated use {@link #getOrder()} instead
-         */
-        public BigInteger getN()
+        public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
         {
-            return this.order;
-        }
+            final int FE_LONGS = (m + 63) >>> 6;
+            final int[] ks = isTrinomial() ? new int[]{ k1 } : new int[]{ k1, k2, k3 }; 
 
-        /**
-         * @deprecated use {@link #getCofactor()} instead
-         */
-        public BigInteger getH()
-        {
-            return this.cofactor;
+            final long[] table = new long[len * FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    ((ECFieldElement.F2m)p.getRawXCoord()).x.copyTo(table, pos); pos += FE_LONGS;
+                    ((ECFieldElement.F2m)p.getRawYCoord()).x.copyTo(table, pos); pos += FE_LONGS;
+                }
+            }
+
+            return new ECLookupTable()
+            {
+                public int getSize()
+                {
+                    return len;
+                }
+
+                public ECPoint lookup(int index)
+                {
+                    long[] x = Nat.create64(FE_LONGS), y = Nat.create64(FE_LONGS);
+                    int pos = 0;
+
+                    for (int i = 0; i < len; ++i)
+                    {
+                        long MASK = ((i ^ index) - 1) >> 31;
+
+                        for (int j = 0; j < FE_LONGS; ++j)
+                        {
+                            x[j] ^= table[pos + j] & MASK;
+                            y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                        }
+
+                        pos += (FE_LONGS * 2);
+                    }
+
+                    return createRawPoint(new ECFieldElement.F2m(m, ks, new LongArray(x)), new ECFieldElement.F2m(m, ks, new LongArray(y)), false);
+                }
+            };
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
index 18409c0..49d1c2f 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
@@ -24,6 +24,11 @@
     public abstract ECFieldElement invert();
     public abstract ECFieldElement sqrt();
 
+    public ECFieldElement()
+    {
+
+    }
+    
     public int bitLength()
     {
         return toBigInteger().bitLength();
@@ -84,7 +89,11 @@
         return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger());
     }
 
-    public static class Fp extends ECFieldElement
+    public static abstract class AbstractFp extends ECFieldElement
+    {
+    }
+
+    public static class Fp extends AbstractFp
     {
         BigInteger q, r, x;
 
@@ -491,6 +500,49 @@
         }
     }
 
+    public static abstract class AbstractF2m extends ECFieldElement
+    {
+        public ECFieldElement halfTrace()
+        {
+            int m = this.getFieldSize();
+            if ((m & 1) == 0)
+            {
+                throw new IllegalStateException("Half-trace only defined for odd m");
+            }
+
+            ECFieldElement fe = this;
+            ECFieldElement ht = fe;
+            for (int i = 2; i < m; i += 2)
+            {
+                fe = fe.squarePow(2);
+                ht = ht.add(fe);
+            }
+
+            return ht;
+        }
+
+        public int trace()
+        {
+            int m = this.getFieldSize();
+            ECFieldElement fe = this;
+            ECFieldElement tr = fe;
+            for (int i = 1; i < m; ++i)
+            {
+                fe = fe.square();
+                tr = tr.add(fe);
+            }
+            if (tr.isZero())
+            {
+                return 0;
+            }
+            if (tr.isOne())
+            {
+                return 1;
+            }
+            throw new IllegalStateException("Internal error in trace calculation");
+        }
+    }
+
     /**
      * Class representing the Elements of the finite field
      * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB)
@@ -498,7 +550,7 @@
      * basis representations are supported. Gaussian normal basis (GNB)
      * representation is not supported.
      */
-    public static class F2m extends ECFieldElement
+    public static class F2m extends AbstractF2m
     {
         /**
          * Indicates gaussian normal basis representation (GNB). Number chosen
@@ -533,7 +585,7 @@
         /**
          * The <code>LongArray</code> holding the bits.
          */
-        private LongArray x;
+        LongArray x;
 
         /**
          * Constructor for PPB.
@@ -588,23 +640,7 @@
             this.x = new LongArray(x);
         }
 
-        /**
-         * Constructor for TPB.
-         * @param m  The exponent <code>m</code> of
-         * <code>F<sub>2<sup>m</sup></sub></code>.
-         * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
-         * x<sup>k</sup> + 1</code> represents the reduction
-         * polynomial <code>f(z)</code>.
-         * @param x The BigInteger representing the value of the field element.
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public F2m(int m, int k, BigInteger x)
-        {
-            // Set k1 to k, and set k2 and k3 to 0
-            this(m, k, 0, 0, x);
-        }
-
-        private F2m(int m, int[] ks, LongArray x)
+        F2m(int m, int[] ks, LongArray x)
         {
             this.m = m;
             this.representation = (ks.length == 1) ? TPB : PPB;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECLookupTable.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECLookupTable.java
new file mode 100644
index 0000000..7ff5c6a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECLookupTable.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.math.ec;
+
+public interface ECLookupTable
+{
+    int getSize();
+    ECPoint lookup(int index);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
index 0ea5026..57dfa33 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
@@ -8,7 +8,7 @@
  */
 public abstract class ECPoint
 {
-    protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
+    protected final static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
 
     protected static ECFieldElement[] getInitialZCoords(ECCurve curve)
     {
@@ -64,14 +64,22 @@
         this.zs = zs;
     }
 
-    protected boolean satisfiesCofactor()
-    {
-        BigInteger h = curve.getCofactor();
-        return h == null || h.equals(ECConstants.ONE) || !ECAlgorithms.referenceMultiply(this, h).isInfinity();
-    }
-
     protected abstract boolean satisfiesCurveEquation();
 
+    protected boolean satisfiesOrder()
+    {
+        if (ECConstants.ONE.equals(curve.getCofactor()))
+        {
+            return true;
+        }
+
+        BigInteger n = curve.getOrder();
+
+        // TODO Require order to be available for all curves
+
+        return n == null || ECAlgorithms.referenceMultiply(this, n).isInfinity();
+    }
+
     public final ECPoint getDetachedPoint()
     {
         return normalize().detach();
@@ -91,33 +99,6 @@
     }
 
     /**
-     * Normalizes this point, and then returns the affine x-coordinate.
-     * 
-     * Note: normalization can be expensive, this method is deprecated in favour
-     * of caller-controlled normalization.
-     * 
-     * @deprecated Use getAffineXCoord(), or normalize() and getXCoord(), instead
-     */
-    public ECFieldElement getX()
-    {
-        return normalize().getXCoord();
-    }
-
-
-    /**
-     * Normalizes this point, and then returns the affine y-coordinate.
-     * 
-     * Note: normalization can be expensive, this method is deprecated in favour
-     * of caller-controlled normalization.
-     * 
-     * @deprecated Use getAffineYCoord(), or normalize() and getYCoord(), instead
-     */
-    public ECFieldElement getY()
-    {
-        return normalize().getYCoord();
-    }
-
-    /**
      * Returns the affine x-coordinate after checking that this point is normalized.
      * 
      * @return The affine x-coordinate of this point
@@ -297,28 +278,58 @@
 
     public boolean isValid()
     {
+        return implIsValid(false, true);
+    }
+
+    boolean isValidPartial()
+    {
+        return implIsValid(false, false);
+    }
+
+    boolean implIsValid(final boolean decompressed, final boolean checkOrder)
+    {
         if (isInfinity())
         {
             return true;
         }
 
-        // TODO Sanity-check the field elements
-
-        ECCurve curve = getCurve();
-        if (curve != null)
+        ValidityPrecompInfo validity = (ValidityPrecompInfo)getCurve().precompute(this, ValidityPrecompInfo.PRECOMP_NAME, new PreCompCallback()
         {
-            if (!satisfiesCurveEquation())
+            public PreCompInfo precompute(PreCompInfo existing)
             {
-                return false;
-            }
+                ValidityPrecompInfo info = (existing instanceof ValidityPrecompInfo) ? (ValidityPrecompInfo)existing : null;
+                if (info == null)
+                {
+                    info = new ValidityPrecompInfo();
+                }
 
-            if (!satisfiesCofactor())
-            {
-                return false;
+                if (info.hasFailed())
+                {
+                    return info;
+                }
+                if (!info.hasCurveEquationPassed())
+                {
+                    if (!decompressed && !satisfiesCurveEquation())
+                    {
+                        info.reportFailed();
+                        return info;
+                    }
+                    info.reportCurveEquationPassed();
+                }
+                if (checkOrder && !info.hasOrderPassed())
+                {
+                    if (!satisfiesOrder())
+                    {
+                        info.reportFailed();
+                        return info;
+                    }
+                    info.reportOrderPassed();
+                }
+                return info;
             }
-        }
+        });
 
-        return true;
+        return !validity.hasFailed();
     }
 
     public ECPoint scaleX(ECFieldElement scale)
@@ -440,6 +451,7 @@
 
     /**
      * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
+     * @return a byte encoding.
      */
     public byte[] getEncoded()
     {
@@ -602,20 +614,6 @@
     public static class Fp extends AbstractFp
     {
         /**
-         * Create a point which encodes without point compression.
-         * 
-         * @param curve the curve to use
-         * @param x affine x co-ordinate
-         * @param y affine y co-ordinate
-         * 
-         * @deprecated Use ECCurve.createPoint to construct points
-         */
-        public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y)
-        {
-            this(curve, x, y, false);
-        }
-
-        /**
          * Create a point that encodes with or without point compression.
          * 
          * @param curve the curve to use
@@ -646,7 +644,7 @@
 
         protected ECPoint detach()
         {
-            return new ECPoint.Fp(null, this.getAffineXCoord(), this.getAffineYCoord());
+            return new ECPoint.Fp(null, this.getAffineXCoord(), this.getAffineYCoord(), false);
         }
 
         public ECFieldElement getZCoord(int index)
@@ -1423,6 +1421,46 @@
             return lhs.equals(rhs);
         }
 
+        protected boolean satisfiesOrder()
+        {
+            BigInteger cofactor = curve.getCofactor();
+            if (ECConstants.TWO.equals(cofactor))
+            {
+                /*
+                 *  Check that the trace of (X + A) is 0, then there exists a solution to L^2 + L = X + A,
+                 *  and so a halving is possible, so this point is the double of another.  
+                 */
+                ECPoint N = this.normalize();
+                ECFieldElement X = N.getAffineXCoord();
+                ECFieldElement rhs = X.add(curve.getA());
+                return ((ECFieldElement.AbstractF2m)rhs).trace() == 0;
+            }
+            if (ECConstants.FOUR.equals(cofactor))
+            {
+                /*
+                 * Solve L^2 + L = X + A to find the half of this point, if it exists (fail if not).
+                 * Generate both possibilities for the square of the half-point's x-coordinate (w),
+                 * and check if Tr(w + A) == 0 for at least one; then a second halving is possible
+                 * (see comments for cofactor 2 above), so this point is four times another.
+                 * 
+                 * Note: Tr(x^2) == Tr(x). 
+                 */
+                ECPoint N = this.normalize();
+                ECFieldElement X = N.getAffineXCoord();
+                ECFieldElement lambda = ((ECCurve.AbstractF2m)curve).solveQuadraticEquation(X.add(curve.getA()));
+                if (lambda == null)
+                {
+                    return false;
+                }
+                ECFieldElement w = X.multiply(lambda).add(N.getAffineYCoord()); 
+                ECFieldElement t = w.add(curve.getA());
+                return ((ECFieldElement.AbstractF2m)t).trace() == 0
+                    || ((ECFieldElement.AbstractF2m)(t.add(X))).trace() == 0;
+            }
+
+            return super.satisfiesOrder();
+        }
+
         public ECPoint scaleX(ECFieldElement scale)
         {
             if (this.isInfinity())
@@ -1580,18 +1618,6 @@
          * @param curve base curve
          * @param x x point
          * @param y y point
-         * 
-         * @deprecated Use ECCurve.createPoint to construct points
-         */
-        public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y)
-        {
-            this(curve, x, y, false);
-        }
-        
-        /**
-         * @param curve base curve
-         * @param x x point
-         * @param y y point
          * @param withCompression true if encode with point compression.
          * 
          * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
@@ -1633,7 +1659,7 @@
 
         protected ECPoint detach()
         {
-            return new ECPoint.F2m(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK
+            return new ECPoint.F2m(null, this.getAffineXCoord(), this.getAffineYCoord(), false); // earlier JDK
         }
 
         public ECFieldElement getYCoord()
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
index c91de7b..f3dad92 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
@@ -2,6 +2,8 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.math.raw.Nat;
+
 public class FixedPointCombMultiplier extends AbstractECMultiplier
 {
     protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
@@ -20,38 +22,35 @@
             throw new IllegalStateException("fixed-point comb doesn't support scalars larger than the curve order");
         }
 
-        int minWidth = getWidthForCombSize(size);
-
-        FixedPointPreCompInfo info = FixedPointUtil.precompute(p, minWidth);
-        ECPoint[] lookupTable = info.getPreComp();
+        FixedPointPreCompInfo info = FixedPointUtil.precompute(p);
+        ECLookupTable lookupTable = info.getLookupTable();
         int width = info.getWidth();
 
         int d = (size + width - 1) / width;
 
         ECPoint R = c.getInfinity();
 
-        int top = d * width - 1; 
+        int fullComb = d * width;
+        int[] K = Nat.fromBigInteger(fullComb, k);
+
+        int top = fullComb - 1; 
         for (int i = 0; i < d; ++i)
         {
-            int index = 0;
+            int secretIndex = 0;
 
             for (int j = top - i; j >= 0; j -= d)
             {
-                index <<= 1;
-                if (k.testBit(j))
-                {
-                    index |= 1;
-                }
+                int secretBit = K[j >>> 5] >>> (j & 0x1F);
+                secretIndex ^= secretBit >>> 1;
+                secretIndex <<= 1;
+                secretIndex ^= secretBit;
             }
 
-            R = R.twicePlus(lookupTable[index]);
+            ECPoint add = lookupTable.lookup(secretIndex);
+
+            R = R.twicePlus(add);
         }
 
         return R.add(info.getOffset());
     }
-
-    protected int getWidthForCombSize(int combSize)
-    {
-        return combSize > 257 ? 6 : 5;
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
index 31f5d10..93889e1 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
@@ -8,10 +8,9 @@
     protected ECPoint offset = null;
 
     /**
-     * Array holding the precomputed <code>ECPoint</code>s used for a fixed
-     * point multiplication.
+     * Lookup table for the precomputed {@link ECPoint}s used for a fixed point multiplication.
      */
-    protected ECPoint[] preComp = null;
+    protected ECLookupTable lookupTable = null;
 
     /**
      * The width used for the precomputation. If a larger width precomputation
@@ -20,6 +19,16 @@
      */
     protected int width = -1;
 
+    public ECLookupTable getLookupTable()
+    {
+        return lookupTable;
+    }
+
+    public void setLookupTable(ECLookupTable lookupTable)
+    {
+        this.lookupTable = lookupTable;
+    }
+
     public ECPoint getOffset()
     {
         return offset;
@@ -30,16 +39,6 @@
         this.offset = offset;
     }
 
-    public ECPoint[] getPreComp()
-    {
-        return preComp;
-    }
-
-    public void setPreComp(ECPoint[] preComp)
-    {
-        this.preComp = preComp;
-    }
-
     public int getWidth()
     {
         return width;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
index 93b435c..6b81d23 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
@@ -14,62 +14,74 @@
 
     public static FixedPointPreCompInfo getFixedPointPreCompInfo(PreCompInfo preCompInfo)
     {
-        if ((preCompInfo != null) && (preCompInfo instanceof FixedPointPreCompInfo))
-        {
-            return (FixedPointPreCompInfo)preCompInfo;
-        }
-
-        return new FixedPointPreCompInfo();
+        return (preCompInfo instanceof FixedPointPreCompInfo) ? (FixedPointPreCompInfo)preCompInfo : null;
     }
 
-    public static FixedPointPreCompInfo precompute(ECPoint p, int minWidth)
+    public static FixedPointPreCompInfo precompute(final ECPoint p)
     {
-        ECCurve c = p.getCurve();
+        final ECCurve c = p.getCurve();
 
-        int n = 1 << minWidth;
-        FixedPointPreCompInfo info = getFixedPointPreCompInfo(c.getPreCompInfo(p, PRECOMP_NAME));
-        ECPoint[] lookupTable = info.getPreComp();
-
-        if (lookupTable == null || lookupTable.length < n)
+        return (FixedPointPreCompInfo)c.precompute(p, PRECOMP_NAME, new PreCompCallback()
         {
-            int bits = getCombSize(c);
-            int d = (bits + minWidth - 1) / minWidth;
-
-            ECPoint[] pow2Table = new ECPoint[minWidth + 1];
-            pow2Table[0] = p;
-            for (int i = 1; i < minWidth; ++i)
+            public PreCompInfo precompute(PreCompInfo existing)
             {
-                pow2Table[i] = pow2Table[i - 1].timesPow2(d);
-            }
+                FixedPointPreCompInfo existingFP = (existing instanceof FixedPointPreCompInfo) ? (FixedPointPreCompInfo)existing : null;
 
-            // This will be the 'offset' value 
-            pow2Table[minWidth] = pow2Table[0].subtract(pow2Table[1]);
+                int bits = getCombSize(c);
+                int minWidth = bits > 250 ? 6 : 5;
+                int n = 1 << minWidth;
 
-            c.normalizeAll(pow2Table);
-
-            lookupTable = new ECPoint[n];
-            lookupTable[0] = pow2Table[0];
-
-            for (int bit = minWidth - 1; bit >= 0; --bit)
-            {
-                ECPoint pow2 = pow2Table[bit];
-
-                int step = 1 << bit;
-                for (int i = step; i < n; i += (step << 1))
+                if (checkExisting(existingFP, n))
                 {
-                    lookupTable[i] = lookupTable[i - step].add(pow2);
+                    return existingFP;
                 }
+
+                int d = (bits + minWidth - 1) / minWidth;
+
+                ECPoint[] pow2Table = new ECPoint[minWidth + 1];
+                pow2Table[0] = p;
+                for (int i = 1; i < minWidth; ++i)
+                {
+                    pow2Table[i] = pow2Table[i - 1].timesPow2(d);
+                }
+
+                // This will be the 'offset' value 
+                pow2Table[minWidth] = pow2Table[0].subtract(pow2Table[1]);
+
+                c.normalizeAll(pow2Table);
+
+                ECPoint[] lookupTable = new ECPoint[n];
+                lookupTable[0] = pow2Table[0];
+
+                for (int bit = minWidth - 1; bit >= 0; --bit)
+                {
+                    ECPoint pow2 = pow2Table[bit];
+
+                    int step = 1 << bit;
+                    for (int i = step; i < n; i += (step << 1))
+                    {
+                        lookupTable[i] = lookupTable[i - step].add(pow2);
+                    }
+                }
+
+                c.normalizeAll(lookupTable);
+
+                FixedPointPreCompInfo result = new FixedPointPreCompInfo();
+                result.setLookupTable(c.createCacheSafeLookupTable(lookupTable, 0, lookupTable.length));
+                result.setOffset(pow2Table[minWidth]);
+                result.setWidth(minWidth);
+                return result;
             }
 
-            c.normalizeAll(lookupTable);
+            private boolean checkExisting(FixedPointPreCompInfo existingFP, int n)
+            {
+                return existingFP != null && checkTable(existingFP.getLookupTable(), n);
+            }
 
-            info.setOffset(pow2Table[minWidth]);
-            info.setPreComp(lookupTable);
-            info.setWidth(minWidth);
-
-            c.setPreCompInfo(p, PRECOMP_NAME, info);
-        }
-
-        return info;
+            private boolean checkTable(ECLookupTable table, int n)
+            {
+                return table != null && table.getSize() >= n;
+            }
+        });
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java
index b963118..b9a1535 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java
@@ -373,6 +373,11 @@
         }
     }
 
+    void copyTo(long[] z, int zOff)
+    {
+        System.arraycopy(m_ints, 0, z, zOff, m_ints.length);
+    }
+
     public boolean isOne()
     {
         long[] a = m_ints;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompCallback.java b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompCallback.java
new file mode 100644
index 0000000..5cbd8d0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompCallback.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.math.ec;
+
+public interface PreCompCallback
+{
+    PreCompInfo precompute(PreCompInfo existing);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleLookupTable.java b/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleLookupTable.java
new file mode 100644
index 0000000..98a903e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/SimpleLookupTable.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.math.ec;
+
+public class SimpleLookupTable
+    implements ECLookupTable
+{
+    private static ECPoint[] copy(ECPoint[] points, int off, int len)
+    {
+        ECPoint[] result = new ECPoint[len];
+        for (int i = 0; i < len; ++i)
+        {
+            result[i] = points[off + i];
+        }
+        return result;
+    }
+    
+    private final ECPoint[] points;
+
+    public SimpleLookupTable(ECPoint[] points, int off, int len)
+    {
+        this.points = copy(points, off, len);
+    }
+
+    public int getSize()
+    {
+        return points.length;
+    }
+
+    public ECPoint lookup(int index)
+    {
+        return points[index];
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ValidityPrecompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ValidityPrecompInfo.java
new file mode 100644
index 0000000..d3a3ce3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ValidityPrecompInfo.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.math.ec;
+
+class ValidityPrecompInfo implements PreCompInfo
+{
+    static final String PRECOMP_NAME = "bc_validity";
+
+    private boolean failed = false;
+    private boolean curveEquationPassed = false;
+    private boolean orderPassed = false;
+
+    boolean hasFailed()
+    {
+        return failed;
+    }
+
+    void reportFailed()
+    {
+        failed = true;
+    }
+
+    boolean hasCurveEquationPassed()
+    {
+        return curveEquationPassed;
+    }
+
+    void reportCurveEquationPassed()
+    {
+        curveEquationPassed = true;
+    }
+
+    boolean hasOrderPassed()
+    {
+        return orderPassed;
+    }
+
+    void reportOrderPassed()
+    {
+        orderPassed = true;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java
index 301b5ae..f383308 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java
@@ -304,12 +304,7 @@
 
     public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo)
     {
-        if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo))
-        {
-            return (WNafPreCompInfo)preCompInfo;
-        }
-
-        return new WNafPreCompInfo();
+        return (preCompInfo instanceof WNafPreCompInfo) ? (WNafPreCompInfo)preCompInfo : null;
     }
 
     /**
@@ -343,178 +338,214 @@
         return w + 2;
     }
 
-    public static ECPoint mapPointWithPrecomp(ECPoint p, int width, boolean includeNegated,
-        ECPointMap pointMap)
+    public static ECPoint mapPointWithPrecomp(ECPoint p, final int width, final boolean includeNegated,
+        final ECPointMap pointMap)
     {
-        ECCurve c = p.getCurve();
-        WNafPreCompInfo wnafPreCompP = precompute(p, width, includeNegated);
+        final ECCurve c = p.getCurve();
+        final WNafPreCompInfo wnafPreCompP = precompute(p, width, includeNegated);
 
         ECPoint q = pointMap.map(p);
-        WNafPreCompInfo wnafPreCompQ = getWNafPreCompInfo(c.getPreCompInfo(q, PRECOMP_NAME));
-
-        ECPoint twiceP = wnafPreCompP.getTwice();
-        if (twiceP != null)
+        c.precompute(q, PRECOMP_NAME, new PreCompCallback()
         {
-            ECPoint twiceQ = pointMap.map(twiceP);
-            wnafPreCompQ.setTwice(twiceQ);
-        }
-
-        ECPoint[] preCompP = wnafPreCompP.getPreComp();
-        ECPoint[] preCompQ = new ECPoint[preCompP.length];
-        for (int i = 0; i < preCompP.length; ++i)
-        {
-            preCompQ[i] = pointMap.map(preCompP[i]);
-        }
-        wnafPreCompQ.setPreComp(preCompQ);
-
-        if (includeNegated)
-        {
-            ECPoint[] preCompNegQ = new ECPoint[preCompQ.length];
-            for (int i = 0; i < preCompNegQ.length; ++i)
+            public PreCompInfo precompute(PreCompInfo existing)
             {
-                preCompNegQ[i] = preCompQ[i].negate();
-            }
-            wnafPreCompQ.setPreCompNeg(preCompNegQ);
-        }
+                WNafPreCompInfo result = new WNafPreCompInfo();
 
-        c.setPreCompInfo(q, PRECOMP_NAME, wnafPreCompQ);
+                ECPoint twiceP = wnafPreCompP.getTwice();
+                if (twiceP != null)
+                {
+                    ECPoint twiceQ = pointMap.map(twiceP);
+                    result.setTwice(twiceQ);
+                }
+
+                ECPoint[] preCompP = wnafPreCompP.getPreComp();
+                ECPoint[] preCompQ = new ECPoint[preCompP.length];
+                for (int i = 0; i < preCompP.length; ++i)
+                {
+                    preCompQ[i] = pointMap.map(preCompP[i]);
+                }
+                result.setPreComp(preCompQ);
+
+                if (includeNegated)
+                {
+                    ECPoint[] preCompNegQ = new ECPoint[preCompQ.length];
+                    for (int i = 0; i < preCompNegQ.length; ++i)
+                    {
+                        preCompNegQ[i] = preCompQ[i].negate();
+                    }
+                    result.setPreCompNeg(preCompNegQ);
+                }
+
+                return result;
+            }
+        });
 
         return q;
     }
 
-    public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated)
+    public static WNafPreCompInfo precompute(final ECPoint p, final int width, final boolean includeNegated)
     {
-        ECCurve c = p.getCurve();
-        WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p, PRECOMP_NAME));
+        final ECCurve c = p.getCurve();
 
-        int iniPreCompLen = 0, reqPreCompLen = 1 << Math.max(0, width - 2);
-
-        ECPoint[] preComp = wnafPreCompInfo.getPreComp();
-        if (preComp == null)
+        return (WNafPreCompInfo)c.precompute(p, PRECOMP_NAME, new PreCompCallback()
         {
-            preComp = EMPTY_POINTS;
-        }
-        else
-        {
-            iniPreCompLen = preComp.length;
-        }
-
-        if (iniPreCompLen < reqPreCompLen)
-        {
-            preComp = resizeTable(preComp, reqPreCompLen);
-
-            if (reqPreCompLen == 1)
+            public PreCompInfo precompute(PreCompInfo existing)
             {
-                preComp[0] = p.normalize();
-            }
-            else
-            {
-                int curPreCompLen = iniPreCompLen;
-                if (curPreCompLen == 0)
+                WNafPreCompInfo existingWNaf = (existing instanceof WNafPreCompInfo) ? (WNafPreCompInfo)existing : null;
+
+                int reqPreCompLen = 1 << Math.max(0, width - 2);
+
+                if (checkExisting(existingWNaf, reqPreCompLen, includeNegated))
                 {
-                    preComp[0] = p;
-                    curPreCompLen = 1;
+                    return existingWNaf;
                 }
 
-                ECFieldElement iso = null;
+                ECPoint[] preComp = null, preCompNeg = null;
+                ECPoint twiceP = null;
 
-                if (reqPreCompLen == 2)
+                if (existingWNaf != null)
                 {
-                    preComp[1] = p.threeTimes();
+                    preComp = existingWNaf.getPreComp();
+                    preCompNeg = existingWNaf.getPreCompNeg();
+                    twiceP = existingWNaf.getTwice();
+                }
+
+                int iniPreCompLen = 0;
+                if (preComp == null)
+                {
+                    preComp = EMPTY_POINTS;
                 }
                 else
                 {
-                    ECPoint twiceP = wnafPreCompInfo.getTwice(), last = preComp[curPreCompLen - 1];
-                    if (twiceP == null)
+                    iniPreCompLen = preComp.length;
+                }
+
+                if (iniPreCompLen < reqPreCompLen)
+                {
+                    preComp = resizeTable(preComp, reqPreCompLen);
+
+                    if (reqPreCompLen == 1)
                     {
-                        twiceP = preComp[0].twice();
-                        wnafPreCompInfo.setTwice(twiceP);
+                        preComp[0] = p.normalize();
+                    }
+                    else
+                    {
+                        int curPreCompLen = iniPreCompLen;
+                        if (curPreCompLen == 0)
+                        {
+                            preComp[0] = p;
+                            curPreCompLen = 1;
+                        }
+
+                        ECFieldElement iso = null;
+
+                        if (reqPreCompLen == 2)
+                        {
+                            preComp[1] = p.threeTimes();
+                        }
+                        else
+                        {
+                            ECPoint isoTwiceP = twiceP, last = preComp[curPreCompLen - 1];
+                            if (isoTwiceP == null)
+                            {
+                                isoTwiceP = preComp[0].twice();
+                                twiceP = isoTwiceP;
+
+                                /*
+                                 * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
+                                 * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
+                                 * also requires scaling the initial point's X, Y coordinates, and reversing the
+                                 * isomorphism as part of the subsequent normalization.
+                                 * 
+                                 *  NOTE: The correctness of this optimization depends on:
+                                 *      1) additions do not use the curve's A, B coefficients.
+                                 *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+                                 */
+                                if (!twiceP.isInfinity() && ECAlgorithms.isFpCurve(c) && c.getFieldSize() >= 64)
+                                {
+                                    switch (c.getCoordinateSystem())
+                                    {
+                                    case ECCurve.COORD_JACOBIAN:
+                                    case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+                                    case ECCurve.COORD_JACOBIAN_MODIFIED:
+                                    {
+                                        iso = twiceP.getZCoord(0);
+                                        isoTwiceP = c.createPoint(twiceP.getXCoord().toBigInteger(), twiceP.getYCoord()
+                                            .toBigInteger());
+
+                                        ECFieldElement iso2 = iso.square(), iso3 = iso2.multiply(iso);
+                                        last = last.scaleX(iso2).scaleY(iso3);
+
+                                        if (iniPreCompLen == 0)
+                                        {
+                                            preComp[0] = last;
+                                        }
+                                        break;
+                                    }
+                                    }
+                                }
+                            }
+
+                            while (curPreCompLen < reqPreCompLen)
+                            {
+                                /*
+                                 * Compute the new ECPoints for the precomputation array. The values 1, 3,
+                                 * 5, ..., 2^(width-1)-1 times p are computed
+                                 */
+                                preComp[curPreCompLen++] = last = last.add(isoTwiceP);
+                            }
+                        }
 
                         /*
-                         * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
-                         * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
-                         * also requires scaling the initial point's X, Y coordinates, and reversing the
-                         * isomorphism as part of the subsequent normalization.
-                         * 
-                         *  NOTE: The correctness of this optimization depends on:
-                         *      1) additions do not use the curve's A, B coefficients.
-                         *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+                         * Having oft-used operands in affine form makes operations faster.
                          */
-                        if (!twiceP.isInfinity() && ECAlgorithms.isFpCurve(c) && c.getFieldSize() >= 64)
+                        c.normalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso);
+                    }
+                }
+
+                if (includeNegated)
+                {
+                    int pos;
+                    if (preCompNeg == null)
+                    {
+                        pos = 0;
+                        preCompNeg = new ECPoint[reqPreCompLen]; 
+                    }
+                    else
+                    {
+                        pos = preCompNeg.length;
+                        if (pos < reqPreCompLen)
                         {
-                            switch (c.getCoordinateSystem())
-                            {
-                            case ECCurve.COORD_JACOBIAN:
-                            case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
-                            case ECCurve.COORD_JACOBIAN_MODIFIED:
-                            {
-                                iso = twiceP.getZCoord(0);
-                                twiceP = c.createPoint(twiceP.getXCoord().toBigInteger(), twiceP.getYCoord()
-                                    .toBigInteger());
-
-                                ECFieldElement iso2 = iso.square(), iso3 = iso2.multiply(iso);
-                                last = last.scaleX(iso2).scaleY(iso3);
-
-                                if (iniPreCompLen == 0)
-                                {
-                                    preComp[0] = last;
-                                }
-                                break;
-                            }
-                            }
+                            preCompNeg = resizeTable(preCompNeg, reqPreCompLen);
                         }
                     }
 
-                    while (curPreCompLen < reqPreCompLen)
+                    while (pos < reqPreCompLen)
                     {
-                        /*
-                         * Compute the new ECPoints for the precomputation array. The values 1, 3,
-                         * 5, ..., 2^(width-1)-1 times p are computed
-                         */
-                        preComp[curPreCompLen++] = last = last.add(twiceP);
+                        preCompNeg[pos] = preComp[pos].negate();
+                        ++pos;
                     }
                 }
 
-                /*
-                 * Having oft-used operands in affine form makes operations faster.
-                 */
-                c.normalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso);
+                WNafPreCompInfo result = new WNafPreCompInfo();
+                result.setPreComp(preComp);
+                result.setPreCompNeg(preCompNeg);
+                result.setTwice(twiceP);
+                return result;
             }
-        }
 
-        wnafPreCompInfo.setPreComp(preComp);
-
-        if (includeNegated)
-        {
-            ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
-            
-            int pos;
-            if (preCompNeg == null)
+            private boolean checkExisting(WNafPreCompInfo existingWNaf, int reqPreCompLen, boolean includeNegated)
             {
-                pos = 0;
-                preCompNeg = new ECPoint[reqPreCompLen]; 
+                return existingWNaf != null
+                    && checkTable(existingWNaf.getPreComp(), reqPreCompLen)
+                    && (!includeNegated || checkTable(existingWNaf.getPreCompNeg(), reqPreCompLen));
             }
-            else
+
+            private boolean checkTable(ECPoint[] table, int reqLen)
             {
-                pos = preCompNeg.length;
-                if (pos < reqPreCompLen)
-                {
-                    preCompNeg = resizeTable(preCompNeg, reqPreCompLen);
-                }
+                return table != null && table.length >= reqLen;
             }
-
-            while (pos < reqPreCompLen)
-            {
-                preCompNeg[pos] = preComp[pos].negate();
-                ++pos;
-            }
-
-            wnafPreCompInfo.setPreCompNeg(preCompNeg);
-        }
-
-        c.setPreCompInfo(p, PRECOMP_NAME, wnafPreCompInfo);
-
-        return wnafPreCompInfo;
+        });
     }
 
     private static byte[] trim(byte[] a, int length)
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
index 7974e1d..0438e1d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -36,7 +36,7 @@
 
         ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
 
-        return multiplyWTnaf(p, rho, curve.getPreCompInfo(p, PRECOMP_NAME), a, mu);
+        return multiplyWTnaf(p, rho, a, mu);
     }
 
     /**
@@ -49,8 +49,7 @@
      * <code>[&tau;]</code>-adic NAF.
      * @return <code>p</code> multiplied by <code>&lambda;</code>.
      */
-    private ECPoint.AbstractF2m multiplyWTnaf(ECPoint.AbstractF2m p, ZTauElement lambda,
-            PreCompInfo preCompInfo, byte a, byte mu)
+    private ECPoint.AbstractF2m multiplyWTnaf(ECPoint.AbstractF2m p, ZTauElement lambda, byte a, byte mu)
     {
         ZTauElement[] alpha = (a == 0) ? Tnaf.alpha0 : Tnaf.alpha1;
 
@@ -59,7 +58,7 @@
         byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
             BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
 
-        return multiplyFromWTnaf(p, u, preCompInfo);
+        return multiplyFromWTnaf(p, u);
     }
 
     /**
@@ -71,24 +70,27 @@
      * @param u The the WTNAF of <code>&lambda;</code>..
      * @return <code>&lambda; * p</code>
      */
-    private static ECPoint.AbstractF2m multiplyFromWTnaf(ECPoint.AbstractF2m p, byte[] u, PreCompInfo preCompInfo)
+    private static ECPoint.AbstractF2m multiplyFromWTnaf(final ECPoint.AbstractF2m p, byte[] u)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
-        byte a = curve.getA().toBigInteger().byteValue();
+        final byte a = curve.getA().toBigInteger().byteValue();
 
-        ECPoint.AbstractF2m[] pu;
-        if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo))
+        WTauNafPreCompInfo preCompInfo = (WTauNafPreCompInfo)curve.precompute(p, PRECOMP_NAME, new PreCompCallback()
         {
-            pu = Tnaf.getPreComp(p, a);
+            public PreCompInfo precompute(PreCompInfo existing)
+            {
+                if (existing instanceof WTauNafPreCompInfo)
+                {
+                    return existing;
+                }
 
-            WTauNafPreCompInfo pre = new WTauNafPreCompInfo();
-            pre.setPreComp(pu);
-            curve.setPreCompInfo(p, PRECOMP_NAME, pre);
-        }
-        else
-        {
-            pu = ((WTauNafPreCompInfo)preCompInfo).getPreComp();
-        }
+                WTauNafPreCompInfo result = new WTauNafPreCompInfo();
+                result.setPreComp(Tnaf.getPreComp(p, a));
+                return result;
+            }
+        });
+
+        ECPoint.AbstractF2m[] pu = preCompInfo.getPreComp();
 
         // TODO Include negations in precomp (optionally) and use from here
         ECPoint.AbstractF2m[] puNeg = new ECPoint.AbstractF2m[pu.length];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519.java
index e7839ce..d9fa6c3 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.encoders.Hex;
@@ -77,4 +78,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 8;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy(((Curve25519FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat256.copy(((Curve25519FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat256.create(), y = Nat256.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new Curve25519FieldElement(x), new Curve25519FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519FieldElement.java
index 010b6f5..c8e6120 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/djb/Curve25519FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class Curve25519FieldElement extends ECFieldElement
+public class Curve25519FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = Curve25519.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Curve.java
new file mode 100644
index 0000000..e88746f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Curve.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.math.ec.custom.gm;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SM2P256V1Curve extends ECCurve.AbstractFp
+{
+    public static final BigInteger q = new BigInteger(1,
+        Hex.decode("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"));
+
+    private static final int SM2P256V1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+    protected SM2P256V1Point infinity;
+
+    public SM2P256V1Curve()
+    {
+        super(q);
+
+        this.infinity = new SM2P256V1Point(this, null, null);
+
+        this.a = fromBigInteger(new BigInteger(1,
+            Hex.decode("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC")));
+        this.b = fromBigInteger(new BigInteger(1,
+            Hex.decode("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93")));
+        this.order = new BigInteger(1, Hex.decode("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"));
+        this.cofactor = BigInteger.valueOf(1);
+
+        this.coord = SM2P256V1_DEFAULT_COORDS;
+    }
+
+    protected ECCurve cloneCurve()
+    {
+        return new SM2P256V1Curve();
+    }
+
+    public boolean supportsCoordinateSystem(int coord)
+    {
+        switch (coord)
+        {
+        case COORD_JACOBIAN:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    public BigInteger getQ()
+    {
+        return q;
+    }
+
+    public int getFieldSize()
+    {
+        return q.bitLength();
+    }
+
+    public ECFieldElement fromBigInteger(BigInteger x)
+    {
+        return new SM2P256V1FieldElement(x);
+    }
+
+    protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+    {
+        return new SM2P256V1Point(this, x, y, withCompression);
+    }
+
+    protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+    {
+        return new SM2P256V1Point(this, x, y, zs, withCompression);
+    }
+
+    public ECPoint getInfinity()
+    {
+        return infinity;
+    }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 8;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy(((SM2P256V1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat256.copy(((SM2P256V1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat256.create(), y = Nat256.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SM2P256V1FieldElement(x), new SM2P256V1FieldElement(y), false);
+            }
+        };
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Field.java
new file mode 100644
index 0000000..3304d0d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Field.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.gm;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SM2P256V1Field
+{
+    private static final long M = 0xFFFFFFFFL;
+
+    // 2^256 - 2^224 - 2^96 + 2^64 - 1
+    static final int[] P = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+        0xFFFFFFFF, 0xFFFFFFFE };
+    static final int[] PExt = new int[]{ 00000001, 0x00000000, 0xFFFFFFFE, 0x00000001, 0x00000001,
+        0xFFFFFFFE, 0x00000000, 0x00000002, 0xFFFFFFFE, 0xFFFFFFFD, 0x00000003, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+        0x00000000, 0xFFFFFFFE };
+    private static final int P7s1 = 0xFFFFFFFE >>> 1;
+    private static final int PExt15s1 = 0xFFFFFFFE >>> 1;
+
+    public static void add(int[] x, int[] y, int[] z)
+    {
+        int c = Nat256.add(x, y, z);
+        if (c != 0 || ((z[7] >>> 1) >= P7s1 && Nat256.gte(z, P)))
+        {
+            addPInvTo(z);
+        }
+    }
+
+    public static void addExt(int[] xx, int[] yy, int[] zz)
+    {
+        int c = Nat.add(16, xx, yy, zz);
+        if (c != 0 || ((zz[15] >>> 1) >= PExt15s1 && Nat.gte(16, zz, PExt)))
+        {
+            Nat.subFrom(16, PExt, zz);
+        }
+    }
+
+    public static void addOne(int[] x, int[] z)
+    {
+        int c = Nat.inc(8, x, z);
+        if (c != 0 || ((z[7] >>> 1) >= P7s1 && Nat256.gte(z, P)))
+        {
+            addPInvTo(z);
+        }
+    }
+
+    public static int[] fromBigInteger(BigInteger x)
+    {
+        int[] z = Nat256.fromBigInteger(x);
+        if ((z[7] >>> 1) >= P7s1 && Nat256.gte(z, P))
+        {
+            Nat256.subFrom(P, z);
+        }
+        return z;
+    }
+
+    public static void half(int[] x, int[] z)
+    {
+        if ((x[0] & 1) == 0)
+        {
+            Nat.shiftDownBit(8, x, 0, z);
+        }
+        else
+        {
+            int c = Nat256.add(x, P, z);
+            Nat.shiftDownBit(8, z, c);
+        }
+    }
+
+    public static void multiply(int[] x, int[] y, int[] z)
+    {
+        int[] tt = Nat256.createExt();
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
+    public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
+    {
+        int c = Nat256.mulAddTo(x, y, zz);
+        if (c != 0 || ((zz[15] >>> 1) >= PExt15s1 && Nat.gte(16, zz, PExt)))
+        {
+            Nat.subFrom(16, PExt, zz);
+        }
+    }
+
+    public static void negate(int[] x, int[] z)
+    {
+        if (Nat256.isZero(x))
+        {
+            Nat256.zero(z);
+        }
+        else
+        {
+            Nat256.sub(P, x, z);
+        }
+    }
+
+    public static void reduce(int[] xx, int[] z)
+    {
+        long xx08 = xx[8] & M, xx09 = xx[9] & M, xx10 = xx[10] & M, xx11 = xx[11] & M;
+        long xx12 = xx[12] & M, xx13 = xx[13] & M, xx14 = xx[14] & M, xx15 = xx[15] & M;
+
+        long t0 = xx08 + xx09;
+        long t1 = xx10 + xx11;
+        long t2 = xx12 + xx15;
+        long t3 = xx13 + xx14;
+        long t4 = t3 + (xx15 << 1);
+
+        long ts = t0 + t3;
+        long tt = t1 + t2 + ts;
+
+        long cc = 0;
+        cc += (xx[0] & M) + tt + xx13 + xx14 + xx15;
+        z[0] = (int)cc;
+        cc >>= 32;
+        cc += (xx[1] & M) + tt - xx08 + xx14 + xx15;
+        z[1] = (int)cc;
+        cc >>= 32;
+        cc += (xx[2] & M) - ts;
+        z[2] = (int)cc;
+        cc >>= 32;
+        cc += (xx[3] & M) + tt - xx09 - xx10 + xx13;
+        z[3] = (int)cc;
+        cc >>= 32;
+        cc += (xx[4] & M) + tt - t1 - xx08 + xx14;
+        z[4] = (int)cc;
+        cc >>= 32;
+        cc += (xx[5] & M) + t4 + xx10;
+        z[5] = (int)cc;
+        cc >>= 32;
+        cc += (xx[6] & M) + xx11 + xx14 + xx15;
+        z[6] = (int)cc;
+        cc >>= 32;
+        cc += (xx[7] & M) + tt + t4 + xx12;
+        z[7] = (int)cc;
+        cc >>= 32;
+
+//        assert cc >= 0;
+
+        reduce32((int)cc, z);
+    }
+
+    public static void reduce32(int x, int[] z)
+    {
+        long cc = 0;
+
+        if (x != 0)
+        {
+            long xx08 = x & M;
+
+            cc += (z[0] & M) + xx08;
+            z[0] = (int)cc;
+            cc >>= 32;
+            if (cc != 0)
+            {
+                cc += (z[1] & M);
+                z[1] = (int)cc;
+                cc >>= 32;
+            }
+            cc += (z[2] & M) - xx08;
+            z[2] = (int)cc;
+            cc >>= 32;
+            cc += (z[3] & M) + xx08;
+            z[3] = (int)cc;
+            cc >>= 32;
+            if (cc != 0)
+            {
+                cc += (z[4] & M);
+                z[4] = (int)cc;
+                cc >>= 32;
+                cc += (z[5] & M);
+                z[5] = (int)cc;
+                cc >>= 32;
+                cc += (z[6] & M);
+                z[6] = (int)cc;
+                cc >>= 32;
+            }
+            cc += (z[7] & M) + xx08;
+            z[7] = (int)cc;
+            cc >>= 32;
+
+//          assert cc == 0 || cc == 1;
+        }
+
+        if (cc != 0 || ((z[7] >>> 1) >= P7s1 && Nat256.gte(z, P)))
+        {
+            addPInvTo(z);
+        }
+    }
+
+    public static void square(int[] x, int[] z)
+    {
+        int[] tt = Nat256.createExt();
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
+    public static void squareN(int[] x, int n, int[] z)
+    {
+//        assert n > 0;
+
+        int[] tt = Nat256.createExt();
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
+    public static void subtract(int[] x, int[] y, int[] z)
+    {
+        int c = Nat256.sub(x, y, z);
+        if (c != 0)
+        {
+            subPInvFrom(z);
+        }
+    }
+
+    public static void subtractExt(int[] xx, int[] yy, int[] zz)
+    {
+        int c = Nat.sub(16, xx, yy, zz);
+        if (c != 0)
+        {
+            Nat.addTo(16, PExt, zz);
+        }
+    }
+
+    public static void twice(int[] x, int[] z)
+    {
+        int c = Nat.shiftUpBit(8, x, 0, z);
+        if (c != 0 || ((z[7] >>> 1) >= P7s1 && Nat256.gte(z, P)))
+        {
+            addPInvTo(z);
+        }
+    }
+
+    private static void addPInvTo(int[] z)
+    {
+        long c = (z[0] & M) + 1;
+        z[0] = (int)c;
+        c >>= 32;
+        if (c != 0)
+        {
+            c += (z[1] & M);
+            z[1] = (int)c;
+            c >>= 32;
+        }
+        c += (z[2] & M) - 1;
+        z[2] = (int)c;
+        c >>= 32;
+        c += (z[3] & M) + 1;
+        z[3] = (int)c;
+        c >>= 32;
+        if (c != 0)
+        {
+            c += (z[4] & M);
+            z[4] = (int)c;
+            c >>= 32;
+            c += (z[5] & M);
+            z[5] = (int)c;
+            c >>= 32;
+            c += (z[6] & M);
+            z[6] = (int)c;
+            c >>= 32;
+        }
+        c += (z[7] & M) + 1;
+        z[7] = (int)c;
+//        c >>= 32;
+    }
+
+    private static void subPInvFrom(int[] z)
+    {
+        long c = (z[0] & M) - 1;
+        z[0] = (int)c;
+        c >>= 32;
+        if (c != 0)
+        {
+            c += (z[1] & M);
+            z[1] = (int)c;
+            c >>= 32;
+        }
+        c += (z[2] & M) + 1;
+        z[2] = (int)c;
+        c >>= 32;
+        c += (z[3] & M) - 1;
+        z[3] = (int)c;
+        c >>= 32;
+        if (c != 0)
+        {
+            c += (z[4] & M);
+            z[4] = (int)c;
+            c >>= 32;
+            c += (z[5] & M);
+            z[5] = (int)c;
+            c >>= 32;
+            c += (z[6] & M);
+            z[6] = (int)c;
+            c >>= 32;
+        }
+        c += (z[7] & M) - 1;
+        z[7] = (int)c;
+//        c >>= 32;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1FieldElement.java
new file mode 100644
index 0000000..2d5b06e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1FieldElement.java
@@ -0,0 +1,210 @@
+package org.bouncycastle.math.ec.custom.gm;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.raw.Mod;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.Arrays;
+
+public class SM2P256V1FieldElement extends ECFieldElement.AbstractFp
+{
+    public static final BigInteger Q = SM2P256V1Curve.q;
+
+    protected int[] x;
+
+    public SM2P256V1FieldElement(BigInteger x)
+    {
+        if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0)
+        {
+            throw new IllegalArgumentException("x value invalid for SM2P256V1FieldElement");
+        }
+
+        this.x = SM2P256V1Field.fromBigInteger(x);
+    }
+
+    public SM2P256V1FieldElement()
+    {
+        this.x = Nat256.create();
+    }
+
+    protected SM2P256V1FieldElement(int[] x)
+    {
+        this.x = x;
+    }
+
+    public boolean isZero()
+    {
+        return Nat256.isZero(x);
+    }
+
+    public boolean isOne()
+    {
+        return Nat256.isOne(x);
+    }
+
+    public boolean testBitZero()
+    {
+        return Nat256.getBit(x, 0) == 1;
+    }
+
+    public BigInteger toBigInteger()
+    {
+        return Nat256.toBigInteger(x);
+    }
+
+    public String getFieldName()
+    {
+        return "SM2P256V1Field";
+    }
+
+    public int getFieldSize()
+    {
+        return Q.bitLength();
+    }
+
+    public ECFieldElement add(ECFieldElement b)
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.add(x, ((SM2P256V1FieldElement)b).x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement addOne()
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.addOne(x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement subtract(ECFieldElement b)
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.subtract(x, ((SM2P256V1FieldElement)b).x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement multiply(ECFieldElement b)
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.multiply(x, ((SM2P256V1FieldElement)b).x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement divide(ECFieldElement b)
+    {
+//        return multiply(b.invert());
+        int[] z = Nat256.create();
+        Mod.invert(SM2P256V1Field.P, ((SM2P256V1FieldElement)b).x, z);
+        SM2P256V1Field.multiply(z, x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement negate()
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.negate(x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement square()
+    {
+        int[] z = Nat256.create();
+        SM2P256V1Field.square(x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    public ECFieldElement invert()
+    {
+//        return new SM2P256V1FieldElement(toBigInteger().modInverse(Q));
+        int[] z = Nat256.create();
+        Mod.invert(SM2P256V1Field.P, x, z);
+        return new SM2P256V1FieldElement(z);
+    }
+
+    /**
+     * return a sqrt root - the routine verifies that the calculation returns the right value - if
+     * none exists it returns null.
+     */
+    public ECFieldElement sqrt()
+    {
+        /*
+         * Raise this element to the exponent 2^254 - 2^222 - 2^94 + 2^62
+         *
+         * Breaking up the exponent's binary representation into "repunits", we get:
+         * { 31 1s } { 1 0s } { 128 1s } { 31 0s } { 1 1s } { 62 0s}
+         *
+         * We use an addition chain for the beginning: [1], 2, 3, 6, 12, [24], 30, [31] 
+         */
+
+        int[] x1 = this.x;
+        if (Nat256.isZero(x1) || Nat256.isOne(x1))
+        {
+            return this;
+        }
+
+        int[] x2 = Nat256.create();
+        SM2P256V1Field.square(x1, x2);
+        SM2P256V1Field.multiply(x2, x1, x2);
+        int[] x4 = Nat256.create();
+        SM2P256V1Field.squareN(x2, 2, x4);
+        SM2P256V1Field.multiply(x4, x2, x4);
+        int[] x6 = Nat256.create();
+        SM2P256V1Field.squareN(x4, 2, x6);
+        SM2P256V1Field.multiply(x6, x2, x6);
+        int[] x12 = x2;
+        SM2P256V1Field.squareN(x6, 6, x12);
+        SM2P256V1Field.multiply(x12, x6, x12);
+        int[] x24 = Nat256.create();
+        SM2P256V1Field.squareN(x12, 12, x24);
+        SM2P256V1Field.multiply(x24, x12, x24);
+        int[] x30 = x12;
+        SM2P256V1Field.squareN(x24, 6, x30);
+        SM2P256V1Field.multiply(x30, x6, x30);
+        int[] x31 = x6;
+        SM2P256V1Field.square(x30, x31);
+        SM2P256V1Field.multiply(x31, x1, x31);
+
+        int[] t1 = x24;
+        SM2P256V1Field.squareN(x31, 31, t1);
+
+        int[] x62 = x30;
+        SM2P256V1Field.multiply(t1, x31, x62);
+
+        SM2P256V1Field.squareN(t1, 32, t1);
+        SM2P256V1Field.multiply(t1, x62, t1);
+        SM2P256V1Field.squareN(t1, 62, t1);
+        SM2P256V1Field.multiply(t1, x62, t1);
+        SM2P256V1Field.squareN(t1, 4, t1);
+        SM2P256V1Field.multiply(t1, x4, t1);
+        SM2P256V1Field.squareN(t1, 32, t1);
+        SM2P256V1Field.multiply(t1, x1, t1);
+        SM2P256V1Field.squareN(t1, 62, t1);
+
+        int[] t2 = x4;
+        SM2P256V1Field.square(t1, t2);
+
+        return Nat256.eq(x1, t2) ? new SM2P256V1FieldElement(t1) : null;
+    }
+
+    public boolean equals(Object other)
+    {
+        if (other == this)
+        {
+            return true;
+        }
+
+        if (!(other instanceof SM2P256V1FieldElement))
+        {
+            return false;
+        }
+
+        SM2P256V1FieldElement o = (SM2P256V1FieldElement)other;
+        return Nat256.eq(x, o.x);
+    }
+
+    public int hashCode()
+    {
+        return Q.hashCode() ^ Arrays.hashCode(x, 0, 8);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Point.java
new file mode 100644
index 0000000..7cc174a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/SM2P256V1Point.java
@@ -0,0 +1,308 @@
+package org.bouncycastle.math.ec.custom.gm;
+
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat256;
+
+public class SM2P256V1Point extends ECPoint.AbstractFp
+{
+    /**
+     * Create a point which encodes with point compression.
+     *
+     * @param curve
+     *            the curve to use
+     * @param x
+     *            affine x co-ordinate
+     * @param y
+     *            affine y co-ordinate
+     *
+     * @deprecated Use ECCurve.createPoint to construct points
+     */
+    public SM2P256V1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+    {
+        this(curve, x, y, false);
+    }
+
+    /**
+     * Create a point that encodes with or without point compresion.
+     *
+     * @param curve
+     *            the curve to use
+     * @param x
+     *            affine x co-ordinate
+     * @param y
+     *            affine y co-ordinate
+     * @param withCompression
+     *            if true encode with point compression
+     *
+     * @deprecated per-point compression property will be removed, refer
+     *             {@link #getEncoded(boolean)}
+     */
+    public SM2P256V1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
+    {
+        super(curve, x, y);
+
+        if ((x == null) != (y == null))
+        {
+            throw new IllegalArgumentException("Exactly one of the field elements is null");
+        }
+
+        this.withCompression = withCompression;
+    }
+
+    SM2P256V1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
+    {
+        super(curve, x, y, zs);
+
+        this.withCompression = withCompression;
+    }
+
+    protected ECPoint detach()
+    {
+        return new SM2P256V1Point(null, getAffineXCoord(), getAffineYCoord());
+    }
+
+    public ECPoint add(ECPoint b)
+    {
+        if (this.isInfinity())
+        {
+            return b;
+        }
+        if (b.isInfinity())
+        {
+            return this;
+        }
+        if (this == b)
+        {
+            return twice();
+        }
+
+        ECCurve curve = this.getCurve();
+
+        SM2P256V1FieldElement X1 = (SM2P256V1FieldElement)this.x, Y1 = (SM2P256V1FieldElement)this.y;
+        SM2P256V1FieldElement X2 = (SM2P256V1FieldElement)b.getXCoord(), Y2 = (SM2P256V1FieldElement)b.getYCoord();
+
+        SM2P256V1FieldElement Z1 = (SM2P256V1FieldElement)this.zs[0];
+        SM2P256V1FieldElement Z2 = (SM2P256V1FieldElement)b.getZCoord(0);
+
+        int c;
+        int[] tt1 = Nat256.createExt();
+        int[] t2 = Nat256.create();
+        int[] t3 = Nat256.create();
+        int[] t4 = Nat256.create();
+
+        boolean Z1IsOne = Z1.isOne();
+        int[] U2, S2;
+        if (Z1IsOne)
+        {
+            U2 = X2.x;
+            S2 = Y2.x;
+        }
+        else
+        {
+            S2 = t3;
+            SM2P256V1Field.square(Z1.x, S2);
+
+            U2 = t2;
+            SM2P256V1Field.multiply(S2, X2.x, U2);
+
+            SM2P256V1Field.multiply(S2, Z1.x, S2);
+            SM2P256V1Field.multiply(S2, Y2.x, S2);
+        }
+
+        boolean Z2IsOne = Z2.isOne();
+        int[] U1, S1;
+        if (Z2IsOne)
+        {
+            U1 = X1.x;
+            S1 = Y1.x;
+        }
+        else
+        {
+            S1 = t4;
+            SM2P256V1Field.square(Z2.x, S1);
+
+            U1 = tt1;
+            SM2P256V1Field.multiply(S1, X1.x, U1);
+
+            SM2P256V1Field.multiply(S1, Z2.x, S1);
+            SM2P256V1Field.multiply(S1, Y1.x, S1);
+        }
+
+        int[] H = Nat256.create();
+        SM2P256V1Field.subtract(U1, U2, H);
+
+        int[] R = t2;
+        SM2P256V1Field.subtract(S1, S2, R);
+
+        // Check if b == this or b == -this
+        if (Nat256.isZero(H))
+        {
+            if (Nat256.isZero(R))
+            {
+                // this == b, i.e. this must be doubled
+                return this.twice();
+            }
+
+            // this == -b, i.e. the result is the point at infinity
+            return curve.getInfinity();
+        }
+
+        int[] HSquared = t3;
+        SM2P256V1Field.square(H, HSquared);
+
+        int[] G = Nat256.create();
+        SM2P256V1Field.multiply(HSquared, H, G);
+
+        int[] V = t3;
+        SM2P256V1Field.multiply(HSquared, U1, V);
+
+        SM2P256V1Field.negate(G, G);
+        Nat256.mul(S1, G, tt1);
+
+        c = Nat256.addBothTo(V, V, G);
+        SM2P256V1Field.reduce32(c, G);
+
+        SM2P256V1FieldElement X3 = new SM2P256V1FieldElement(t4);
+        SM2P256V1Field.square(R, X3.x);
+        SM2P256V1Field.subtract(X3.x, G, X3.x);
+
+        SM2P256V1FieldElement Y3 = new SM2P256V1FieldElement(G);
+        SM2P256V1Field.subtract(V, X3.x, Y3.x);
+        SM2P256V1Field.multiplyAddToExt(Y3.x, R, tt1);
+        SM2P256V1Field.reduce(tt1, Y3.x);
+
+        SM2P256V1FieldElement Z3 = new SM2P256V1FieldElement(H);
+        if (!Z1IsOne)
+        {
+            SM2P256V1Field.multiply(Z3.x, Z1.x, Z3.x);
+        }
+        if (!Z2IsOne)
+        {
+            SM2P256V1Field.multiply(Z3.x, Z2.x, Z3.x);
+        }
+
+        ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
+
+        return new SM2P256V1Point(curve, X3, Y3, zs, this.withCompression);
+    }
+
+    public ECPoint twice()
+    {
+        if (this.isInfinity())
+        {
+            return this;
+        }
+
+        ECCurve curve = this.getCurve();
+
+        SM2P256V1FieldElement Y1 = (SM2P256V1FieldElement)this.y;
+        if (Y1.isZero())
+        {
+            return curve.getInfinity();
+        }
+
+        SM2P256V1FieldElement X1 = (SM2P256V1FieldElement)this.x, Z1 = (SM2P256V1FieldElement)this.zs[0];
+
+        int c;
+        int[] t1 = Nat256.create();
+        int[] t2 = Nat256.create();
+
+        int[] Y1Squared = Nat256.create();
+        SM2P256V1Field.square(Y1.x, Y1Squared);
+
+        int[] T = Nat256.create();
+        SM2P256V1Field.square(Y1Squared, T);
+
+        boolean Z1IsOne = Z1.isOne();
+
+        int[] Z1Squared = Z1.x;
+        if (!Z1IsOne)
+        {
+            Z1Squared = t2;
+            SM2P256V1Field.square(Z1.x, Z1Squared);
+        }
+
+        SM2P256V1Field.subtract(X1.x, Z1Squared, t1);
+
+        int[] M = t2;
+        SM2P256V1Field.add(X1.x, Z1Squared, M);
+        SM2P256V1Field.multiply(M, t1, M);
+        c = Nat256.addBothTo(M, M, M);
+        SM2P256V1Field.reduce32(c, M);
+
+        int[] S = Y1Squared;
+        SM2P256V1Field.multiply(Y1Squared, X1.x, S);
+        c = Nat.shiftUpBits(8, S, 2, 0);
+        SM2P256V1Field.reduce32(c, S);
+
+        c = Nat.shiftUpBits(8, T, 3, 0, t1);
+        SM2P256V1Field.reduce32(c, t1);
+
+        SM2P256V1FieldElement X3 = new SM2P256V1FieldElement(T);
+        SM2P256V1Field.square(M, X3.x);
+        SM2P256V1Field.subtract(X3.x, S, X3.x);
+        SM2P256V1Field.subtract(X3.x, S, X3.x);
+
+        SM2P256V1FieldElement Y3 = new SM2P256V1FieldElement(S);
+        SM2P256V1Field.subtract(S, X3.x, Y3.x);
+        SM2P256V1Field.multiply(Y3.x, M, Y3.x);
+        SM2P256V1Field.subtract(Y3.x, t1, Y3.x);
+
+        SM2P256V1FieldElement Z3 = new SM2P256V1FieldElement(M);
+        SM2P256V1Field.twice(Y1.x, Z3.x);
+        if (!Z1IsOne)
+        {
+            SM2P256V1Field.multiply(Z3.x, Z1.x, Z3.x);
+        }
+
+        return new SM2P256V1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+    }
+
+    public ECPoint twicePlus(ECPoint b)
+    {
+        if (this == b)
+        {
+            return threeTimes();
+        }
+        if (this.isInfinity())
+        {
+            return b;
+        }
+        if (b.isInfinity())
+        {
+            return twice();
+        }
+
+        ECFieldElement Y1 = this.y;
+        if (Y1.isZero())
+        {
+            return b;
+        }
+
+        return twice().add(b);
+    }
+
+    public ECPoint threeTimes()
+    {
+        if (this.isInfinity() || this.y.isZero())
+        {
+            return this;
+        }
+
+        // NOTE: Be careful about recursions between twicePlus and threeTimes
+        return twice().add(this);
+    }
+
+    public ECPoint negate()
+    {
+        if (this.isInfinity())
+        {
+            return this;
+        }
+
+        return new SM2P256V1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/package.html b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/package.html
new file mode 100644
index 0000000..1d0567e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/gm/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Custom implementation of SM2 EC curve, SM2-P256V1.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java
index 8d555f6..59a9993 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat128;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP128R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 4;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat128.copy(((SecP128R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat128.copy(((SecP128R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat128.create(), y = Nat128.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP128R1FieldElement(x), new SecP128R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java
index 171b492..f77ba39 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java
@@ -16,13 +16,13 @@
         0xFFFFFFFF, 0x00000003, 0xFFFFFFFC };
     private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB,
         0x00000001, 0x00000000, 0xFFFFFFFC, 0x00000003 };
-    private static final int P3 = 0xFFFFFFFD;
-    private static final int PExt7 = 0xFFFFFFFC;
+    private static final int P3s1 = 0xFFFFFFFD >>> 1;
+    private static final int PExt7s1 = 0xFFFFFFFC >>> 1;
 
     public static void add(int[] x, int[] y, int[] z)
     {
         int c = Nat128.add(x, y, z);
-        if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+        if (c != 0 || ((z[3] >>> 1) >= P3s1 && Nat128.gte(z, P)))
         {
             addPInvTo(z);
         }
@@ -31,7 +31,7 @@
     public static void addExt(int[] xx, int[] yy, int[] zz)
     {
         int c = Nat256.add(xx, yy, zz);
-        if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt)))
+        if (c != 0 || ((zz[7] >>> 1) >= PExt7s1 && Nat256.gte(zz, PExt)))
         {
             Nat.addTo(PExtInv.length, PExtInv, zz);
         }
@@ -40,7 +40,7 @@
     public static void addOne(int[] x, int[] z)
     {
         int c = Nat.inc(4, x, z);
-        if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+        if (c != 0 || ((z[3] >>> 1) >= P3s1 && Nat128.gte(z, P)))
         {
             addPInvTo(z);
         }
@@ -49,7 +49,7 @@
     public static int[] fromBigInteger(BigInteger x)
     {
         int[] z = Nat128.fromBigInteger(x);
-        if (z[3] == P3 && Nat128.gte(z, P))
+        if ((z[3] >>> 1) >= P3s1 && Nat128.gte(z, P))
         {
             Nat128.subFrom(P, z);
         }
@@ -79,7 +79,7 @@
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat128.mulAddTo(x, y, zz);
-        if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt)))
+        if (c != 0 || ((zz[7] >>> 1) >= PExt7s1 && Nat256.gte(zz, PExt)))
         {
             Nat.addTo(PExtInv.length, PExtInv, zz);
         }
@@ -182,7 +182,7 @@
     public static void twice(int[] x, int[] z)
     {
         int c = Nat.shiftUpBit(4, x, 0, z);
-        if (c != 0 || (z[3] == P3 && Nat128.gte(z, P)))
+        if (c != 0 || ((z[3] >>> 1) >= P3s1 && Nat128.gte(z, P)))
         {
             addPInvTo(z);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java
index 91d999d..7d490a4 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat128;
 import org.bouncycastle.util.Arrays;
 
-public class SecP128R1FieldElement extends ECFieldElement
+public class SecP128R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP128R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java
index 9285e7c..6bc7609 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat160;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP160K1Curve extends ECCurve.AbstractFp
@@ -74,4 +76,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 5;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat160.copy(((SecP160R2FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat160.copy(((SecP160R2FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat160.create(), y = Nat160.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP160R2FieldElement(x), new SecP160R2FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java
index fd39a4a..74cad82 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat160;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP160R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 5;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat160.copy(((SecP160R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat160.copy(((SecP160R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat160.create(), y = Nat160.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP160R1FieldElement(x), new SecP160R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java
index 1b411b0..9999f48 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat160;
 import org.bouncycastle.util.Arrays;
 
-public class SecP160R1FieldElement extends ECFieldElement
+public class SecP160R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP160R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java
index 3e1cbbb..01bb2cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat160;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP160R2Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 5;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat160.copy(((SecP160R2FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat160.copy(((SecP160R2FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat160.create(), y = Nat160.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP160R2FieldElement(x), new SecP160R2FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java
index 3717136..943d260 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat160;
 import org.bouncycastle.util.Arrays;
 
-public class SecP160R2FieldElement extends ECFieldElement
+public class SecP160R2FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP160R2Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java
index b46cba6..f160ab3 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP192K1Curve extends ECCurve.AbstractFp
@@ -76,4 +78,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 6;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy(((SecP192K1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat192.copy(((SecP192K1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat192.create(), y = Nat192.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP192K1FieldElement(x), new SecP192K1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java
index 642c44c..39e62af 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192K1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.Arrays;
 
-public class SecP192K1FieldElement extends ECFieldElement
+public class SecP192K1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP192K1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java
index be67100..a43a596 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP192R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 6;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy(((SecP192R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat192.copy(((SecP192R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat192.create(), y = Nat192.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP192R1FieldElement(x), new SecP192R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java
index 68c8080..15fdcd6 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP192R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.Arrays;
 
-public class SecP192R1FieldElement extends ECFieldElement
+public class SecP192R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP192R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java
index ad733da..6b28be7 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat224;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP224K1Curve extends ECCurve.AbstractFp
@@ -75,4 +77,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 7;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat224.copy(((SecP224K1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat224.copy(((SecP224K1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat224.create(), y = Nat224.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP224K1FieldElement(x), new SecP224K1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java
index 8285a4e..2093a06 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224K1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat224;
 import org.bouncycastle.util.Arrays;
 
-public class SecP224K1FieldElement extends ECFieldElement
+public class SecP224K1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP224K1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java
index c844329..febb323 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat224;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP224R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 7;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat224.copy(((SecP224R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat224.copy(((SecP224R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat224.create(), y = Nat224.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP224R1FieldElement(x), new SecP224R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java
index 4a28f3d..ed2334a 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP224R1FieldElement.java
@@ -8,7 +8,7 @@
 import org.bouncycastle.math.raw.Nat224;
 import org.bouncycastle.util.Arrays;
 
-public class SecP224R1FieldElement extends ECFieldElement
+public class SecP224R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP224R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java
index 9b88576..6235381 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP256K1Curve extends ECCurve.AbstractFp
@@ -75,4 +77,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 8;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy(((SecP256K1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat256.copy(((SecP256K1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat256.create(), y = Nat256.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP256K1FieldElement(x), new SecP256K1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
index 467b17f..30bca2e 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class SecP256K1FieldElement extends ECFieldElement
+public class SecP256K1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP256K1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java
index 5ff6a38..7d7b51d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP256R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 8;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy(((SecP256R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat256.copy(((SecP256R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat256.create(), y = Nat256.create();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP256R1FieldElement(x), new SecP256R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
index 1e04f4b..cea1af7 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
@@ -16,7 +16,7 @@
         0xFFFFFFFF, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0x00000001, 0xFFFFFFFE,
         0x00000002, 0xFFFFFFFE };
     private static final int P7 = 0xFFFFFFFF;
-    private static final int PExt15 = 0xFFFFFFFF;
+    private static final int PExt15s1 = 0xFFFFFFFE >>> 1;
 
     public static void add(int[] x, int[] y, int[] z)
     {
@@ -30,7 +30,7 @@
     public static void addExt(int[] xx, int[] yy, int[] zz)
     {
         int c = Nat.add(16, xx, yy, zz);
-        if (c != 0 || (zz[15] == PExt15 && Nat.gte(16, zz, PExt)))
+        if (c != 0 || ((zz[15] >>> 1) >= PExt15s1 && Nat.gte(16, zz, PExt)))
         {
             Nat.subFrom(16, PExt, zz);
         }
@@ -78,7 +78,7 @@
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
-        if (c != 0 || (zz[15] == PExt15 && Nat.gte(16, zz, PExt)))
+        if (c != 0 || ((zz[15] >>> 1) >= PExt15s1 && Nat.gte(16, zz, PExt)))
         {
             Nat.subFrom(16, PExt, zz);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
index be250d1..6be46f2 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class SecP256R1FieldElement extends ECFieldElement
+public class SecP256R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP256R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java
index 27cbcdb..7a5603d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP384R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 12;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat.copy(FE_INTS, ((SecP384R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat.copy(FE_INTS, ((SecP384R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat.create(FE_INTS), y = Nat.create(FE_INTS);
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP384R1FieldElement(x), new SecP384R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
index 24e585d..3116b44 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.Arrays;
 
-public class SecP384R1FieldElement extends ECFieldElement
+public class SecP384R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP384R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java
index 16691b1..267defc 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecP521R1Curve extends ECCurve.AbstractFp
@@ -77,4 +79,49 @@
     {
         return infinity;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_INTS = 17;
+
+        final int[] table = new int[len * FE_INTS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat.copy(FE_INTS, ((SecP521R1FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_INTS;
+                Nat.copy(FE_INTS, ((SecP521R1FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_INTS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                int[] x = Nat.create(FE_INTS), y = Nat.create(FE_INTS);
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    int MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_INTS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_INTS + j] & MASK;
+                    }
+
+                    pos += (FE_INTS * 2);
+                }
+
+                return createRawPoint(new SecP521R1FieldElement(x), new SecP521R1FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
index ce9b639..5cf30fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.Arrays;
 
-public class SecP521R1FieldElement extends ECFieldElement
+public class SecP521R1FieldElement extends ECFieldElement.AbstractFp
 {
     public static final BigInteger Q = SecP521R1Curve.q;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java
index 6fda7ef..ef9ecce 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat128;
 import org.bouncycastle.util.Arrays;
 
-public class SecT113FieldElement extends ECFieldElement
+public class SecT113FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT113FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT113Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat128.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java
index 7c12d0a..b2a55f0 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat128;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT113R1Curve extends AbstractF2m
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 2;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat128.copy64(((SecT113FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat128.copy64(((SecT113FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat128.create64(), y = Nat128.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT113FieldElement(x), new SecT113FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java
index 209987b..92da298 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat128;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT113R2Curve extends AbstractF2m
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 2;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat128.copy64(((SecT113FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat128.copy64(((SecT113FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat128.create64(), y = Nat128.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT113FieldElement(x), new SecT113FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java
index 5dc18c5..d0ac60c 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.Arrays;
 
-public class SecT131FieldElement extends ECFieldElement
+public class SecT131FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -160,6 +160,11 @@
         return new SecT131FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT131Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat192.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java
index 6b216ad..6f45aeb 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT131R1Curve extends AbstractF2m
@@ -98,4 +100,49 @@
     {
         return 8;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 3;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy64(((SecT131FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat192.copy64(((SecT131FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat192.create64(), y = Nat192.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT131FieldElement(x), new SecT131FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java
index 8d19e37..7a1c985 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 8;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 3;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy64(((SecT131FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat192.copy64(((SecT131FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat192.create64(), y = Nat192.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT131FieldElement(x), new SecT131FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java
index 7a95c22..51a88bc 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.Arrays;
 
-public class SecT163FieldElement extends ECFieldElement
+public class SecT163FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT163FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT163Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat192.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java
index 387cc55..f7dedab 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java
@@ -5,9 +5,11 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT163K1Curve extends AbstractF2m
@@ -105,4 +107,49 @@
     {
         return 7;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 3;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy64(((SecT163FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat192.copy64(((SecT163FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat192.create64(), y = Nat192.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java
index 88b14c0..bfc9634 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 7;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 3;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy64(((SecT163FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat192.copy64(((SecT163FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat192.create64(), y = Nat192.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java
index 44054fe..3b44d22 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat192;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 7;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 3;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat192.copy64(((SecT163FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat192.copy64(((SecT163FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat192.create64(), y = Nat192.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java
index d7a712d..118e4d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class SecT193FieldElement extends ECFieldElement
+public class SecT193FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT193FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT193Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat256.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java
index 2dd53d1..e977061 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java
@@ -5,7 +5,9 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT193R1Curve extends AbstractF2m
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 4;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy64(((SecT193FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat256.copy64(((SecT193FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat256.create64(), y = Nat256.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT193FieldElement(x), new SecT193FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java
index 334ce3a..f08e7c0 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 4;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy64(((SecT193FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat256.copy64(((SecT193FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat256.create64(), y = Nat256.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT193FieldElement(x), new SecT193FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java
index e5ab93a..f34081e 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java
@@ -130,35 +130,6 @@
         z[zOff + 3]  = z3 & M41;
     }
 
-    public static void square(long[] x, long[] z)
-    {
-        long[] tt = Nat256.createExt64();
-        implSquare(x, tt);
-        reduce(tt, z);
-    }
-
-    public static void squareAddToExt(long[] x, long[] zz)
-    {
-        long[] tt = Nat256.createExt64();
-        implSquare(x, tt);
-        addExt(zz, tt, zz);
-    }
-
-    public static void squareN(long[] x, int n, long[] z)
-    {
-//        assert n > 0;
-
-        long[] tt = Nat256.createExt64();
-        implSquare(x, tt);
-        reduce(tt, z);
-
-        while (--n > 0)
-        {
-            implSquare(z, tt);
-            reduce(tt, z);
-        }
-    }
-
     public static void sqrt(long[] x, long[] z)
     {
         long u0, u1;
@@ -194,6 +165,35 @@
         z[1] ^= e1;
     }
 
+    public static void square(long[] x, long[] z)
+    {
+        long[] tt = Nat256.createExt64();
+        implSquare(x, tt);
+        reduce(tt, z);
+    }
+
+    public static void squareAddToExt(long[] x, long[] zz)
+    {
+        long[] tt = Nat256.createExt64();
+        implSquare(x, tt);
+        addExt(zz, tt, zz);
+    }
+
+    public static void squareN(long[] x, int n, long[] z)
+    {
+//        assert n > 0;
+
+        long[] tt = Nat256.createExt64();
+        implSquare(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            implSquare(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static int trace(long[] x)
     {
         // Non-zero-trace bits: 0, 159
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java
index 7370651..6ec68d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class SecT233FieldElement extends ECFieldElement
+public class SecT233FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT233FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT233Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat256.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java
index 2e83d1c..724f9d6 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java
@@ -5,9 +5,11 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT233K1Curve extends AbstractF2m
@@ -105,4 +107,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 4;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy64(((SecT233FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat256.copy64(((SecT233FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat256.create64(), y = Nat256.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT233FieldElement(x), new SecT233FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java
index 7a9c522..05b6fac 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 4;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy64(((SecT233FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat256.copy64(((SecT233FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat256.create64(), y = Nat256.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT233FieldElement(x), new SecT233FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java
index f6553ba..e148b8a 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.util.Arrays;
 
-public class SecT239FieldElement extends ECFieldElement
+public class SecT239FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT239FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT239Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat256.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java
index 074ff71..45b0a5e 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat256;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
@@ -105,4 +107,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 4;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat256.copy64(((SecT239FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat256.copy64(((SecT239FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat256.create64(), y = Nat256.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT239FieldElement(x), new SecT239FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java
index ee0024b..91685fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat320;
 import org.bouncycastle.util.Arrays;
 
-public class SecT283FieldElement extends ECFieldElement
+public class SecT283FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -160,6 +160,11 @@
         return new SecT283FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT283Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat320.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java
index 1c48a6f..84c3849 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java
@@ -5,9 +5,11 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.math.raw.Nat320;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT283K1Curve extends AbstractF2m
@@ -105,4 +107,49 @@
     {
         return 12;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 5;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat320.copy64(((SecT283FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat320.copy64(((SecT283FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat320.create64(), y = Nat320.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT283FieldElement(x), new SecT283FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java
index 2909fca..1562625 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat320;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 12;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 5;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat320.copy64(((SecT283FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat320.copy64(((SecT283FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat320.create64(), y = Nat320.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT283FieldElement(x), new SecT283FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java
index eaf6fc9..6dee877 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.math.raw.Nat448;
 import org.bouncycastle.util.Arrays;
 
-public class SecT409FieldElement extends ECFieldElement
+public class SecT409FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -160,6 +160,11 @@
         return new SecT409FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT409Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat448.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java
index fb66cd8..7d30467 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java
@@ -5,9 +5,11 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.math.raw.Nat448;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT409K1Curve extends AbstractF2m
@@ -105,4 +107,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 7;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat448.copy64(((SecT409FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat448.copy64(((SecT409FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat448.create64(), y = Nat448.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT409FieldElement(x), new SecT409FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java
index 55a4b7d..f96c179 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat448;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -98,4 +100,49 @@
     {
         return 0;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 7;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat448.copy64(((SecT409FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat448.copy64(((SecT409FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat448.create64(), y = Nat448.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT409FieldElement(x), new SecT409FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java
index 31f5f5a..484ad8c 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java
@@ -6,7 +6,7 @@
 import org.bouncycastle.math.raw.Nat576;
 import org.bouncycastle.util.Arrays;
 
-public class SecT571FieldElement extends ECFieldElement
+public class SecT571FieldElement extends ECFieldElement.AbstractF2m
 {
     protected long[] x;
 
@@ -159,6 +159,11 @@
         return new SecT571FieldElement(z);
     }
 
+    public int trace()
+    {
+        return SecT571Field.trace(x);
+    }
+
     public ECFieldElement invert()
     {
         long[] z = Nat576.create64();
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java
index 1fd2ce3..935fc39 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java
@@ -5,9 +5,11 @@
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECMultiplier;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.math.ec.WTauNafMultiplier;
+import org.bouncycastle.math.raw.Nat576;
 import org.bouncycastle.util.encoders.Hex;
 
 public class SecT571K1Curve extends AbstractF2m
@@ -105,4 +107,49 @@
     {
         return 10;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 9;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat576.copy64(((SecT571FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat576.copy64(((SecT571FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat576.create64(), y = Nat576.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT571FieldElement(x), new SecT571FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java
index df39e50..00dd63b 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java
@@ -4,7 +4,9 @@
 
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECCurve.AbstractF2m;
+import org.bouncycastle.math.raw.Nat576;
 import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECLookupTable;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -102,4 +104,49 @@
     {
         return 10;
     }
+
+    public ECLookupTable createCacheSafeLookupTable(ECPoint[] points, int off, final int len)
+    {
+        final int FE_LONGS = 9;
+
+        final long[] table = new long[len * FE_LONGS * 2];
+        {
+            int pos = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                Nat576.copy64(((SecT571FieldElement)p.getRawXCoord()).x, 0, table, pos); pos += FE_LONGS;
+                Nat576.copy64(((SecT571FieldElement)p.getRawYCoord()).x, 0, table, pos); pos += FE_LONGS;
+            }
+        }
+
+        return new ECLookupTable()
+        {
+            public int getSize()
+            {
+                return len;
+            }
+
+            public ECPoint lookup(int index)
+            {
+                long[] x = Nat576.create64(), y = Nat576.create64();
+                int pos = 0;
+
+                for (int i = 0; i < len; ++i)
+                {
+                    long MASK = ((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= table[pos + j] & MASK;
+                        y[j] ^= table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                return createRawPoint(new SecT571FieldElement(x), new SecT571FieldElement(y), false);
+            }
+        };
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java
new file mode 100644
index 0000000..fb69601
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.math.ec.rfc7748;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+import org.bouncycastle.util.Arrays;
+
+public abstract class X25519
+{
+    public static class Friend
+    {
+        private static final Friend INSTANCE = new Friend();
+        private Friend() {}
+    }
+
+    public static final int POINT_SIZE = 32;
+    public static final int SCALAR_SIZE = 32;
+
+    private static final int C_A = 486662;
+    private static final int C_A24 = (C_A + 2)/4;
+
+//    private static final int[] SQRT_NEG_486664 = { 0x03457E06, 0x03812ABF, 0x01A82CC6, 0x028A5BE8, 0x018B43A7,
+//        0x03FC4F7E, 0x02C23700, 0x006BBD27, 0x03A30500, 0x001E4DDB };
+
+    public static boolean calculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+    {
+        scalarMult(k, kOff, u, uOff, r, rOff);
+        return !Arrays.areAllZeroes(r, rOff, POINT_SIZE);
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |=  bs[++off]         << 24;
+        return n;
+    }
+
+    private static void decodeScalar(byte[] k, int kOff, int[] n)
+    {
+        for (int i = 0; i < 8; ++i)
+        {
+            n[i] = decode32(k, kOff + i * 4);
+        }
+
+        n[0] &= 0xFFFFFFF8;
+        n[7] &= 0x7FFFFFFF;
+        n[7] |= 0x40000000;
+    }
+
+    public static void generatePrivateKey(SecureRandom random, byte[] k)
+    {
+        random.nextBytes(k);
+
+        k[0] &= 0xF8;
+        k[SCALAR_SIZE - 1] &= 0x7F;
+        k[SCALAR_SIZE - 1] |= 0x40;
+    }
+
+    public static void generatePublicKey(byte[] k, int kOff, byte[] r, int rOff)
+    {
+        scalarMultBase(k, kOff, r, rOff);
+    }
+
+    private static void pointDouble(int[] x, int[] z)
+    {
+        int[] A = X25519Field.create();
+        int[] B = X25519Field.create();
+
+        X25519Field.apm(x, z, A, B);
+        X25519Field.sqr(A, A);
+        X25519Field.sqr(B, B);
+        X25519Field.mul(A, B, x);
+        X25519Field.sub(A, B, A);
+        X25519Field.mul(A, C_A24, z);
+        X25519Field.add(z, B, z);
+        X25519Field.mul(z, A, z);
+    }
+
+    public static void precompute()
+    {
+        Ed25519.precompute();
+    }
+
+    public static void scalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+    {
+        int[] n = new int[8];   decodeScalar(k, kOff, n);
+
+        int[] x1 = X25519Field.create();        X25519Field.decode(u, uOff, x1);
+        int[] x2 = X25519Field.create();        X25519Field.copy(x1, 0, x2, 0);
+        int[] z2 = X25519Field.create();        z2[0] = 1;
+        int[] x3 = X25519Field.create();        x3[0] = 1;
+        int[] z3 = X25519Field.create();
+
+        int[] t1 = X25519Field.create();
+        int[] t2 = X25519Field.create();
+
+//        assert n[7] >>> 30 == 1;
+
+        int bit = 254, swap = 1;
+        do
+        {
+            X25519Field.apm(x3, z3, t1, x3);
+            X25519Field.apm(x2, z2, z3, x2);
+            X25519Field.mul(t1, x2, t1);
+            X25519Field.mul(x3, z3, x3);
+            X25519Field.sqr(z3, z3);
+            X25519Field.sqr(x2, x2);
+
+            X25519Field.sub(z3, x2, t2);
+            X25519Field.mul(t2, C_A24, z2);
+            X25519Field.add(z2, x2, z2);
+            X25519Field.mul(z2, t2, z2);
+            X25519Field.mul(x2, z3, x2);
+
+            X25519Field.apm(t1, x3, x3, z3);
+            X25519Field.sqr(x3, x3);
+            X25519Field.sqr(z3, z3);
+            X25519Field.mul(z3, x1, z3);
+
+            --bit;
+
+            int word = bit >>> 5, shift = bit & 0x1F;
+            int kt = (n[word] >>> shift) & 1;
+            swap ^= kt;
+            X25519Field.cswap(swap, x2, x3);
+            X25519Field.cswap(swap, z2, z3);
+            swap = kt;
+        }
+        while (bit >= 3);
+
+//        assert swap == 0;
+
+        for (int i = 0; i < 3; ++i)
+        {
+            pointDouble(x2, z2);
+        }
+
+        X25519Field.inv(z2, z2);
+        X25519Field.mul(x2, z2, x2);
+
+        X25519Field.normalize(x2);
+        X25519Field.encode(x2, r, rOff);
+    }
+
+    public static void scalarMultBase(byte[] k, int kOff, byte[] r, int rOff)
+    {
+        int[] y = X25519Field.create();
+        int[] z = X25519Field.create();
+
+        Ed25519.scalarMultBaseYZ(Friend.INSTANCE, k, kOff, y, z);
+
+        X25519Field.apm(z, y, y, z);
+
+        X25519Field.inv(z, z);
+        X25519Field.mul(y, z, y);
+
+        X25519Field.normalize(y);
+        X25519Field.encode(y, r, rOff);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519Field.java
new file mode 100644
index 0000000..69f2bd3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519Field.java
@@ -0,0 +1,634 @@
+package org.bouncycastle.math.ec.rfc7748;
+
+public abstract class X25519Field
+{
+    public static final int SIZE = 10;
+
+    private static final int M24 = 0x00FFFFFF;
+    private static final int M25 = 0x01FFFFFF;
+    private static final int M26 = 0x03FFFFFF;
+
+    private static final int[] ROOT_NEG_ONE = new int[]{ 0x020EA0B0, 0x0386C9D2, 0x00478C4E, 0x0035697F, 0x005E8630,
+        0x01FBD7A7, 0x0340264F, 0x01F0B2B4, 0x00027E0E, 0x00570649 };
+
+    private X25519Field() {}
+
+    public static void add(int[] x, int[] y, int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = x[i] + y[i];
+        }
+    }
+
+    public static void addOne(int[] z)
+    {
+        z[0] += 1;
+    }
+
+    public static void addOne(int[] z, int zOff)
+    {
+        z[zOff] += 1;
+    }
+
+    public static void apm(int[] x, int[] y, int[] zp, int[] zm)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            int xi = x[i], yi = y[i];
+            zp[i] = xi + yi;
+            zm[i] = xi - yi;
+        }
+    }
+
+    public static void carry(int[] z)
+    {
+        int z0 = z[0], z1 = z[1], z2 = z[2], z3 = z[3], z4 = z[4];
+        int z5 = z[5], z6 = z[6], z7 = z[7], z8 = z[8], z9 = z[9];
+
+        z3 += (z2 >> 25); z2 &= M25;
+        z5 += (z4 >> 25); z4 &= M25;
+        z8 += (z7 >> 25); z7 &= M25;
+//        z0 += (z9 >> 24) * 19; z9 &= M24;
+        z0 += (z9 >> 25) * 38; z9 &= M25;
+
+        z1 += (z0 >> 26); z0 &= M26;
+        z6 += (z5 >> 26); z5 &= M26;
+
+        z2 += (z1 >> 26); z1 &= M26;
+        z4 += (z3 >> 26); z3 &= M26;
+        z7 += (z6 >> 26); z6 &= M26;
+        z9 += (z8 >> 26); z8 &= M26;
+
+        z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3; z[4] = z4;
+        z[5] = z5; z[6] = z6; z[7] = z7; z[8] = z8; z[9] = z9;
+    }
+
+    public static void cnegate(int negate, int[] z)
+    {
+//      assert negate >>> 1 == 0;
+
+        int mask = 0 - negate;
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = (z[i] ^ mask) - mask;
+        }
+    }
+
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[zOff + i] = x[xOff + i];
+        }
+    }
+
+    public static int[] create()
+    {
+        return new int[SIZE];
+    }
+
+    public static int[] createTable(int n)
+    {
+        return new int[SIZE * n];
+    }
+
+    public static void cswap(int swap, int[] a, int[] b)
+    {
+//        assert swap >>> 1 == 0;
+//        assert a != b;
+
+        int mask = 0 - swap;
+        for (int i = 0; i < SIZE; ++i)
+        {
+            int ai = a[i], bi = b[i];
+            int dummy = mask & (ai ^ bi);
+            a[i] = ai ^ dummy; 
+            b[i] = bi ^ dummy; 
+        }
+    }
+
+    public static void decode(byte[] x, int xOff, int[] z)
+    {
+        decode128(x, xOff, z, 0);
+        decode128(x, xOff + 16, z, 5);
+        z[9] &= M24;
+    }
+
+    private static void decode128(byte[] bs, int off, int[] z, int zOff)
+    {
+        int t0 = decode32(bs, off + 0);
+        int t1 = decode32(bs, off + 4);
+        int t2 = decode32(bs, off + 8);
+        int t3 = decode32(bs, off + 12);
+
+        z[zOff + 0] = t0 & M26;
+        z[zOff + 1] = ((t1 <<  6) | (t0 >>> 26)) & M26;
+        z[zOff + 2] = ((t2 << 12) | (t1 >>> 20)) & M25;
+        z[zOff + 3] = ((t3 << 19) | (t2 >>> 13)) & M26;
+        z[zOff + 4] = t3 >>> 7;
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |=  bs[++off]         << 24;
+        return n;
+    }
+
+    public static void encode(int[] x, byte[] z, int zOff)
+    {
+        encode128(x, 0, z, zOff);
+        encode128(x, 5, z, zOff + 16);
+    }
+
+    private static void encode128(int[] x, int xOff, byte[] bs, int off)
+    {
+        int x0 = x[xOff + 0], x1 = x[xOff + 1], x2 = x[xOff + 2], x3 = x[xOff + 3], x4 = x[xOff + 4];
+
+        int t0 =  x0         | (x1 << 26);  encode32(t0, bs, off + 0);
+        int t1 = (x1 >>>  6) | (x2 << 20);  encode32(t1, bs, off + 4);
+        int t2 = (x2 >>> 12) | (x3 << 13);  encode32(t2, bs, off + 8);
+        int t3 = (x3 >>> 19) | (x4 <<  7);  encode32(t3, bs, off + 12);
+    }
+
+    private static void encode32(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+        bs[++off] = (byte)(n >>> 24);
+    }
+
+    public static void inv(int[] x, int[] z)
+    {
+        // z = x^(p-2) = x^7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+        // (250 1s) (1 0s) (1 1s) (1 0s) (2 1s)
+        // Addition chain: [1] [2] 3 5 10 15 25 50 75 125 [250]
+
+        int[] x2 = create();
+        int[] t = create();
+        powPm5d8(x, x2, t);
+        sqr(t, 3, t);
+        mul(t, x2, z);
+    }
+
+    public static boolean isZeroVar(int[] x)
+    {
+        int d = 0;
+        for (int i = 0; i < SIZE; ++i)
+        {
+            d |= x[i];
+        }
+        return d == 0;
+    }
+
+    public static void mul(int[] x, int y, int[] z)
+    {
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4];
+        int x5 = x[5], x6 = x[6], x7 = x[7], x8 = x[8], x9 = x[9];
+        long c0, c1, c2, c3;
+
+        c0  = (long)x2 * y; x2 = (int)c0 & M25; c0 >>= 25;
+        c1  = (long)x4 * y; x4 = (int)c1 & M25; c1 >>= 25;
+        c2  = (long)x7 * y; x7 = (int)c2 & M25; c2 >>= 25;
+//        c3  = (long)x9 * y; x9 = (int)c3 & M24; c3 >>= 24;
+//        c3 *= 19;
+        c3  = (long)x9 * y; x9 = (int)c3 & M25; c3 >>= 25;
+        c3 *= 38;
+
+        c3 += (long)x0 * y; z[0] = (int)c3 & M26; c3 >>= 26;
+        c1 += (long)x5 * y; z[5] = (int)c1 & M26; c1 >>= 26;
+
+        c3 += (long)x1 * y; z[1] = (int)c3 & M26; c3 >>= 26;
+        c0 += (long)x3 * y; z[3] = (int)c0 & M26; c0 >>= 26;
+        c1 += (long)x6 * y; z[6] = (int)c1 & M26; c1 >>= 26;
+        c2 += (long)x8 * y; z[8] = (int)c2 & M26; c2 >>= 26;
+
+        z[2] = x2 + (int)c3;
+        z[4] = x4 + (int)c0;
+        z[7] = x7 + (int)c1;
+        z[9] = x9 + (int)c2;
+    }
+
+    public static void mul(int[] x, int[] y, int[] z)
+    {
+        int x0 = x[0], y0 = y[0];
+        int x1 = x[1], y1 = y[1];
+        int x2 = x[2], y2 = y[2];
+        int x3 = x[3], y3 = y[3];
+        int x4 = x[4], y4 = y[4];
+
+        int u0 = x[5], v0 = y[5];
+        int u1 = x[6], v1 = y[6];
+        int u2 = x[7], v2 = y[7];
+        int u3 = x[8], v3 = y[8];
+        int u4 = x[9], v4 = y[9];
+
+        long a0  = (long)x0 * y0;
+        long a1  = (long)x0 * y1
+                 + (long)x1 * y0;
+        long a2  = (long)x0 * y2
+                 + (long)x1 * y1
+                 + (long)x2 * y0;
+        long a3  = (long)x1 * y2
+                 + (long)x2 * y1;
+        a3     <<= 1;
+        a3      += (long)x0 * y3
+                 + (long)x3 * y0;
+        long a4  = (long)x2 * y2;
+        a4     <<= 1;
+        a4      += (long)x0 * y4
+                 + (long)x1 * y3
+                 + (long)x3 * y1
+                 + (long)x4 * y0;
+        long a5  = (long)x1 * y4
+                 + (long)x2 * y3
+                 + (long)x3 * y2
+                 + (long)x4 * y1;
+        a5     <<= 1;
+        long a6  = (long)x2 * y4
+                 + (long)x4 * y2;
+        a6     <<= 1;
+        a6      += (long)x3 * y3;
+        long a7  = (long)x3 * y4
+                 + (long)x4 * y3;
+        long a8  = (long)x4 * y4;
+        a8     <<= 1;
+
+        long b0  = (long)u0 * v0;
+        long b1  = (long)u0 * v1
+                 + (long)u1 * v0;
+        long b2  = (long)u0 * v2
+                 + (long)u1 * v1
+                 + (long)u2 * v0;
+        long b3  = (long)u1 * v2
+                 + (long)u2 * v1;
+        b3     <<= 1;
+        b3      += (long)u0 * v3
+                 + (long)u3 * v0;
+        long b4  = (long)u2 * v2;
+        b4     <<= 1;
+        b4      += (long)u0 * v4
+                 + (long)u1 * v3
+                 + (long)u3 * v1
+                 + (long)u4 * v0;
+        long b5  = (long)u1 * v4
+                 + (long)u2 * v3
+                 + (long)u3 * v2
+                 + (long)u4 * v1;
+//        b5     <<= 1;
+        long b6  = (long)u2 * v4
+                 + (long)u4 * v2;
+        b6     <<= 1;
+        b6      += (long)u3 * v3;
+        long b7  = (long)u3 * v4
+                 + (long)u4 * v3;
+        long b8  = (long)u4 * v4;
+//        b8     <<= 1;
+
+        a0 -= b5 * 76;
+        a1 -= b6 * 38;
+        a2 -= b7 * 38;
+        a3 -= b8 * 76;
+
+        a5 -= b0;
+        a6 -= b1;
+        a7 -= b2;
+        a8 -= b3;
+//        long a9 = -b4;
+
+        x0 += u0; y0 += v0;
+        x1 += u1; y1 += v1;
+        x2 += u2; y2 += v2;
+        x3 += u3; y3 += v3;
+        x4 += u4; y4 += v4;
+
+        long c0  = (long)x0 * y0;
+        long c1  = (long)x0 * y1
+                 + (long)x1 * y0;
+        long c2  = (long)x0 * y2
+                 + (long)x1 * y1
+                 + (long)x2 * y0;
+        long c3  = (long)x1 * y2
+                 + (long)x2 * y1;
+        c3     <<= 1;
+        c3      += (long)x0 * y3
+                 + (long)x3 * y0;
+        long c4  = (long)x2 * y2;
+        c4     <<= 1;
+        c4      += (long)x0 * y4
+                 + (long)x1 * y3
+                 + (long)x3 * y1
+                 + (long)x4 * y0;
+        long c5  = (long)x1 * y4
+                 + (long)x2 * y3
+                 + (long)x3 * y2
+                 + (long)x4 * y1;
+        c5     <<= 1;
+        long c6  = (long)x2 * y4
+                 + (long)x4 * y2;
+        c6     <<= 1;
+        c6      += (long)x3 * y3;
+        long c7  = (long)x3 * y4
+                 + (long)x4 * y3;
+        long c8  = (long)x4 * y4;
+        c8     <<= 1;
+
+        int z8, z9;
+        long t;
+
+        t        = a8 + (c3 - a3);
+        z8       = (int)t & M26; t >>= 26;
+//        t       += a9 + (c4 - a4);
+        t       +=      (c4 - a4) - b4;
+//        z9       = (int)t & M24; t >>= 24;
+//        t        = a0 + (t + ((c5 - a5) << 1)) * 19;
+        z9       = (int)t & M25; t >>= 25;
+        t        = a0 + (t + c5 - a5) * 38;
+        z[0]     = (int)t & M26; t >>= 26;
+        t       += a1 + (c6 - a6) * 38;
+        z[1]     = (int)t & M26; t >>= 26;
+        t       += a2 + (c7 - a7) * 38;
+        z[2]     = (int)t & M25; t >>= 25;
+        t       += a3 + (c8 - a8) * 38;
+        z[3]     = (int)t & M26; t >>= 26;
+//        t       += a4 - a9 * 38;
+        t       += a4 + b4 * 38;
+        z[4]     = (int)t & M25; t >>= 25;
+        t       += a5 + (c0 - a0);
+        z[5]     = (int)t & M26; t >>= 26;
+        t       += a6 + (c1 - a1);
+        z[6]     = (int)t & M26; t >>= 26;
+        t       += a7 + (c2 - a2);
+        z[7]     = (int)t & M25; t >>= 25;
+        t       += z8;
+        z[8]     = (int)t & M26; t >>= 26;
+        z[9]     = z9 + (int)t;
+    }
+
+    public static void negate(int[] x, int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = -x[i];
+        }
+    }
+
+    public static void normalize(int[] z)
+    {
+        int x = ((z[9] >>> 23) & 1);
+        reduce(z, x);
+        reduce(z, -x);
+//        assert z[9] >>> 24 == 0;
+    }
+
+    public static void one(int[] z)
+    {
+        z[0] = 1;
+        for (int i = 1; i < SIZE; ++i)
+        {
+            z[i] = 0;
+        }
+    }
+
+    private static void powPm5d8(int[] x, int[] rx2, int[] rz)
+    {
+        // z = x^((p-5)/8) = x^FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+        // (250 1s) (1 0s) (1 1s)
+        // Addition chain: [1] 2 3 5 10 15 25 50 75 125 [250]
+
+        int[] x2 = rx2;         sqr(x, x2);             mul(x, x2, x2);
+        int[] x3 = create();    sqr(x2, x3);            mul(x, x3, x3);
+        int[] x5 = x3;          sqr(x3, 2, x5);         mul(x2, x5, x5);
+        int[] x10 = create();   sqr(x5, 5, x10);        mul(x5, x10, x10);
+        int[] x15 = create();   sqr(x10, 5, x15);       mul(x5, x15, x15);
+        int[] x25 = x5;         sqr(x15, 10, x25);      mul(x10, x25, x25);
+        int[] x50 = x10;        sqr(x25, 25, x50);      mul(x25, x50, x50);
+        int[] x75 = x15;        sqr(x50, 25, x75);      mul(x25, x75, x75);
+        int[] x125 = x25;       sqr(x75, 50, x125);     mul(x50, x125, x125);
+        int[] x250 = x50;       sqr(x125, 125, x250);   mul(x125, x250, x250);
+
+        int[] t = x125;
+        sqr(x250, 2, t);
+        mul(t, x, rz);
+    }
+
+    private static void reduce(int[] z, int c)
+    {
+        int z9 = z[9], t = z9;
+                   z9   = t & M24; t >>= 24;
+        t += c;
+        t *= 19;
+        t += z[0]; z[0] = t & M26; t >>= 26;
+        t += z[1]; z[1] = t & M26; t >>= 26;
+        t += z[2]; z[2] = t & M25; t >>= 25;
+        t += z[3]; z[3] = t & M26; t >>= 26;
+        t += z[4]; z[4] = t & M25; t >>= 25;
+        t += z[5]; z[5] = t & M26; t >>= 26;
+        t += z[6]; z[6] = t & M26; t >>= 26;
+        t += z[7]; z[7] = t & M25; t >>= 25;
+        t += z[8]; z[8] = t & M26; t >>= 26;
+        t += z9;   z[9] = t;
+    }
+
+    public static void sqr(int[] x, int[] z)
+    {
+        int x0 = x[0];
+        int x1 = x[1];
+        int x2 = x[2];
+        int x3 = x[3];
+        int x4 = x[4];
+
+        int u0 = x[5];
+        int u1 = x[6];
+        int u2 = x[7];
+        int u3 = x[8];
+        int u4 = x[9];
+
+        int x1_2 = x1 * 2;
+        int x2_2 = x2 * 2;
+        int x3_2 = x3 * 2;
+        int x4_2 = x4 * 2;
+
+        long a0  = (long)x0 * x0;
+        long a1  = (long)x0 * x1_2;
+        long a2  = (long)x0 * x2_2
+                 + (long)x1 * x1;
+        long a3  = (long)x1_2 * x2_2
+                 + (long)x0 * x3_2;
+        long a4  = (long)x2 * x2_2
+                 + (long)x0 * x4_2
+                 + (long)x1 * x3_2;
+        long a5  = (long)x1_2 * x4_2
+                 + (long)x2_2 * x3_2;
+        long a6  = (long)x2_2 * x4_2
+                 + (long)x3 * x3;
+        long a7  = (long)x3 * x4_2;
+        long a8  = (long)x4 * x4_2;
+
+        int u1_2 = u1 * 2;
+        int u2_2 = u2 * 2;
+        int u3_2 = u3 * 2;
+        int u4_2 = u4 * 2;
+        
+        long b0  = (long)u0 * u0;
+        long b1  = (long)u0 * u1_2;
+        long b2  = (long)u0 * u2_2
+                 + (long)u1 * u1;
+        long b3  = (long)u1_2 * u2_2
+                 + (long)u0 * u3_2;
+        long b4  = (long)u2 * u2_2
+                 + (long)u0 * u4_2
+                 + (long)u1 * u3_2;
+        long b5  = (long)u1_2 * u4_2
+                 + (long)u2_2 * u3_2;
+        long b6  = (long)u2_2 * u4_2
+                 + (long)u3 * u3;
+        long b7  = (long)u3 * u4_2;
+        long b8  = (long)u4 * u4_2;
+
+        a0 -= b5 * 38;
+        a1 -= b6 * 38;
+        a2 -= b7 * 38;
+        a3 -= b8 * 38;
+
+        a5 -= b0;
+        a6 -= b1;
+        a7 -= b2;
+        a8 -= b3;
+//        long a9 = -b4;
+
+        x0 += u0;
+        x1 += u1;
+        x2 += u2;
+        x3 += u3;
+        x4 += u4;
+
+        x1_2 = x1 * 2;
+        x2_2 = x2 * 2;
+        x3_2 = x3 * 2;
+        x4_2 = x4 * 2;
+
+        long c0  = (long)x0 * x0;
+        long c1  = (long)x0 * x1_2;
+        long c2  = (long)x0 * x2_2
+                 + (long)x1 * x1;
+        long c3  = (long)x1_2 * x2_2
+                 + (long)x0 * x3_2;
+        long c4  = (long)x2 * x2_2
+                 + (long)x0 * x4_2
+                 + (long)x1 * x3_2;
+        long c5  = (long)x1_2 * x4_2
+                 + (long)x2_2 * x3_2;
+        long c6  = (long)x2_2 * x4_2
+                 + (long)x3 * x3;
+        long c7  = (long)x3 * x4_2;
+        long c8  = (long)x4 * x4_2;
+
+        int z8, z9;
+        long t;
+
+        t        = a8 + (c3 - a3);
+        z8       = (int)t & M26; t >>= 26;
+//        t       += a9 + (c4 - a4);
+        t       +=      (c4 - a4) - b4;
+//        z9       = (int)t & M24; t >>= 24;
+//        t        = a0 + (t + ((c5 - a5) << 1)) * 19;
+        z9       = (int)t & M25; t >>= 25;
+        t        = a0 + (t + c5 - a5) * 38;
+        z[0]     = (int)t & M26; t >>= 26;
+        t       += a1 + (c6 - a6) * 38;
+        z[1]     = (int)t & M26; t >>= 26;
+        t       += a2 + (c7 - a7) * 38;
+        z[2]     = (int)t & M25; t >>= 25;
+        t       += a3 + (c8 - a8) * 38;
+        z[3]     = (int)t & M26; t >>= 26;
+//        t       += a4 - a9 * 38;
+        t       += a4 + b4 * 38;
+        z[4]     = (int)t & M25; t >>= 25;
+        t       += a5 + (c0 - a0);
+        z[5]     = (int)t & M26; t >>= 26;
+        t       += a6 + (c1 - a1);
+        z[6]     = (int)t & M26; t >>= 26;
+        t       += a7 + (c2 - a2);
+        z[7]     = (int)t & M25; t >>= 25;
+        t       += z8;
+        z[8]     = (int)t & M26; t >>= 26;
+        z[9]     = z9 + (int)t;
+    }
+
+    public static void sqr(int[] x, int n, int[] z)
+    {
+//        assert n > 0;
+
+        sqr(x, z);
+
+        while (--n > 0)
+        {
+            sqr(z, z);
+        }
+    }
+
+    public static boolean sqrtRatioVar(int[] u, int[] v, int[] z)
+    {
+        int[] uv3 = create();
+        int[] uv7 = create();
+
+        mul(u, v, uv3);
+        sqr(v, uv7);
+        mul(uv3, uv7, uv3);
+        sqr(uv7, uv7);
+        mul(uv7, uv3, uv7);
+
+        int[] t = create();
+        int[] x = create();
+        powPm5d8(uv7, t, x);
+        mul(x, uv3, x);
+
+        int[] vx2 = create();
+        sqr(x, vx2);
+        mul(vx2, v, vx2);
+
+        sub(vx2, u, t);
+        normalize(t);
+        if (isZeroVar(t))
+        {
+            copy(x, 0, z, 0);
+            return true;
+        }
+
+        add(vx2, u, t);
+        normalize(t);
+        if (isZeroVar(t))
+        {
+            mul(x, ROOT_NEG_ONE, z);
+            return true;
+        }
+
+        return false;
+    }
+
+    public static void sub(int[] x, int[] y, int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = x[i] - y[i];
+        }
+    }
+
+    public static void subOne(int[] z)
+    {
+        z[0] -= 1;
+    }
+
+    public static void zero(int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = 0;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java
new file mode 100644
index 0000000..dda38b8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java
@@ -0,0 +1,169 @@
+package org.bouncycastle.math.ec.rfc7748;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.math.ec.rfc8032.Ed448;
+import org.bouncycastle.util.Arrays;
+
+public abstract class X448
+{
+    public static class Friend
+    {
+        private static final Friend INSTANCE = new Friend();
+        private Friend() {}
+    }
+
+    public static final int POINT_SIZE = 56;
+    public static final int SCALAR_SIZE = 56;
+
+    private static final int C_A = 156326;
+    private static final int C_A24 = (C_A + 2)/4;
+
+//    private static final int[] SQRT_156324 = { 0x0551B193, 0x07A21E17, 0x0E635AD3, 0x00812ABB, 0x025B3F99, 0x01605224,
+//        0x0AF8CB32, 0x0D2E7D68, 0x06BA50FD, 0x08E55693, 0x0CB08EB4, 0x02ABEBC1, 0x051BA0BB, 0x02F8812E, 0x0829B611,
+//        0x0BA4D3A0 };
+
+    public static boolean calculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+    {
+        scalarMult(k, kOff, u, uOff, r, rOff);
+        return !Arrays.areAllZeroes(r, rOff, POINT_SIZE);
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[  off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |= bs[++off] << 24;
+        return n;
+    }
+
+    private static void decodeScalar(byte[] k, int kOff, int[] n)
+    {
+        for (int i = 0; i < 14; ++i)
+        {
+            n[i] = decode32(k, kOff + i * 4);
+        }
+
+        n[ 0] &= 0xFFFFFFFC;
+        n[13] |= 0x80000000;
+    }
+
+    public static void generatePrivateKey(SecureRandom random, byte[] k)
+    {
+        random.nextBytes(k);
+
+        k[0] &= 0xFC;
+        k[SCALAR_SIZE - 1] |= 0x80;
+    }
+
+    public static void generatePublicKey(byte[] k, int kOff, byte[] r, int rOff)
+    {
+        scalarMultBase(k, kOff, r, rOff);
+    }
+
+    private static void pointDouble(int[] x, int[] z)
+    {
+        int[] A = X448Field.create();
+        int[] B = X448Field.create();
+
+//        X448Field.apm(x, z, A, B);
+        X448Field.add(x, z, A);
+        X448Field.sub(x, z, B);
+        X448Field.sqr(A, A);
+        X448Field.sqr(B, B);
+        X448Field.mul(A, B, x);
+        X448Field.sub(A, B, A);
+        X448Field.mul(A, C_A24, z);
+        X448Field.add(z, B, z);
+        X448Field.mul(z, A, z);
+    }
+
+    public static void precompute()
+    {
+        Ed448.precompute();
+    }
+
+    public static void scalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+    {
+        int[] n = new int[14];  decodeScalar(k, kOff, n);
+
+        int[] x1 = X448Field.create();        X448Field.decode(u, uOff, x1);
+        int[] x2 = X448Field.create();        X448Field.copy(x1, 0, x2, 0);
+        int[] z2 = X448Field.create();        z2[0] = 1;
+        int[] x3 = X448Field.create();        x3[0] = 1;
+        int[] z3 = X448Field.create();
+
+        int[] t1 = X448Field.create();
+        int[] t2 = X448Field.create();
+
+//        assert n[13] >>> 31 == 1;
+
+        int bit = 447, swap = 1;
+        do
+        {
+//            X448Field.apm(x3, z3, t1, x3);
+            X448Field.add(x3, z3, t1);
+            X448Field.sub(x3, z3, x3);
+//            X448Field.apm(x2, z2, z3, x2);
+            X448Field.add(x2, z2, z3);
+            X448Field.sub(x2, z2, x2);
+
+            X448Field.mul(t1, x2, t1);
+            X448Field.mul(x3, z3, x3);
+            X448Field.sqr(z3, z3);
+            X448Field.sqr(x2, x2);
+
+            X448Field.sub(z3, x2, t2);
+            X448Field.mul(t2, C_A24, z2);
+            X448Field.add(z2, x2, z2);
+            X448Field.mul(z2, t2, z2);
+            X448Field.mul(x2, z3, x2);
+
+//            X448Field.apm(t1, x3, x3, z3);
+            X448Field.sub(t1, x3, z3);
+            X448Field.add(t1, x3, x3);
+            X448Field.sqr(x3, x3);
+            X448Field.sqr(z3, z3);
+            X448Field.mul(z3, x1, z3);
+
+            --bit;
+
+            int word = bit >>> 5, shift = bit & 0x1F;
+            int kt = (n[word] >>> shift) & 1;
+            swap ^= kt;
+            X448Field.cswap(swap, x2, x3);
+            X448Field.cswap(swap, z2, z3);
+            swap = kt;
+        }
+        while (bit >= 2);
+
+//        assert swap == 0;
+
+        for (int i = 0; i < 2; ++i)
+        {
+            pointDouble(x2, z2);
+        }
+
+        X448Field.inv(z2, z2);
+        X448Field.mul(x2, z2, x2);
+
+        X448Field.normalize(x2);
+        X448Field.encode(x2, r, rOff);
+    }
+
+    public static void scalarMultBase(byte[] k, int kOff, byte[] r, int rOff)
+    {
+        int[] x = X448Field.create();
+        int[] y = X448Field.create();
+
+        Ed448.scalarMultBaseXY(Friend.INSTANCE, k, kOff, x, y);
+
+        X448Field.inv(x, x);
+        X448Field.mul(x, y, x);
+        X448Field.sqr(x, x);
+
+        X448Field.normalize(x);
+        X448Field.encode(x, r, rOff);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448Field.java
new file mode 100644
index 0000000..8d0d14a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc7748/X448Field.java
@@ -0,0 +1,1008 @@
+package org.bouncycastle.math.ec.rfc7748;
+
+import org.bouncycastle.math.raw.Nat;
+
+public abstract class X448Field
+{
+    public static final int SIZE = 16;
+
+    private static final int M28 = 0x0FFFFFFF;
+
+    private X448Field() {}
+
+    public static void add(int[] x, int[] y, int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = x[i] + y[i];
+        }
+    }
+
+    public static void addOne(int[] z)
+    {
+        z[0] += 1;
+    }
+
+    public static void addOne(int[] z, int zOff)
+    {
+        z[zOff] += 1;
+    }
+
+//    public static void apm(int[] x, int[] y, int[] zp, int[] zm)
+//    {
+//        for (int i = 0; i < SIZE; ++i)
+//        {
+//            int xi = x[i], yi = y[i];
+//            zp[i] = xi + yi;
+//            zm[i] = xi - yi;
+//        }
+//    }
+
+    public static void carry(int[] z)
+    {
+        int z0 = z[0], z1 = z[1], z2 = z[2], z3 = z[3], z4 = z[4], z5 = z[5], z6 = z[6], z7 = z[7];
+        int z8 = z[8], z9 = z[9], z10 = z[10], z11 = z[11], z12 = z[12], z13 = z[13], z14 = z[14], z15 = z[15];
+
+        z2   += (z1 >>> 28); z1 &= M28;
+        z6   += (z5 >>> 28); z5 &= M28;
+        z10  += (z9 >>> 28); z9 &= M28;
+        z14  += (z13 >>> 28); z13 &= M28;
+
+        z3   += (z2 >>> 28); z2 &= M28;
+        z7   += (z6 >>> 28); z6 &= M28;
+        z11  += (z10 >>> 28); z10 &= M28;
+        z15  += (z14 >>> 28); z14 &= M28;
+
+        int t = z15 >>> 28; z15 &= M28;
+        z0   += t;
+        z8   += t;
+
+        z4   += (z3 >>> 28); z3 &= M28;
+        z8   += (z7 >>> 28); z7 &= M28;
+        z12  += (z11 >>> 28); z11 &= M28;
+
+        z1   += (z0 >>> 28); z0 &= M28;
+        z5   += (z4 >>> 28); z4 &= M28;
+        z9   += (z8 >>> 28); z8 &= M28;
+        z13  += (z12 >>> 28); z12 &= M28;
+        
+        z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3; z[4] = z4; z[5] = z5; z[6] = z6; z[7] = z7;
+        z[8] = z8; z[9] = z9; z[10] = z10; z[11] = z11; z[12] = z12; z[13] = z13; z[14] = z14; z[15] = z15;
+    }
+
+    public static void cnegate(int negate, int[] z)
+    {
+//        assert negate >>> 1 == 0;
+
+        int[] t = create();
+        sub(t, z, t);
+
+        Nat.cmov(SIZE, negate, t, 0, z, 0);
+    }
+
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[zOff + i] = x[xOff + i];
+        }
+    }
+
+    public static int[] create()
+    {
+        return new int[SIZE];
+    }
+
+    public static int[] createTable(int n)
+    {
+        return new int[SIZE * n];
+    }
+
+    public static void cswap(int swap, int[] a, int[] b)
+    {
+//        assert swap >>> 1 == 0;
+//        assert a != b;
+
+        int mask = 0 - swap;
+        for (int i = 0; i < SIZE; ++i)
+        {
+            int ai = a[i], bi = b[i];
+            int dummy = mask & (ai ^ bi);
+            a[i] = ai ^ dummy; 
+            b[i] = bi ^ dummy; 
+        }
+    }
+
+    public static void decode(byte[] x, int xOff, int[] z)
+    {
+        decode56(x, xOff, z, 0);
+        decode56(x, xOff + 7, z, 2);
+        decode56(x, xOff + 14, z, 4);
+        decode56(x, xOff + 21, z, 6);
+        decode56(x, xOff + 28, z, 8);
+        decode56(x, xOff + 35, z, 10);
+        decode56(x, xOff + 42, z, 12);
+        decode56(x, xOff + 49, z, 14);
+    }
+
+    private static int decode24(byte[] bs, int off)
+    {
+        int n = bs[  off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        return n;
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[  off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |= bs[++off] << 24;
+        return n;
+    }
+
+    private static void decode56(byte[] bs, int off, int[] z, int zOff)
+    {
+        int lo = decode32(bs, off);
+        int hi = decode24(bs, off + 4);
+        z[zOff] = lo & M28;
+        z[zOff + 1] = (lo >>> 28) | (hi << 4);
+        
+    }
+
+    public static void encode(int[] x,  byte[] z , int zOff)
+    {
+        encode56(x, 0, z, zOff);
+        encode56(x, 2, z, zOff + 7);
+        encode56(x, 4, z, zOff + 14);
+        encode56(x, 6, z, zOff + 21);
+        encode56(x, 8, z, zOff + 28);
+        encode56(x, 10, z, zOff + 35);
+        encode56(x, 12, z, zOff + 42);
+        encode56(x, 14, z, zOff + 49);
+    }
+
+    private static void encode24(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+    }
+
+    private static void encode32(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+        bs[++off] = (byte)(n >>> 24);
+    }
+
+    private static void encode56(int[] x, int xOff, byte[] bs, int off)
+    {
+        int lo = x[xOff], hi = x[xOff + 1];
+        encode32(lo | (hi << 28), bs, off);
+        encode24(hi >>> 4, bs, off + 4);
+    }
+
+    public static void inv(int[] x, int[] z)
+    {
+        // z = x^(p-2) = x^(2^448 - 2^224 - 3)
+        // (223 1s) (1 0s) (222 1s) (1 0s) (1 1s)
+        // Addition chain: [1] 2 3 6 9 18 19 37 74 111 [222] [223]
+
+        int[] t = create();
+        powPm3d4(x, t);
+        sqr(t, 2, t);
+        mul(t, x, z);
+    }
+
+    public static boolean isZeroVar(int[] x)
+    {
+        int d = 0;
+        for (int i = 0; i < SIZE; ++i)
+        {
+            d |= x[i];
+        }
+        return d == 0;
+    }
+
+    public static void mul(int[] x, int y, int[] z)
+    {
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+        int x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11], x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15];
+
+        int z1, z5, z9, z13;
+        long c, d, e, f;
+
+        c     = (long)x1 * y;
+        z1    = (int)c & M28; c >>>= 28;
+        d     = (long)x5 * y;
+        z5    = (int)d & M28; d >>>= 28;
+        e     = (long)x9 * y;
+        z9    = (int)e & M28; e >>>= 28;
+        f     = (long)x13 * y;
+        z13   = (int)f & M28; f >>>= 28;
+
+        c    += (long)x2 * y;
+        z[2]  = (int)c & M28; c >>>= 28;
+        d    += (long)x6 * y;
+        z[6]  = (int)d & M28; d >>>= 28;
+        e    += (long)x10 * y;
+        z[10] = (int)e & M28; e >>>= 28;
+        f    += (long)x14 * y;
+        z[14] = (int)f & M28; f >>>= 28;
+
+        c    += (long)x3 * y;
+        z[3]  = (int)c & M28; c >>>= 28;
+        d    += (long)x7 * y;
+        z[7]  = (int)d & M28; d >>>= 28;
+        e    += (long)x11 * y;
+        z[11] = (int)e & M28; e >>>= 28;
+        f    += (long)x15 * y;
+        z[15] = (int)f & M28; f >>>= 28;
+
+        d    += f;
+
+        c    += (long)x4 * y;
+        z[4]  = (int)c & M28; c >>>= 28;
+        d    += (long)x8 * y;
+        z[8]  = (int)d & M28; d >>>= 28;
+        e    += (long)x12 * y;
+        z[12] = (int)e & M28; e >>>= 28;
+        f    += (long)x0 * y;
+        z[0]  = (int)f & M28; f >>>= 28;
+
+        z[1]  = z1 + (int)f;
+        z[5]  = z5 + (int)c;
+        z[9]  = z9 + (int)d;
+        z[13] = z13 + (int)e;
+    }
+
+    public static void mul(int[] x, int[] y, int[] z)
+    {
+        int x0 = x[0];
+        int x1 = x[1];
+        int x2 = x[2];
+        int x3 = x[3];
+        int x4 = x[4];
+        int x5 = x[5];
+        int x6 = x[6];
+        int x7 = x[7];
+
+        int u0 = x[8];
+        int u1 = x[9];
+        int u2 = x[10];
+        int u3 = x[11];
+        int u4 = x[12];
+        int u5 = x[13];
+        int u6 = x[14];
+        int u7 = x[15];
+
+        int y0 = y[0];
+        int y1 = y[1];
+        int y2 = y[2];
+        int y3 = y[3];
+        int y4 = y[4];
+        int y5 = y[5];
+        int y6 = y[6];
+        int y7 = y[7];
+
+        int v0 = y[8];
+        int v1 = y[9];
+        int v2 = y[10];
+        int v3 = y[11];
+        int v4 = y[12];
+        int v5 = y[13];
+        int v6 = y[14];
+        int v7 = y[15];
+
+        int s0 = x0 + u0;
+        int s1 = x1 + u1;
+        int s2 = x2 + u2;
+        int s3 = x3 + u3;
+        int s4 = x4 + u4;
+        int s5 = x5 + u5;
+        int s6 = x6 + u6;
+        int s7 = x7 + u7;
+
+        int t0 = y0 + v0;
+        int t1 = y1 + v1;
+        int t2 = y2 + v2;
+        int t3 = y3 + v3;
+        int t4 = y4 + v4;
+        int t5 = y5 + v5;
+        int t6 = y6 + v6;
+        int t7 = y7 + v7;
+
+        int z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15;
+        long c, d;
+
+        long f0  = (long)x0 * y0;
+        long f8  = (long)x7 * y1
+                 + (long)x6 * y2
+                 + (long)x5 * y3
+                 + (long)x4 * y4
+                 + (long)x3 * y5
+                 + (long)x2 * y6
+                 + (long)x1 * y7;
+        long g0  = (long)u0 * v0;
+        long g8  = (long)u7 * v1
+                 + (long)u6 * v2
+                 + (long)u5 * v3
+                 + (long)u4 * v4
+                 + (long)u3 * v5
+                 + (long)u2 * v6
+                 + (long)u1 * v7;
+        long h0  = (long)s0 * t0;
+        long h8  = (long)s7 * t1
+                 + (long)s6 * t2
+                 + (long)s5 * t3
+                 + (long)s4 * t4
+                 + (long)s3 * t5
+                 + (long)s2 * t6
+                 + (long)s1 * t7;
+
+        c        = f0 + g0 + h8 - f8;
+        z0       = (int)c & M28; c >>>= 28;
+        d        = g8 + h0 - f0 + h8;
+        z8       = (int)d & M28; d >>>= 28;
+
+        long f1  = (long)x1 * y0
+                 + (long)x0 * y1;
+        long f9  = (long)x7 * y2
+                 + (long)x6 * y3
+                 + (long)x5 * y4
+                 + (long)x4 * y5
+                 + (long)x3 * y6
+                 + (long)x2 * y7;
+        long g1  = (long)u1 * v0
+                 + (long)u0 * v1;
+        long g9  = (long)u7 * v2
+                 + (long)u6 * v3
+                 + (long)u5 * v4
+                 + (long)u4 * v5
+                 + (long)u3 * v6
+                 + (long)u2 * v7;
+        long h1  = (long)s1 * t0
+                 + (long)s0 * t1;
+        long h9  = (long)s7 * t2
+                 + (long)s6 * t3
+                 + (long)s5 * t4
+                 + (long)s4 * t5
+                 + (long)s3 * t6
+                 + (long)s2 * t7;
+
+        c       += f1 + g1 + h9 - f9;
+        z1       = (int)c & M28; c >>>= 28;
+        d       += g9 + h1 - f1 + h9;
+        z9       = (int)d & M28; d >>>= 28;
+
+        long f2  = (long)x2 * y0
+                 + (long)x1 * y1
+                 + (long)x0 * y2;
+        long f10 = (long)x7 * y3
+                 + (long)x6 * y4
+                 + (long)x5 * y5
+                 + (long)x4 * y6
+                 + (long)x3 * y7;
+        long g2  = (long)u2 * v0
+                 + (long)u1 * v1
+                 + (long)u0 * v2;
+        long g10 = (long)u7 * v3
+                 + (long)u6 * v4
+                 + (long)u5 * v5
+                 + (long)u4 * v6
+                 + (long)u3 * v7;
+        long h2  = (long)s2 * t0
+                 + (long)s1 * t1
+                 + (long)s0 * t2;
+        long h10 = (long)s7 * t3
+                 + (long)s6 * t4
+                 + (long)s5 * t5
+                 + (long)s4 * t6
+                 + (long)s3 * t7;
+
+        c       += f2 + g2 + h10 - f10;
+        z2       = (int)c & M28; c >>>= 28;
+        d       += g10 + h2 - f2 + h10;
+        z10      = (int)d & M28; d >>>= 28;
+
+        long f3  = (long)x3 * y0
+                 + (long)x2 * y1
+                 + (long)x1 * y2
+                 + (long)x0 * y3;
+        long f11 = (long)x7 * y4
+                 + (long)x6 * y5
+                 + (long)x5 * y6
+                 + (long)x4 * y7;
+        long g3  = (long)u3 * v0
+                 + (long)u2 * v1
+                 + (long)u1 * v2
+                 + (long)u0 * v3;
+        long g11 = (long)u7 * v4
+                 + (long)u6 * v5
+                 + (long)u5 * v6
+                 + (long)u4 * v7;
+        long h3  = (long)s3 * t0
+                 + (long)s2 * t1
+                 + (long)s1 * t2
+                 + (long)s0 * t3;
+        long h11 = (long)s7 * t4
+                 + (long)s6 * t5
+                 + (long)s5 * t6
+                 + (long)s4 * t7;
+
+        c       += f3 + g3 + h11 - f11;
+        z3       = (int)c & M28; c >>>= 28;
+        d       += g11 + h3 - f3 + h11;
+        z11      = (int)d & M28; d >>>= 28;
+
+        long f4  = (long)x4 * y0
+                 + (long)x3 * y1
+                 + (long)x2 * y2
+                 + (long)x1 * y3
+                 + (long)x0 * y4;
+        long f12 = (long)x7 * y5
+                 + (long)x6 * y6
+                 + (long)x5 * y7;
+        long g4  = (long)u4 * v0
+                 + (long)u3 * v1
+                 + (long)u2 * v2
+                 + (long)u1 * v3
+                 + (long)u0 * v4;
+        long g12 = (long)u7 * v5
+                 + (long)u6 * v6
+                 + (long)u5 * v7;
+        long h4  = (long)s4 * t0
+                 + (long)s3 * t1
+                 + (long)s2 * t2
+                 + (long)s1 * t3
+                 + (long)s0 * t4;
+        long h12 = (long)s7 * t5
+                 + (long)s6 * t6
+                 + (long)s5 * t7;
+
+        c       += f4 + g4 + h12 - f12;
+        z4       = (int)c & M28; c >>>= 28;
+        d       += g12 + h4 - f4 + h12;
+        z12      = (int)d & M28; d >>>= 28;
+
+        long f5  = (long)x5 * y0
+                 + (long)x4 * y1
+                 + (long)x3 * y2
+                 + (long)x2 * y3
+                 + (long)x1 * y4
+                 + (long)x0 * y5;
+        long f13 = (long)x7 * y6
+                 + (long)x6 * y7;
+        long g5  = (long)u5 * v0
+                 + (long)u4 * v1
+                 + (long)u3 * v2
+                 + (long)u2 * v3
+                 + (long)u1 * v4
+                 + (long)u0 * v5;
+        long g13 = (long)u7 * v6
+                 + (long)u6 * v7;
+        long h5  = (long)s5 * t0
+                 + (long)s4 * t1
+                 + (long)s3 * t2
+                 + (long)s2 * t3
+                 + (long)s1 * t4
+                 + (long)s0 * t5;
+        long h13 = (long)s7 * t6
+                 + (long)s6 * t7;
+
+        c       += f5 + g5 + h13 - f13;
+        z5       = (int)c & M28; c >>>= 28;
+        d       += g13 + h5 - f5 + h13;
+        z13      = (int)d & M28; d >>>= 28;
+
+        long f6  = (long)x6 * y0
+                 + (long)x5 * y1
+                 + (long)x4 * y2
+                 + (long)x3 * y3
+                 + (long)x2 * y4
+                 + (long)x1 * y5
+                 + (long)x0 * y6;
+        long f14 = (long)x7 * y7;
+        long g6  = (long)u6 * v0
+                 + (long)u5 * v1
+                 + (long)u4 * v2
+                 + (long)u3 * v3
+                 + (long)u2 * v4
+                 + (long)u1 * v5
+                 + (long)u0 * v6;
+        long g14 = (long)u7 * v7;
+        long h6  = (long)s6 * t0
+                 + (long)s5 * t1
+                 + (long)s4 * t2
+                 + (long)s3 * t3
+                 + (long)s2 * t4
+                 + (long)s1 * t5
+                 + (long)s0 * t6;
+        long h14 = (long)s7 * t7;
+
+        c       += f6 + g6 + h14 - f14;
+        z6       = (int)c & M28; c >>>= 28;
+        d       += g14 + h6 - f6 + h14;
+        z14      = (int)d & M28; d >>>= 28;
+
+        long f7  = (long)x7 * y0
+                 + (long)x6 * y1
+                 + (long)x5 * y2
+                 + (long)x4 * y3
+                 + (long)x3 * y4
+                 + (long)x2 * y5
+                 + (long)x1 * y6
+                 + (long)x0 * y7;
+        long g7  = (long)u7 * v0
+                 + (long)u6 * v1
+                 + (long)u5 * v2
+                 + (long)u4 * v3
+                 + (long)u3 * v4
+                 + (long)u2 * v5
+                 + (long)u1 * v6
+                 + (long)u0 * v7;
+        long h7  = (long)s7 * t0
+                 + (long)s6 * t1
+                 + (long)s5 * t2
+                 + (long)s4 * t3
+                 + (long)s3 * t4
+                 + (long)s2 * t5
+                 + (long)s1 * t6
+                 + (long)s0 * t7;
+
+        c       += f7 + g7;
+        z7       = (int)c & M28; c >>>= 28;
+        d       += h7 - f7;
+        z15      = (int)d & M28; d >>>= 28;
+
+        c       += d;
+
+        c       += z8;
+        z8       = (int)c & M28; c >>>= 28;
+        d       += z0;
+        z0       = (int)d & M28; d >>>= 28;
+        z9      += (int)c;
+        z1      += (int)d;
+
+        z[0] = z0;
+        z[1] = z1;
+        z[2] = z2;
+        z[3] = z3;
+        z[4] = z4;
+        z[5] = z5;
+        z[6] = z6;
+        z[7] = z7;
+        z[8] = z8;
+        z[9] = z9;
+        z[10] = z10;
+        z[11] = z11;
+        z[12] = z12;
+        z[13] = z13;
+        z[14] = z14;
+        z[15] = z15;
+    }
+
+    public static void negate(int[] x, int[] z)
+    {
+        int[] zero = create();
+        sub(zero, x, z);
+    }
+
+    public static void normalize(int[] z)
+    {
+//        int x = ((z[15] >>> (28 - 1)) & 1);
+        reduce(z, 1);
+        reduce(z, -1);
+//        assert z[15] >>> 28 == 0;
+    }
+
+    public static void one(int[] z)
+    {
+        z[0] = 1;
+        for (int i = 1; i < SIZE; ++i)
+        {
+            z[i] = 0;
+        }
+    }
+
+    private static void powPm3d4(int[] x, int[] z)
+    {
+        // z = x^((p-3)/4) = x^(2^446 - 2^222 - 1)
+        // (223 1s) (1 0s) (222 1s)
+        // Addition chain: 1 2 3 6 9 18 19 37 74 111 [222] [223]
+        int[] x2 = create();    sqr(x, x2);             mul(x, x2, x2);
+        int[] x3 = create();    sqr(x2, x3);            mul(x, x3, x3);
+        int[] x6 = create();    sqr(x3, 3, x6);         mul(x3, x6, x6);
+        int[] x9 = create();    sqr(x6, 3, x9);         mul(x3, x9, x9);
+        int[] x18 = create();   sqr(x9, 9, x18);        mul(x9, x18, x18);
+        int[] x19 = create();   sqr(x18, x19);          mul(x, x19, x19);
+        int[] x37 = create();   sqr(x19, 18, x37);      mul(x18, x37, x37);
+        int[] x74 = create();   sqr(x37, 37, x74);      mul(x37, x74, x74);
+        int[] x111 = create();  sqr(x74, 37, x111);     mul(x37, x111, x111);
+        int[] x222 = create();  sqr(x111, 111, x222);   mul(x111, x222, x222);
+        int[] x223 = create();  sqr(x222, x223);        mul(x, x223, x223);
+
+        int[] t = create();
+        sqr(x223, 223, t);
+        mul(t, x222, z);
+    }
+
+    private static void reduce(int[] z, int c)
+    {
+        int t = z[15], z15 = t & M28;
+        t = (t >> 28) + c;
+        z[8] += t;
+        for (int i = 0; i < 15; ++i)
+        {
+            t += z[i]; z[i] = t & M28; t >>= 28;
+        }
+        z[15] = z15 + t;
+    }
+
+    public static void sqr(int[] x, int[] z)
+    {
+        int x0 = x[0];
+        int x1 = x[1];
+        int x2 = x[2];
+        int x3 = x[3];
+        int x4 = x[4];
+        int x5 = x[5];
+        int x6 = x[6];
+        int x7 = x[7];
+
+        int u0 = x[8];
+        int u1 = x[9];
+        int u2 = x[10];
+        int u3 = x[11];
+        int u4 = x[12];
+        int u5 = x[13];
+        int u6 = x[14];
+        int u7 = x[15];
+
+        int x0_2 = x0 * 2;
+        int x1_2 = x1 * 2;
+        int x2_2 = x2 * 2;
+        int x3_2 = x3 * 2;
+        int x4_2 = x4 * 2;
+        int x5_2 = x5 * 2;
+        int x6_2 = x6 * 2;
+
+        int u0_2 = u0 * 2;
+        int u1_2 = u1 * 2;
+        int u2_2 = u2 * 2;
+        int u3_2 = u3 * 2;
+        int u4_2 = u4 * 2;
+        int u5_2 = u5 * 2;
+        int u6_2 = u6 * 2;
+        
+        int s0 = x0 + u0;
+        int s1 = x1 + u1;
+        int s2 = x2 + u2;
+        int s3 = x3 + u3;
+        int s4 = x4 + u4;
+        int s5 = x5 + u5;
+        int s6 = x6 + u6;
+        int s7 = x7 + u7;
+
+        int s0_2 = s0 * 2;
+        int s1_2 = s1 * 2;
+        int s2_2 = s2 * 2;
+        int s3_2 = s3 * 2;
+        int s4_2 = s4 * 2;
+        int s5_2 = s5 * 2;
+        int s6_2 = s6 * 2;
+
+        int z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15;
+        long c, d;
+
+        long f0  = (long)x0 * x0;
+        long f8  = (long)x7 * x1_2
+                 + (long)x6 * x2_2
+                 + (long)x5 * x3_2
+                 + (long)x4 * x4;
+        long g0  = (long)u0 * u0;
+        long g8  = (long)u7 * u1_2
+                 + (long)u6 * u2_2
+                 + (long)u5 * u3_2
+                 + (long)u4 * u4;
+        long h0  = (long)s0 * s0;
+        long h8  = (long)s7 * s1_2
+                 + (long)s6 * s2_2
+                 + (long)s5 * s3_2
+                 + (long)s4 * s4;
+
+        c        = f0 + g0 + h8 - f8;
+        z0       = (int)c & M28; c >>>= 28;
+        d        = g8 + h0 - f0 + h8;
+        z8       = (int)d & M28; d >>>= 28;
+
+        long f1  = (long)x1 * x0_2;
+        long f9  = (long)x7 * x2_2
+                 + (long)x6 * x3_2
+                 + (long)x5 * x4_2;
+        long g1  = (long)u1 * u0_2;
+        long g9  = (long)u7 * u2_2
+                 + (long)u6 * u3_2
+                 + (long)u5 * u4_2;
+        long h1  = (long)s1 * s0_2;
+        long h9  = (long)s7 * s2_2
+                 + (long)s6 * s3_2
+                 + (long)s5 * s4_2;
+
+        c       += f1 + g1 + h9 - f9;
+        z1       = (int)c & M28; c >>>= 28;
+        d       += g9 + h1 - f1 + h9;
+        z9       = (int)d & M28; d >>>= 28;
+
+        long f2  = (long)x2 * x0_2
+                 + (long)x1 * x1;
+        long f10 = (long)x7 * x3_2
+                 + (long)x6 * x4_2
+                 + (long)x5 * x5;
+        long g2  = (long)u2 * u0_2
+                 + (long)u1 * u1;
+        long g10 = (long)u7 * u3_2
+                 + (long)u6 * u4_2
+                 + (long)u5 * u5;
+        long h2  = (long)s2 * s0_2
+                 + (long)s1 * s1;
+        long h10 = (long)s7 * s3_2
+                 + (long)s6 * s4_2
+                 + (long)s5 * s5;
+
+        c       += f2 + g2 + h10 - f10;
+        z2       = (int)c & M28; c >>>= 28;
+        d       += g10 + h2 - f2 + h10;
+        z10      = (int)d & M28; d >>>= 28;
+
+        long f3  = (long)x3 * x0_2
+                 + (long)x2 * x1_2;
+        long f11 = (long)x7 * x4_2
+                 + (long)x6 * x5_2;
+        long g3  = (long)u3 * u0_2
+                 + (long)u2 * u1_2;
+        long g11 = (long)u7 * u4_2
+                 + (long)u6 * u5_2;
+        long h3  = (long)s3 * s0_2
+                 + (long)s2 * s1_2;
+        long h11 = (long)s7 * s4_2
+                 + (long)s6 * s5_2;
+
+        c       += f3 + g3 + h11 - f11;
+        z3       = (int)c & M28; c >>>= 28;
+        d       += g11 + h3 - f3 + h11;
+        z11      = (int)d & M28; d >>>= 28;
+
+        long f4  = (long)x4 * x0_2
+                 + (long)x3 * x1_2
+                 + (long)x2 * x2;
+        long f12 = (long)x7 * x5_2
+                 + (long)x6 * x6;
+        long g4  = (long)u4 * u0_2
+                 + (long)u3 * u1_2
+                 + (long)u2 * u2;
+        long g12 = (long)u7 * u5_2
+                 + (long)u6 * u6;
+        long h4  = (long)s4 * s0_2
+                 + (long)s3 * s1_2
+                 + (long)s2 * s2;
+        long h12 = (long)s7 * s5_2
+                 + (long)s6 * s6;
+
+        c       += f4 + g4 + h12 - f12;
+        z4       = (int)c & M28; c >>>= 28;
+        d       += g12 + h4 - f4 + h12;
+        z12      = (int)d & M28; d >>>= 28;
+
+        long f5  = (long)x5 * x0_2
+                 + (long)x4 * x1_2
+                 + (long)x3 * x2_2;
+        long f13 = (long)x7 * x6_2;
+        long g5  = (long)u5 * u0_2
+                 + (long)u4 * u1_2
+                 + (long)u3 * u2_2;
+        long g13 = (long)u7 * u6_2;
+        long h5  = (long)s5 * s0_2
+                 + (long)s4 * s1_2
+                 + (long)s3 * s2_2;
+        long h13 = (long)s7 * s6_2;
+
+        c       += f5 + g5 + h13 - f13;
+        z5       = (int)c & M28; c >>>= 28;
+        d       += g13 + h5 - f5 + h13;
+        z13      = (int)d & M28; d >>>= 28;
+
+        long f6  = (long)x6 * x0_2
+                 + (long)x5 * x1_2
+                 + (long)x4 * x2_2
+                 + (long)x3 * x3;
+        long f14 = (long)x7 * x7;
+        long g6  = (long)u6 * u0_2
+                 + (long)u5 * u1_2
+                 + (long)u4 * u2_2
+                 + (long)u3 * u3;
+        long g14 = (long)u7 * u7;
+        long h6  = (long)s6 * s0_2
+                 + (long)s5 * s1_2
+                 + (long)s4 * s2_2
+                 + (long)s3 * s3;
+        long h14 = (long)s7 * s7;
+
+        c       += f6 + g6 + h14 - f14;
+        z6       = (int)c & M28; c >>>= 28;
+        d       += g14 + h6 - f6 + h14;
+        z14      = (int)d & M28; d >>>= 28;
+
+        long f7  = (long)x7 * x0_2
+                 + (long)x6 * x1_2
+                 + (long)x5 * x2_2
+                 + (long)x4 * x3_2;
+        long g7  = (long)u7 * u0_2
+                 + (long)u6 * u1_2
+                 + (long)u5 * u2_2
+                 + (long)u4 * u3_2;
+        long h7  = (long)s7 * s0_2
+                 + (long)s6 * s1_2
+                 + (long)s5 * s2_2
+                 + (long)s4 * s3_2;
+
+        c       += f7 + g7;
+        z7       = (int)c & M28; c >>>= 28;
+        d       += h7 - f7;
+        z15      = (int)d & M28; d >>>= 28;
+
+        c       += d;
+
+        c       += z8;
+        z8       = (int)c & M28; c >>>= 28;
+        d       += z0;
+        z0       = (int)d & M28; d >>>= 28;
+        z9      += (int)c;
+        z1      += (int)d;
+
+        z[0] = z0;
+        z[1] = z1;
+        z[2] = z2;
+        z[3] = z3;
+        z[4] = z4;
+        z[5] = z5;
+        z[6] = z6;
+        z[7] = z7;
+        z[8] = z8;
+        z[9] = z9;
+        z[10] = z10;
+        z[11] = z11;
+        z[12] = z12;
+        z[13] = z13;
+        z[14] = z14;
+        z[15] = z15;
+    }
+
+    public static void sqr(int[] x, int n, int[] z)
+    {
+//        assert n > 0;
+
+        sqr(x, z);
+
+        while (--n > 0)
+        {
+            sqr(z, z);
+        }
+    }
+
+    public static boolean sqrtRatioVar(int[] u, int[] v, int[] z)
+    {
+        int[] u3v = create();
+        int[] u5v3 = create();
+
+        sqr(u, u3v);
+        mul(u3v, v, u3v);
+        sqr(u3v, u5v3);
+        mul(u3v, u, u3v);
+        mul(u5v3, u, u5v3);
+        mul(u5v3, v, u5v3);
+
+        int[] x = create();
+        powPm3d4(u5v3, x);
+        mul(x, u3v, x);
+
+        int[] t = create();
+        sqr(x, t);
+        mul(t, v, t);
+
+        sub(u, t, t);
+        normalize(t);
+
+        if (isZeroVar(t))
+        {
+            copy(x, 0, z, 0);
+            return true;
+        }
+
+        return false;
+    }
+
+    public static void sub(int[] x, int[] y, int[] z)
+    {
+        int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+        int x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11], x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15];
+        int y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3], y4 = y[4], y5 = y[5], y6 = y[6], y7 = y[7];
+        int y8 = y[8], y9 = y[9], y10 = y[10], y11 = y[11], y12 = y[12], y13 = y[13], y14 = y[14], y15 = y[15];
+
+        int z0 = x0 + 0x1FFFFFFE - y0;
+        int z1 = x1 + 0x1FFFFFFE - y1;
+        int z2 = x2 + 0x1FFFFFFE - y2;
+        int z3 = x3 + 0x1FFFFFFE - y3;
+        int z4 = x4 + 0x1FFFFFFE - y4;
+        int z5 = x5 + 0x1FFFFFFE - y5;
+        int z6 = x6 + 0x1FFFFFFE - y6;
+        int z7 = x7 + 0x1FFFFFFE - y7;
+        int z8 = x8 + 0x1FFFFFFC - y8;
+        int z9 = x9 + 0x1FFFFFFE - y9;
+        int z10 = x10 + 0x1FFFFFFE - y10;
+        int z11 = x11 + 0x1FFFFFFE - y11;
+        int z12 = x12 + 0x1FFFFFFE - y12;
+        int z13 = x13 + 0x1FFFFFFE - y13;
+        int z14 = x14 + 0x1FFFFFFE - y14;
+        int z15 = x15 + 0x1FFFFFFE - y15;
+
+        z2   += z1 >>> 28; z1 &= M28;
+        z6   += z5 >>> 28; z5 &= M28;
+        z10  += z9 >>> 28; z9 &= M28;
+        z14  += z13 >>> 28; z13 &= M28;
+
+        z3   += z2 >>> 28; z2 &= M28;
+        z7   += z6 >>> 28; z6 &= M28;
+        z11  += z10 >>> 28; z10 &= M28;
+        z15  += z14 >>> 28; z14 &= M28;
+
+        int t = z15 >>> 28; z15 &= M28;
+        z0   += t;
+        z8   += t;
+
+        z4   += z3 >>> 28; z3 &= M28;
+        z8   += z7 >>> 28; z7 &= M28;
+        z12  += z11 >>> 28; z11 &= M28;
+
+        z1   += z0 >>> 28; z0 &= M28;
+        z5   += z4 >>> 28; z4 &= M28;
+        z9   += z8 >>> 28; z8 &= M28;
+        z13  += z12 >>> 28; z12 &= M28;
+
+        z[0] = z0;
+        z[1] = z1;
+        z[2] = z2;
+        z[3] = z3;
+        z[4] = z4;
+        z[5] = z5;
+        z[6] = z6;
+        z[7] = z7;
+        z[8] = z8;
+        z[9] = z9;
+        z[10] = z10;
+        z[11] = z11;
+        z[12] = z12;
+        z[13] = z13;
+        z[14] = z14;
+        z[15] = z15;
+    }
+
+    public static void subOne(int[] z)
+    {
+        int[] one = create();
+        one[0] = 1;
+
+        sub(z, one, z);
+    }
+
+    public static void zero(int[] z)
+    {
+        for (int i = 0; i < SIZE; ++i)
+        {
+            z[i] = 0;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed25519.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed25519.java
new file mode 100644
index 0000000..bcfb427
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed25519.java
@@ -0,0 +1,1132 @@
+package org.bouncycastle.math.ec.rfc8032;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.math.ec.rfc7748.X25519;
+import org.bouncycastle.math.ec.rfc7748.X25519Field;
+import org.bouncycastle.math.raw.Interleave;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.math.raw.Nat256;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public abstract class Ed25519
+{
+    public static final class Algorithm
+    {
+        public static final int Ed25519 = 0;
+        public static final int Ed25519ctx = 1;
+        public static final int Ed25519ph = 2;
+    }
+
+    private static final long M28L = 0x0FFFFFFFL;
+    private static final long M32L = 0xFFFFFFFFL;
+
+    private static final int POINT_BYTES = 32;
+    private static final int SCALAR_INTS = 8;
+    private static final int SCALAR_BYTES = SCALAR_INTS * 4;
+
+    public static final int PREHASH_SIZE = 64;
+    public static final int PUBLIC_KEY_SIZE = POINT_BYTES;
+    public static final int SECRET_KEY_SIZE = 32;
+    public static final int SIGNATURE_SIZE = POINT_BYTES + SCALAR_BYTES;
+
+    private static final byte[] DOM2_PREFIX = Strings.toByteArray("SigEd25519 no Ed25519 collisions");
+
+    private static final int[] P = new int[]{ 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF };
+    private static final int[] L = new int[]{ 0x5CF5D3ED, 0x5812631A, 0xA2F79CD6, 0x14DEF9DE, 0x00000000, 0x00000000, 0x00000000, 0x10000000 };
+
+    private static final int L0 = 0xFCF5D3ED;   // L0:26/--
+    private static final int L1 = 0x012631A6;   // L1:24/22
+    private static final int L2 = 0x079CD658;   // L2:27/--
+    private static final int L3 = 0xFF9DEA2F;   // L3:23/--
+    private static final int L4 = 0x000014DF;   // L4:12/11
+
+    private static final int[] B_x = new int[]{ 0x0325D51A, 0x018B5823, 0x007B2C95, 0x0304A92D, 0x00D2598E, 0x01D6DC5C,
+        0x01388C7F, 0x013FEC0A, 0x029E6B72, 0x0042D26D };    
+    private static final int[] B_y = new int[]{ 0x02666658, 0x01999999, 0x00666666, 0x03333333, 0x00CCCCCC, 0x02666666,
+        0x01999999, 0x00666666, 0x03333333, 0x00CCCCCC, };
+    private static final int[] C_d = new int[]{ 0x035978A3, 0x02D37284, 0x018AB75E, 0x026A0A0E, 0x0000E014, 0x0379E898,
+        0x01D01E5D, 0x01E738CC, 0x03715B7F, 0x00A406D9 };
+    private static final int[] C_d2 = new int[]{ 0x02B2F159, 0x01A6E509, 0x01156EBD, 0x00D4141D, 0x0001C029, 0x02F3D130,
+        0x03A03CBB, 0x01CE7198, 0x02E2B6FF, 0x00480DB3 };
+    private static final int[] C_d4 = new int[]{ 0x0165E2B2, 0x034DCA13, 0x002ADD7A, 0x01A8283B, 0x00038052, 0x01E7A260,
+        0x03407977, 0x019CE331, 0x01C56DFF, 0x00901B67 };
+
+    private static final int WNAF_WIDTH_BASE = 7;
+
+    private static final int PRECOMP_BLOCKS = 8;
+    private static final int PRECOMP_TEETH = 4;
+    private static final int PRECOMP_SPACING = 8;
+    private static final int PRECOMP_POINTS = 1 << (PRECOMP_TEETH - 1);
+    private static final int PRECOMP_MASK = PRECOMP_POINTS - 1;
+
+    private static Object precompLock = new Object();
+    // TODO[ed25519] Convert to PointPrecomp
+    private static PointExt[] precompBaseTable = null;
+    private static int[] precompBase = null;
+
+    private static class PointAccum
+    {
+        int[] x = X25519Field.create();
+        int[] y = X25519Field.create();
+        int[] z = X25519Field.create();
+        int[] u = X25519Field.create();
+        int[] v = X25519Field.create();
+    }
+
+    private static class PointExt
+    {
+        int[] x = X25519Field.create();
+        int[] y = X25519Field.create();
+        int[] z = X25519Field.create();
+        int[] t = X25519Field.create();
+    }
+
+    private static class PointPrecomp
+    {
+        int[] ypx_h = X25519Field.create();
+        int[] ymx_h = X25519Field.create();
+        int[] xyd = X25519Field.create();
+    }
+
+    private static byte[] calculateS(byte[] r, byte[] k, byte[] s)
+    {
+        int[] t = new int[SCALAR_INTS * 2];     decodeScalar(r, 0, t);
+        int[] u = new int[SCALAR_INTS];         decodeScalar(k, 0, u);
+        int[] v = new int[SCALAR_INTS];         decodeScalar(s, 0, v);
+
+        Nat256.mulAddTo(u, v, t);
+
+        byte[] result = new byte[SCALAR_BYTES * 2];
+        for (int i = 0; i < t.length; ++i)
+        {
+            encode32(t[i], result, i * 4);
+        }
+        return reduceScalar(result);
+    }
+
+    private static boolean checkContextVar(byte[] ctx , byte phflag)
+    {
+        return ctx == null && phflag == 0x00 
+            || ctx != null && ctx.length < 256;
+    }
+
+    private static boolean checkPointVar(byte[] p)
+    {
+        int[] t = new int[8];
+        decode32(p, 0, t, 0, 8);
+        t[7] &= 0x7FFFFFFF;
+        return !Nat256.gte(t, P);
+    }
+
+    private static boolean checkScalarVar(byte[] s)
+    {
+        int[] n = new int[SCALAR_INTS];
+        decodeScalar(s, 0, n);
+        return !Nat256.gte(n, L);
+    }
+
+    private static Digest createDigest()
+    {
+        return new SHA512Digest();
+    }
+
+    public static Digest createPrehash()
+    {
+        return createDigest();
+    }
+
+    private static int decode24(byte[] bs, int off)
+    {
+        int n = bs[  off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        return n;
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |=  bs[++off]         << 24;
+        return n;
+    }
+
+    private static void decode32(byte[] bs, int bsOff, int[] n, int nOff, int nLen)
+    {
+        for (int i = 0; i < nLen; ++i)
+        {
+            n[nOff + i] = decode32(bs, bsOff + i * 4);
+        }
+    }
+
+    private static boolean decodePointVar(byte[] p, int pOff, boolean negate, PointExt r)
+    {
+        byte[] py = Arrays.copyOfRange(p, pOff, pOff + POINT_BYTES);
+        if (!checkPointVar(py))
+        {
+            return false;
+        }
+
+        int x_0 = (py[POINT_BYTES - 1] & 0x80) >>> 7;
+        py[POINT_BYTES - 1] &= 0x7F;
+
+        X25519Field.decode(py, 0, r.y);
+
+        int[] u = X25519Field.create();
+        int[] v = X25519Field.create();
+
+        X25519Field.sqr(r.y, u);
+        X25519Field.mul(C_d, u, v);
+        X25519Field.subOne(u);
+        X25519Field.addOne(v);
+
+        if (!X25519Field.sqrtRatioVar(u, v, r.x))
+        {
+            return false;
+        }
+
+        X25519Field.normalize(r.x);
+        if (x_0 == 1 && X25519Field.isZeroVar(r.x))
+        {
+            return false;
+        }
+
+        if (negate ^ (x_0 != (r.x[0] & 1)))
+        {
+            X25519Field.negate(r.x, r.x);
+        }
+
+        pointExtendXY(r);
+        return true;
+    }
+
+    private static void decodeScalar(byte[] k, int kOff, int[] n)
+    {
+        decode32(k, kOff, n, 0, SCALAR_INTS);
+    }
+
+    private static void dom2(Digest d, byte phflag, byte[] ctx)
+    {
+        if (ctx != null)
+        {
+            d.update(DOM2_PREFIX, 0, DOM2_PREFIX.length);
+            d.update(phflag);
+            d.update((byte)ctx.length);
+            d.update(ctx, 0, ctx.length);
+        }
+    }
+
+    private static void encode24(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+    }
+
+    private static void encode32(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+        bs[++off] = (byte)(n >>> 24);
+    }
+
+    private static void encode56(long n, byte[] bs, int off)
+    {
+        encode32((int)n, bs, off);
+        encode24((int)(n >>> 32), bs, off + 4);
+    }
+
+    private static void encodePoint(PointAccum p, byte[] r, int rOff)
+    {
+        int[] x = X25519Field.create();
+        int[] y = X25519Field.create();
+
+        X25519Field.inv(p.z, y);
+        X25519Field.mul(p.x, y, x);
+        X25519Field.mul(p.y, y, y);
+        X25519Field.normalize(x);
+        X25519Field.normalize(y);
+
+        X25519Field.encode(y, r, rOff);
+        r[rOff + POINT_BYTES - 1] |= ((x[0] & 1) << 7);
+    }
+
+    public static void generatePrivateKey(SecureRandom random, byte[] k)
+    {
+        random.nextBytes(k);
+    }
+
+    public static void generatePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff)
+    {
+        Digest d = createDigest();
+        byte[] h = new byte[d.getDigestSize()];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        scalarMultBaseEncoded(s, pk, pkOff);
+    }
+
+    private static byte[] getWNAF(int[] n, int width)
+    {
+//        assert n[SCALAR_INTS - 1] >>> 31 == 0;
+
+        int[] t = new int[SCALAR_INTS * 2];
+        {
+            int tPos = t.length, c = 0;
+            int i = SCALAR_INTS;
+            while (--i >= 0)
+            {
+                int next = n[i];
+                t[--tPos] = (next >>> 16) | (c << 16);
+                t[--tPos] = c = next;
+            }
+        }
+
+        byte[] ws = new byte[256];
+
+        final int pow2 = 1 << width;
+        final int mask = pow2 - 1;
+        final int sign = pow2 >>> 1;
+
+        int j = 0, carry = 0;
+        for (int i = 0; i < t.length; ++i, j -= 16)
+        {
+            int word = t[i];
+            while (j < 16)
+            {
+                int word16 = word >>> j;
+                int bit = word16 & 1;
+
+                if (bit == carry)
+                {
+                    ++j;
+                    continue;
+                }
+
+                int digit = (word16 & mask) + carry;
+                carry = digit & sign;
+                digit -= (carry << 1);
+                carry >>>= (width - 1);
+
+                ws[(i << 4) + j] = (byte)digit;
+
+                j += width;
+            }
+        }
+
+//        assert carry == 0;
+
+        return ws;
+    }
+
+    private static void implSign(Digest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        dom2(d, phflag, ctx);
+        d.update(h, SCALAR_BYTES, SCALAR_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0);
+
+        byte[] r = reduceScalar(h);
+        byte[] R = new byte[POINT_BYTES];
+        scalarMultBaseEncoded(r, R, 0);
+
+        dom2(d, phflag, ctx);
+        d.update(R, 0, POINT_BYTES);
+        d.update(pk, pkOff, POINT_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0);
+
+        byte[] k = reduceScalar(h);
+        byte[] S = calculateS(r, k, s);
+
+        System.arraycopy(R, 0, sig, sigOff, POINT_BYTES);
+        System.arraycopy(S, 0, sig, sigOff + POINT_BYTES, SCALAR_BYTES);
+    }
+
+    private static void implSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen,
+        byte[] sig, int sigOff)
+    {
+        if (!checkContextVar(ctx, phflag))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        Digest d = createDigest();
+        byte[] h = new byte[d.getDigestSize()];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        byte[] pk = new byte[POINT_BYTES];
+        scalarMultBaseEncoded(s, pk, 0);
+
+        implSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    private static void implSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        if (!checkContextVar(ctx, phflag))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        Digest d = createDigest();
+        byte[] h = new byte[d.getDigestSize()];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        implSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    private static boolean implVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen)
+    {
+        if (!checkContextVar(ctx, phflag))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        byte[] R = Arrays.copyOfRange(sig, sigOff, sigOff + POINT_BYTES);
+        byte[] S = Arrays.copyOfRange(sig, sigOff + POINT_BYTES, sigOff + SIGNATURE_SIZE);
+
+        if (!checkPointVar(R))
+        {
+            return false;
+        }
+        if (!checkScalarVar(S))
+        {
+            return false;
+        }
+
+        PointExt pA = new PointExt();
+        if (!decodePointVar(pk, pkOff, true, pA))
+        {
+            return false;
+        }
+
+        Digest d = createDigest();
+        byte[] h = new byte[d.getDigestSize()];
+
+        dom2(d, phflag, ctx);
+        d.update(R, 0, POINT_BYTES);
+        d.update(pk, pkOff, POINT_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0);
+
+        byte[] k = reduceScalar(h);
+
+        int[] nS = new int[SCALAR_INTS];
+        decodeScalar(S, 0, nS);
+
+        int[] nA = new int[SCALAR_INTS];
+        decodeScalar(k, 0, nA);
+
+        PointAccum pR = new PointAccum();
+        scalarMultStraussVar(nS, nA, pA, pR);
+
+        byte[] check = new byte[POINT_BYTES];
+        encodePoint(pR, check, 0);
+
+        return Arrays.areEqual(check, R);
+    }
+
+    private static void pointAddVar(boolean negate, PointExt p, PointAccum r)
+    {
+        int[] A = X25519Field.create();
+        int[] B = X25519Field.create();
+        int[] C = X25519Field.create();
+        int[] D = X25519Field.create();
+        int[] E = r.u;
+        int[] F = X25519Field.create();
+        int[] G = X25519Field.create();
+        int[] H = r.v;
+
+        int[] c, d, f, g;
+        if (negate)
+        {
+            c = D; d = C; f = G; g = F;
+        }
+        else
+        {
+            c = C; d = D; f = F; g = G;
+        }
+
+        X25519Field.apm(r.y, r.x, B, A);
+        X25519Field.apm(p.y, p.x, d, c);
+        X25519Field.mul(A, C, A);
+        X25519Field.mul(B, D, B);
+        X25519Field.mul(r.u, r.v, C);
+        X25519Field.mul(C, p.t, C);
+        X25519Field.mul(C, C_d2, C);
+        X25519Field.mul(r.z, p.z, D);
+        X25519Field.add(D, D, D);
+        X25519Field.apm(B, A, H, E);
+        X25519Field.apm(D, C, g, f);
+        X25519Field.carry(g);
+        X25519Field.mul(E, F, r.x);
+        X25519Field.mul(G, H, r.y);
+        X25519Field.mul(F, G, r.z);
+    }
+
+    private static void pointAddVar(boolean negate, PointExt p, PointExt q, PointExt r)
+    {
+        int[] A = X25519Field.create();
+        int[] B = X25519Field.create();
+        int[] C = X25519Field.create();
+        int[] D = X25519Field.create();
+        int[] E = X25519Field.create();
+        int[] F = X25519Field.create();
+        int[] G = X25519Field.create();
+        int[] H = X25519Field.create();
+
+        int[] c, d, f, g;
+        if (negate)
+        {
+            c = D; d = C; f = G; g = F;
+        }
+        else
+        {
+            c = C; d = D; f = F; g = G;
+        }
+
+        X25519Field.apm(p.y, p.x, B, A);
+        X25519Field.apm(q.y, q.x, d, c);
+        X25519Field.mul(A, C, A);
+        X25519Field.mul(B, D, B);
+        X25519Field.mul(p.t, q.t, C);
+        X25519Field.mul(C, C_d2, C);
+        X25519Field.mul(p.z, q.z, D);
+        X25519Field.add(D, D, D);
+        X25519Field.apm(B, A, H, E);
+        X25519Field.apm(D, C, g, f);
+        X25519Field.carry(g);
+        X25519Field.mul(E, F, r.x);
+        X25519Field.mul(G, H, r.y);
+        X25519Field.mul(F, G, r.z);
+        X25519Field.mul(E, H, r.t);
+    }
+
+    private static void pointAddPrecomp(PointPrecomp p, PointAccum r)
+    {
+        int[] A = X25519Field.create();
+        int[] B = X25519Field.create();
+        int[] C = X25519Field.create();
+        int[] E = r.u;
+        int[] F = X25519Field.create();
+        int[] G = X25519Field.create();
+        int[] H = r.v;
+
+        X25519Field.apm(r.y, r.x, B, A);
+        X25519Field.mul(A, p.ymx_h, A);
+        X25519Field.mul(B, p.ypx_h, B);
+        X25519Field.mul(r.u, r.v, C);
+        X25519Field.mul(C, p.xyd, C);
+        X25519Field.apm(B, A, H, E);
+        X25519Field.apm(r.z, C, G, F);
+        X25519Field.carry(G);
+        X25519Field.mul(E, F, r.x);
+        X25519Field.mul(G, H, r.y);
+        X25519Field.mul(F, G, r.z);
+    }
+
+    private static PointExt pointCopy(PointAccum p)
+    {
+        PointExt r = new PointExt();
+        X25519Field.copy(p.x, 0, r.x, 0);
+        X25519Field.copy(p.y, 0, r.y, 0);
+        X25519Field.copy(p.z, 0, r.z, 0);
+        X25519Field.mul(p.u, p.v, r.t);
+        return r;
+    }
+
+    private static PointExt pointCopy(PointExt p)
+    {
+        PointExt r = new PointExt();
+        X25519Field.copy(p.x, 0, r.x, 0);
+        X25519Field.copy(p.y, 0, r.y, 0);
+        X25519Field.copy(p.z, 0, r.z, 0);
+        X25519Field.copy(p.t, 0, r.t, 0);
+        return r;
+    }
+
+    private static void pointDouble(PointAccum r)
+    {
+        int[] A = X25519Field.create();
+        int[] B = X25519Field.create();
+        int[] C = X25519Field.create();
+        int[] E = r.u;
+        int[] F = X25519Field.create();
+        int[] G = X25519Field.create();
+        int[] H = r.v;
+
+        X25519Field.sqr(r.x, A);
+        X25519Field.sqr(r.y, B);
+        X25519Field.sqr(r.z, C);
+        X25519Field.add(C, C, C);
+        X25519Field.apm(A, B, H, G);
+        X25519Field.add(r.x, r.y, E);
+        X25519Field.sqr(E, E);
+        X25519Field.sub(H, E, E);
+        X25519Field.add(C, G, F);
+        X25519Field.carry(F);
+        X25519Field.mul(E, F, r.x);
+        X25519Field.mul(G, H, r.y);
+        X25519Field.mul(F, G, r.z);
+    }
+
+    private static void pointExtendXY(PointAccum p)
+    {
+        X25519Field.one(p.z);
+        X25519Field.copy(p.x, 0, p.u, 0);
+        X25519Field.copy(p.y, 0, p.v, 0);
+    }
+
+    private static void pointExtendXY(PointExt p)
+    {
+        X25519Field.one(p.z);
+        X25519Field.mul(p.x, p.y, p.t);
+    }
+
+    private static void pointLookup(int block, int index, PointPrecomp p)
+    {
+//        assert 0 <= block && block < PRECOMP_BLOCKS;
+//        assert 0 <= index && index < PRECOMP_POINTS;
+
+        int off = block * PRECOMP_POINTS * 3 * X25519Field.SIZE;
+
+        for (int i = 0; i < PRECOMP_POINTS; ++i)
+        {
+            int mask = ((i ^ index) - 1) >> 31;
+            Nat.cmov(X25519Field.SIZE, mask, precompBase, off, p.ypx_h, 0);    off += X25519Field.SIZE;
+            Nat.cmov(X25519Field.SIZE, mask, precompBase, off, p.ymx_h, 0);    off += X25519Field.SIZE;
+            Nat.cmov(X25519Field.SIZE, mask, precompBase, off, p.xyd,   0);    off += X25519Field.SIZE;
+        }
+    }
+
+    private static PointExt[] pointPrecompVar(PointExt p, int count)
+    {
+//        assert count > 0;
+
+        PointExt d = new PointExt();
+        pointAddVar(false, p, p, d);
+
+        PointExt[] table = new PointExt[count];
+        table[0] = pointCopy(p);
+        for (int i = 1; i < count; ++i)
+        {
+            pointAddVar(false, table[i - 1], d, table[i] = new PointExt());
+        }
+        return table;
+    }
+
+    private static void pointSetNeutral(PointAccum p)
+    {
+        X25519Field.zero(p.x);
+        X25519Field.one(p.y);
+        X25519Field.one(p.z);
+        X25519Field.zero(p.u);
+        X25519Field.one(p.v);
+    }
+
+    private static void pointSetNeutral(PointExt p)
+    {
+        X25519Field.zero(p.x);
+        X25519Field.one(p.y);
+        X25519Field.one(p.z);
+        X25519Field.zero(p.t);
+    }
+
+    public static void precompute()
+    {
+        synchronized (precompLock)
+        {
+            if (precompBase != null)
+            {
+                return;
+            }
+
+            // Precomputed table for the base point in verification ladder
+            {
+                PointExt b = new PointExt();
+                X25519Field.copy(B_x, 0, b.x, 0);
+                X25519Field.copy(B_y, 0, b.y, 0);
+                pointExtendXY(b);
+
+                precompBaseTable = pointPrecompVar(b, 1 << (WNAF_WIDTH_BASE - 2));
+            }
+
+            PointAccum p = new PointAccum();
+            X25519Field.copy(B_x, 0, p.x, 0);
+            X25519Field.copy(B_y, 0, p.y, 0);
+            pointExtendXY(p);
+
+            precompBase = new int[PRECOMP_BLOCKS * PRECOMP_POINTS * 3 * X25519Field.SIZE];
+
+            int off = 0;
+            for (int b = 0; b < PRECOMP_BLOCKS; ++b)
+            {
+                PointExt[] ds = new PointExt[PRECOMP_TEETH];
+
+                PointExt sum = new PointExt();
+                pointSetNeutral(sum);
+
+                for (int t = 0; t < PRECOMP_TEETH; ++t)
+                {
+                    PointExt q = pointCopy(p);
+                    pointAddVar(true, sum, q, sum);
+                    pointDouble(p);
+
+                    ds[t] = pointCopy(p);
+
+                    if (b + t != PRECOMP_BLOCKS + PRECOMP_TEETH - 2)
+                    {
+                        for (int s = 1; s < PRECOMP_SPACING; ++s)
+                        {
+                            pointDouble(p);
+                        }
+                    }
+                }
+
+                PointExt[] points = new PointExt[PRECOMP_POINTS];
+                int k = 0;
+                points[k++] = sum;
+
+                for (int t = 0; t < (PRECOMP_TEETH - 1); ++t)
+                {
+                    int size = 1 << t;
+                    for (int j = 0; j < size; ++j, ++k)
+                    {
+                        pointAddVar(false, points[k - size], ds[t], points[k] = new PointExt());
+                    }
+                }
+
+//                assert k == PRECOMP_POINTS;
+
+                for (int i = 0; i < PRECOMP_POINTS; ++i)
+                {
+                    PointExt q = points[i];
+
+                    int[] x = X25519Field.create();
+                    int[] y = X25519Field.create();
+
+                    X25519Field.add(q.z, q.z, x);
+                    // TODO[ed25519] Batch inversion
+                    X25519Field.inv(x, y);
+                    X25519Field.mul(q.x, y, x);
+                    X25519Field.mul(q.y, y, y);
+
+                    PointPrecomp r = new PointPrecomp();
+                    X25519Field.apm(y, x, r.ypx_h, r.ymx_h);
+                    X25519Field.mul(x, y, r.xyd);
+                    X25519Field.mul(r.xyd, C_d4, r.xyd);
+
+                    X25519Field.normalize(r.ypx_h);
+                    X25519Field.normalize(r.ymx_h);
+//                    X25519Field.normalize(r.xyd);
+
+                    X25519Field.copy(r.ypx_h, 0, precompBase, off);    off += X25519Field.SIZE;
+                    X25519Field.copy(r.ymx_h, 0, precompBase, off);    off += X25519Field.SIZE;
+                    X25519Field.copy(r.xyd,   0, precompBase, off);    off += X25519Field.SIZE;
+                }
+            }
+
+//            assert off == precompBase.length;
+        }
+    }
+
+    private static void pruneScalar(byte[] n, int nOff, byte[] r)
+    {
+        System.arraycopy(n, nOff, r, 0, SCALAR_BYTES);
+
+        r[0] &= 0xF8;
+        r[SCALAR_BYTES - 1] &= 0x7F;
+        r[SCALAR_BYTES - 1] |= 0x40;
+    }
+
+    private static byte[] reduceScalar(byte[] n)
+    {
+        long x00 =  decode32(n,  0)       & M32L;   // x00:32/--
+        long x01 = (decode24(n,  4) << 4) & M32L;   // x01:28/--
+        long x02 =  decode32(n,  7)       & M32L;   // x02:32/--
+        long x03 = (decode24(n, 11) << 4) & M32L;   // x03:28/--
+        long x04 =  decode32(n, 14)       & M32L;   // x04:32/--
+        long x05 = (decode24(n, 18) << 4) & M32L;   // x05:28/--
+        long x06 =  decode32(n, 21)       & M32L;   // x06:32/--
+        long x07 = (decode24(n, 25) << 4) & M32L;   // x07:28/--
+        long x08 =  decode32(n, 28)       & M32L;   // x08:32/--
+        long x09 = (decode24(n, 32) << 4) & M32L;   // x09:28/--
+        long x10 =  decode32(n, 35)       & M32L;   // x10:32/--
+        long x11 = (decode24(n, 39) << 4) & M32L;   // x11:28/--
+        long x12 =  decode32(n, 42)       & M32L;   // x12:32/--
+        long x13 = (decode24(n, 46) << 4) & M32L;   // x13:28/--
+        long x14 =  decode32(n, 49)       & M32L;   // x14:32/--
+        long x15 = (decode24(n, 53) << 4) & M32L;   // x15:28/--
+        long x16 =  decode32(n, 56)       & M32L;   // x16:32/--
+        long x17 = (decode24(n, 60) << 4) & M32L;   // x17:28/--
+        long x18 =  n[63]                 & 0xFFL;  // x18:08/--
+        long t;
+
+//        x18 += (x17 >> 28); x17 &= M28L;
+        x09 -= x18 * L0;                            // x09:34/28
+        x10 -= x18 * L1;                            // x10:33/30
+        x11 -= x18 * L2;                            // x11:35/28
+        x12 -= x18 * L3;                            // x12:32/31
+        x13 -= x18 * L4;                            // x13:28/21
+
+        x17 += (x16 >> 28); x16 &= M28L;            // x17:28/--, x16:28/--
+        x08 -= x17 * L0;                            // x08:54/32
+        x09 -= x17 * L1;                            // x09:52/51
+        x10 -= x17 * L2;                            // x10:55/34
+        x11 -= x17 * L3;                            // x11:51/36
+        x12 -= x17 * L4;                            // x12:41/--
+
+//        x16 += (x15 >> 28); x15 &= M28L;
+        x07 -= x16 * L0;                            // x07:54/28
+        x08 -= x16 * L1;                            // x08:54/53
+        x09 -= x16 * L2;                            // x09:55/53
+        x10 -= x16 * L3;                            // x10:55/52
+        x11 -= x16 * L4;                            // x11:51/41
+
+        x15 += (x14 >> 28); x14 &= M28L;            // x15:28/--, x14:28/--
+        x06 -= x15 * L0;                            // x06:54/32
+        x07 -= x15 * L1;                            // x07:54/53
+        x08 -= x15 * L2;                            // x08:56/--
+        x09 -= x15 * L3;                            // x09:55/54
+        x10 -= x15 * L4;                            // x10:55/53
+
+//        x14 += (x13 >> 28); x13 &= M28L;
+        x05 -= x14 * L0;                            // x05:54/28
+        x06 -= x14 * L1;                            // x06:54/53
+        x07 -= x14 * L2;                            // x07:56/--
+        x08 -= x14 * L3;                            // x08:56/51
+        x09 -= x14 * L4;                            // x09:56/--
+
+        x13 += (x12 >> 28); x12 &= M28L;            // x13:28/22, x12:28/--
+        x04 -= x13 * L0;                            // x04:54/49
+        x05 -= x13 * L1;                            // x05:54/53
+        x06 -= x13 * L2;                            // x06:56/--
+        x07 -= x13 * L3;                            // x07:56/52
+        x08 -= x13 * L4;                            // x08:56/52
+
+        x12 += (x11 >> 28); x11 &= M28L;            // x12:28/24, x11:28/--
+        x03 -= x12 * L0;                            // x03:54/49
+        x04 -= x12 * L1;                            // x04:54/51
+        x05 -= x12 * L2;                            // x05:56/--
+        x06 -= x12 * L3;                            // x06:56/52
+        x07 -= x12 * L4;                            // x07:56/53
+
+        x11 += (x10 >> 28); x10 &= M28L;            // x11:29/--, x10:28/--
+        x02 -= x11 * L0;                            // x02:55/32
+        x03 -= x11 * L1;                            // x03:55/--
+        x04 -= x11 * L2;                            // x04:56/55
+        x05 -= x11 * L3;                            // x05:56/52
+        x06 -= x11 * L4;                            // x06:56/53
+
+        x10 += (x09 >> 28); x09 &= M28L;            // x10:29/--, x09:28/--
+        x01 -= x10 * L0;                            // x01:55/28
+        x02 -= x10 * L1;                            // x02:55/54
+        x03 -= x10 * L2;                            // x03:56/55
+        x04 -= x10 * L3;                            // x04:57/--
+        x05 -= x10 * L4;                            // x05:56/53
+
+        x08 += (x07 >> 28); x07 &= M28L;            // x08:56/53, x07:28/--
+        x09 += (x08 >> 28); x08 &= M28L;            // x09:29/25, x08:28/--
+
+        t    = x08 >>> 27;
+        x09 += t;                                   // x09:29/26
+
+        x00 -= x09 * L0;                            // x00:55/53
+        x01 -= x09 * L1;                            // x01:55/54
+        x02 -= x09 * L2;                            // x02:57/--
+        x03 -= x09 * L3;                            // x03:57/--
+        x04 -= x09 * L4;                            // x04:57/42
+
+        x01 += (x00 >> 28); x00 &= M28L;
+        x02 += (x01 >> 28); x01 &= M28L;
+        x03 += (x02 >> 28); x02 &= M28L;
+        x04 += (x03 >> 28); x03 &= M28L;
+        x05 += (x04 >> 28); x04 &= M28L;
+        x06 += (x05 >> 28); x05 &= M28L;
+        x07 += (x06 >> 28); x06 &= M28L;
+        x08 += (x07 >> 28); x07 &= M28L;
+        x09  = (x08 >> 28); x08 &= M28L;
+
+        x09 -= t;
+
+//        assert x09 == 0L || x09 == -1L;
+
+        x00 += x09 & L0;
+        x01 += x09 & L1;
+        x02 += x09 & L2;
+        x03 += x09 & L3;
+        x04 += x09 & L4;
+
+        x01 += (x00 >> 28); x00 &= M28L;
+        x02 += (x01 >> 28); x01 &= M28L;
+        x03 += (x02 >> 28); x02 &= M28L;
+        x04 += (x03 >> 28); x03 &= M28L;
+        x05 += (x04 >> 28); x04 &= M28L;
+        x06 += (x05 >> 28); x05 &= M28L;
+        x07 += (x06 >> 28); x06 &= M28L;
+        x08 += (x07 >> 28); x07 &= M28L;
+
+        byte[] r = new byte[SCALAR_BYTES];
+        encode56(x00 | (x01 << 28), r,  0);
+        encode56(x02 | (x03 << 28), r,  7);
+        encode56(x04 | (x05 << 28), r, 14);
+        encode56(x06 | (x07 << 28), r, 21);
+        encode32((int)x08,          r, 28);
+        return r;
+    }
+
+    private static void scalarMultBase(byte[] k, PointAccum r)
+    {
+        precompute();
+
+        pointSetNeutral(r);
+
+        int[] n = new int[SCALAR_INTS];
+        decodeScalar(k, 0, n);
+
+        // Recode the scalar into signed-digit form, then group comb bits in each block
+        {
+//            int c1 = Nat.cadd(SCALAR_INTS, ~n[0] & 1, n, L, n);     assert c1 == 0;
+            Nat.cadd(SCALAR_INTS, ~n[0] & 1, n, L, n);
+//            int c2 = Nat.shiftDownBit(SCALAR_INTS, n, 1);           assert c2 == (1 << 31);
+            Nat.shiftDownBit(SCALAR_INTS, n, 1);
+
+            for (int i = 0; i < SCALAR_INTS; ++i)
+            {
+                n[i] = Interleave.shuffle2(n[i]);
+            }
+        }
+
+        PointPrecomp p = new PointPrecomp();
+
+        int cOff = (PRECOMP_SPACING - 1) * PRECOMP_TEETH;
+        for (;;)
+        {
+            for (int b = 0; b < PRECOMP_BLOCKS; ++b)
+            {
+                int w = n[b] >>> cOff;
+                int sign = (w >>> (PRECOMP_TEETH - 1)) & 1;
+                int abs = (w ^ -sign) & PRECOMP_MASK;
+
+//                assert sign == 0 || sign == 1;
+//                assert 0 <= abs && abs < PRECOMP_POINTS;
+
+                pointLookup(b, abs, p);
+
+                X25519Field.cswap(sign, p.ypx_h, p.ymx_h);
+                X25519Field.cnegate(sign, p.xyd);
+
+                pointAddPrecomp(p, r);
+            }
+
+            if ((cOff -= PRECOMP_TEETH) < 0)
+            {
+                break;
+            }
+
+            pointDouble(r);
+        }
+    }
+
+    private static void scalarMultBaseEncoded(byte[] k, byte[] r, int rOff)
+    {
+        PointAccum p = new PointAccum();
+        scalarMultBase(k, p);
+        encodePoint(p, r, rOff);
+    }
+
+    /**
+     * NOTE: Only for use by X25519
+     */
+    public static void scalarMultBaseYZ(X25519.Friend friend, byte[] k, int kOff, int[] y, int[] z)
+    {
+        if (null == friend)
+        {
+            throw new NullPointerException("This method is only for use by X25519");
+        }
+
+        byte[] n = new byte[SCALAR_BYTES];
+        pruneScalar(k, kOff, n);
+
+        PointAccum p = new PointAccum();
+        scalarMultBase(n, p);
+        X25519Field.copy(p.y, 0, y, 0);
+        X25519Field.copy(p.z, 0, z, 0);
+    }
+
+    private static void scalarMultStraussVar(int[] nb, int[] np, PointExt p, PointAccum r)
+    {
+        precompute();
+
+        final int width = 5;
+
+        byte[] ws_b = getWNAF(nb, WNAF_WIDTH_BASE);
+        byte[] ws_p = getWNAF(np, width);
+
+        PointExt[] tp = pointPrecompVar(p, 1 << (width - 2));
+
+        pointSetNeutral(r);
+
+        int bit = 255;
+        while (bit > 0 && (ws_b[bit] | ws_p[bit]) == 0)
+        {
+            --bit;
+        }
+
+        for (;;)
+        {
+            int wb = ws_b[bit];
+            if (wb != 0)
+            {
+                int sign = wb >> 31;
+                int index = (wb ^ sign) >>> 1;
+
+                pointAddVar((sign != 0), precompBaseTable[index], r);
+            }
+
+            int wp = ws_p[bit];
+            if (wp != 0)
+            {
+                int sign = wp >> 31;
+                int index = (wp ^ sign) >>> 1;
+
+                pointAddVar((sign != 0), tp[index], r);
+            }
+
+            if (--bit < 0)
+            {
+                break;
+            }
+
+            pointDouble(r);
+        }
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte[] ctx = null;
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte[] ctx = null;
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, ctx, phflag, ph, phOff, PREHASH_SIZE, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PREHASH_SIZE, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] ctx, Digest ph, byte[] sig, int sigOff)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, ctx, phflag, m, 0, m.length, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, Digest ph, byte[] sig, int sigOff)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.length, sig, sigOff);
+    }
+
+    public static boolean verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen)
+    {
+        byte[] ctx = null;
+        byte phflag = 0x00;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+    }
+
+    public static boolean verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen)
+    {
+        byte phflag = 0x00;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+    }
+
+    public static boolean verifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff)
+    {
+        byte phflag = 0x01;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PREHASH_SIZE);
+    }
+
+    public static boolean verifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, Digest ph)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.length);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed448.java b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed448.java
new file mode 100644
index 0000000..a42cc59
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/rfc8032/Ed448.java
@@ -0,0 +1,1195 @@
+package org.bouncycastle.math.ec.rfc8032;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.math.ec.rfc7748.X448;
+import org.bouncycastle.math.ec.rfc7748.X448Field;
+import org.bouncycastle.math.raw.Nat;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public abstract class Ed448
+{
+    public static final class Algorithm
+    {
+        public static final int Ed448 = 0;
+        public static final int Ed448ph = 1;
+    }
+
+    private static final long M26L = 0x03FFFFFFL;
+    private static final long M28L = 0x0FFFFFFFL;
+    private static final long M32L = 0xFFFFFFFFL;
+
+    private static final int POINT_BYTES = 57;
+    private static final int SCALAR_INTS = 14;
+    private static final int SCALAR_BYTES = SCALAR_INTS * 4 + 1;
+
+    public static final int PREHASH_SIZE = 64;
+    public static final int PUBLIC_KEY_SIZE = POINT_BYTES;
+    public static final int SECRET_KEY_SIZE = 57;
+    public static final int SIGNATURE_SIZE = POINT_BYTES + SCALAR_BYTES;
+
+    private static final byte[] DOM4_PREFIX = Strings.toByteArray("SigEd448");
+
+    private static final int[] P = new int[] { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+        0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+    private static final int[] L = new int[] { 0xAB5844F3, 0x2378C292, 0x8DC58F55, 0x216CC272, 0xAED63690, 0xC44EDB49, 0x7CCA23E9,
+        0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x3FFFFFFF };
+
+    private static final int L_0 = 0x04A7BB0D;      // L_0:26/24
+    private static final int L_1 = 0x0873D6D5;      // L_1:27/23
+    private static final int L_2 = 0x0A70AADC;      // L_2:27/26
+    private static final int L_3 = 0x03D8D723;      // L_3:26/--
+    private static final int L_4 = 0x096FDE93;      // L_4:27/25
+    private static final int L_5 = 0x0B65129C;      // L_5:27/26
+    private static final int L_6 = 0x063BB124;      // L_6:27/--
+    private static final int L_7 = 0x08335DC1;      // L_7:27/22
+
+    private static final int L4_0 = 0x029EEC34;     // L4_0:25/24
+    private static final int L4_1 = 0x01CF5B55;     // L4_1:25/--
+    private static final int L4_2 = 0x09C2AB72;     // L4_2:27/25
+    private static final int L4_3 = 0x0F635C8E;     // L4_3:28/--
+    private static final int L4_4 = 0x05BF7A4C;     // L4_4:26/25
+    private static final int L4_5 = 0x0D944A72;     // L4_5:28/--
+    private static final int L4_6 = 0x08EEC492;     // L4_6:27/24
+    private static final int L4_7 = 0x20CD7705;     // L4_7:29/24
+
+    private static final int[] B_x = new int[] { 0x070CC05E, 0x026A82BC, 0x00938E26, 0x080E18B0, 0x0511433B, 0x0F72AB66, 0x0412AE1A,
+        0x0A3D3A46, 0x0A6DE324, 0x00F1767E, 0x04657047, 0x036DA9E1, 0x05A622BF, 0x0ED221D1, 0x066BED0D, 0x04F1970C };
+    private static final int[] B_y = new int[] { 0x0230FA14, 0x008795BF, 0x07C8AD98, 0x0132C4ED, 0x09C4FDBD, 0x01CE67C3, 0x073AD3FF,
+        0x005A0C2D, 0x07789C1E, 0x0A398408, 0x0A73736C, 0x0C7624BE, 0x003756C9, 0x02488762, 0x016EB6BC, 0x0693F467 };
+    private static final int C_d = -39081;
+
+    private static final int WNAF_WIDTH_BASE = 7;
+
+    private static final int PRECOMP_BLOCKS = 5;
+    private static final int PRECOMP_TEETH = 5;
+    private static final int PRECOMP_SPACING = 18;
+    private static final int PRECOMP_POINTS = 1 << (PRECOMP_TEETH - 1);
+    private static final int PRECOMP_MASK = PRECOMP_POINTS - 1;
+
+    private static Object precompLock = new Object();
+    // TODO[ed448] Convert to PointPrecomp
+    private static PointExt[] precompBaseTable = null;
+    private static int[] precompBase = null;
+
+    private static class PointExt
+    {
+        int[] x = X448Field.create();
+        int[] y = X448Field.create();
+        int[] z = X448Field.create();
+    }
+
+    private static class PointPrecomp
+    {
+        int[] x = X448Field.create();
+        int[] y = X448Field.create();
+    }
+
+    private static byte[] calculateS(byte[] r, byte[] k, byte[] s)
+    {
+        int[] t = new int[SCALAR_INTS * 2];     decodeScalar(r, 0, t);
+        int[] u = new int[SCALAR_INTS];         decodeScalar(k, 0, u);
+        int[] v = new int[SCALAR_INTS];         decodeScalar(s, 0, v);
+
+        Nat.mulAddTo(14, u, v, t);
+
+        byte[] result = new byte[SCALAR_BYTES * 2];
+        for (int i = 0; i < t.length; ++i)
+        {
+            encode32(t[i], result, i * 4);
+        }
+        return reduceScalar(result);
+    }
+
+    private static boolean checkContextVar(byte[] ctx)
+    {
+        return ctx != null && ctx.length < 256;
+    }
+
+    private static boolean checkPointVar(byte[] p)
+    {
+        if ((p[POINT_BYTES - 1] & 0x7F) != 0x00)
+        {
+            return false;
+        }
+
+        int[] t = new int[14];
+        decode32(p, 0, t, 0, 14);
+        return !Nat.gte(14, t, P);
+    }
+
+    private static boolean checkScalarVar(byte[] s)
+    {
+        if (s[SCALAR_BYTES - 1] != 0x00)
+        {
+            return false;
+        }
+
+        int[] n = new int[SCALAR_INTS];
+        decodeScalar(s, 0, n);
+        return !Nat.gte(SCALAR_INTS, n, L);
+    }
+
+    public static Xof createPrehash()
+    {
+        return createXof();
+    }
+
+    private static Xof createXof()
+    {
+        return new SHAKEDigest(256);
+    }
+
+    private static int decode16(byte[] bs, int off)
+    {
+        int n = bs[off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        return n;
+    }
+
+    private static int decode24(byte[] bs, int off)
+    {
+        int n = bs[  off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        return n;
+    }
+
+    private static int decode32(byte[] bs, int off)
+    {
+        int n = bs[off] & 0xFF;
+        n |= (bs[++off] & 0xFF) << 8;
+        n |= (bs[++off] & 0xFF) << 16;
+        n |=  bs[++off]         << 24;
+        return n;
+    }
+
+    private static void decode32(byte[] bs, int bsOff, int[] n, int nOff, int nLen)
+    {
+        for (int i = 0; i < nLen; ++i)
+        {
+            n[nOff + i] = decode32(bs, bsOff + i * 4);
+        }
+    }
+
+    private static boolean decodePointVar(byte[] p, int pOff, boolean negate, PointExt r)
+    {
+        byte[] py = Arrays.copyOfRange(p, pOff, pOff + POINT_BYTES);
+        if (!checkPointVar(py))
+        {
+            return false;
+        }
+
+        int x_0 = (py[POINT_BYTES - 1] & 0x80) >>> 7;
+        py[POINT_BYTES - 1] &= 0x7F;
+
+        X448Field.decode(py, 0, r.y);
+
+        int[] u = X448Field.create();
+        int[] v = X448Field.create();
+
+        X448Field.sqr(r.y, u);
+        X448Field.mul(u, -C_d, v);
+        X448Field.negate(u, u);
+        X448Field.addOne(u);
+        X448Field.addOne(v);
+
+        if (!X448Field.sqrtRatioVar(u, v, r.x))
+        {
+            return false;
+        }
+
+        X448Field.normalize(r.x);
+        if (x_0 == 1 && X448Field.isZeroVar(r.x))
+        {
+            return false;
+        }
+
+        if (negate ^ (x_0 != (r.x[0] & 1)))
+        {
+            X448Field.negate(r.x, r.x);
+        }
+
+        pointExtendXY(r);
+        return true;
+    }
+
+    private static void decodeScalar(byte[] k, int kOff, int[] n)
+    {
+//        assert k[kOff + SCALAR_BYTES - 1] == 0x00;
+
+        decode32(k, kOff, n, 0, SCALAR_INTS);
+    }
+
+    private static void dom4(Xof d, byte x, byte[] y)
+    {
+        d.update(DOM4_PREFIX, 0, DOM4_PREFIX.length);
+        d.update(x);
+        d.update((byte)y.length);
+        d.update(y, 0, y.length);
+    }
+
+    private static void encode24(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+    }
+
+    private static void encode32(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n       );
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n >>> 16);
+        bs[++off] = (byte)(n >>> 24);
+    }
+
+    private static void encode56(long n, byte[] bs, int off)
+    {
+        encode32((int)n, bs, off);
+        encode24((int)(n >>> 32), bs, off + 4);
+    }
+
+    private static void encodePoint(PointExt p, byte[] r, int rOff)
+    {
+        int[] x = X448Field.create();
+        int[] y = X448Field.create();
+
+        X448Field.inv(p.z, y);
+        X448Field.mul(p.x, y, x);
+        X448Field.mul(p.y, y, y);
+        X448Field.normalize(x);
+        X448Field.normalize(y);
+
+        X448Field.encode(y, r, rOff);
+        r[rOff + POINT_BYTES - 1] = (byte)((x[0] & 1) << 7);
+    }
+
+    public static void generatePrivateKey(SecureRandom random, byte[] k)
+    {
+        random.nextBytes(k);
+    }
+
+    public static void generatePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff)
+    {
+        Xof d = createXof();
+        byte[] h = new byte[SCALAR_BYTES * 2];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0, h.length);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        scalarMultBaseEncoded(s, pk, pkOff);
+    }
+
+    private static byte[] getWNAF(int[] n, int width)
+    {
+//        assert n[SCALAR_INTS - 1] >>> 31 == 0;
+
+        int[] t = new int[SCALAR_INTS * 2];
+        {
+            int tPos = t.length, c = 0;
+            int i = SCALAR_INTS;
+            while (--i >= 0)
+            {
+                int next = n[i];
+                t[--tPos] = (next >>> 16) | (c << 16);
+                t[--tPos] = c = next;
+            }
+        }
+
+        byte[] ws = new byte[448];
+
+        final int pow2 = 1 << width;
+        final int mask = pow2 - 1;
+        final int sign = pow2 >>> 1;
+
+        int j = 0, carry = 0;
+        for (int i = 0; i < t.length; ++i, j -= 16)
+        {
+            int word = t[i];
+            while (j < 16)
+            {
+                int word16 = word >>> j;
+                int bit = word16 & 1;
+
+                if (bit == carry)
+                {
+                    ++j;
+                    continue;
+                }
+
+                int digit = (word16 & mask) + carry;
+                carry = digit & sign;
+                digit -= (carry << 1);
+                carry >>>= (width - 1);
+
+                ws[(i << 4) + j] = (byte)digit;
+
+                j += width;
+            }
+        }
+
+//        assert carry == 0;
+
+        return ws;
+    }
+
+    private static void implSign(Xof d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        dom4(d, phflag, ctx);
+        d.update(h, SCALAR_BYTES, SCALAR_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0, h.length);
+
+        byte[] r = reduceScalar(h);
+        byte[] R = new byte[POINT_BYTES];
+        scalarMultBaseEncoded(r, R, 0);
+
+        dom4(d, phflag, ctx);
+        d.update(R, 0, POINT_BYTES);
+        d.update(pk, pkOff, POINT_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0, h.length);
+
+        byte[] k = reduceScalar(h);
+        byte[] S = calculateS(r, k, s);
+
+        System.arraycopy(R, 0, sig, sigOff, POINT_BYTES);
+        System.arraycopy(S, 0, sig, sigOff + POINT_BYTES, SCALAR_BYTES);
+    }
+
+    private static void implSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen,
+        byte[] sig, int sigOff)
+    {
+        if (!checkContextVar(ctx))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        Xof d = createXof();
+        byte[] h = new byte[SCALAR_BYTES * 2];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0, h.length);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        byte[] pk = new byte[POINT_BYTES];
+        scalarMultBaseEncoded(s, pk, 0);
+
+        implSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    private static void implSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        if (!checkContextVar(ctx))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        Xof d = createXof();
+        byte[] h = new byte[SCALAR_BYTES * 2];
+
+        d.update(sk, skOff, SECRET_KEY_SIZE);
+        d.doFinal(h, 0, h.length);
+
+        byte[] s = new byte[SCALAR_BYTES];
+        pruneScalar(h, 0, s);
+
+        implSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    private static boolean implVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+        byte[] m, int mOff, int mLen)
+    {
+        if (!checkContextVar(ctx))
+        {
+            throw new IllegalArgumentException("ctx");
+        }
+
+        byte[] R = Arrays.copyOfRange(sig, sigOff, sigOff + POINT_BYTES);
+        byte[] S = Arrays.copyOfRange(sig, sigOff + POINT_BYTES, sigOff + SIGNATURE_SIZE);
+
+        if (!checkPointVar(R))
+        {
+            return false;
+        }
+        if (!checkScalarVar(S))
+        {
+            return false;
+        }
+
+        PointExt pA = new PointExt();
+        if (!decodePointVar(pk, pkOff, true, pA))
+        {
+            return false;
+        }
+
+        Xof d = createXof();
+        byte[] h = new byte[SCALAR_BYTES * 2];
+
+        dom4(d, phflag, ctx);
+        d.update(R, 0, POINT_BYTES);
+        d.update(pk, pkOff, POINT_BYTES);
+        d.update(m, mOff, mLen);
+        d.doFinal(h, 0, h.length);
+
+        byte[] k = reduceScalar(h);
+
+        int[] nS = new int[SCALAR_INTS];
+        decodeScalar(S, 0, nS);
+
+        int[] nA = new int[SCALAR_INTS];
+        decodeScalar(k, 0, nA);
+
+        PointExt pR = new PointExt();
+        scalarMultStraussVar(nS, nA, pA, pR);
+
+        byte[] check = new byte[POINT_BYTES];
+        encodePoint(pR, check, 0);
+
+        return Arrays.areEqual(check, R);
+    }
+
+    private static void pointAddVar(boolean negate, PointExt p, PointExt r)
+    {
+        int[] A = X448Field.create();
+        int[] B = X448Field.create();
+        int[] C = X448Field.create();
+        int[] D = X448Field.create();
+        int[] E = X448Field.create();
+        int[] F = X448Field.create();
+        int[] G = X448Field.create();
+        int[] H = X448Field.create();
+
+        int[] b, e, f, g;
+        if (negate)
+        {
+            b = E; e = B; f = G; g = F;
+            X448Field.sub(p.y, p.x, H);
+        }
+        else
+        {
+            b = B; e = E; f = F; g = G;
+            X448Field.add(p.y, p.x, H);
+        }
+
+        X448Field.mul(p.z, r.z, A);
+        X448Field.sqr(A, B);
+        X448Field.mul(p.x, r.x, C);
+        X448Field.mul(p.y, r.y, D);
+        X448Field.mul(C, D, E);
+        X448Field.mul(E, -C_d, E);
+//        X448Field.apm(B, E, F, G);
+        X448Field.add(B, E, f);
+        X448Field.sub(B, E, g);
+        X448Field.add(r.x, r.y, E);
+        X448Field.mul(H, E, H);
+//        X448Field.apm(D, C, B, E);
+        X448Field.add(D, C, b);
+        X448Field.sub(D, C, e);
+        X448Field.carry(b);
+        X448Field.sub(H, B, H);
+        X448Field.mul(H, A, H);
+        X448Field.mul(E, A, E);
+        X448Field.mul(F, H, r.x);
+        X448Field.mul(E, G, r.y);
+        X448Field.mul(F, G, r.z);
+    }
+
+    private static void pointAddPrecomp(PointPrecomp p, PointExt r)
+    {
+        int[] B = X448Field.create();
+        int[] C = X448Field.create();
+        int[] D = X448Field.create();
+        int[] E = X448Field.create();
+        int[] F = X448Field.create();
+        int[] G = X448Field.create();
+        int[] H = X448Field.create();
+
+        X448Field.sqr(r.z, B);
+        X448Field.mul(p.x, r.x, C);
+        X448Field.mul(p.y, r.y, D);
+        X448Field.mul(C, D, E);
+        X448Field.mul(E, -C_d, E);
+//        X448Field.apm(B, E, F, G);
+        X448Field.add(B, E, F);
+        X448Field.sub(B, E, G);
+        X448Field.add(p.x, p.y, B);
+        X448Field.add(r.x, r.y, E);
+        X448Field.mul(B, E, H);
+//        X448Field.apm(D, C, B, E);
+        X448Field.add(D, C, B);
+        X448Field.sub(D, C, E);
+        X448Field.carry(B);
+        X448Field.sub(H, B, H);
+        X448Field.mul(H, r.z, H);
+        X448Field.mul(E, r.z, E);
+        X448Field.mul(F, H, r.x);
+        X448Field.mul(E, G, r.y);
+        X448Field.mul(F, G, r.z);
+    }
+
+    private static PointExt pointCopy(PointExt p)
+    {
+        PointExt r = new PointExt();
+        X448Field.copy(p.x, 0, r.x, 0);
+        X448Field.copy(p.y, 0, r.y, 0);
+        X448Field.copy(p.z, 0, r.z, 0);
+        return r;
+    }
+
+    private static void pointDouble(PointExt r)
+    {
+        int[] B = X448Field.create();
+        int[] C = X448Field.create();
+        int[] D = X448Field.create();
+        int[] E = X448Field.create();
+        int[] H = X448Field.create();
+        int[] J = X448Field.create();
+
+        X448Field.add(r.x, r.y, B);
+        X448Field.sqr(B, B);
+        X448Field.sqr(r.x, C);
+        X448Field.sqr(r.y, D);
+        X448Field.add(C, D, E);
+        X448Field.carry(E);
+        X448Field.sqr(r.z, H);
+        X448Field.add(H, H, H);
+        X448Field.carry(H);
+        X448Field.sub(E, H, J);
+        X448Field.sub(B, E, B);
+        X448Field.sub(C, D, C);
+        X448Field.mul(B, J, r.x);
+        X448Field.mul(E, C, r.y);
+        X448Field.mul(E, J, r.z);
+    }
+
+    private static void pointExtendXY(PointExt p)
+    {
+        X448Field.one(p.z);
+    }
+
+    private static void pointLookup(int block, int index, PointPrecomp p)
+    {
+//        assert 0 <= block && block < PRECOMP_BLOCKS;
+//        assert 0 <= index && index < PRECOMP_POINTS;
+
+        int off = block * PRECOMP_POINTS * 2 * X448Field.SIZE;
+
+        for (int i = 0; i < PRECOMP_POINTS; ++i)
+        {
+            int mask = ((i ^ index) - 1) >> 31;
+            Nat.cmov(X448Field.SIZE, mask, precompBase, off, p.x, 0);   off += X448Field.SIZE;
+            Nat.cmov(X448Field.SIZE, mask, precompBase, off, p.y, 0);   off += X448Field.SIZE;
+        }
+    }
+
+    private static PointExt[] pointPrecompVar(PointExt p, int count)
+    {
+//        assert count > 0;
+
+        PointExt d = pointCopy(p);
+        pointDouble(d);
+
+        PointExt[] table = new PointExt[count];
+        table[0] = pointCopy(p);
+        for (int i = 1; i < count; ++i)
+        {
+            table[i] = pointCopy(table[i - 1]);
+            pointAddVar(false, d, table[i]);
+        }
+        return table;
+    }
+
+    private static void pointSetNeutral(PointExt p)
+    {
+        X448Field.zero(p.x);
+        X448Field.one(p.y);
+        X448Field.one(p.z);
+    }
+
+    public static void precompute()
+    {
+        synchronized (precompLock)
+        {
+            if (precompBase != null)
+            {
+                return;
+            }
+
+            PointExt p = new PointExt();
+            X448Field.copy(B_x, 0, p.x, 0);
+            X448Field.copy(B_y, 0, p.y, 0);
+            pointExtendXY(p);
+
+            precompBaseTable = pointPrecompVar(p, 1 << (WNAF_WIDTH_BASE - 2));
+
+            precompBase = new int[PRECOMP_BLOCKS * PRECOMP_POINTS * 2 * X448Field.SIZE];
+
+            int off = 0;
+            for (int b = 0; b < PRECOMP_BLOCKS; ++b)
+            {
+                PointExt[] ds = new PointExt[PRECOMP_TEETH];
+
+                PointExt sum = new PointExt();
+                pointSetNeutral(sum);
+
+                for (int t = 0; t < PRECOMP_TEETH; ++t)
+                {
+                    pointAddVar(true, p, sum);
+                    pointDouble(p);
+
+                    ds[t] = pointCopy(p);
+
+                    if (b + t != PRECOMP_BLOCKS + PRECOMP_TEETH - 2)
+                    {
+                        for (int s = 1; s < PRECOMP_SPACING; ++s)
+                        {
+                            pointDouble(p);
+                        }
+                    }
+                }
+
+                PointExt[] points = new PointExt[PRECOMP_POINTS];
+                int k = 0;
+                points[k++] = sum;
+
+                for (int t = 0; t < (PRECOMP_TEETH - 1); ++t)
+                {
+                    int size = 1 << t;
+                    for (int j = 0; j < size; ++j, ++k)
+                    {
+                        points[k] = pointCopy(points[k - size]);
+                        pointAddVar(false, ds[t], points[k]);
+                    }
+                }
+
+//                assert k == PRECOMP_POINTS;
+
+                for (int i = 0; i < PRECOMP_POINTS; ++i)
+                {
+                    PointExt q = points[i];
+                    // TODO[ed448] Batch inversion
+                    X448Field.inv(q.z, q.z);
+                    X448Field.mul(q.x, q.z, q.x);
+                    X448Field.mul(q.y, q.z, q.y);
+
+//                    X448Field.normalize(q.x);
+//                    X448Field.normalize(q.y);
+
+                    X448Field.copy(q.x, 0, precompBase, off);   off += X448Field.SIZE;
+                    X448Field.copy(q.y, 0, precompBase, off);   off += X448Field.SIZE;
+                }
+            }
+
+//            assert off == precompBase.length;
+        }
+    }
+
+    private static void pruneScalar(byte[] n, int nOff, byte[] r)
+    {
+        System.arraycopy(n, nOff, r, 0, SCALAR_BYTES - 1);
+
+        r[0] &= 0xFC;
+        r[SCALAR_BYTES - 2] |= 0x80;
+        r[SCALAR_BYTES - 1]  = 0x00;
+    }
+
+    private static byte[] reduceScalar(byte[] n)
+    {
+        long x00 =  decode32(n,   0)       & M32L;  // x00:32/--
+        long x01 = (decode24(n,   4) << 4) & M32L;  // x01:28/--
+        long x02 =  decode32(n,   7)       & M32L;  // x02:32/--
+        long x03 = (decode24(n,  11) << 4) & M32L;  // x03:28/--
+        long x04 =  decode32(n,  14)       & M32L;  // x04:32/--
+        long x05 = (decode24(n,  18) << 4) & M32L;  // x05:28/--
+        long x06 =  decode32(n,  21)       & M32L;  // x06:32/--
+        long x07 = (decode24(n,  25) << 4) & M32L;  // x07:28/--
+        long x08 =  decode32(n,  28)       & M32L;  // x08:32/--
+        long x09 = (decode24(n,  32) << 4) & M32L;  // x09:28/--
+        long x10 =  decode32(n,  35)       & M32L;  // x10:32/--
+        long x11 = (decode24(n,  39) << 4) & M32L;  // x11:28/--
+        long x12 =  decode32(n,  42)       & M32L;  // x12:32/--
+        long x13 = (decode24(n,  46) << 4) & M32L;  // x13:28/--
+        long x14 =  decode32(n,  49)       & M32L;  // x14:32/--
+        long x15 = (decode24(n,  53) << 4) & M32L;  // x15:28/--
+        long x16 =  decode32(n,  56)       & M32L;  // x16:32/--
+        long x17 = (decode24(n,  60) << 4) & M32L;  // x17:28/--
+        long x18 =  decode32(n,  63)       & M32L;  // x18:32/--
+        long x19 = (decode24(n,  67) << 4) & M32L;  // x19:28/--
+        long x20 =  decode32(n,  70)       & M32L;  // x20:32/--
+        long x21 = (decode24(n,  74) << 4) & M32L;  // x21:28/--
+        long x22 =  decode32(n,  77)       & M32L;  // x22:32/--
+        long x23 = (decode24(n,  81) << 4) & M32L;  // x23:28/--
+        long x24 =  decode32(n,  84)       & M32L;  // x24:32/--
+        long x25 = (decode24(n,  88) << 4) & M32L;  // x25:28/--
+        long x26 =  decode32(n,  91)       & M32L;  // x26:32/--
+        long x27 = (decode24(n,  95) << 4) & M32L;  // x27:28/--
+        long x28 =  decode32(n,  98)       & M32L;  // x28:32/--
+        long x29 = (decode24(n, 102) << 4) & M32L;  // x29:28/--
+        long x30 =  decode32(n, 105)       & M32L;  // x30:32/--
+        long x31 = (decode24(n, 109) << 4) & M32L;  // x31:28/--
+        long x32 =  decode16(n, 112)       & M32L;  // x32:16/--
+
+//        x32 += (x31 >>> 28); x31 &= M28L;
+        x16 += x32 * L4_0;                          // x16:42/--
+        x17 += x32 * L4_1;                          // x17:41/28
+        x18 += x32 * L4_2;                          // x18:43/42
+        x19 += x32 * L4_3;                          // x19:44/28
+        x20 += x32 * L4_4;                          // x20:43/--
+        x21 += x32 * L4_5;                          // x21:44/28
+        x22 += x32 * L4_6;                          // x22:43/41
+        x23 += x32 * L4_7;                          // x23:45/41
+
+        x31 += (x30 >>> 28); x30 &= M28L;           // x31:28/--, x30:28/--
+        x15 += x31 * L4_0;                          // x15:54/--
+        x16 += x31 * L4_1;                          // x16:53/42
+        x17 += x31 * L4_2;                          // x17:55/54
+        x18 += x31 * L4_3;                          // x18:56/44
+        x19 += x31 * L4_4;                          // x19:55/--
+        x20 += x31 * L4_5;                          // x20:56/43
+        x21 += x31 * L4_6;                          // x21:55/53
+        x22 += x31 * L4_7;                          // x22:57/53
+
+//        x30 += (x29 >>> 28); x29 &= M28L;
+        x14 += x30 * L4_0;                          // x14:54/--
+        x15 += x30 * L4_1;                          // x15:54/53
+        x16 += x30 * L4_2;                          // x16:56/--
+        x17 += x30 * L4_3;                          // x17:57/--
+        x18 += x30 * L4_4;                          // x18:56/55
+        x19 += x30 * L4_5;                          // x19:56/55
+        x20 += x30 * L4_6;                          // x20:57/--
+        x21 += x30 * L4_7;                          // x21:57/56
+
+        x29 += (x28 >>> 28); x28 &= M28L;           // x29:28/--, x28:28/--
+        x13 += x29 * L4_0;                          // x13:54/--
+        x14 += x29 * L4_1;                          // x14:54/53
+        x15 += x29 * L4_2;                          // x15:56/--
+        x16 += x29 * L4_3;                          // x16:57/--
+        x17 += x29 * L4_4;                          // x17:57/55
+        x18 += x29 * L4_5;                          // x18:57/55
+        x19 += x29 * L4_6;                          // x19:57/52
+        x20 += x29 * L4_7;                          // x20:58/52
+
+//        x28 += (x27 >>> 28); x27 &= M28L;
+        x12 += x28 * L4_0;                          // x12:54/--
+        x13 += x28 * L4_1;                          // x13:54/53
+        x14 += x28 * L4_2;                          // x14:56/--
+        x15 += x28 * L4_3;                          // x15:57/--
+        x16 += x28 * L4_4;                          // x16:57/55
+        x17 += x28 * L4_5;                          // x17:58/--
+        x18 += x28 * L4_6;                          // x18:58/--
+        x19 += x28 * L4_7;                          // x19:58/53
+
+        x27 += (x26 >>> 28); x26 &= M28L;           // x27:28/--, x26:28/--
+        x11 += x27 * L4_0;                          // x11:54/--
+        x12 += x27 * L4_1;                          // x12:54/53
+        x13 += x27 * L4_2;                          // x13:56/--
+        x14 += x27 * L4_3;                          // x14:57/--
+        x15 += x27 * L4_4;                          // x15:57/55
+        x16 += x27 * L4_5;                          // x16:58/--
+        x17 += x27 * L4_6;                          // x17:58/56
+        x18 += x27 * L4_7;                          // x18:59/--
+
+//        x26 += (x25 >>> 28); x25 &= M28L;
+        x10 += x26 * L4_0;                          // x10:54/--
+        x11 += x26 * L4_1;                          // x11:54/53
+        x12 += x26 * L4_2;                          // x12:56/--
+        x13 += x26 * L4_3;                          // x13:57/--
+        x14 += x26 * L4_4;                          // x14:57/55
+        x15 += x26 * L4_5;                          // x15:58/--
+        x16 += x26 * L4_6;                          // x16:58/56
+        x17 += x26 * L4_7;                          // x17:59/--
+
+        x25 += (x24 >>> 28); x24 &= M28L;           // x25:28/--, x24:28/--
+        x09 += x25 * L4_0;                          // x09:54/--
+        x10 += x25 * L4_1;                          // x10:54/53
+        x11 += x25 * L4_2;                          // x11:56/--
+        x12 += x25 * L4_3;                          // x12:57/--
+        x13 += x25 * L4_4;                          // x13:57/55
+        x14 += x25 * L4_5;                          // x14:58/--
+        x15 += x25 * L4_6;                          // x15:58/56
+        x16 += x25 * L4_7;                          // x16:59/--
+
+        x21 += (x20 >>> 28); x20 &= M28L;           // x21:58/--, x20:28/--
+        x22 += (x21 >>> 28); x21 &= M28L;           // x22:57/54, x21:28/--
+        x23 += (x22 >>> 28); x22 &= M28L;           // x23:45/42, x22:28/--
+        x24 += (x23 >>> 28); x23 &= M28L;           // x24:28/18, x23:28/--
+
+        x08 += x24 * L4_0;                          // x08:54/--
+        x09 += x24 * L4_1;                          // x09:55/--
+        x10 += x24 * L4_2;                          // x10:56/46
+        x11 += x24 * L4_3;                          // x11:57/46
+        x12 += x24 * L4_4;                          // x12:57/55
+        x13 += x24 * L4_5;                          // x13:58/--
+        x14 += x24 * L4_6;                          // x14:58/56
+        x15 += x24 * L4_7;                          // x15:59/--
+
+        x07 += x23 * L4_0;                          // x07:54/--
+        x08 += x23 * L4_1;                          // x08:54/53
+        x09 += x23 * L4_2;                          // x09:56/53
+        x10 += x23 * L4_3;                          // x10:57/46
+        x11 += x23 * L4_4;                          // x11:57/55
+        x12 += x23 * L4_5;                          // x12:58/--
+        x13 += x23 * L4_6;                          // x13:58/56
+        x14 += x23 * L4_7;                          // x14:59/--
+
+        x06 += x22 * L4_0;                          // x06:54/--
+        x07 += x22 * L4_1;                          // x07:54/53
+        x08 += x22 * L4_2;                          // x08:56/--
+        x09 += x22 * L4_3;                          // x09:57/53
+        x10 += x22 * L4_4;                          // x10:57/55
+        x11 += x22 * L4_5;                          // x11:58/--
+        x12 += x22 * L4_6;                          // x12:58/56
+        x13 += x22 * L4_7;                          // x13:59/--
+
+        x18 += (x17 >>> 28); x17 &= M28L;           // x18:59/31, x17:28/--
+        x19 += (x18 >>> 28); x18 &= M28L;           // x19:58/54, x18:28/--
+        x20 += (x19 >>> 28); x19 &= M28L;           // x20:30/29, x19:28/--
+        x21 += (x20 >>> 28); x20 &= M28L;           // x21:28/03, x20:28/--
+
+        x05 += x21 * L4_0;                          // x05:54/--
+        x06 += x21 * L4_1;                          // x06:55/--
+        x07 += x21 * L4_2;                          // x07:56/31
+        x08 += x21 * L4_3;                          // x08:57/31
+        x09 += x21 * L4_4;                          // x09:57/56
+        x10 += x21 * L4_5;                          // x10:58/--
+        x11 += x21 * L4_6;                          // x11:58/56
+        x12 += x21 * L4_7;                          // x12:59/--
+
+        x04 += x20 * L4_0;                          // x04:54/--
+        x05 += x20 * L4_1;                          // x05:54/53
+        x06 += x20 * L4_2;                          // x06:56/53
+        x07 += x20 * L4_3;                          // x07:57/31
+        x08 += x20 * L4_4;                          // x08:57/55
+        x09 += x20 * L4_5;                          // x09:58/--
+        x10 += x20 * L4_6;                          // x10:58/56
+        x11 += x20 * L4_7;                          // x11:59/--
+
+        x03 += x19 * L4_0;                          // x03:54/--
+        x04 += x19 * L4_1;                          // x04:54/53
+        x05 += x19 * L4_2;                          // x05:56/--
+        x06 += x19 * L4_3;                          // x06:57/53
+        x07 += x19 * L4_4;                          // x07:57/55
+        x08 += x19 * L4_5;                          // x08:58/--
+        x09 += x19 * L4_6;                          // x09:58/56
+        x10 += x19 * L4_7;                          // x10:59/--
+
+        x15 += (x14 >>> 28); x14 &= M28L;           // x15:59/31, x14:28/--
+        x16 += (x15 >>> 28); x15 &= M28L;           // x16:59/32, x15:28/--
+        x17 += (x16 >>> 28); x16 &= M28L;           // x17:31/29, x16:28/--
+        x18 += (x17 >>> 28); x17 &= M28L;           // x18:28/04, x17:28/--
+
+        x02 += x18 * L4_0;                          // x02:54/--
+        x03 += x18 * L4_1;                          // x03:55/--
+        x04 += x18 * L4_2;                          // x04:56/32
+        x05 += x18 * L4_3;                          // x05:57/32
+        x06 += x18 * L4_4;                          // x06:57/56
+        x07 += x18 * L4_5;                          // x07:58/--
+        x08 += x18 * L4_6;                          // x08:58/56
+        x09 += x18 * L4_7;                          // x09:59/--
+
+        x01 += x17 * L4_0;                          // x01:54/--
+        x02 += x17 * L4_1;                          // x02:54/53
+        x03 += x17 * L4_2;                          // x03:56/53
+        x04 += x17 * L4_3;                          // x04:57/32
+        x05 += x17 * L4_4;                          // x05:57/55
+        x06 += x17 * L4_5;                          // x06:58/--
+        x07 += x17 * L4_6;                          // x07:58/56
+        x08 += x17 * L4_7;                          // x08:59/--
+
+        x16 *= 4;
+        x16 += (x15 >>> 26); x15 &= M26L;
+        x16 += 1;                                   // x16:30/01
+
+        x00 += x16 * L_0;
+        x01 += x16 * L_1;
+        x02 += x16 * L_2;
+        x03 += x16 * L_3;
+        x04 += x16 * L_4;
+        x05 += x16 * L_5;
+        x06 += x16 * L_6;
+        x07 += x16 * L_7;
+
+        x01 += (x00 >>> 28); x00 &= M28L;
+        x02 += (x01 >>> 28); x01 &= M28L;
+        x03 += (x02 >>> 28); x02 &= M28L;
+        x04 += (x03 >>> 28); x03 &= M28L;
+        x05 += (x04 >>> 28); x04 &= M28L;
+        x06 += (x05 >>> 28); x05 &= M28L;
+        x07 += (x06 >>> 28); x06 &= M28L;
+        x08 += (x07 >>> 28); x07 &= M28L;
+        x09 += (x08 >>> 28); x08 &= M28L;
+        x10 += (x09 >>> 28); x09 &= M28L;
+        x11 += (x10 >>> 28); x10 &= M28L;
+        x12 += (x11 >>> 28); x11 &= M28L;
+        x13 += (x12 >>> 28); x12 &= M28L;
+        x14 += (x13 >>> 28); x13 &= M28L;
+        x15 += (x14 >>> 28); x14 &= M28L;
+        x16  = (x15 >>> 26); x15 &= M26L;
+
+        x16 -= 1;
+
+//        assert x16 == 0L || x16 == -1L;
+
+        x00 -= x16 & L_0;
+        x01 -= x16 & L_1;
+        x02 -= x16 & L_2;
+        x03 -= x16 & L_3;
+        x04 -= x16 & L_4;
+        x05 -= x16 & L_5;
+        x06 -= x16 & L_6;
+        x07 -= x16 & L_7;
+
+        x01 += (x00 >> 28); x00 &= M28L;
+        x02 += (x01 >> 28); x01 &= M28L;
+        x03 += (x02 >> 28); x02 &= M28L;
+        x04 += (x03 >> 28); x03 &= M28L;
+        x05 += (x04 >> 28); x04 &= M28L;
+        x06 += (x05 >> 28); x05 &= M28L;
+        x07 += (x06 >> 28); x06 &= M28L;
+        x08 += (x07 >> 28); x07 &= M28L;
+        x09 += (x08 >> 28); x08 &= M28L;
+        x10 += (x09 >> 28); x09 &= M28L;
+        x11 += (x10 >> 28); x10 &= M28L;
+        x12 += (x11 >> 28); x11 &= M28L;
+        x13 += (x12 >> 28); x12 &= M28L;
+        x14 += (x13 >> 28); x13 &= M28L;
+        x15 += (x14 >> 28); x14 &= M28L;
+
+//        assert x15 >>> 26 == 0L;
+
+        byte[] r = new byte[SCALAR_BYTES];
+        encode56(x00 | (x01 << 28), r,  0);
+        encode56(x02 | (x03 << 28), r,  7);
+        encode56(x04 | (x05 << 28), r, 14);
+        encode56(x06 | (x07 << 28), r, 21);
+        encode56(x08 | (x09 << 28), r, 28);
+        encode56(x10 | (x11 << 28), r, 35);
+        encode56(x12 | (x13 << 28), r, 42);
+        encode56(x14 | (x15 << 28), r, 49);
+//        r[SCALAR_BYTES - 1] = 0;
+        return r;
+    }
+
+    private static void scalarMultBase(byte[] k, PointExt r)
+    {
+        precompute();
+
+        pointSetNeutral(r);
+
+        int[] n = new int[SCALAR_INTS + 1];
+        decodeScalar(k, 0, n);
+
+        // Recode the scalar into signed-digit form
+        {
+            n[SCALAR_INTS] = 4 + Nat.cadd(SCALAR_INTS, ~n[0] & 1, n, L, n);
+//            int c = Nat.shiftDownBit(n.length, n, 0);                           assert c == (1 << 31);
+            Nat.shiftDownBit(n.length, n, 0);
+        }
+
+        PointPrecomp p = new PointPrecomp();
+
+        int cOff = PRECOMP_SPACING - 1;
+        for (;;)
+        {
+            int tPos = cOff;
+
+            for (int b = 0; b < PRECOMP_BLOCKS; ++b)
+            {
+                int w = 0;
+                for (int t = 0; t < PRECOMP_TEETH; ++t)
+                {
+                    int tBit = n[tPos >>> 5] >>> (tPos & 0x1F);
+                    w &= ~(1 << t);
+                    w ^= (tBit << t);
+                    tPos += PRECOMP_SPACING;
+                }
+
+                int sign = (w >>> (PRECOMP_TEETH - 1)) & 1;
+                int abs = (w ^ -sign) & PRECOMP_MASK;
+
+//                assert sign == 0 || sign == 1;
+//                assert 0 <= abs && abs < PRECOMP_POINTS;
+
+                pointLookup(b, abs, p);
+
+                X448Field.cnegate(sign, p.x);
+
+                pointAddPrecomp(p, r);
+            }
+
+            if (--cOff < 0)
+            {
+                break;
+            }
+
+            pointDouble(r);
+        }
+    }
+
+    private static void scalarMultBaseEncoded(byte[] k, byte[] r, int rOff)
+    {
+        PointExt p = new PointExt();
+        scalarMultBase(k, p);
+        encodePoint(p, r, rOff);
+    }
+
+    /**
+     * NOTE: Only for use by X448
+     */
+    public static void scalarMultBaseXY(X448.Friend friend, byte[] k, int kOff, int[] x, int[] y)
+    {
+        if (null == friend)
+        {
+            throw new NullPointerException("This method is only for use by X448");
+        }
+
+        byte[] n = new byte[SCALAR_BYTES];
+        pruneScalar(k, kOff, n);
+
+        PointExt p = new PointExt();
+        scalarMultBase(n, p);
+        X448Field.copy(p.x, 0, x, 0);
+        X448Field.copy(p.y, 0, y, 0);
+    }
+
+    private static void scalarMultStraussVar(int[] nb, int[] np, PointExt p, PointExt r)
+    {
+        precompute();
+
+        final int width = 5;
+
+        byte[] ws_b = getWNAF(nb, WNAF_WIDTH_BASE);
+        byte[] ws_p = getWNAF(np, width);
+
+        PointExt[] tp = pointPrecompVar(p, 1 << (width - 2));
+
+        pointSetNeutral(r);
+
+        int bit = 447;
+        while (bit > 0 && (ws_b[bit] | ws_p[bit]) == 0)
+        {
+            --bit;
+        }
+
+        for (;;)
+        {
+            int wb = ws_b[bit];
+            if (wb != 0)
+            {
+                int sign = wb >> 31;
+                int index = (wb ^ sign) >>> 1;
+
+                pointAddVar((sign != 0), precompBaseTable[index], r);
+            }
+
+            int wp = ws_p[bit];
+            if (wp != 0)
+            {
+                int sign = wp >> 31;
+                int index = (wp ^ sign) >>> 1;
+
+                pointAddVar((sign != 0), tp[index], r);
+            }
+
+            if (--bit < 0)
+            {
+                break;
+            }
+
+            pointDouble(r);
+        }
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x00;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, ctx, phflag, ph, phOff, PREHASH_SIZE, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+    {
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PREHASH_SIZE, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] ctx, Xof ph, byte[] sig, int sigOff)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0, PREHASH_SIZE))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, ctx, phflag, m, 0, m.length, sig, sigOff);
+    }
+
+    public static void signPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, Xof ph, byte[] sig, int sigOff)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0, PREHASH_SIZE))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        implSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.length, sig, sigOff);
+    }
+
+    public static boolean verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen)
+    {
+        byte phflag = 0x00;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+    }
+
+    public static boolean verifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff)
+    {
+        byte phflag = 0x01;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PREHASH_SIZE);
+    }
+
+    public static boolean verifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, Xof ph)
+    {
+        byte[] m = new byte[PREHASH_SIZE];
+        if (PREHASH_SIZE != ph.doFinal(m, 0, PREHASH_SIZE))
+        {
+            throw new IllegalArgumentException("ph");
+        }
+
+        byte phflag = 0x01;
+
+        return implVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.length);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/package.html b/bcprov/src/main/java/org/bouncycastle/math/package.html
new file mode 100644
index 0000000..0e6088d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+The Bouncy Castle math package.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java
index 0d25a5e..85f4f6d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java
@@ -4,6 +4,7 @@
 {
     private static final long M32 = 0x55555555L;
     private static final long M64 = 0x5555555555555555L;
+    private static final long M64R = 0xAAAAAAAAAAAAAAAAL;
 
     /*
      * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
@@ -92,6 +93,65 @@
         z[zOff + 1] = (x >>> 1) & M64;
     }
 
+    public static void expand64To128Rev(long x, long[] z, int zOff)
+    {
+        // "shuffle" low half to even bits and high half to odd bits
+        long t;
+        t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16));
+        t = (x ^ (x >>>  8)) & 0x0000FF000000FF00L; x ^= (t ^ (t <<  8));
+        t = (x ^ (x >>>  4)) & 0x00F000F000F000F0L; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>>  2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t <<  2));
+        t = (x ^ (x >>>  1)) & 0x2222222222222222L; x ^= (t ^ (t <<  1));
+
+        z[zOff    ] = (x     ) & M64R;
+        z[zOff + 1] = (x << 1) & M64R;
+    }
+
+    public static int shuffle(int x)
+    {
+        // "shuffle" low half to even bits and high half to odd bits
+        int t;
+        t = (x ^ (x >>>  8)) & 0x0000FF00; x ^= (t ^ (t <<  8));
+        t = (x ^ (x >>>  4)) & 0x00F000F0; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>>  2)) & 0x0C0C0C0C; x ^= (t ^ (t <<  2));
+        t = (x ^ (x >>>  1)) & 0x22222222; x ^= (t ^ (t <<  1));
+        return x;
+    }
+
+    public static long shuffle(long x)
+    {
+        // "shuffle" low half to even bits and high half to odd bits
+        long t;
+        t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16));
+        t = (x ^ (x >>>  8)) & 0x0000FF000000FF00L; x ^= (t ^ (t <<  8));
+        t = (x ^ (x >>>  4)) & 0x00F000F000F000F0L; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>>  2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t <<  2));
+        t = (x ^ (x >>>  1)) & 0x2222222222222222L; x ^= (t ^ (t <<  1));
+        return x;
+    }
+
+    public static int shuffle2(int x)
+    {
+        // "shuffle" (twice) low half to even bits and high half to odd bits
+        int t;
+        t = (x ^ (x >>>  7)) & 0x00AA00AA; x ^= (t ^ (t <<  7));
+        t = (x ^ (x >>> 14)) & 0x0000CCCC; x ^= (t ^ (t << 14));
+        t = (x ^ (x >>>  4)) & 0x00F000F0; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>>  8)) & 0x0000FF00; x ^= (t ^ (t <<  8));
+        return x;
+    }
+
+    public static int unshuffle(int x)
+    {
+        // "unshuffle" even bits to low half and odd bits to high half
+        int t;
+        t = (x ^ (x >>>  1)) & 0x22222222; x ^= (t ^ (t <<  1));
+        t = (x ^ (x >>>  2)) & 0x0C0C0C0C; x ^= (t ^ (t <<  2));
+        t = (x ^ (x >>>  4)) & 0x00F000F0; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>>  8)) & 0x0000FF00; x ^= (t ^ (t <<  8));
+        return x;
+    }
+
     public static long unshuffle(long x)
     {
         // "unshuffle" even bits to low half and odd bits to high half
@@ -103,4 +163,15 @@
         t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16));
         return x;
     }
+
+    public static int unshuffle2(int x)
+    {
+        // "unshuffle" (twice) even bits to low half and odd bits to high half
+        int t;
+        t = (x ^ (x >>>  8)) & 0x0000FF00; x ^= (t ^ (t <<  8));
+        t = (x ^ (x >>>  4)) & 0x00F000F0; x ^= (t ^ (t <<  4));
+        t = (x ^ (x >>> 14)) & 0x0000CCCC; x ^= (t ^ (t << 14));
+        t = (x ^ (x >>>  7)) & 0x00AA00AA; x ^= (t ^ (t <<  7));
+        return x;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
index 6b77f8f..d9482cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
@@ -194,6 +194,41 @@
         return c == 0 ? 0 : incAt(len, z, zOff, 1);
     }
 
+    public static int cadd(int len, int mask, int[] x, int[] y, int[] z)
+    {
+        long MASK = -(mask & 1) & M;
+        long c = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            c += (x[i] & M) + (y[i] & MASK);
+            z[i] = (int)c;
+            c >>>= 32;
+        }
+        return (int)c;
+    }
+
+    public static void cmov(int len, int mask, int[] x, int xOff, int[] z, int zOff)
+    {
+        mask = -(mask & 1);
+
+        for (int i = 0; i < len; ++i)
+        {
+            int z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+            z_i ^= (diff & mask);
+            z[zOff + i] = z_i;
+        }
+
+//        final int half = 0x55555555, rest = half << (-mask);
+//
+//        for (int i = 0; i < len; ++i)
+//        {
+//            int z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+//            z_i ^= (diff & half);
+//            z_i ^= (diff & rest);
+//            z[zOff + i] = z_i;
+//        }
+    }
+
     public static int[] copy(int len, int[] x)
     {
         int[] z = new int[len];
@@ -206,6 +241,11 @@
         System.arraycopy(x, 0, z, 0, len);
     }
 
+    public static void copy(int len, int[] x, int xOff, int[] z, int zOff)
+    {
+        System.arraycopy(x, xOff, z, zOff, len);
+    }
+
     public static int[] create(int len)
     {
         return new int[len];
@@ -216,6 +256,19 @@
         return new long[len];
     }
 
+    public static int csub(int len, int mask, int[] x, int[] y, int[] z)
+    {
+        long MASK = -(mask & 1) & M;
+        long c = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            c += (x[i] & M) - (y[i] & MASK);
+            z[i] = (int)c;
+            c >>= 32;
+        }
+        return (int)c;
+    }
+
     public static int dec(int len, int[] z)
     {
         for (int i = 0; i < len; ++i)
@@ -441,6 +494,16 @@
         }
     }
 
+    public static void mul(int[] x, int xOff, int xLen, int[] y, int yOff, int yLen, int[] zz, int zzOff)
+    {
+        zz[zzOff + yLen] = mulWord(yLen, x[xOff], y, yOff, zz, zzOff);
+
+        for (int i = 1; i < xLen; ++i)
+        {
+            zz[zzOff + i + yLen] = mulWordAddTo(yLen, x[xOff + i], y, yOff, zz, zzOff + i);
+        }
+    }
+
     public static int mulAddTo(int len, int[] x, int[] y, int[] zz)
     {
         long zc = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java
index ae4ae48..5a4fbfc 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java
@@ -110,12 +110,26 @@
         z[3] = x[3];
     }
 
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+    }
+
     public static void copy64(long[] x, long[] z)
     {
         z[0] = x[0];
         z[1] = x[1];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+    }
+
     public static int[] create()
     {
         return new int[4];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java
index 620f9bc..9bfe1ce 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java
@@ -127,6 +127,15 @@
         z[4] = x[4];
     }
 
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+    }
+
     public static int[] create()
     {
         return new int[5];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat192.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat192.java
index 12db01b..ffe74d7 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat192.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat192.java
@@ -144,6 +144,16 @@
         z[5] = x[5];
     }
 
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+        z[zOff + 5] = x[xOff + 5];
+    }
+
     public static void copy64(long[] x, long[] z)
     {
         z[0] = x[0];
@@ -151,6 +161,13 @@
         z[2] = x[2];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+    }
+
     public static int[] create()
     {
         return new int[6];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
index 9ff107c..59be1f5 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
@@ -215,6 +215,17 @@
         z[6] = x[6];
     }
 
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+        z[zOff + 5] = x[xOff + 5];
+        z[zOff + 6] = x[xOff + 6];
+    }
+
     public static int[] create()
     {
         return new int[7];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
index 726bae3..f7d80b3 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
@@ -238,6 +238,18 @@
         z[7] = x[7];
     }
 
+    public static void copy(int[] x, int xOff, int[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+        z[zOff + 5] = x[xOff + 5];
+        z[zOff + 6] = x[xOff + 6];
+        z[zOff + 7] = x[xOff + 7];
+    }
+
     public static void copy64(long[] x, long[] z)
     {
         z[0] = x[0];
@@ -246,6 +258,14 @@
         z[3] = x[3];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+    }
+
     public static int[] create()
     {
         return new int[8];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java
index 764f796..2ee3f64 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java
@@ -15,6 +15,15 @@
         z[4] = x[4];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+    }
+
     public static long[] create64()
     {
         return new long[5];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java
index 29c1842..3d93973 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java
@@ -17,6 +17,17 @@
         z[6] = x[6];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+        z[zOff + 5] = x[xOff + 5];
+        z[zOff + 6] = x[xOff + 6];
+    }
+
     public static long[] create64()
     {
         return new long[7];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java
index d9e06a6..82e28e2 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java
@@ -19,6 +19,19 @@
         z[8] = x[8];
     }
 
+    public static void copy64(long[] x, int xOff, long[] z, int zOff)
+    {
+        z[zOff + 0] = x[xOff + 0];
+        z[zOff + 1] = x[xOff + 1];
+        z[zOff + 2] = x[xOff + 2];
+        z[zOff + 3] = x[xOff + 3];
+        z[zOff + 4] = x[xOff + 4];
+        z[zOff + 5] = x[xOff + 5];
+        z[zOff + 6] = x[xOff + 6];
+        z[zOff + 7] = x[xOff + 7];
+        z[zOff + 8] = x[xOff + 8];
+    }
+
     public static long[] create64()
     {
         return new long[9];
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java
index c3b976b..c00563e 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/McElieceCCA2PrivateKey.java
@@ -19,6 +19,7 @@
  * Return the keyData to encode in the PrivateKeyInfo structure.
  * <p>
  * The ASN.1 definition of the key structure is
+ * </p>
  * <pre>
  *   McElieceCCA2PrivateKey ::= SEQUENCE {
  *     m             INTEGER                  -- extension degree of the field
@@ -29,7 +30,6 @@
  *     digest        AlgorithmIdentifier      -- algorithm identifier for CCA2 digest
  *   }
  * </pre>
- * </p>
  */
 public class McElieceCCA2PrivateKey
     extends ASN1Object
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
index 938d8cf..52daba1 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
@@ -54,4 +54,76 @@
     public static final ASN1ObjectIdentifier sphincs256_with_SHA3_512 = BCObjectIdentifiers.sphincs256_with_SHA3_512;
 
     public static final ASN1ObjectIdentifier newHope = BCObjectIdentifiers.newHope;
+
+    /**
+     * XMSS
+     */
+    public static final ASN1ObjectIdentifier xmss                      = BCObjectIdentifiers.xmss;
+    public static final ASN1ObjectIdentifier xmss_SHA256ph             = BCObjectIdentifiers.xmss_SHA256ph;
+    public static final ASN1ObjectIdentifier xmss_SHA512ph             = BCObjectIdentifiers.xmss_SHA512ph;
+    public static final ASN1ObjectIdentifier xmss_SHAKE128ph           = BCObjectIdentifiers.xmss_SHAKE128ph;
+    public static final ASN1ObjectIdentifier xmss_SHAKE256ph           = BCObjectIdentifiers.xmss_SHAKE256ph;
+    public static final ASN1ObjectIdentifier xmss_SHA256               = BCObjectIdentifiers.xmss_SHA256;
+    public static final ASN1ObjectIdentifier xmss_SHA512               = BCObjectIdentifiers.xmss_SHA512;
+    public static final ASN1ObjectIdentifier xmss_SHAKE128             = BCObjectIdentifiers.xmss_SHAKE128;
+    public static final ASN1ObjectIdentifier xmss_SHAKE256             = BCObjectIdentifiers.xmss_SHAKE256;
+
+
+    /**
+     * XMSS^MT
+     */
+    public static final ASN1ObjectIdentifier xmss_mt                   = BCObjectIdentifiers.xmss_mt;
+    public static final ASN1ObjectIdentifier xmss_mt_SHA256ph          = BCObjectIdentifiers.xmss_mt_SHA256ph;
+    public static final ASN1ObjectIdentifier xmss_mt_SHA512ph          = BCObjectIdentifiers.xmss_mt_SHA512ph;
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128ph        = BCObjectIdentifiers.xmss_mt_SHAKE128ph;
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256ph        = BCObjectIdentifiers.xmss_mt_SHAKE256ph;
+    public static final ASN1ObjectIdentifier xmss_mt_SHA256            = BCObjectIdentifiers.xmss_mt_SHA256;
+    public static final ASN1ObjectIdentifier xmss_mt_SHA512            = BCObjectIdentifiers.xmss_mt_SHA512;
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128          = BCObjectIdentifiers.xmss_mt_SHAKE128;
+    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256          = BCObjectIdentifiers.xmss_mt_SHAKE256;
+
+    // old OIDs.
+    /**
+     * @deprecated use xmss_SHA256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHA256          = xmss_SHA256ph;
+    /**
+     * @deprecated use xmss_SHA512ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHA512 = xmss_SHA512ph;
+    /**
+     * @deprecated use xmss_SHAKE128ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHAKE128 = xmss_SHAKE128ph;
+    /**
+     * @deprecated use xmss_SHAKE256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_with_SHAKE256        = xmss_SHAKE256ph;
+
+    /**
+     * @deprecated use xmss_mt_SHA256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHA256          = xmss_mt_SHA256ph;
+    /**
+     * @deprecated use xmss_mt_SHA512ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHA512          = xmss_mt_SHA512ph;
+    /**
+     * @deprecated use xmss_mt_SHAKE128ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE128        = xmss_mt_SHAKE128;
+    /**
+     * @deprecated use xmss_mt_SHAKE256ph
+     */
+    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE256        = xmss_mt_SHAKE256;
+
+    /**
+     * qTESLA
+     */
+    public static final ASN1ObjectIdentifier qTESLA = BCObjectIdentifiers.qTESLA;
+    public static final ASN1ObjectIdentifier qTESLA_I = BCObjectIdentifiers.qTESLA_I;
+    public static final ASN1ObjectIdentifier qTESLA_III_size = BCObjectIdentifiers.qTESLA_III_size;
+    public static final ASN1ObjectIdentifier qTESLA_III_speed = BCObjectIdentifiers.qTESLA_III_speed;
+    public static final ASN1ObjectIdentifier qTESLA_p_I = BCObjectIdentifiers.qTESLA_p_I;
+    public static final ASN1ObjectIdentifier qTESLA_p_III = BCObjectIdentifiers.qTESLA_p_III;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSKeyParams.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSKeyParams.java
new file mode 100644
index 0000000..0ceb6c7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSKeyParams.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * XMSSKeyParams
+ * <pre>
+ *     XMSSKeyParams ::= SEQUENCE {
+ *     version INTEGER -- 0
+ *     height INTEGER
+ *     treeDigest AlgorithmIdentifier
+ * }
+ * </pre>
+ */
+public class XMSSKeyParams
+    extends ASN1Object
+{
+    private final ASN1Integer version;
+    private final int height;
+    private final AlgorithmIdentifier treeDigest;
+
+    public XMSSKeyParams(int height, AlgorithmIdentifier treeDigest)
+    {
+        this.version = new ASN1Integer(0);
+        this.height = height;
+        this.treeDigest = treeDigest;
+    }
+
+    private XMSSKeyParams(ASN1Sequence sequence)
+    {
+        this.version = ASN1Integer.getInstance(sequence.getObjectAt(0));
+        this.height = ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue().intValue();
+        this.treeDigest = AlgorithmIdentifier.getInstance(sequence.getObjectAt(2));
+    }
+
+    public static XMSSKeyParams getInstance(Object o)
+    {
+        if (o instanceof XMSSKeyParams)
+        {
+            return (XMSSKeyParams)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSKeyParams(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public int getHeight()
+    {
+        return height;
+    }
+
+    public AlgorithmIdentifier getTreeDigest()
+    {
+        return treeDigest;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(version);
+        v.add(new ASN1Integer(height));
+        v.add(treeDigest);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTKeyParams.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTKeyParams.java
new file mode 100644
index 0000000..664bcc5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTKeyParams.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.pqc.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * XMMSMTKeyParams
+ * <pre>
+ *     XMMSMTKeyParams ::= SEQUENCE {
+ *         version INTEGER -- 0
+ *         height INTEGER
+ *         layers INTEGER
+ *         treeDigest AlgorithmIdentifier
+ * }
+ * </pre>
+ */
+public class XMSSMTKeyParams
+    extends ASN1Object
+{
+    private final ASN1Integer version;
+    private final int height;
+    private final int layers;
+    private final AlgorithmIdentifier treeDigest;
+
+    public XMSSMTKeyParams(int height, int layers, AlgorithmIdentifier treeDigest)
+    {
+        this.version = new ASN1Integer(0);
+        this.height = height;
+        this.layers = layers;
+        this.treeDigest = treeDigest;
+    }
+
+    private XMSSMTKeyParams(ASN1Sequence sequence)
+    {
+        this.version = ASN1Integer.getInstance(sequence.getObjectAt(0));
+        this.height = ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue().intValue();
+        this.layers = ASN1Integer.getInstance(sequence.getObjectAt(2)).getValue().intValue();
+        this.treeDigest = AlgorithmIdentifier.getInstance(sequence.getObjectAt(3));
+    }
+
+    public static XMSSMTKeyParams getInstance(Object o)
+    {
+        if (o instanceof XMSSMTKeyParams)
+        {
+            return (XMSSMTKeyParams)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSMTKeyParams(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public int getHeight()
+    {
+        return height;
+    }
+
+    public int getLayers()
+    {
+        return layers;
+    }
+
+    public AlgorithmIdentifier getTreeDigest()
+    {
+        return treeDigest;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(version);
+        v.add(new ASN1Integer(height));
+        v.add(new ASN1Integer(layers));
+        v.add(treeDigest);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPrivateKey.java
new file mode 100644
index 0000000..79c5aec
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPrivateKey.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * XMMSMTPrivateKey
+ * <pre>
+ *     XMMSMTPrivateKey ::= SEQUENCE {
+ *         version INTEGER -- 0
+ *         keyData SEQUENCE {
+ *            index         INTEGER
+ *            secretKeySeed OCTET STRING
+ *            secretKeyPRF  OCTET STRING
+ *            publicSeed    OCTET STRING
+ *            root          OCTET STRING
+ *         }
+ *         bdsState CHOICE {
+ *            platformSerialization [0] OCTET STRING
+ *         } OPTIONAL
+ *    }
+ * </pre>
+ */
+public class XMSSMTPrivateKey
+    extends ASN1Object
+{
+    private final int index;
+    private final byte[] secretKeySeed;
+    private final byte[] secretKeyPRF;
+    private final byte[] publicSeed;
+    private final byte[] root;
+    private final byte[] bdsState;
+
+    public XMSSMTPrivateKey(int index, byte[] secretKeySeed, byte[] secretKeyPRF, byte[] publicSeed, byte[] root, byte[] bdsState)
+    {
+        this.index = index;
+        this.secretKeySeed = Arrays.clone(secretKeySeed);
+        this.secretKeyPRF = Arrays.clone(secretKeyPRF);
+        this.publicSeed = Arrays.clone(publicSeed);
+        this.root = Arrays.clone(root);
+        this.bdsState = Arrays.clone(bdsState);
+    }
+
+    private XMSSMTPrivateKey(ASN1Sequence seq)
+    {
+        if (!ASN1Integer.getInstance(seq.getObjectAt(0)).getValue().equals(BigInteger.valueOf(0)))
+        {
+            throw new IllegalArgumentException("unknown version of sequence");
+        }
+
+        if (seq.size() != 2 && seq.size() != 3)
+        {
+            throw new IllegalArgumentException("key sequence wrong size");
+        }
+
+        ASN1Sequence keySeq = ASN1Sequence.getInstance(seq.getObjectAt(1));
+
+        this.index = ASN1Integer.getInstance(keySeq.getObjectAt(0)).getValue().intValue();
+        this.secretKeySeed = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(1)).getOctets());
+        this.secretKeyPRF = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(2)).getOctets());
+        this.publicSeed = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(3)).getOctets());
+        this.root = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(4)).getOctets());
+
+        if(seq.size() == 3)
+        {
+            this.bdsState = Arrays.clone(DEROctetString.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2)), true).getOctets());
+        }
+        else
+        {
+            this.bdsState = null;
+        }
+    }
+
+    public static XMSSMTPrivateKey getInstance(Object o)
+    {
+        if (o instanceof XMSSMTPrivateKey)
+        {
+            return (XMSSMTPrivateKey)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSMTPrivateKey(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public int getIndex()
+    {
+        return index;
+    }
+
+    public byte[] getSecretKeySeed()
+    {
+        return Arrays.clone(secretKeySeed);
+    }
+
+    public byte[] getSecretKeyPRF()
+    {
+        return Arrays.clone(secretKeyPRF);
+    }
+
+    public byte[] getPublicSeed()
+    {
+        return Arrays.clone(publicSeed);
+    }
+
+    public byte[] getRoot()
+    {
+        return Arrays.clone(root);
+    }
+
+    public byte[] getBdsState()
+    {
+        return Arrays.clone(bdsState);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new ASN1Integer(0)); // version
+
+        ASN1EncodableVector vK = new ASN1EncodableVector();
+
+        vK.add(new ASN1Integer(index));
+        vK.add(new DEROctetString(secretKeySeed));
+        vK.add(new DEROctetString(secretKeyPRF));
+        vK.add(new DEROctetString(publicSeed));
+        vK.add(new DEROctetString(root));
+
+        v.add(new DERSequence(vK));
+        v.add(new DERTaggedObject(true, 0, new DEROctetString(bdsState)));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPublicKey.java
new file mode 100644
index 0000000..8c7c0a9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSMTPublicKey.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * XMSSMTPublicKey
+ * <pre>
+ *     XMSSMTPublicKey ::= SEQUENCE {
+ *         version       INTEGER -- 0
+ *         publicSeed    OCTET STRING
+ *         root          OCTET STRING
+ *    }
+ * </pre>
+ */
+public class XMSSMTPublicKey
+    extends ASN1Object
+{
+    private final byte[] publicSeed;
+    private final byte[] root;
+
+    public XMSSMTPublicKey(byte[] publicSeed, byte[] root)
+    {
+        this.publicSeed = Arrays.clone(publicSeed);
+        this.root = Arrays.clone(root);
+    }
+
+    private XMSSMTPublicKey(ASN1Sequence seq)
+    {
+        if (!ASN1Integer.getInstance(seq.getObjectAt(0)).getValue().equals(BigInteger.valueOf(0)))
+        {
+            throw new IllegalArgumentException("unknown version of sequence");
+        }
+
+        this.publicSeed = Arrays.clone(DEROctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        this.root = Arrays.clone(DEROctetString.getInstance(seq.getObjectAt(2)).getOctets());
+    }
+
+    public static XMSSMTPublicKey getInstance(Object o)
+    {
+        if (o instanceof XMSSMTPublicKey)
+        {
+            return (XMSSMTPublicKey)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSMTPublicKey(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public byte[] getPublicSeed()
+    {
+        return Arrays.clone(publicSeed);
+    }
+
+    public byte[] getRoot()
+    {
+        return Arrays.clone(root);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new ASN1Integer(0)); // version
+
+        v.add(new DEROctetString(publicSeed));
+        v.add(new DEROctetString(root));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPrivateKey.java
new file mode 100644
index 0000000..8bcda79
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPrivateKey.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * XMMSPrivateKey
+ * <pre>
+ *     XMMSPrivateKey ::= SEQUENCE {
+ *         version INTEGER -- 0
+ *         keyData SEQUENCE {
+ *            index         INTEGER
+ *            secretKeySeed OCTET STRING
+ *            secretKeyPRF  OCTET STRING
+ *            publicSeed    OCTET STRING
+ *            root          OCTET STRING
+ *         }
+ *         bdsState CHOICE {
+ *            platformSerialization [0] OCTET STRING
+ *         } OPTIONAL
+ *    }
+ * </pre>
+ */
+public class XMSSPrivateKey
+    extends ASN1Object
+{
+    private final int index;
+    private final byte[] secretKeySeed;
+    private final byte[] secretKeyPRF;
+    private final byte[] publicSeed;
+    private final byte[] root;
+    private final byte[] bdsState;
+
+    public XMSSPrivateKey(int index, byte[] secretKeySeed, byte[] secretKeyPRF, byte[] publicSeed, byte[] root, byte[] bdsState)
+    {
+        this.index = index;
+        this.secretKeySeed = Arrays.clone(secretKeySeed);
+        this.secretKeyPRF = Arrays.clone(secretKeyPRF);
+        this.publicSeed = Arrays.clone(publicSeed);
+        this.root = Arrays.clone(root);
+        this.bdsState = Arrays.clone(bdsState);
+    }
+
+    private XMSSPrivateKey(ASN1Sequence seq)
+    {
+        if (!ASN1Integer.getInstance(seq.getObjectAt(0)).getValue().equals(BigInteger.valueOf(0)))
+        {
+            throw new IllegalArgumentException("unknown version of sequence");
+        }
+
+        if (seq.size() != 2 && seq.size() != 3)
+        {
+            throw new IllegalArgumentException("key sequence wrong size");
+        }
+
+        ASN1Sequence keySeq = ASN1Sequence.getInstance(seq.getObjectAt(1));
+
+        this.index = ASN1Integer.getInstance(keySeq.getObjectAt(0)).getValue().intValue();
+        this.secretKeySeed = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(1)).getOctets());
+        this.secretKeyPRF = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(2)).getOctets());
+        this.publicSeed = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(3)).getOctets());
+        this.root = Arrays.clone(DEROctetString.getInstance(keySeq.getObjectAt(4)).getOctets());
+
+        if (seq.size() == 3)
+        {
+            this.bdsState = Arrays.clone(DEROctetString.getInstance(ASN1TaggedObject.getInstance(seq.getObjectAt(2)), true).getOctets());
+        }
+        else
+        {
+            this.bdsState = null;
+        }
+    }
+
+    public static XMSSPrivateKey getInstance(Object o)
+    {
+        if (o instanceof XMSSPrivateKey)
+        {
+            return (XMSSPrivateKey)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSPrivateKey(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public int getIndex()
+    {
+        return index;
+    }
+
+    public byte[] getSecretKeySeed()
+    {
+        return Arrays.clone(secretKeySeed);
+    }
+
+    public byte[] getSecretKeyPRF()
+    {
+        return Arrays.clone(secretKeyPRF);
+    }
+
+    public byte[] getPublicSeed()
+    {
+        return Arrays.clone(publicSeed);
+    }
+
+    public byte[] getRoot()
+    {
+        return Arrays.clone(root);
+    }
+
+    public byte[] getBdsState()
+    {
+        return Arrays.clone(bdsState);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new ASN1Integer(0)); // version
+
+        ASN1EncodableVector vK = new ASN1EncodableVector();
+
+        vK.add(new ASN1Integer(index));
+        vK.add(new DEROctetString(secretKeySeed));
+        vK.add(new DEROctetString(secretKeyPRF));
+        vK.add(new DEROctetString(publicSeed));
+        vK.add(new DEROctetString(root));
+
+        v.add(new DERSequence(vK));
+        v.add(new DERTaggedObject(true, 0, new DEROctetString(bdsState)));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPublicKey.java
new file mode 100644
index 0000000..2973322
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/XMSSPublicKey.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.pqc.asn1;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * XMSSPublicKey
+ * <pre>
+ *     XMSSPublicKey ::= SEQUENCE {
+ *         version       INTEGER -- 0
+ *         publicSeed    OCTET STRING
+ *         root          OCTET STRING
+ *    }
+ * </pre>
+ */
+public class XMSSPublicKey
+    extends ASN1Object
+{
+    private final byte[] publicSeed;
+    private final byte[] root;
+
+    public XMSSPublicKey(byte[] publicSeed, byte[] root)
+    {
+        this.publicSeed = Arrays.clone(publicSeed);
+        this.root = Arrays.clone(root);
+    }
+
+    private XMSSPublicKey(ASN1Sequence seq)
+    {
+        if (!ASN1Integer.getInstance(seq.getObjectAt(0)).getValue().equals(BigInteger.valueOf(0)))
+        {
+            throw new IllegalArgumentException("unknown version of sequence");
+        }
+
+        this.publicSeed = Arrays.clone(DEROctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        this.root = Arrays.clone(DEROctetString.getInstance(seq.getObjectAt(2)).getOctets());
+    }
+
+    public static XMSSPublicKey getInstance(Object o)
+    {
+        if (o instanceof XMSSPublicKey)
+        {
+            return (XMSSPublicKey)o;
+        }
+        else if (o != null)
+        {
+            return new XMSSPublicKey(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public byte[] getPublicSeed()
+    {
+        return Arrays.clone(publicSeed);
+    }
+
+    public byte[] getRoot()
+    {
+        return Arrays.clone(root);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(new ASN1Integer(0)); // version
+
+        v.add(new DEROctetString(publicSeed));
+        v.add(new DEROctetString(root));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/package.html b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/package.html
new file mode 100644
index 0000000..af07617
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+ASN.1 Support classes for PQC algorithms.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/DigestingStateAwareMessageSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/DigestingStateAwareMessageSigner.java
new file mode 100644
index 0000000..8be0cfb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/DigestingStateAwareMessageSigner.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.pqc.crypto;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+
+/**
+ * Implements the sign and verify functions for a Signature Scheme using a hash function to allow processing of large messages.
+ * <p>
+ *  This class can be used with algorithms where the state associated with the private key changes as each signature is
+ *  generated. Calling getUpdatedPrivateKey() will recover the private key that can be used to initialize a signer
+ *  next time around.
+ * </p>
+ */
+public class DigestingStateAwareMessageSigner
+    extends DigestingMessageSigner
+{
+    private final StateAwareMessageSigner signer;
+
+    public DigestingStateAwareMessageSigner(StateAwareMessageSigner messSigner, Digest messDigest)
+    {
+        super(messSigner, messDigest);
+
+        this.signer = messSigner;
+    }
+
+    /**
+     * Return the current version of the private key with the updated state.
+     * <p>
+     * <b>Note:</b> calling this method will effectively disable the Signer from being used for further
+     *  signature generation without another call to init().
+     * </p>
+     * @return an updated private key object, which can be used for later signature generation.
+     */
+    public AsymmetricKeyParameter getUpdatedPrivateKey()
+    {
+        return signer.getUpdatedPrivateKey();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java
index d455d9d..ada5138 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/MessageEncryptor.java
@@ -21,14 +21,13 @@
     /**
      *
      * @param message the message to be signed.
-     * @throws Exception 
      */
     public byte[] messageEncrypt(byte[] message);
 
     /**
      *
      * @param cipher the cipher text of the message
-     * @throws Exception 
+     * @throws InvalidCipherTextException
      */
     public byte[] messageDecrypt(byte[] cipher)
         throws InvalidCipherTextException;
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/StateAwareMessageSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/StateAwareMessageSigner.java
new file mode 100644
index 0000000..18ca835
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/StateAwareMessageSigner.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.pqc.crypto;
+
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+/**
+ * Base interface for a PQC stateful signature algorithm.
+ */
+public interface StateAwareMessageSigner
+    extends MessageSigner
+{
+    /**
+     * Return the current version of the private key with the updated state.
+     * <p>
+     * <b>Note:</b> calling this method will effectively disable the Signer from being used for further
+     *  signature generation without another call to init().
+     * </p>
+     * @return an updated private key object, which can be used for later signature generation.
+     */
+    public AsymmetricKeyParameter getUpdatedPrivateKey();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
index 013441e..95fff30 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSKeyPairGenerator.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
@@ -178,26 +179,19 @@
         // from bottom up to the root
         for (int h = numLayer - 1; h >= 0; h--)
         {
-            GMSSRootCalc tree = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
-            try
-            {
-                // on lowest layer no lower root is available, so just call
-                // the method with null as first parameter
-                if (h == numLayer - 1)
-                {
-                    tree = this.generateCurrentAuthpathAndRoot(null, currentStack[h], seeds[h], h);
-                }
-                else
-                // otherwise call the method with the former computed root
-                // value
-                {
-                    tree = this.generateCurrentAuthpathAndRoot(currentRoots[h + 1], currentStack[h], seeds[h], h);
-                }
+            GMSSRootCalc tree;
 
-            }
-            catch (Exception e1)
+            // on lowest layer no lower root is available, so just call
+            // the method with null as first parameter
+            if (h == numLayer - 1)
             {
-                e1.printStackTrace();
+                tree = this.generateCurrentAuthpathAndRoot(null, currentStack[h], seeds[h], h);
+            }
+            else
+            // otherwise call the method with the former computed root
+            // value
+            {
+                tree = this.generateCurrentAuthpathAndRoot(currentRoots[h + 1], currentStack[h], seeds[h], h);
             }
 
             // set initial values needed for the private key construction
@@ -436,7 +430,7 @@
         this.nextNextSeeds = new byte[numLayer - 1][mdLength];
 
         // construct SecureRandom for initial seed generation
-        SecureRandom secRan = new SecureRandom();
+        SecureRandom secRan = CryptoServicesRegistrar.getSecureRandom();
 
         // generation of initial seeds
         for (int i = 0; i < numLayer; i++)
@@ -458,7 +452,7 @@
         int[] defw = {3, 3, 3, 3};
         int[] defk = {2, 2, 2, 2};
 
-        KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(new SecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
+        KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
         this.initialize(kgp);
 
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java
index 6823ce3..8cbfe8d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSLeaf.java
@@ -112,7 +112,7 @@
      * The constructor precomputes some needed variables for distributed leaf
      * calculation
      *
-     * @param digest     an array of strings, containing the digest of the used hash
+     * @param digest   an array of strings, containing the digest of the used hash
      *                 function and PRNG and the digest of the corresponding
      *                 provider
      * @param w        the winterniz parameter of that tree the leaf is computed
@@ -231,7 +231,7 @@
      */
     private void updateLeafCalc()
     {
-         byte[] buf = new byte[messDigestOTS.getDigestSize()];
+        byte[] buf = new byte[messDigestOTS.getDigestSize()];
 
         // steps times do
         // TODO: this really needs to be looked at, the 10000 has been added as
@@ -273,7 +273,7 @@
             }
         }
 
-       throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
+        throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
     }
 
     /**
@@ -292,7 +292,7 @@
      *
      * @param intValue an integer
      * @return The least integer greater or equal to the logarithm to the base 2
-     *         of <code>intValue</code>
+     * of <code>intValue</code>
      */
     private int getLog(int intValue)
     {
@@ -315,10 +315,6 @@
     {
 
         byte[][] statByte = new byte[4][];
-        statByte[0] = new byte[mdsize];
-        statByte[1] = new byte[mdsize];
-        statByte[2] = new byte[mdsize * keysize];
-        statByte[3] = new byte[mdsize];
         statByte[0] = privateKeyOTS;
         statByte[1] = seed;
         statByte[2] = concHashs;
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
index 83cf797..ffce88d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSPrivateKeyParameters.java
@@ -198,7 +198,7 @@
         this.currentSeeds = currentSeeds;
         this.nextNextSeeds = nextNextSeeds;
 
-        this.currentAuthPaths = currentAuthPaths;
+        this.currentAuthPaths = Arrays.clone(currentAuthPaths);
         this.nextAuthPaths = nextAuthPaths;
 
         // initialize keep if null
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java
index 35ac2e3..d5e4bc2 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSRootCalc.java
@@ -103,80 +103,6 @@
     private int heightOfNextSeed;
 
     /**
-     * This constructor regenerates a prior treehash object
-     *
-     * @param digest     an array of strings, containing the digest of the used hash
-     *                 function and PRNG and the digest of the corresponding
-     *                 provider
-     * @param statByte status bytes
-     * @param statInt  status ints
-     */
-    public GMSSRootCalc(Digest digest, byte[][] statByte, int[] statInt,
-                        Treehash[] treeH, Vector[] ret)
-    {
-        this.messDigestTree = digestProvider.get();
-        this.digestProvider = digestProvider;
-        // decode statInt
-        this.heightOfTree = statInt[0];
-        this.mdLength = statInt[1];
-        this.K = statInt[2];
-        this.indexForNextSeed = statInt[3];
-        this.heightOfNextSeed = statInt[4];
-        if (statInt[5] == 1)
-        {
-            this.isFinished = true;
-        }
-        else
-        {
-            this.isFinished = false;
-        }
-        if (statInt[6] == 1)
-        {
-            this.isInitialized = true;
-        }
-        else
-        {
-            this.isInitialized = false;
-        }
-
-        int tailLength = statInt[7];
-
-        this.index = new int[heightOfTree];
-        for (int i = 0; i < heightOfTree; i++)
-        {
-            this.index[i] = statInt[8 + i];
-        }
-
-        this.heightOfNodes = new Vector();
-        for (int i = 0; i < tailLength; i++)
-        {
-            this.heightOfNodes.addElement(Integers.valueOf(statInt[8 + heightOfTree
-                + i]));
-        }
-
-        // decode statByte
-        this.root = statByte[0];
-
-        this.AuthPath = new byte[heightOfTree][mdLength];
-        for (int i = 0; i < heightOfTree; i++)
-        {
-            this.AuthPath[i] = statByte[1 + i];
-        }
-
-        this.tailStack = new Vector();
-        for (int i = 0; i < tailLength; i++)
-        {
-            this.tailStack.addElement(statByte[1 + heightOfTree + i]);
-        }
-
-        // decode treeH
-        this.treehash = GMSSUtils.clone(treeH);
-
-        // decode ret
-        this.retain = GMSSUtils.clone(ret);
-    }
-
-    /**
      * Constructor
      *
      * @param heightOfTree maximal height of the tree
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java
index 8832fb3..c7ab75c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSSigner.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.MessageSigner;
@@ -133,7 +134,7 @@
             else
             {
 
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (GMSSPrivateKeyParameters)param;
                 initSign();
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSStateAwareSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSStateAwareSigner.java
new file mode 100644
index 0000000..77fe25a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/gmss/GMSSStateAwareSigner.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.pqc.crypto.gmss;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.StateAwareMessageSigner;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * This class implements the GMSS signature scheme, but allows multiple signatures to be generated.
+ * <p>
+ *     Note:  getUpdatedPrivateKey() needs to be called to fetch the current value of the usable private key.
+ * </p>
+ */
+public class GMSSStateAwareSigner
+    implements StateAwareMessageSigner
+{
+    private final GMSSSigner gmssSigner;
+
+    private GMSSPrivateKeyParameters key;
+
+    public GMSSStateAwareSigner(final Digest digest)
+    {
+        if (!(digest instanceof Memoable))
+        {
+            throw new IllegalArgumentException("digest must implement Memoable");
+        }
+
+        final Memoable dig = ((Memoable)digest).copy();
+        gmssSigner = new GMSSSigner(new GMSSDigestProvider()
+        {
+            public Digest get()
+            {
+                return (Digest)dig.copy();
+            }
+        });
+    }
+
+    public void init(boolean forSigning, CipherParameters param)
+    {
+        if (forSigning)
+        {
+            if (param instanceof ParametersWithRandom)
+            {
+                ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+                this.key = (GMSSPrivateKeyParameters)rParam.getParameters();
+            }
+            else
+            {
+                this.key = (GMSSPrivateKeyParameters)param;
+            }
+        }
+
+        gmssSigner.init(forSigning, param);
+    }
+
+    public byte[] generateSignature(byte[] message)
+    {
+        if (key == null)
+        {
+            throw new IllegalStateException("signing key no longer usable");
+        }
+        
+        byte[] sig = gmssSigner.generateSignature(message);
+
+        key = key.nextKey();
+
+        return sig;
+    }
+
+    public boolean verifySignature(byte[] message, byte[] signature)
+    {
+        return gmssSigner.verifySignature(message, signature);
+    }
+
+    public AsymmetricKeyParameter getUpdatedPrivateKey()
+    {
+        AsymmetricKeyParameter k = key;
+
+        key = null;
+
+        return k;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
index 7854bea..b52370d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCCA2KeyPairGenerator.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
 import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
@@ -53,7 +54,7 @@
      */
     private void initializeDefault()
     {
-        McElieceCCA2KeyGenerationParameters mcCCA2Params = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters());
+        McElieceCCA2KeyGenerationParameters mcCCA2Params = new McElieceCCA2KeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new McElieceCCA2Parameters());
         init(mcCCA2Params);
     }
 
@@ -64,7 +65,7 @@
         this.mcElieceCCA2Params = (McElieceCCA2KeyGenerationParameters)param;
 
         // set source of randomness
-        this.random = new SecureRandom();
+        this.random = CryptoServicesRegistrar.getSecureRandom();
 
         this.m = this.mcElieceCCA2Params.getParameters().getM();
         this.n = this.mcElieceCCA2Params.getParameters().getN();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCipher.java
index 4c87e00..7223d45 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceCipher.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.MessageEncryptor;
@@ -65,7 +66,7 @@
             }
             else
             {
-                this.sr = new SecureRandom();
+                this.sr = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (McEliecePublicKeyParameters)param;
                 this.initCipherEncrypt((McEliecePublicKeyParameters)key);
             }
@@ -102,9 +103,9 @@
     }
 
 
-    public void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
+    private void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
     {
-        this.sr = sr != null ? sr : new SecureRandom();
+        this.sr = sr != null ? sr : CryptoServicesRegistrar.getSecureRandom();
         n = pubKey.getN();
         k = pubKey.getK();
         t = pubKey.getT();
@@ -113,7 +114,7 @@
     }
 
 
-    public void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
+    private void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
     {
         n = privKey.getN();
         k = privKey.getK();
@@ -157,7 +158,7 @@
      *
      * @param input the cipher text
      * @return the plain text
-     * @throws Exception if the cipher text is invalid.
+     * @throws InvalidCipherTextException if the cipher text is invalid.
      */
     public byte[] messageDecrypt(byte[] input)
         throws InvalidCipherTextException
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
index 52e5753..dcd3b7d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceFujisakiCipher.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -61,7 +62,7 @@
             }
             else
             {
-                this.sr = new SecureRandom();
+                this.sr = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (McElieceCCA2PublicKeyParameters)param;
                 this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
             }
@@ -93,7 +94,7 @@
 
     private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
     {
-        this.sr = sr != null ? sr : new SecureRandom();
+        this.sr = sr != null ? sr : CryptoServicesRegistrar.getSecureRandom();
         this.messDigest = Utils.getDigest(pubKey.getDigest());
         n = pubKey.getN();
         k = pubKey.getK();
@@ -101,7 +102,7 @@
     }
 
 
-    public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+    private void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
     {
         this.messDigest = Utils.getDigest(privKey.getDigest());
         n = privKey.getN();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
index b511fee..5285045 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKeyPairGenerator.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
 import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
@@ -60,7 +61,7 @@
      */
     private void initializeDefault()
     {
-        McElieceKeyGenerationParameters mcParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters());
+        McElieceKeyGenerationParameters mcParams = new McElieceKeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new McElieceParameters());
         initialize(mcParams);
     }
 
@@ -70,7 +71,11 @@
         this.mcElieceParams = (McElieceKeyGenerationParameters)param;
 
         // set source of randomness
-        this.random = new SecureRandom();
+        this.random = param.getRandom();
+        if (this.random == null)
+        {
+            this.random = CryptoServicesRegistrar.getSecureRandom();
+        }
 
         this.m = this.mcElieceParams.getParameters().getM();
         this.n = this.mcElieceParams.getParameters().getN();
@@ -139,7 +144,6 @@
     public void init(KeyGenerationParameters param)
     {
         this.initialize(param);
-
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
index 89e3232..7e75c21 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McElieceKobaraImaiCipher.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -67,7 +68,7 @@
             }
             else
             {
-                this.sr = new SecureRandom();
+                this.sr = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (McElieceCCA2PublicKeyParameters)param;
                 this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
             }
@@ -109,7 +110,7 @@
 
     }
 
-    public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+    private void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
     {
         this.messDigest = Utils.getDigest(privKey.getDigest());
         n = privKey.getN();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
index c24b5b4..7fa02ed 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePointchevalCipher.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -59,7 +60,7 @@
             }
             else
             {
-                this.sr = new SecureRandom();
+                this.sr = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (McElieceCCA2PublicKeyParameters)param;
                 this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
             }
@@ -108,16 +109,16 @@
     }
 
 
-    public void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
+    private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
     {
-        this.sr = sr != null ? sr : new SecureRandom();
+        this.sr = sr != null ? sr : CryptoServicesRegistrar.getSecureRandom();
         this.messDigest = Utils.getDigest(pubKey.getDigest());
         n = pubKey.getN();
         k = pubKey.getK();
         t = pubKey.getT();
     }
 
-    public void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
+    private void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
     {
         this.messDigest = Utils.getDigest(privKey.getDigest());
         n = privKey.getN();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NHOtherInfoGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NHOtherInfoGenerator.java
new file mode 100644
index 0000000..409afe0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NHOtherInfoGenerator.java
@@ -0,0 +1,164 @@
+package org.bouncycastle.pqc.crypto.newhope;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.util.DEROtherInfo;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.crypto.ExchangePair;
+
+/**
+ * OtherInfo Generator for which can be used for populating the SuppPrivInfo field used to provide shared
+ * secret data used with NIST SP 800-56A agreement algorithms.
+ */
+public class NHOtherInfoGenerator
+{
+    protected final DEROtherInfo.Builder otherInfoBuilder;
+    protected final SecureRandom random;
+
+    protected boolean used = false;
+    
+    /**
+     * Create a basic builder with just the compulsory fields.
+     *
+     * @param algorithmID the algorithm associated with this invocation of the KDF.
+     * @param partyUInfo  sender party info.
+     * @param partyVInfo  receiver party info.
+     * @param random a source of randomness.
+     */
+    public NHOtherInfoGenerator(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo, SecureRandom random)
+    {
+        this.otherInfoBuilder = new DEROtherInfo.Builder(algorithmID, partyUInfo, partyVInfo);
+        this.random = random;
+    }
+
+    /**
+     * Party U (initiator) generation.
+     */
+    public static class PartyU
+        extends NHOtherInfoGenerator
+    {
+        private AsymmetricCipherKeyPair aKp;
+        private NHAgreement agreement = new NHAgreement();
+
+        public PartyU(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo, java.security.SecureRandom random)
+        {
+            super(algorithmID, partyUInfo, partyVInfo, random);
+
+            NHKeyPairGenerator kpGen = new NHKeyPairGenerator();
+
+            kpGen.init(new KeyGenerationParameters(random, 2048));
+
+            aKp = kpGen.generateKeyPair();
+
+            agreement.init(aKp.getPrivate());
+        }
+
+        /**
+         * Add optional supplementary public info (DER tagged, implicit, 0).
+         *
+         * @param suppPubInfo supplementary public info.
+         * @return the current builder instance.
+         */
+        public NHOtherInfoGenerator withSuppPubInfo(byte[] suppPubInfo)
+        {
+            this.otherInfoBuilder.withSuppPubInfo(suppPubInfo);
+
+            return this;
+        }
+
+        public byte[] getSuppPrivInfoPartA()
+        {
+            return getEncoded((NHPublicKeyParameters)aKp.getPublic());
+        }
+
+        public DEROtherInfo generate(byte[] suppPrivInfoPartB)
+        {
+            if (used)
+            {
+                throw new IllegalStateException("builder already used");
+            }
+
+            used = true;
+
+            this.otherInfoBuilder.withSuppPrivInfo(agreement.calculateAgreement(NHOtherInfoGenerator.getPublicKey(suppPrivInfoPartB)));
+
+            return otherInfoBuilder.build();
+        }
+    }
+
+    /**
+     * Party V (responder) generation.
+     */
+    public static class PartyV
+        extends NHOtherInfoGenerator
+    {
+        public PartyV(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo, SecureRandom random)
+        {
+            super(algorithmID, partyUInfo, partyVInfo, random);
+        }
+
+        /**
+         * Add optional supplementary public info (DER tagged, implicit, 0).
+         *
+         * @param suppPubInfo supplementary public info.
+         * @return the current builder instance.
+         */
+        public NHOtherInfoGenerator withSuppPubInfo(byte[] suppPubInfo)
+        {
+            this.otherInfoBuilder.withSuppPubInfo(suppPubInfo);
+
+            return this;
+        }
+
+        public byte[] getSuppPrivInfoPartB(byte[] suppPrivInfoPartA)
+        {
+            NHExchangePairGenerator exchGen = new NHExchangePairGenerator(random);
+
+            ExchangePair bEp = exchGen.generateExchange(getPublicKey(suppPrivInfoPartA));
+
+            this.otherInfoBuilder.withSuppPrivInfo(bEp.getSharedValue());
+
+            return getEncoded((NHPublicKeyParameters)bEp.getPublicKey());
+        }
+
+        public DEROtherInfo generate()
+        {
+            if (used)
+            {
+                throw new IllegalStateException("builder already used");
+            }
+
+            used = true;
+
+            return otherInfoBuilder.build();
+        }
+    }
+
+    private static byte[] getEncoded(NHPublicKeyParameters pubKey)
+    {
+        SubjectPublicKeyInfo pki;
+        try
+        {
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope);
+            pki = new SubjectPublicKeyInfo(algorithmIdentifier, pubKey.getPubData());
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    private static NHPublicKeyParameters getPublicKey(byte[] enc)
+    {
+        SubjectPublicKeyInfo pki = SubjectPublicKeyInfo.getInstance(enc);
+
+        return new NHPublicKeyParameters(pki.getPublicKeyData().getOctets());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NewHope.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NewHope.java
index 7ba75d5..8853aee 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NewHope.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/NewHope.java
@@ -20,6 +20,8 @@
     {
         byte[] seed = new byte[Params.SEED_BYTES];
         rand.nextBytes(seed);
+        
+        sha3(seed);     // don't expose RNG output
 
         short[] a = new short[Params.N];
         generateA(a, seed);
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Params.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Params.java
index 1f13d97..8552f46 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Params.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Params.java
@@ -8,5 +8,5 @@
 
     static final int POLY_BYTES = 1792;
     static final int REC_BYTES = 256;
-    static final int SEED_BYTES = 32;
+    static final int SEED_BYTES = 32;     // care changing this one - connected to digest size used.
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Poly.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Poly.java
index e7228ba..a1986ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Poly.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/Poly.java
@@ -113,8 +113,7 @@
             for (int i = 0; i < output.length; i += 2)
             {
                 int val = (output[i] & 0xFF) | ((output[i + 1] & 0xFF) << 8);
-                val &= 0x3FFF;
-                if (val < Params.Q)
+                if (val < 5 * Params.Q)
                 {
                     a[pos++] = (short)val;
                     if (pos == Params.N)
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/package.html b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/package.html
new file mode 100644
index 0000000..86e86d6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/newhope/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Low level implementation of the NewHope key exchange algorithm.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
index 7426724..e050fcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEncryptionKeyGenerationParameters.java
@@ -5,9 +5,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.SecureRandom;
 import java.util.Arrays;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.digests.SHA256Digest;
@@ -97,7 +97,7 @@
      */
     public NTRUEncryptionKeyGenerationParameters(int N, int q, int df, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
     {
-        super(new SecureRandom(), db);
+        super(CryptoServicesRegistrar.getSecureRandom(), db);
         this.N = N;
         this.q = q;
         this.df = df;
@@ -136,7 +136,7 @@
      */
     public NTRUEncryptionKeyGenerationParameters(int N, int q, int df1, int df2, int df3, int dm0, int db, int c, int minCallsR, int minCallsMask, boolean hashSeed, byte[] oid, boolean sparse, boolean fastFp, Digest hashAlg)
     {
-        super(new SecureRandom(), db);
+        super(CryptoServicesRegistrar.getSecureRandom(), db);
 
         this.N = N;
         this.q = q;
@@ -180,7 +180,7 @@
     public NTRUEncryptionKeyGenerationParameters(InputStream is)
         throws IOException
     {
-        super(new SecureRandom(), -1);
+        super(CryptoServicesRegistrar.getSecureRandom(), -1);
         DataInputStream dis = new DataInputStream(is);
         N = dis.readInt();
         q = dis.readInt();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java
index 32141ae..4aeee2d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUEngine.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
@@ -51,7 +52,7 @@
             }
             else
             {
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
                 this.pubKey = (NTRUEncryptionPublicKeyParameters)parameters;
             }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
index b6ff8c5..3aa4514 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyGenerationParameters.java
@@ -5,9 +5,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.SecureRandom;
 import java.text.DecimalFormat;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.digests.SHA256Digest;
@@ -92,7 +92,7 @@
      */
     public NTRUSigningKeyGenerationParameters(int N, int q, int d, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
     {
-        super(new SecureRandom(), N);
+        super(CryptoServicesRegistrar.getSecureRandom(), N);
         this.N = N;
         this.q = q;
         this.d = d;
@@ -129,7 +129,7 @@
      */
     public NTRUSigningKeyGenerationParameters(int N, int q, int d1, int d2, int d3, int B, int basisType, double beta, double normBound, double keyNormBound, boolean primeCheck, boolean sparse, int keyGenAlg, Digest hashAlg)
     {
-        super(new SecureRandom(), N);
+        super(CryptoServicesRegistrar.getSecureRandom(), N);
         this.N = N;
         this.q = q;
         this.d1 = d1;
@@ -164,7 +164,7 @@
     public NTRUSigningKeyGenerationParameters(InputStream is)
         throws IOException
     {
-        super(new SecureRandom(), 0);     // TODO:
+        super(CryptoServicesRegistrar.getSecureRandom(), 0);     // TODO:
         DataInputStream dis = new DataInputStream(is);
         N = dis.readInt();
         q = dis.readInt();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
index 4fce579..66bb260 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/ntru/NTRUSigningKeyPairGenerator.java
@@ -2,7 +2,6 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -12,6 +11,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.math.ntru.euclid.BigIntEuclidean;
 import org.bouncycastle.pqc.math.ntru.polynomial.BigDecimalPolynomial;
@@ -188,7 +188,7 @@
         {
             do
             {
-                f = params.polyType== NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+                f = params.polyType== NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, CryptoServicesRegistrar.getSecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, CryptoServicesRegistrar.getSecureRandom());
                 fInt = f.toIntegerPolynomial();
             }
             while (primeCheck && fInt.resultant(_2n1).res.equals(ZERO));
@@ -203,7 +203,7 @@
             {
                 do
                 {
-                    g = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, new SecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, new SecureRandom());
+                    g = params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_SIMPLE ? DenseTernaryPolynomial.generateRandom(N, d + 1, d, CryptoServicesRegistrar.getSecureRandom()) : ProductFormPolynomial.generateRandom(N, d1, d2, d3 + 1, d3, CryptoServicesRegistrar.getSecureRandom());
                     gInt = g.toIntegerPolynomial();
                 }
                 while (primeCheck && gInt.resultant(_2n1).res.equals(ZERO));
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/CommonFunction.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/CommonFunction.java
new file mode 100644
index 0000000..f650347
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/CommonFunction.java
@@ -0,0 +1,286 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+class CommonFunction
+{
+
+    /****************************************************************************************************
+     * Description:	Checks Whether the Two Parts of Arrays are Equal to Each Other
+     *
+     * @param        left            Left Array
+     * @param        leftOffset        Starting Point of the Left Array
+     * @param        right            Right Array
+     * @param        rightOffset        Starting Point of the Right Array
+     * @param        length            Length to be Compared from the Starting Point
+     *
+     * @return true            Equal
+     *				false			Different
+     ****************************************************************************************************/
+    public static boolean memoryEqual(byte[] left, int leftOffset, byte[] right, int rightOffset, int length)
+    {
+
+        if ((leftOffset + length <= left.length) && (rightOffset + length <= right.length))
+        {
+
+            for (int i = 0; i < length; i++)
+            {
+
+                if (left[leftOffset + i] != right[rightOffset + i])
+                {
+
+                    return false;
+
+                }
+
+            }
+
+            return true;
+
+        }
+        else
+        {
+
+            return false;
+
+        }
+
+    }
+
+    /****************************************************************************
+     * Description:	Converts 2 Consecutive Bytes in "load" to A Number of "Short"
+     *				from A Known Position
+     *
+     * @param        load            Source Array
+     * @param        loadOffset        Starting Position
+     *
+     * @return A Number of "Short"
+     ****************************************************************************/
+    public static short load16(final byte[] load, int loadOffset)
+    {
+
+        short number = 0;
+
+        if (load.length - loadOffset >= Const.SHORT_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.SHORT_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                number ^= (short)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+        else
+        {
+
+            for (int i = 0; i < load.length - loadOffset; i++)
+            {
+
+                number ^= (short)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+
+        return number;
+
+    }
+
+    /******************************************************************************
+     * Description:	Converts 4 Consecutive Bytes in "load" to A Number of "Integer"
+     *				from A Known Position
+     *
+     * @param        load            Source Array
+     * @param        loadOffset        Starting Position
+     *
+     * @return A Number of "Integer"
+     ******************************************************************************/
+    public static int load32(final byte[] load, int loadOffset)
+    {
+
+        int number = 0;
+
+        if (load.length - loadOffset >= Const.INT_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.INT_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                number ^= (int)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+        else
+        {
+
+
+            for (int i = 0; i < load.length - loadOffset; i++)
+            {
+
+                number ^= (int)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+
+        return number;
+
+    }
+
+    /***************************************************************************
+     * Description:	Converts 8 Consecutive Bytes in "load" to A Number of "Long"
+     *				from A Known Position
+     *
+     * @param        load            Source Array
+     * @param        loadOffset        Starting Position
+     *
+     * @return A Number of "Long"
+     ***************************************************************************/
+    public static long load64(final byte[] load, int loadOffset)
+    {
+
+        long number = 0L;
+
+        if (load.length - loadOffset >= Const.LONG_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.LONG_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                number ^= (long)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+        else
+        {
+
+            for (int i = 0; i < load.length - loadOffset; i++)
+            {
+
+                number ^= (long)(load[loadOffset + i] & 0xFF) << (Const.BYTE_SIZE * i);
+
+            }
+
+        }
+
+        return number;
+
+    }
+
+    /*****************************************************************************
+     * Description:	Converts A Number of "Short" to 2 Consecutive Bytes in "store"
+     *				from a known position
+     *
+     * @param        store            Destination Array
+     * @param        storeOffset        Starting position
+     * @param        number            Source Number
+     *
+     * @return none
+     *****************************************************************************/
+    public static void store16(byte[] store, int storeOffset, short number)
+    {
+
+        if (store.length - storeOffset >= Const.SHORT_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.SHORT_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFF);
+
+            }
+
+        }
+        else
+        {
+
+            for (int i = 0; i < store.length - storeOffset; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFF);
+
+            }
+
+        }
+
+    }
+
+    /*******************************************************************************
+     * Description:	Converts A Number of "Integer" to 4 Consecutive Bytes in "store"
+     * 				from A Known Position
+     *
+     * @param        store            Destination Array
+     * @param        storeOffset        Starting Position
+     * @param        number:			Source Number
+     *
+     * @return none
+     *******************************************************************************/
+    public static void store32(byte[] store, int storeOffset, int number)
+    {
+
+        if (store.length - storeOffset >= Const.INT_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.INT_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFF);
+
+            }
+
+        }
+        else
+        {
+
+            for (int i = 0; i < store.length - storeOffset; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFF);
+
+            }
+
+        }
+
+    }
+
+    /****************************************************************************
+     * Description:	Converts A Number of "Long" to 8 Consecutive Bytes in "store"
+     * 				from A Known Position
+     *
+     * @param        store            Destination Array
+     * @param        storeOffset        Starting Position
+     * @param        number            Source Number
+     *
+     * @return none
+     ****************************************************************************/
+    public static void store64(byte[] store, int storeOffset, long number)
+    {
+
+        if (store.length - storeOffset >= Const.LONG_SIZE / Const.BYTE_SIZE)
+        {
+
+            for (int i = 0; i < Const.LONG_SIZE / Const.BYTE_SIZE; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFFL);
+
+            }
+
+        }
+        else
+        {
+
+            for (int i = 0; i < store.length - storeOffset; i++)
+            {
+
+                store[storeOffset + i] = (byte)((number >> (Const.BYTE_SIZE * i)) & 0xFFL);
+
+            }
+
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Const.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Const.java
new file mode 100644
index 0000000..76d3e6f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Const.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+class Const
+{
+    static final int BYTE_SIZE = 8;
+    static final int SHORT_SIZE = 16;
+    static final int INT_SIZE = 32;
+    static final int LONG_SIZE = 64;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/HashUtils.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/HashUtils.java
new file mode 100644
index 0000000..0ea9dba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/HashUtils.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import org.bouncycastle.crypto.digests.CSHAKEDigest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+
+class HashUtils
+{
+
+    public static final int SECURE_HASH_ALGORITHM_KECCAK_128_RATE = 168;
+    public static final int SECURE_HASH_ALGORITHM_KECCAK_256_RATE = 136;
+
+    /***************************************************************************************************************************************************************
+     * Description:	The Secure-Hash-Algorithm-3 Extendable-Output Function That Generally Supports 128 Bits of Security Strength, If the Output is Sufficiently Long
+     ***************************************************************************************************************************************************************/
+    static void secureHashAlgorithmKECCAK128(byte[] output, int outputOffset, int outputLength, byte[] input, int inputOffset, int inputLength)
+    {
+        SHAKEDigest dig = new SHAKEDigest(128);
+        dig.update(input, inputOffset, inputLength);
+
+        dig.doFinal(output, outputOffset, outputLength);
+    }
+
+    /***************************************************************************************************************************************************************
+     * Description:	The Secure-Hash-Algorithm-3 Extendable-Output Function That Generally Supports 256 Bits of Security Strength, If the Output is Sufficiently Long
+     ***************************************************************************************************************************************************************/
+    static void secureHashAlgorithmKECCAK256(byte[] output, int outputOffset, int outputLength, byte[] input, int inputOffset, int inputLength)
+    {
+        SHAKEDigest dig = new SHAKEDigest(256);
+        dig.update(input, inputOffset, inputLength);
+
+        dig.doFinal(output, outputOffset, outputLength);
+    }
+
+    /* Customizable Secure Hash Algorithm KECCAK 128 / Customizable Secure Hash Algorithm KECCAK 256 */
+
+
+    static void customizableSecureHashAlgorithmKECCAK128Simple(byte[] output, int outputOffset, int outputLength, short continuousTimeStochasticModelling, byte[] input, int inputOffset, int inputLength)
+    {
+        CSHAKEDigest dig = new CSHAKEDigest(128, null, new byte[] {(byte)continuousTimeStochasticModelling, (byte)(continuousTimeStochasticModelling >> 8) });
+        dig.update(input, inputOffset, inputLength);
+
+        dig.doFinal(output, outputOffset, outputLength);
+    }
+
+    static void customizableSecureHashAlgorithmKECCAK256Simple(byte[] output, int outputOffset, int outputLength, short continuousTimeStochasticModelling, byte[] input, int inputOffset, int inputLength)
+    {
+        CSHAKEDigest dig = new CSHAKEDigest(256, null, new byte[] {(byte)continuousTimeStochasticModelling, (byte)(continuousTimeStochasticModelling >> 8) });
+        dig.update(input, inputOffset, inputLength);
+
+        dig.doFinal(output, outputOffset, outputLength);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Pack.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Pack.java
new file mode 100644
index 0000000..26a9a12
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Pack.java
@@ -0,0 +1,1370 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+class Pack
+{
+
+    /*******************************************************************************************************************************************************
+     * Description:	Encode Private Key for Heuristic qTESLA Security Category-1
+     *
+     * @param        privateKey                Private Key
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        seed                    Kappa-Bit Seed
+     * @param        seedOffset                Starting Point of the Kappa-Bit Seed
+     *
+     * @return none
+     *******************************************************************************************************************************************************/
+    public static void encodePrivateKeyI(byte[] privateKey, final int[] secretPolynomial, final int[] errorPolynomial, final byte[] seed, int seedOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_I; i += 4)
+        {
+
+            privateKey[j + 0] = (byte)secretPolynomial[i + 0];
+            privateKey[j + 1] = (byte)(((secretPolynomial[i + 0] >> 8) & 0x03) | (secretPolynomial[i + 1] << 2));
+            privateKey[j + 2] = (byte)(((secretPolynomial[i + 1] >> 6) & 0x0F) | (secretPolynomial[i + 2] << 4));
+            privateKey[j + 3] = (byte)(((secretPolynomial[i + 2] >> 4) & 0x3F) | (secretPolynomial[i + 3] << 6));
+            privateKey[j + 4] = (byte)(secretPolynomial[i + 3] >> 2);
+
+            j += 5;
+
+        }
+
+        for (int i = 0; i < Parameter.N_I; i += 4)
+        {
+
+            privateKey[j + 0] = (byte)errorPolynomial[i + 0];
+            privateKey[j + 1] = (byte)(((errorPolynomial[i + 0] >> 8) & 0x03) | (errorPolynomial[i + 1] << 2));
+            privateKey[j + 2] = (byte)(((errorPolynomial[i + 1] >> 6) & 0x0F) | (errorPolynomial[i + 2] << 4));
+            privateKey[j + 3] = (byte)(((errorPolynomial[i + 2] >> 4) & 0x3F) | (errorPolynomial[i + 3] << 6));
+            privateKey[j + 4] = (byte)(errorPolynomial[i + 3] >> 2);
+
+            j += 5;
+
+        }
+
+        System.arraycopy(seed, seedOffset, privateKey, Parameter.N_I * Parameter.S_BIT_I * 2 / Const.BYTE_SIZE, Polynomial.SEED * 2);
+
+    }
+
+    /*************************************************************************************************************************************************************
+     * Description:	Encode Private Key for Heuristic qTESLA Security Category-3 (Option for Size)
+     *
+     * @param        privateKey                Private Key
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        seed                    Kappa-Bit Seed
+     * @param        seedOffset                Starting Point of the Kappa-Bit Seed
+     *
+     * @return none
+     *************************************************************************************************************************************************************/
+    public static void encodePrivateKeyIIISize(byte[] privateKey, final int[] secretPolynomial, final int[] errorPolynomial, final byte[] seed, int seedOffset)
+    {
+
+        for (int i = 0; i < Parameter.N_III_SIZE; i++)
+        {
+
+            privateKey[i] = (byte)secretPolynomial[i];
+
+        }
+
+        for (int i = 0; i < Parameter.N_III_SIZE; i++)
+        {
+
+            privateKey[Parameter.N_III_SIZE + i] = (byte)errorPolynomial[i];
+
+        }
+
+        System.arraycopy(seed, seedOffset, privateKey, Parameter.N_III_SIZE * Parameter.S_BIT_III_SIZE * 2 / Const.BYTE_SIZE, Polynomial.SEED * 2);
+
+    }
+
+    /***********************************************************************************************************************************************************************************
+     * Description:	Encode Private Key for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        privateKey                Private Key
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        seed                    Kappa-Bit Seed
+     * @param        seedOffset                Starting Point of the Kappa-Bit Seed
+     *
+     * @return none
+     ***********************************************************************************************************************************************************************************/
+    public static void encodePrivateKeyIIISpeed(byte[] privateKey, final int[] secretPolynomial, final int[] errorPolynomial, final byte[] seed, int seedOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += 8)
+        {
+
+            privateKey[j + 0] = (byte)secretPolynomial[i + 0];
+            privateKey[j + 1] = (byte)(((secretPolynomial[i + 0] >> 8) & 0x01) | (secretPolynomial[i + 1] << 1));
+            privateKey[j + 2] = (byte)(((secretPolynomial[i + 1] >> 7) & 0x03) | (secretPolynomial[i + 2] << 2));
+            privateKey[j + 3] = (byte)(((secretPolynomial[i + 2] >> 6) & 0x07) | (secretPolynomial[i + 3] << 3));
+            privateKey[j + 4] = (byte)(((secretPolynomial[i + 3] >> 5) & 0x0F) | (secretPolynomial[i + 4] << 4));
+            privateKey[j + 5] = (byte)(((secretPolynomial[i + 4] >> 4) & 0x1F) | (secretPolynomial[i + 5] << 5));
+            privateKey[j + 6] = (byte)(((secretPolynomial[i + 5] >> 3) & 0x3F) | (secretPolynomial[i + 6] << 6));
+            privateKey[j + 7] = (byte)(((secretPolynomial[i + 6] >> 2) & 0x7F) | (secretPolynomial[i + 7] << 7));
+            privateKey[j + 8] = (byte)(secretPolynomial[i + 7] >> 1);
+
+            j += 9;
+
+        }
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += 8)
+        {
+
+            privateKey[j + 0] = (byte)errorPolynomial[i + 0];
+            privateKey[j + 1] = (byte)(((errorPolynomial[i + 0] >> 8) & 0x01) | (errorPolynomial[i + 1] << 1));
+            privateKey[j + 2] = (byte)(((errorPolynomial[i + 1] >> 7) & 0x03) | (errorPolynomial[i + 2] << 2));
+            privateKey[j + 3] = (byte)(((errorPolynomial[i + 2] >> 6) & 0x07) | (errorPolynomial[i + 3] << 3));
+            privateKey[j + 4] = (byte)(((errorPolynomial[i + 3] >> 5) & 0x0F) | (errorPolynomial[i + 4] << 4));
+            privateKey[j + 5] = (byte)(((errorPolynomial[i + 4] >> 4) & 0x1F) | (errorPolynomial[i + 5] << 5));
+            privateKey[j + 6] = (byte)(((errorPolynomial[i + 5] >> 3) & 0x3F) | (errorPolynomial[i + 6] << 6));
+            privateKey[j + 7] = (byte)(((errorPolynomial[i + 6] >> 2) & 0x7F) | (errorPolynomial[i + 7] << 7));
+            privateKey[j + 8] = (byte)(errorPolynomial[i + 7] >> 1);
+
+            j += 9;
+
+        }
+
+        System.arraycopy(seed, seedOffset, privateKey, Parameter.N_III_SPEED * Parameter.S_BIT_III_SPEED * 2 / Const.BYTE_SIZE, Polynomial.SEED * 2);
+
+    }
+
+    /*******************************************************************************************************************************
+     * Description:	Decode Private Key for Heuristic qTESLA Security Category-1
+     *
+     * @param        seed                    Kappa-Bit Seed
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        privateKey                Private Key
+     *
+     * @return none
+     *******************************************************************************************************************************/
+    public static void decodePrivateKeyI(byte[] seed, short[] secretPolynomial, short[] errorPolynomial, final byte[] privateKey)
+    {
+
+        int j = 0;
+        int temporary = 0;
+
+        for (int i = 0; i < Parameter.N_I; i += 4)
+        {
+
+            temporary = privateKey[j + 0] & 0xFF;
+            secretPolynomial[i + 0] = (short)temporary;
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = (temporary << 30) >> 22;
+            secretPolynomial[i + 0] |= (short)temporary;
+
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = temporary >> 2;
+            secretPolynomial[i + 1] = (short)temporary;
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = (temporary << 28) >> 22;
+            secretPolynomial[i + 1] |= (short)temporary;
+
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = temporary >> 4;
+            secretPolynomial[i + 2] = (short)temporary;
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = (temporary << 26) >> 22;
+            secretPolynomial[i + 2] |= (short)temporary;
+
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = temporary >> 6;
+            secretPolynomial[i + 3] = (short)temporary;
+            temporary = privateKey[j + 4];
+            temporary = (short)temporary << 2;
+            secretPolynomial[i + 3] |= (short)temporary;
+
+            j += 5;
+
+        }
+
+        for (int i = 0; i < Parameter.N_I; i += 4)
+        {
+
+            temporary = privateKey[j + 0] & 0xFF;
+            errorPolynomial[i + 0] = (short)temporary;
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = (temporary << 30) >> 22;
+            errorPolynomial[i + 0] |= (short)temporary;
+
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = temporary >> 2;
+            errorPolynomial[i + 1] = (short)temporary;
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = (temporary << 28) >> 22;
+            errorPolynomial[i + 1] |= (short)temporary;
+
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = temporary >> 4;
+            errorPolynomial[i + 2] = (short)temporary;
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = (temporary << 26) >> 22;
+            errorPolynomial[i + 2] |= (short)temporary;
+
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = temporary >> 6;
+            errorPolynomial[i + 3] = (short)temporary;
+            temporary = privateKey[j + 4];
+            temporary = (short)temporary << 2;
+            errorPolynomial[i + 3] |= (short)temporary;
+
+            j += 5;
+
+        }
+
+        System.arraycopy(privateKey, Parameter.N_I * Parameter.S_BIT_I * 2 / Const.BYTE_SIZE, seed, 0, Polynomial.SEED * 2);
+
+    }
+
+    /*************************************************************************************************************************************
+     * Description:	Decode Private Key for Heuristic qTESLA Security Category-3 (Option for Size)
+     *
+     * @param        seed                    Kappa-Bit Seed
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        privateKey                Private Key
+     *
+     * @return none
+     *************************************************************************************************************************************/
+    public static void decodePrivateKeyIIISize(byte[] seed, short[] secretPolynomial, short[] errorPolynomial, final byte[] privateKey)
+    {
+
+        for (int i = 0; i < Parameter.N_III_SIZE; i++)
+        {
+
+            secretPolynomial[i] = privateKey[i];
+
+        }
+
+        for (int i = 0; i < Parameter.N_III_SIZE; i++)
+        {
+
+            errorPolynomial[i] = privateKey[Parameter.N_III_SIZE + i];
+
+        }
+
+        System.arraycopy(privateKey, Parameter.N_III_SIZE * Parameter.S_BIT_III_SIZE * 2 / Const.BYTE_SIZE, seed, 0, Polynomial.SEED * 2);
+
+    }
+
+    /**************************************************************************************************************************************
+     * Description:	Decode Private Key for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        seed                    Kappa-Bit Seed
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        privateKey                Private Key
+     *
+     * @return none
+     **************************************************************************************************************************************/
+    public static void decodePrivateKeyIIISpeed(byte[] seed, short[] secretPolynomial, short[] errorPolynomial, final byte[] privateKey)
+    {
+
+        int j = 0;
+        int temporary = 0;
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += 8)
+        {
+
+            temporary = privateKey[j + 0] & 0xFF;
+            secretPolynomial[i + 0] = (short)temporary;
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = (temporary << 31) >> 23;
+            secretPolynomial[i + 0] |= (short)temporary;
+
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = temporary >> 1;
+            secretPolynomial[i + 1] = (short)temporary;
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = (temporary << 30) >> 23;
+            secretPolynomial[i + 1] |= (short)temporary;
+
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = temporary >> 2;
+            secretPolynomial[i + 2] = (short)temporary;
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = (temporary << 29) >> 23;
+            secretPolynomial[i + 2] |= (short)temporary;
+
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = temporary >> 3;
+            secretPolynomial[i + 3] = (short)temporary;
+            temporary = privateKey[j + 4] & 0xFF;
+            temporary = (temporary << 28) >> 23;
+            secretPolynomial[i + 3] |= (short)temporary;
+
+            temporary = privateKey[j + 4] & 0xFF;
+            temporary = temporary >> 4;
+            secretPolynomial[i + 4] = (short)temporary;
+            temporary = privateKey[j + 5] & 0xFF;
+            temporary = (temporary << 27) >> 23;
+            secretPolynomial[i + 4] |= (short)temporary;
+
+            temporary = privateKey[j + 5] & 0xFF;
+            temporary = temporary >> 5;
+            secretPolynomial[i + 5] = (short)temporary;
+            temporary = privateKey[j + 6] & 0xFF;
+            temporary = (temporary << 26) >> 23;
+            secretPolynomial[i + 5] |= (short)temporary;
+
+            temporary = privateKey[j + 6] & 0xFF;
+            temporary = temporary >> 6;
+            secretPolynomial[i + 6] = (short)temporary;
+            temporary = privateKey[j + 7] & 0xFF;
+            temporary = (temporary << 25) >> 23;
+            secretPolynomial[i + 6] |= (short)temporary;
+
+            temporary = privateKey[j + 7] & 0xFF;
+            temporary = temporary >> 7;
+            secretPolynomial[i + 7] = (short)temporary;
+            temporary = privateKey[j + 8];
+            temporary = (short)temporary << 1;
+            secretPolynomial[i + 7] |= (short)temporary;
+
+            j += 9;
+
+        }
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += 8)
+        {
+
+            temporary = privateKey[j + 0] & 0xFF;
+            errorPolynomial[i + 0] = (short)temporary;
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = (temporary << 31) >> 23;
+            errorPolynomial[i + 0] |= (short)temporary;
+
+            temporary = privateKey[j + 1] & 0xFF;
+            temporary = temporary >> 1;
+            errorPolynomial[i + 1] = (short)temporary;
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = (temporary << 30) >> 23;
+            errorPolynomial[i + 1] |= (short)temporary;
+
+            temporary = privateKey[j + 2] & 0xFF;
+            temporary = temporary >> 2;
+            errorPolynomial[i + 2] = (short)temporary;
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = (temporary << 29) >> 23;
+            errorPolynomial[i + 2] |= (short)temporary;
+
+            temporary = privateKey[j + 3] & 0xFF;
+            temporary = temporary >> 3;
+            errorPolynomial[i + 3] = (short)temporary;
+            temporary = privateKey[j + 4] & 0xFF;
+            temporary = (temporary << 28) >> 23;
+            errorPolynomial[i + 3] |= (short)temporary;
+
+            temporary = privateKey[j + 4] & 0xFF;
+            temporary = temporary >> 4;
+            errorPolynomial[i + 4] = (short)temporary;
+            temporary = privateKey[j + 5] & 0xFF;
+            temporary = (temporary << 27) >> 23;
+            errorPolynomial[i + 4] |= (short)temporary;
+
+            temporary = privateKey[j + 5] & 0xFF;
+            temporary = temporary >> 5;
+            errorPolynomial[i + 5] = (short)temporary;
+            temporary = privateKey[j + 6] & 0xFF;
+            temporary = (temporary << 26) >> 23;
+            errorPolynomial[i + 5] |= (short)temporary;
+
+            temporary = privateKey[j + 6] & 0xFF;
+            temporary = temporary >> 6;
+            errorPolynomial[i + 6] = (short)temporary;
+            temporary = privateKey[j + 7] & 0xFF;
+            temporary = (temporary << 25) >> 23;
+            errorPolynomial[i + 6] |= (short)temporary;
+
+            temporary = privateKey[j + 7] & 0xFF;
+            temporary = temporary >> 7;
+            errorPolynomial[i + 7] = (short)temporary;
+            temporary = privateKey[j + 8];
+            temporary = (short)temporary << 1;
+            errorPolynomial[i + 7] |= (short)temporary;
+
+            j += 9;
+
+        }
+
+        System.arraycopy(privateKey, Parameter.N_III_SPEED * Parameter.S_BIT_III_SPEED * 2 / Const.BYTE_SIZE, seed, 0, Polynomial.SEED * 2);
+
+    }
+
+    /********************************************************************************************************************************************************************
+     * Description:	Pack Private Key for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        privateKey                Private Key
+     * @param        secretPolynomial        Coefficients of the Secret Polynomial
+     * @param        errorPolynomial            Coefficients of the Error Polynomial
+     * @param        seed                    Kappa-Bit Seed
+     * @param        seedOffset                Starting Point of the Kappa-Bit Seed
+     * @param        n                        Polynomial Degree
+     * @param        k                        Number of Ring-Learning-With-Errors Samples
+     *
+     * @return none
+     ********************************************************************************************************************************************************************/
+    public static void packPrivateKey(byte[] privateKey, final long[] secretPolynomial, final long[] errorPolynomial, final byte[] seed, int seedOffset, int n, int k)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            privateKey[i] = (byte)secretPolynomial[i];
+
+        }
+
+        for (int j = 0; j < k; j++)
+        {
+
+            for (int i = 0; i < n; i++)
+            {
+
+                privateKey[n + j * n + i] = (byte)errorPolynomial[j * n + i];
+
+            }
+
+        }
+
+        System.arraycopy(seed, seedOffset, privateKey, n + k * n, Polynomial.SEED * 2);
+
+    }
+
+    /**************************************************************************************************************************************************
+     * Description:	Encode Public Key for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size)
+     *
+     * @param        publicKey            Public Key
+     * @param        T                    T_1, ..., T_k
+     * @param        seedA                Seed Used to Generate the Polynomials a_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     * @param        n                    Polynomial Degree
+     * @param        qLogarithm            q <= 2 ^ qLogartihm
+     *
+     * @return none
+     **************************************************************************************************************************************************/
+    public static void encodePublicKey(byte[] publicKey, final int[] T, final byte[] seedA, int seedAOffset, int n, int qLogarithm)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < n * qLogarithm / Const.INT_SIZE; i += qLogarithm)
+        {
+
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(T[j + 0] | (T[j + 1] << 23)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((T[j + 1] >> 9) | (T[j + 2] << 14)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)((T[j + 2] >> 18) | (T[j + 3] << 5) | (T[j + 4] << 28)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 3), (int)((T[j + 4] >> 4) | (T[j + 5] << 19)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 4), (int)((T[j + 5] >> 13) | (T[j + 6] << 10)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 5), (int)((T[j + 6] >> 22) | (T[j + 7] << 1) | (T[j + 8] << 24)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 6), (int)((T[j + 8] >> 8) | (T[j + 9] << 15)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 7), (int)((T[j + 9] >> 17) | (T[j + 10] << 6) | (T[j + 11] << 29)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 8), (int)((T[j + 11] >> 3) | (T[j + 12] << 20)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 9), (int)((T[j + 12] >> 12) | (T[j + 13] << 11)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 10), (int)((T[j + 13] >> 21) | (T[j + 14] << 2) | (T[j + 15] << 25)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 11), (int)((T[j + 15] >> 7) | (T[j + 16] << 16)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 12), (int)((T[j + 16] >> 16) | (T[j + 17] << 7) | (T[j + 18] << 30)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 13), (int)((T[j + 18] >> 2) | (T[j + 19] << 21)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 14), (int)((T[j + 19] >> 11) | (T[j + 20] << 12)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 15), (int)((T[j + 20] >> 20) | (T[j + 21] << 3) | (T[j + 22] << 26)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 16), (int)((T[j + 22] >> 6) | (T[j + 23] << 17)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 17), (int)((T[j + 23] >> 15) | (T[j + 24] << 8) | (T[j + 25] << 31)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 18), (int)((T[j + 25] >> 1) | (T[j + 26] << 22)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 19), (int)((T[j + 26] >> 10) | (T[j + 27] << 13)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 20), (int)((T[j + 27] >> 19) | (T[j + 28] << 4) | (T[j + 29] << 27)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 21), (int)((T[j + 29] >> 5) | (T[j + 30] << 18)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 22), (int)((T[j + 30] >> 14) | (T[j + 31] << 9)));
+
+            j += Const.INT_SIZE;
+
+        }
+
+        System.arraycopy(seedA, seedAOffset, publicKey, n * qLogarithm / Const.BYTE_SIZE, Polynomial.SEED);
+
+    }
+
+    /******************************************************************************************************************************************************
+     * Description:	Encode Public Key for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        publicKey            Public Key
+     * @param        T                    T_1, ..., T_k
+     * @param        seedA                Seed Used to Generate the Polynomials a_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     *
+     * @return none
+     ******************************************************************************************************************************************************/
+    public static void encodePublicKeyIIISpeed(byte[] publicKey, final int[] T, final byte[] seedA, int seedAOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_III_SPEED * Parameter.Q_LOGARITHM_III_SPEED / Const.INT_SIZE; i += (Parameter.Q_LOGARITHM_III_SPEED / Const.BYTE_SIZE))
+        {
+
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(T[j + 0] | (T[j + 1] << 24)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((T[j + 1] >> 8) | (T[j + 2] << 16)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)((T[j + 2] >> 16) | (T[j + 3] << 8)));
+
+            j += Const.INT_SIZE / Const.BYTE_SIZE;
+
+        }
+
+        System.arraycopy(seedA, seedAOffset, publicKey, Parameter.N_III_SPEED * Parameter.Q_LOGARITHM_III_SPEED / Const.BYTE_SIZE, Polynomial.SEED);
+
+    }
+
+    /*******************************************************************************************************************************************************
+     * Description:	Encode Public Key for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        publicKey            Public Key
+     * @param        T                    T_1, ..., T_k
+     * @param        seedA                Seed Used to Generate the Polynomials a_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     *
+     * @return none
+     *******************************************************************************************************************************************************/
+    public static void encodePublicKeyIP(byte[] publicKey, final long[] T, final byte[] seedA, int seedAOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_I_P * Parameter.K_I_P * Parameter.Q_LOGARITHM_I_P / Const.INT_SIZE; i += Parameter.Q_LOGARITHM_I_P)
+        {
+
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(T[j + 0] | (T[j + 1] << 29)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((T[j + 1] >> 3) | (T[j + 2] << 26)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)((T[j + 2] >> 6) | (T[j + 3] << 23)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 3), (int)((T[j + 3] >> 9) | (T[j + 4] << 20)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 4), (int)((T[j + 4] >> 12) | (T[j + 5] << 17)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 5), (int)((T[j + 5] >> 15) | (T[j + 6] << 14)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 6), (int)((T[j + 6] >> 18) | (T[j + 7] << 11)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 7), (int)((T[j + 7] >> 21) | (T[j + 8] << 8)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 8), (int)((T[j + 8] >> 24) | (T[j + 9] << 5)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 9), (int)((T[j + 9] >> 27) | (T[j + 10] << 2) | (T[j + 11] << 31)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 10), (int)((T[j + 11] >> 1) | (T[j + 12] << 28)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 11), (int)((T[j + 12] >> 4) | (T[j + 13] << 25)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 12), (int)((T[j + 13] >> 7) | (T[j + 14] << 22)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 13), (int)((T[j + 14] >> 10) | (T[j + 15] << 19)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 14), (int)((T[j + 15] >> 13) | (T[j + 16] << 16)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 15), (int)((T[j + 16] >> 16) | (T[j + 17] << 13)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 16), (int)((T[j + 17] >> 19) | (T[j + 18] << 10)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 17), (int)((T[j + 18] >> 22) | (T[j + 19] << 7)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 18), (int)((T[j + 19] >> 25) | (T[j + 20] << 4)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 19), (int)((T[j + 20] >> 28) | (T[j + 21] << 1) | (T[j + 22] << 30)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 20), (int)((T[j + 22] >> 2) | (T[j + 23] << 27)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 21), (int)((T[j + 23] >> 5) | (T[j + 24] << 24)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 22), (int)((T[j + 24] >> 8) | (T[j + 25] << 21)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 23), (int)((T[j + 25] >> 11) | (T[j + 26] << 18)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 24), (int)((T[j + 26] >> 14) | (T[j + 27] << 15)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 25), (int)((T[j + 27] >> 17) | (T[j + 28] << 12)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 26), (int)((T[j + 28] >> 20) | (T[j + 29] << 9)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 27), (int)((T[j + 29] >> 23) | (T[j + 30] << 6)));
+            CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + 28), (int)((T[j + 30] >> 26) | (T[j + 31] << 3)));
+
+            j += Const.INT_SIZE;
+
+        }
+
+        System.arraycopy(seedA, seedAOffset, publicKey, Parameter.N_I_P * Parameter.K_I_P * Parameter.Q_LOGARITHM_I_P / Const.BYTE_SIZE, Polynomial.SEED);
+
+    }
+
+    /*************************************************************************************************************************************************************************************
+     * Description:	Encode Public Key for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        publicKey            Public Key
+     * @param        T                    T_1, ..., T_k
+     * @param        seedA                Seed Used to Generate the Polynomials a_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     *
+     * @return none
+     *************************************************************************************************************************************************************************************/
+    public static void encodePublicKeyIIIP(byte[] publicKey, final long[] T, final byte[] seedA, int seedAOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_III_P * Parameter.K_III_P * Parameter.Q_LOGARITHM_III_P / Const.INT_SIZE; i += Parameter.Q_LOGARITHM_III_P)
+        {
+
+            for (int index = 0; index < Parameter.Q_LOGARITHM_III_P; index++)
+            {
+
+                CommonFunction.store32(publicKey, Const.INT_SIZE / Const.BYTE_SIZE * (i + index), (int)((T[j + index] >> index) | (T[j + index + 1] << (Parameter.Q_LOGARITHM_III_P - index))));
+
+            }
+
+            j += Const.INT_SIZE;
+
+        }
+
+        System.arraycopy(seedA, seedAOffset, publicKey, Parameter.N_III_P * Parameter.K_III_P * Parameter.Q_LOGARITHM_III_P / Const.BYTE_SIZE, Polynomial.SEED);
+
+    }
+
+    /****************************************************************************************************************************************
+     * Description:	Decode Public Key for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size)
+     *
+     * @param        publicKey            Decoded Public Key
+     * @param        seedA                Seed Used to Generate the Polynomials A_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     * @param        publicKeyInput        Public Key to be Decoded
+     * @param        n                    Polynomial Degree
+     * @param        qLogarithm            q <= 2 ^ qLogartihm
+     *
+     * @return none
+     ****************************************************************************************************************************************/
+    public static void decodePublicKey(int[] publicKey, byte[] seedA, int seedAOffset, final byte[] publicKeyInput, int n, int qLogarithm)
+    {
+
+        int j = 0;
+
+        int mask = (1 << qLogarithm) - 1;
+
+        for (int i = 0; i < n; i += Const.INT_SIZE)
+        {
+
+            publicKey[i + 0] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) & mask;
+
+            publicKey[i + 1] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 23) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 9)) & mask;
+
+            publicKey[i + 2] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 14) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 18)) & mask;
+
+            publicKey[i + 3] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 5) & mask;
+
+            publicKey[i + 4] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 28) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 4)) & mask;
+
+            publicKey[i + 5] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) >>> 19) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 13)) & mask;
+
+            publicKey[i + 6] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) >>> 10) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 22)) & mask;
+
+            publicKey[i + 7] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 1) & mask;
+
+            publicKey[i + 8] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 24) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 8)) & mask;
+
+            publicKey[i + 9] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) >>> 15) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 17)) & mask;
+
+            publicKey[i + 10] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 6) & mask;
+
+            publicKey[i + 11] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 29) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 3)) & mask;
+
+            publicKey[i + 12] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) >>> 20) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 12)) & mask;
+
+            publicKey[i + 13] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 11) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) << 21)) & mask;
+
+            publicKey[i + 14] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >>> 2) & mask;
+
+            publicKey[i + 15] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >>> 25) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) << 7)) & mask;
+
+            publicKey[i + 16] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) >>> 16) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) << 16)) & mask;
+
+            publicKey[i + 17] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) >>> 7) & mask;
+
+            publicKey[i + 18] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) >>> 30) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) << 2)) & mask;
+
+            publicKey[i + 19] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) >>> 21) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) << 11)) & mask;
+
+            publicKey[i + 20] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) >>> 12) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) << 20)) & mask;
+
+            publicKey[i + 21] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) >>> 3) & mask;
+
+            publicKey[i + 22] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) >>> 26) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) << 6)) & mask;
+
+            publicKey[i + 23] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) >>> 17) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) << 15)) & mask;
+
+            publicKey[i + 24] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) >>> 8) & mask;
+
+            publicKey[i + 25] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) >>> 31) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) << 1)) & mask;
+
+            publicKey[i + 26] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) >>> 22) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) << 10)) & mask;
+
+            publicKey[i + 27] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) >>> 13) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) << 19)) & mask;
+
+            publicKey[i + 28] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) >>> 4) & mask;
+
+            publicKey[i + 29] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) >>> 27) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 21)) << 5)) & mask;
+
+            publicKey[i + 30] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 21)) >>> 18) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 22)) << 14)) & mask;
+
+            publicKey[i + 31] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 22)) >>> 9;
+
+            j += qLogarithm;
+
+        }
+
+        System.arraycopy(publicKeyInput, n * qLogarithm / Const.BYTE_SIZE, seedA, seedAOffset, Polynomial.SEED);
+
+    }
+
+    /*************************************************************************************************************************************************
+     * Description:	Decode Public Key for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        publicKey            Decoded Public Key
+     * @param        seedA                Seed Used to Generate the Polynomials A_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     * @param        publicKeyInput        Public Key to be Decoded
+     *
+     * @return none
+     *************************************************************************************************************************************************/
+    public static void decodePublicKeyIIISpeed(int[] publicKey, byte[] seedA, int seedAOffset, final byte[] publicKeyInput)
+    {
+
+        int j = 0;
+
+        int mask = (1 << Parameter.Q_LOGARITHM_III_SPEED) - 1;
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += Const.INT_SIZE / Const.BYTE_SIZE)
+        {
+
+            publicKey[i + 0] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) & mask;
+
+            publicKey[i + 1] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 24) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 8)) & mask;
+
+            publicKey[i + 2] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 16) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 16)) & mask;
+
+            publicKey[i + 3] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 8;
+
+            j += Parameter.Q_LOGARITHM_III_SPEED / Const.BYTE_SIZE;
+
+        }
+
+        System.arraycopy(publicKeyInput, Parameter.N_III_SPEED * Parameter.Q_LOGARITHM_III_SPEED / Const.BYTE_SIZE, seedA, seedAOffset, Polynomial.SEED);
+
+    }
+
+    /************************************************************************************************************************************************************
+     * Description:	Decode Public Key for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        publicKey            Decoded Public Key
+     * @param        seedA                Seed Used to Generate the Polynomials A_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     * @param        publicKeyInput        Public Key to be Decoded
+     *
+     * @return none
+     ************************************************************************************************************************************************************/
+    public static void decodePublicKeyIP(int[] publicKey, byte[] seedA, int seedAOffset, final byte[] publicKeyInput)
+    {
+
+        int j = 0;
+
+        int mask = (1 << Parameter.Q_LOGARITHM_I_P) - 1;
+
+        for (int i = 0; i < Parameter.N_I_P * Parameter.K_I_P; i += Const.INT_SIZE)
+        {
+
+            publicKey[i + 0] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) & mask;
+
+            publicKey[i + 1] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 29) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 3)) & mask;
+
+            publicKey[i + 2] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 26) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 6)) & mask;
+
+            publicKey[i + 3] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 23) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 9)) & mask;
+
+            publicKey[i + 4] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) >>> 20) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 12)) & mask;
+
+            publicKey[i + 5] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) >>> 17) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 15)) & mask;
+
+            publicKey[i + 6] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 14) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 18)) & mask;
+
+            publicKey[i + 7] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) >>> 11) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 21)) & mask;
+
+            publicKey[i + 8] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 8) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 24)) & mask;
+
+            publicKey[i + 9] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) >>> 5) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 27)) & mask;
+
+            publicKey[i + 10] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 2) & mask;
+
+            publicKey[i + 11] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 31) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) << 1)) & mask;
+
+            publicKey[i + 12] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >>> 28) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) << 4)) & mask;
+
+            publicKey[i + 13] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) >>> 25) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) << 7)) & mask;
+
+            publicKey[i + 14] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) >>> 22) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) << 10)) & mask;
+
+            publicKey[i + 15] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) >>> 19) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) << 13)) & mask;
+
+            publicKey[i + 16] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) >>> 16) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) << 16)) & mask;
+
+            publicKey[i + 17] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) >>> 13) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) << 19)) & mask;
+
+            publicKey[i + 18] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) >>> 10) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) << 22)) & mask;
+
+            publicKey[i + 19] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) >>> 7) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) << 25)) & mask;
+
+            publicKey[i + 20] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) >>> 4) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) << 28)) & mask;
+
+            publicKey[i + 21] = (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) >>> 1) & mask;
+
+            publicKey[i + 22] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) >>> 30) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) << 2)) & mask;
+
+            publicKey[i + 23] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) >>> 27) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 21)) << 5)) & mask;
+
+            publicKey[i + 24] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 21)) >>> 24) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 22)) << 8)) & mask;
+
+            publicKey[i + 25] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 22)) >>> 21) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 23)) << 11)) & mask;
+
+            publicKey[i + 26] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 23)) >>> 18) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 24)) << 14)) & mask;
+
+            publicKey[i + 27] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 24)) >>> 15) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 25)) << 17)) & mask;
+
+            publicKey[i + 28] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 25)) >>> 12) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 26)) << 20)) & mask;
+
+            publicKey[i + 29] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 26)) >>> 9) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 27)) << 23)) & mask;
+
+            publicKey[i + 30] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 27)) >>> 6) |
+                (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 28)) << 26)) & mask;
+
+            publicKey[i + 31] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + 28)) >>> 3;
+
+            j += Parameter.Q_LOGARITHM_I_P;
+
+        }
+
+        System.arraycopy(publicKeyInput, Parameter.N_I_P * Parameter.K_I_P * Parameter.Q_LOGARITHM_I_P / Const.BYTE_SIZE, seedA, seedAOffset, Polynomial.SEED);
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Decode Public Key for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        publicKey            Decoded Public Key
+     * @param        seedA                Seed Used to Generate the Polynomials A_i for i = 1, ..., k
+     * @param        seedAOffset            Starting Point of the Seed A
+     * @param        publicKeyInput        Public Key to be Decoded
+     *
+     * @return none
+     ****************************************************************************************************************************************************************/
+    public static void decodePublicKeyIIIP(int[] publicKey, byte[] seedA, int seedAOffset, final byte[] publicKeyInput)
+    {
+
+        int j = 0;
+
+        int mask = (1 << Parameter.Q_LOGARITHM_III_P) - 1;
+
+        for (int i = 0; i < Parameter.N_III_P * Parameter.K_III_P; i += Const.INT_SIZE)
+        {
+
+            publicKey[i] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * j) & mask;
+
+            for (int index = 1; index < Parameter.Q_LOGARITHM_III_P; index++)
+            {
+
+                publicKey[i + index] = ((CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + index - 1)) >>> (Const.INT_SIZE - index)) |
+                    (CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + index)) << index)) & mask;
+
+            }
+
+            publicKey[i + Parameter.Q_LOGARITHM_III_P] = CommonFunction.load32(publicKeyInput, Const.INT_SIZE / Const.BYTE_SIZE * (j + Parameter.Q_LOGARITHM_III_P - 1)) >>> 1;
+
+            j += Parameter.Q_LOGARITHM_III_P;
+
+        }
+
+        System.arraycopy(publicKeyInput, Parameter.N_III_P * Parameter.K_III_P * Parameter.Q_LOGARITHM_III_P / Const.BYTE_SIZE, seedA, seedAOffset, Polynomial.SEED);
+
+    }
+
+
+    /***************************************************************************************************************************************************************************************************************
+     * Description:	Encode Signature for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size)
+     *
+     * @param        signature            Output Package Containing Signature
+     * @param        signatureOffset        Starting Point of the Output Package Containing Signature
+     * @param        C
+     * @param        cOffset
+     * @param        Z
+     * @param        n                    Polynomial Degree
+     * @param        d                    Number of Rounded Bits
+     *
+     * @return none
+     ***************************************************************************************************************************************************************************************************************/
+    public static void encodeSignature(byte[] signature, int signatureOffset, byte[] C, int cOffset, int[] Z, int n, int d)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < (n * d / Const.INT_SIZE); i += d)
+        {
+
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(((Z[j + 0] & ((1 << 21) - 1))) | (Z[j + 1] << 21)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)(((Z[j + 1] >>> 11) & ((1 << 10) - 1)) | ((Z[j + 2] & ((1 << 21) - 1)) << 10) | (Z[j + 3] << 31)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)((((Z[j + 3] >>> 1) & ((1 << 20) - 1))) | (Z[j + 4] << 20)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 3), (int)(((Z[j + 4] >>> 12) & ((1 << 9) - 1)) | ((Z[j + 5] & ((1 << 21) - 1)) << 9) | (Z[j + 6] << 30)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 4), (int)((((Z[j + 6] >>> 2) & ((1 << 19) - 1))) | (Z[j + 7] << 19)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 5), (int)(((Z[j + 7] >>> 13) & ((1 << 8) - 1)) | ((Z[j + 8] & ((1 << 21) - 1)) << 8) | (Z[j + 9] << 29)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 6), (int)((((Z[j + 9] >>> 3) & ((1 << 18) - 1))) | (Z[j + 10] << 18)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 7), (int)(((Z[j + 10] >>> 14) & ((1 << 7) - 1)) | ((Z[j + 11] & ((1 << 21) - 1)) << 7) | (Z[j + 12] << 28)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 8), (int)((((Z[j + 12] >>> 4) & ((1 << 17) - 1))) | (Z[j + 13] << 17)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 9), (int)(((Z[j + 13] >>> 15) & ((1 << 6) - 1)) | ((Z[j + 14] & ((1 << 21) - 1)) << 6) | (Z[j + 15] << 27)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 10), (int)((((Z[j + 15] >>> 5) & ((1 << 16) - 1))) | (Z[j + 16] << 16)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 11), (int)(((Z[j + 16] >>> 16) & ((1 << 5) - 1)) | ((Z[j + 17] & ((1 << 21) - 1)) << 5) | (Z[j + 18] << 26)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 12), (int)((((Z[j + 18] >>> 6) & ((1 << 15) - 1))) | (Z[j + 19] << 15)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 13), (int)(((Z[j + 19] >>> 17) & ((1 << 4) - 1)) | ((Z[j + 20] & ((1 << 21) - 1)) << 4) | (Z[j + 21] << 25)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 14), (int)((((Z[j + 21] >>> 7) & ((1 << 14) - 1))) | (Z[j + 22] << 14)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 15), (int)(((Z[j + 22] >>> 18) & ((1 << 3) - 1)) | ((Z[j + 23] & ((1 << 21) - 1)) << 3) | (Z[j + 24] << 24)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 16), (int)((((Z[j + 24] >>> 8) & ((1 << 13) - 1))) | (Z[j + 25] << 13)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 17), (int)(((Z[j + 25] >>> 19) & ((1 << 2) - 1)) | ((Z[j + 26] & ((1 << 21) - 1)) << 2) | (Z[j + 27] << 23)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 18), (int)((((Z[j + 27] >>> 9) & ((1 << 12) - 1))) | (Z[j + 28] << 12)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 19), (int)(((Z[j + 28] >>> 20) & ((1 << 1) - 1)) | ((Z[j + 29] & ((1 << 21) - 1)) << 1) | (Z[j + 30] << 22)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 20), (int)((((Z[j + 30] >>> 10) & ((1 << 11) - 1))) | (Z[j + 31] << 11)));
+
+            j += Const.INT_SIZE;
+
+        }
+
+        System.arraycopy(C, cOffset, signature, signatureOffset + n * d / Const.BYTE_SIZE, Polynomial.HASH);
+
+    }
+
+    /*************************************************************************************************************************************************************************************************************
+     * Description:	Encode Signature for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        signature            Output Package Containing Signature
+     * @param        signatureOffset        Starting Point of the Output Package Containing Signature
+     * @param        C
+     * @param        cOffset
+     * @param        Z
+     *
+     * @return none
+     *************************************************************************************************************************************************************************************************************/
+    public static void encodeSignatureIIISpeed(byte[] signature, int signatureOffset, byte[] C, int cOffset, int[] Z)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < (Parameter.N_III_SPEED * Parameter.D_III_SPEED / Const.INT_SIZE); i += Parameter.D_III_SPEED / 2)
+        {
+
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(((Z[j + 0] & ((1 << 22) - 1))) | (Z[j + 1] << 22)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((((Z[j + 1] >>> 10) & ((1 << 12) - 1))) | (Z[j + 2] << 12)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)(((Z[j + 2] >>> 20) & ((1 << 2) - 1)) | ((Z[j + 3] & ((1 << 22) - 1)) << 2) | (Z[j + 4] << 24)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 3), (int)((((Z[j + 4] >>> 8) & ((1 << 14) - 1))) | (Z[j + 5] << 14)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 4), (int)(((Z[j + 5] >>> 18) & ((1 << 4) - 1)) | ((Z[j + 6] & ((1 << 22) - 1)) << 4) | (Z[j + 7] << 26)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 5), (int)((((Z[j + 7] >>> 6) & ((1 << 16) - 1))) | (Z[j + 8] << 16)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 6), (int)(((Z[j + 8] >>> 16) & ((1 << 6) - 1)) | ((Z[j + 9] & ((1 << 22) - 1)) << 6) | (Z[j + 10] << 28)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 7), (int)((((Z[j + 10] >>> 4) & ((1 << 18) - 1))) | (Z[j + 11] << 18)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 8), (int)(((Z[j + 11] >>> 14) & ((1 << 8) - 1)) | ((Z[j + 12] & ((1 << 22) - 1)) << 8) | (Z[j + 13] << 30)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 9), (int)((((Z[j + 13] >>> 2) & ((1 << 20) - 1))) | (Z[j + 14] << 20)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 10), (int)((((Z[j + 14] >>> 12) & ((1 << 10) - 1))) | (Z[j + 15] << 10)));
+
+            j += Const.INT_SIZE / 2;
+
+        }
+
+        System.arraycopy(C, cOffset, signature, signatureOffset + Parameter.N_III_SPEED * Parameter.D_III_SPEED / Const.BYTE_SIZE, Polynomial.HASH);
+
+    }
+
+    /*************************************************************************************************************************************************************************************************************
+     * Description:	Encode Signature for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        signature            Output Package Containing Signature
+     * @param        signatureOffset        Starting Point of the Output Package Containing Signature
+     * @param        C
+     * @param        cOffset
+     * @param        Z
+     *
+     * @return none
+     *************************************************************************************************************************************************************************************************************/
+    public static void encodeSignatureIP(byte[] signature, int signatureOffset, byte[] C, int cOffset, long[] Z)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < (Parameter.N_III_SPEED * Parameter.D_III_SPEED / Const.INT_SIZE); i += Parameter.D_III_SPEED / 2)
+        {
+
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(((Z[j + 0] & ((1 << 22) - 1))) | (Z[j + 1] << 22)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((((Z[j + 1] >>> 10) & ((1 << 12) - 1))) | (Z[j + 2] << 12)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)(((Z[j + 2] >>> 20) & ((1 << 2) - 1)) | ((Z[j + 3] & ((1 << 22) - 1)) << 2) | (Z[j + 4] << 24)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 3), (int)((((Z[j + 4] >>> 8) & ((1 << 14) - 1))) | (Z[j + 5] << 14)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 4), (int)(((Z[j + 5] >>> 18) & ((1 << 4) - 1)) | ((Z[j + 6] & ((1 << 22) - 1)) << 4) | (Z[j + 7] << 26)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 5), (int)((((Z[j + 7] >>> 6) & ((1 << 16) - 1))) | (Z[j + 8] << 16)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 6), (int)(((Z[j + 8] >>> 16) & ((1 << 6) - 1)) | ((Z[j + 9] & ((1 << 22) - 1)) << 6) | (Z[j + 10] << 28)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 7), (int)((((Z[j + 10] >>> 4) & ((1 << 18) - 1))) | (Z[j + 11] << 18)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 8), (int)(((Z[j + 11] >>> 14) & ((1 << 8) - 1)) | ((Z[j + 12] & ((1 << 22) - 1)) << 8) | (Z[j + 13] << 30)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 9), (int)((((Z[j + 13] >>> 2) & ((1 << 20) - 1))) | (Z[j + 14] << 20)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 10), (int)((((Z[j + 14] >>> 12) & ((1 << 10) - 1))) | (Z[j + 15] << 10)));
+
+            j += Const.INT_SIZE / 2;
+
+        }
+
+        System.arraycopy(C, cOffset, signature, signatureOffset + Parameter.N_III_SPEED * Parameter.D_III_SPEED / Const.BYTE_SIZE, Polynomial.HASH);
+
+    }
+
+    /***************************************************************************************************************************************************************
+     * Description:	Encode Signature for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        signature            Output Package Containing Signature
+     * @param        signatureOffset        Starting Point of the Output Package Containing Signature
+     * @param        C
+     * @param        cOffset
+     * @param        Z
+     *
+     * @return none
+     ***************************************************************************************************************************************************************/
+    public static void encodeSignatureIIIP(byte[] signature, int signatureOffset, byte[] C, int cOffset, long[] Z)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < (Parameter.N_III_P * Parameter.D_III_P / Const.INT_SIZE); i += Parameter.D_III_P / Const.BYTE_SIZE)
+        {
+
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 0), (int)(((Z[j + 0] & ((1 << 24) - 1))) | (Z[j + 1] << 24)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 1), (int)((((Z[j + 1] >>> 8) & ((1 << 16) - 1))) | (Z[j + 2] << 16)));
+            CommonFunction.store32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (i + 2), (int)((((Z[j + 2] >>> 16) & ((1 << 8) - 1))) | (Z[j + 3] << 8)));
+
+            j += Const.BYTE_SIZE / 2;
+
+        }
+
+        System.arraycopy(C, cOffset, signature, signatureOffset + Parameter.N_III_P * Parameter.D_III_P / Const.BYTE_SIZE, Polynomial.HASH);
+
+    }
+
+    /******************************************************************************************************************************
+     * Description:	Decode Signature for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size)
+     *
+     * @param    C
+     * @param    Z
+     * @param    signature            Output Package Containing Signature
+     * @param    signatureOffset        Starting Point of the Output Package Containing Signature
+     * @param    n                    Polynomial Degree
+     * @param    d                    Number of Rounded Bits
+     *
+     * @return none
+     ******************************************************************************************************************************/
+    public static void decodeSignature(byte[] C, int[] Z, final byte[] signature, int signatureOffset, int n, int d)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < n; i += Const.INT_SIZE)
+        {
+
+            Z[i + 0] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) << 11) >> 11;
+
+            Z[i + 1] = ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 21) |
+                (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 22) >> 11);
+
+            Z[i + 2] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 1) >> 11;
+
+            Z[i + 3] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 31) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 12) >> 11);
+
+            Z[i + 4] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 20) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 23) >> 11);
+
+            Z[i + 5] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 2) >> 11;
+
+            Z[i + 6] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) >>> 30) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 13) >> 11);
+
+            Z[i + 7] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) >>> 19) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 24) >> 11);
+
+            Z[i + 8] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 3) >> 11;
+
+            Z[i + 9] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 29) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 14) >> 11);
+
+            Z[i + 10] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) >>> 18) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 25) >> 11);
+
+            Z[i + 11] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 4) >> 11;
+
+            Z[i + 12] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 28) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 15) >> 11);
+
+            Z[i + 13] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) >>> 17) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 26) >> 11);
+
+            Z[i + 14] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 5) >> 11;
+
+            Z[i + 15] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 27) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) << 16) >> 11);
+
+            Z[i + 16] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >>> 16) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) << 27) >> 11);
+
+            Z[i + 17] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) << 6) >> 11;
+
+            Z[i + 18] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 11)) >>> 26) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) << 17) >> 11);
+
+            Z[i + 19] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 12)) >>> 15) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) << 28) >> 11);
+
+            Z[i + 20] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) << 7) >> 11;
+
+            Z[i + 21] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 13)) >>> 25) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) << 18) >> 11);
+
+            Z[i + 22] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 14)) >>> 14) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) << 29) >> 11);
+
+            Z[i + 23] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) << 8) >> 11;
+
+            Z[i + 24] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 15)) >>> 24) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) << 19) >> 11);
+
+            Z[i + 25] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 16)) >>> 13) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) << 30) >> 11);
+
+            Z[i + 26] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) << 9) >> 11;
+
+            Z[i + 27] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 17)) >>> 23) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) << 20) >> 11);
+
+            Z[i + 28] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 18)) >>> 12) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) << 31) >> 11);
+
+            Z[i + 29] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) << 10) >> 11;
+
+            Z[i + 30] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 19)) >>> 22) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) << 21) >> 11);
+
+            Z[i + 31] = CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 20)) >> 11;
+
+            j += d;
+
+        }
+
+        System.arraycopy(signature, signatureOffset + n * d / Const.BYTE_SIZE, C, 0, Polynomial.HASH);
+
+    }
+
+    /**************************************************************************************************************************************
+     * Description:	Decode Signature for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param    C
+     * @param    Z
+     * @param    signature            Output Package Containing Signature
+     * @param    signatureOffset        Starting Point of the Output Package Containing Signature
+     *
+     * @return none
+     **************************************************************************************************************************************/
+    public static void decodeSignatureIIISpeed(byte[] C, int[] Z, final byte[] signature, int signatureOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_III_SPEED; i += Const.INT_SIZE / 2)
+        {
+
+            Z[i + 0] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) << 10) >> 10;
+
+            Z[i + 1] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 22) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 20) >> 10);
+
+            Z[i + 2] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 12) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 30) >> 10);
+
+            Z[i + 3] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 8) >> 10;
+
+            Z[i + 4] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 24) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 18) >> 10);
+
+            Z[i + 5] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) >>> 14) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 28) >> 10);
+
+            Z[i + 6] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 6) >> 10;
+
+            Z[i + 7] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) >>> 26) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 16) >> 10);
+
+            Z[i + 8] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 16) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 26) >> 10);
+
+            Z[i + 9] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 4) >> 10;
+
+            Z[i + 10] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) >>> 28) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 14) >> 10);
+
+            Z[i + 11] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 18) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 24) >> 10);
+
+            Z[i + 12] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 2) >> 10;
+
+            Z[i + 13] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) >>> 30) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 12) >> 10);
+
+            Z[i + 14] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 20) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) << 22) >> 10);
+
+            Z[i + 15] = CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >> 10;
+
+            j += Parameter.D_III_SPEED / 2;
+
+        }
+
+        System.arraycopy(signature, signatureOffset + Parameter.N_III_SPEED * Parameter.D_III_SPEED / Const.BYTE_SIZE, C, 0, Polynomial.HASH);
+
+    }
+
+    /****************************************************************************************************************************
+     * Description:	Decode Signature for Provably-Secure qTESLA Security Category-1
+     *
+     * @param    C
+     * @param    Z
+     * @param    signature            Output Package Containing Signature
+     * @param    signatureOffset        Starting Point of the Output Package Containing Signature
+     *
+     * @return none
+     ****************************************************************************************************************************/
+    public static void decodeSignatureIP(byte[] C, long[] Z, final byte[] signature, int signatureOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_I_P; i += Const.INT_SIZE / 2)
+        {
+
+            Z[i + 0] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) << 10) >> 10;
+
+            Z[i + 1] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 22) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 20) >> 10);
+
+            Z[i + 2] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 12) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 30) >> 10);
+
+            Z[i + 3] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 8) >> 10;
+
+            Z[i + 4] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >>> 24) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) << 18) >> 10);
+
+            Z[i + 5] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 3)) >>> 14) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 28) >> 10);
+
+            Z[i + 6] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) << 6) >> 10;
+
+            Z[i + 7] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 4)) >>> 26) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) << 16) >> 10);
+
+            Z[i + 8] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 5)) >>> 16) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 26) >> 10);
+
+            Z[i + 9] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) << 4) >> 10;
+
+            Z[i + 10] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 6)) >>> 28) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) << 14) >> 10);
+
+            Z[i + 11] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 7)) >>> 18) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 24) >> 10);
+
+            Z[i + 12] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) << 2) >> 10;
+
+            Z[i + 13] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 8)) >>> 30) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) << 12) >> 10);
+
+            Z[i + 14] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 9)) >>> 20) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) << 22) >> 10);
+
+            Z[i + 15] = CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 10)) >> 10;
+
+            j += Parameter.D_I_P / 2;
+
+        }
+
+        System.arraycopy(signature, signatureOffset + Parameter.N_I_P * Parameter.D_I_P / Const.BYTE_SIZE, C, 0, Polynomial.HASH);
+
+    }
+
+    /****************************************************************************************************************************************
+     * Description:	Decode Signature for Provably-Secure qTESLA Security Category-3
+     *
+     * @param    C
+     * @param    Z
+     * @param    signature            Output Package Containing Signature
+     * @param    signatureOffset        Starting Point of the Output Package Containing Signature
+     *
+     * @return none
+     ****************************************************************************************************************************************/
+    public static void decodeSignatureIIIP(byte[] C, long[] Z, final byte[] signature, int signatureOffset)
+    {
+
+        int j = 0;
+
+        for (int i = 0; i < Parameter.N_III_P; i += Const.BYTE_SIZE / 2)
+        {
+
+            Z[i + 0] = (CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) << 8) >> 8;
+
+            Z[i + 1] = ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 0)) >>> 24) & ((1 << 8) - 1)) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) << 16) >> 8);
+
+            Z[i + 2] = ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 1)) >>> 16) & ((1 << 16) - 1)) |
+                ((CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) << 24) >> 8);
+
+            Z[i + 3] = CommonFunction.load32(signature, signatureOffset + Const.INT_SIZE / Const.BYTE_SIZE * (j + 2)) >> 8;
+
+            j += Const.BYTE_SIZE / 2 - 1;
+
+        }
+
+        System.arraycopy(signature, signatureOffset + Parameter.N_III_P * Parameter.D_III_P / Const.BYTE_SIZE, C, 0, Polynomial.HASH);
+
+    }
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Parameter.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Parameter.java
new file mode 100644
index 0000000..aeae8cb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Parameter.java
@@ -0,0 +1,413 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+final class Parameter
+{
+
+    /**
+     * Dimension, (Dimension - 1) is the Polynomial Degree for Heuristic qTESLA Security Category-1
+     */
+    public static final int N_I = 512;
+
+    /**
+     * Dimension, (Dimension - 1) is the Polynomial Degree for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int N_I_P = 1024;
+
+    /**
+     * Dimension, (Dimension - 1) is the Polynomial Degree for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int N_III_SIZE = 1024;
+
+    /**
+     * Dimension, (Dimension - 1) is the Polynomial Degree for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int N_III_SPEED = 1024;
+
+    /**
+     * Dimension, (Dimension - 1) is the Polynomial Degree for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int N_III_P = 2048;
+
+    /**
+     * N_LOGARITHM = LOGARITHM (N) / LOGARITHM (2) for Heuristic qTESLA Security Category-1
+     */
+    public static final int N_LOGARITHM_I = 9;
+
+    /**
+     * N_LOGARITHM = LOGARITHM (N) / LOGARITHM (2) for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int N_LOGARITHM_I_P = 10;
+
+    /**
+     * N_LOGARITHM = LOGARITHM (N) / LOGARITHM (2) for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int N_LOGARITHM_III_SIZE = 10;
+
+    /**
+     * N_LOGARITHM = LOGARITHM (N) / LOGARITHM (2) for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int N_LOGARITHM_III_SPEED = 10;
+
+    /**
+     * N_LOGARITHM = LOGARITHM (N) / LOGARITHM (2) for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int N_LOGARITHM_III_P = 11;
+
+    /**
+     * Modulus for Heuristic qTESLA Security Category-1
+     */
+    public static final int Q_I = 4205569;
+
+    /**
+     * Modulus for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int Q_I_P = 485978113;
+
+    /**
+     * Modulus for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int Q_III_SIZE = 4206593;
+
+    /**
+     * Modulus for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int Q_III_SPEED = 8404993;
+
+    /**
+     * Modulus for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int Q_III_P = 1129725953;
+
+    /**
+     * Q <= 2 ^ Q_LOGARITHM for Heuristic qTESLA Security Category-1
+     */
+    public static final int Q_LOGARITHM_I = 23;
+
+    /**
+     * Q <= 2 ^ Q_LOGARITHM for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int Q_LOGARITHM_I_P = 29;
+
+    /**
+     * Q <= 2 ^ Q_LOGARITHM for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int Q_LOGARITHM_III_SIZE = 23;
+
+    /**
+     * Q <= 2 ^ Q_LOGARITHM for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int Q_LOGARITHM_III_SPEED = 24;
+
+    /**
+     * Q <= 2 ^ Q_LOGARITHM for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int Q_LOGARITHM_III_P = 31;
+
+    public static final long Q_INVERSE_I = 3098553343L;
+    public static final long Q_INVERSE_I_P = 3421990911L;
+    public static final long Q_INVERSE_III_SIZE = 4148178943L;
+    public static final long Q_INVERSE_III_SPEED = 4034936831L;
+    public static final long Q_INVERSE_III_P = 861290495L;
+
+    /**
+     * B Determines the Interval the Randomness is Chosen in During Signing for Heuristic qTESLA Security Category-1
+     */
+    public static final int B_I = 1048575;
+
+    /**
+     * B Determines the Interval the Randomness is Chosen in During Signing for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int B_I_P = 2097151;
+
+    /**
+     * B Determines the Interval the Randomness is Chosen in During Signing for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int B_III_SIZE = 1048575;
+
+    /**
+     * B Determines the Interval the Randomness is Chosen in During Signing for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int B_III_SPEED = 2097151;
+
+    /**
+     * B Determines the Interval the Randomness is Chosen in During Signing for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int B_III_P = 8388607;
+
+    /**
+     * B = 2 ^ B_BIT - 1 for Heuristic qTESLA Security Category-1
+     */
+    public static final int B_BIT_I = 20;
+
+    /**
+     * B = 2 ^ B_BIT - 1 for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int B_BIT_I_P = 21;
+
+    /**
+     * B = 2 ^ B_BIT - 1 for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int B_BIT_III_SIZE = 20;
+
+    /**
+     * B = 2 ^ B_BIT - 1 for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int B_BIT_III_SPEED = 21;
+
+    /**
+     * B = 2 ^ B_BIT - 1 for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int B_BIT_III_P = 23;
+
+    public static final int S_BIT_I = 10;
+    public static final int S_BIT_I_P = 8;
+    public static final int S_BIT_III_SIZE = 8;
+    public static final int S_BIT_III_SPEED = 9;
+    public static final int S_BIT_III_P = 8;
+
+    /**
+     * Number of Ring-Learning-With-Errors Samples for Heuristic qTESLA Security Category-1
+     */
+    public static final int K_I = 1;
+
+    /**
+     * Number of Ring-Learning-With-Errors Samples for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int K_I_P = 4;
+
+    /**
+     * Number of Ring-Learning-With-Errors Samples for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int K_III_SIZE = 1;
+
+    /**
+     * Number of Ring-Learning-With-Errors Samples for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int K_III_SPEED = 1;
+
+    /**
+     * Number of Ring-Learning-With-Errors Samples for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int K_III_P = 5;
+
+    /**
+     * Number of Non-Zero Entries of Output Elements of Encryption for Heuristic qTESLA Security Category-1
+     */
+    public static final int H_I = 30;
+
+    /**
+     * Number of Non-Zero Entries of Output Elements of Encryption for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int H_I_P = 25;
+
+    /**
+     * Number of Non-Zero Entries of Output Elements of Encryption for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int H_III_SIZE = 48;
+
+    /**
+     * Number of Non-Zero Entries of Output Elements of Encryption for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int H_III_SPEED = 48;
+
+    /**
+     * Number of Non-Zero Entries of Output Elements of Encryption for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int H_III_P = 40;
+
+    /**
+     * Number of Rounded Bits for Heuristic qTESLA Security Category-1
+     */
+    public static final int D_I = 21;
+
+    /**
+     * Number of Rounded Bits for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int D_I_P = 22;
+
+    /**
+     * Number of Rounded Bits for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int D_III_SIZE = 21;
+
+    /**
+     * Number of Rounded Bits for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int D_III_SPEED = 22;
+
+    /**
+     * Number of Rounded Bits for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int D_III_P = 24;
+
+    /**
+     * Bound in Checking Error Polynomial for Heuristic qTESLA Security Category-1
+     */
+    public static final int KEY_GENERATOR_BOUND_E_I = 1586;
+
+    /**
+     * Bound in Checking Error Polynomial for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int KEY_GENERATOR_BOUND_E_I_P = 554;
+
+    /**
+     * Bound in Checking Error Polynomial for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int KEY_GENERATOR_BOUND_E_III_SIZE = 910;
+
+    /**
+     * Bound in Checking Error Polynomial for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int KEY_GENERATOR_BOUND_E_III_SPEED = 1147;
+
+    /**
+     * Bound in Checking Error Polynomial for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int KEY_GENERATOR_BOUND_E_III_P = 901;
+
+    public static final int REJECTION_I = KEY_GENERATOR_BOUND_E_I;
+    public static final int REJECTION_I_P = KEY_GENERATOR_BOUND_E_I_P;
+    public static final int REJECTION_III_SIZE = KEY_GENERATOR_BOUND_E_III_SIZE;
+    public static final int REJECTION_III_SPEED = KEY_GENERATOR_BOUND_E_III_SPEED;
+    public static final int REJECTION_III_P = KEY_GENERATOR_BOUND_E_III_P;
+
+    /**
+     * Bound in Checking Secret Polynomial for Heuristic qTESLA Security Category-1
+     */
+    public static final int KEY_GENERATOR_BOUND_S_I = 1586;
+
+    /**
+     * Bound in Checking Secret Polynomial for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int KEY_GENERATOR_BOUND_S_I_P = 554;
+
+    /**
+     * Bound in Checking Secret Polynomial for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int KEY_GENERATOR_BOUND_S_III_SIZE = 910;
+
+    /**
+     * Bound in Checking Secret Polynomial for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int KEY_GENERATOR_BOUND_S_III_SPEED = 1233;
+
+    /**
+     * Bound in Checking Secret Polynomial for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int KEY_GENERATOR_BOUND_S_III_P = 901;
+
+    public static final int U_I = KEY_GENERATOR_BOUND_S_I;
+    public static final int U_I_P = KEY_GENERATOR_BOUND_S_I_P;
+    public static final int U_III_SIZE = KEY_GENERATOR_BOUND_S_III_SIZE;
+    public static final int U_III_SPEED = KEY_GENERATOR_BOUND_S_III_SPEED;
+    public static final int U_III_P = KEY_GENERATOR_BOUND_S_III_P;
+
+    /**
+     * Standard Deviation of Centered Discrete Gaussian Distribution for Heuristic qTESLA Security Category-1
+     */
+    public static final double SIGMA_I = 22.93;
+
+    /**
+     * Standard Deviation of Centered Discrete Gaussian Distribution for Provably-Secure qTESLA Security Category-1
+     */
+    public static final double SIGMA_I_P = 8.5;
+
+    /**
+     * Standard Deviation of Centered Discrete Gaussian Distribution for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final double SIGMA_III_SIZE = 7.64;
+
+    /**
+     * Standard Deviation of Centered Discrete Gaussian Distribution for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final double SIGMA_III_SPEED = 10.2;
+
+    /**
+     * Standard Deviation of Centered Discrete Gaussian Distribution for Provably-Secure qTESLA Security Category-3
+     */
+    public static final double SIGMA_III_P = 8.5;
+
+    public static final double SIGMA_E_I = SIGMA_I;
+    public static final double SIGMA_E_I_P = SIGMA_I_P;
+    public static final double SIGMA_E_III_SIZE = SIGMA_III_SIZE;
+    public static final double SIGMA_E_III_SPEED = SIGMA_III_SPEED;
+    public static final double SIGMA_E_III_P = SIGMA_III_P;
+
+    /**
+     * XI = SIGMA * SQUARE_ROOT (2 * LOGARITHM (2) / LOGARITHM (e)) for Heuristic qTESLA Security Category-1
+     */
+    public static final double XI_I = 27;
+
+    /**
+     * XI = SIGMA * SQUARE_ROOT (2 * LOGARITHM (2) / LOGARITHM (e)) for Provably-Secure qTESLA Security Category-1
+     */
+    public static final double XI_I_P = 10;
+
+    /**
+     * XI = SIGMA * SQUARE_ROOT (2 * LOGARITHM (2) / LOGARITHM (e)) for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final double XI_III_SIZE = 9;
+
+    /**
+     * XI = SIGMA * SQUARE_ROOT (2 * LOGARITHM (2) / LOGARITHM (e)) for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final double XI_III_SPEED = 12;
+
+    /**
+     * XI = SIGMA * SQUARE_ROOT (2 * LOGARITHM (2) / LOGARITHM (e)) for Provably-Secure qTESLA Security Category-3
+     */
+    public static final double XI_III_P = 10;
+
+    public static final int BARRETT_MULTIPLICATION_I = 1021;
+    public static final int BARRETT_MULTIPLICATION_I_P = 1;
+    public static final int BARRETT_MULTIPLICATION_III_SIZE = 1021;
+    public static final int BARRETT_MULTIPLICATION_III_SPEED = 511;
+    public static final int BARRETT_MULTIPLICATION_III_P = 15;
+
+    public static final int BARRETT_DIVISION_I = 32;
+    public static final int BARRETT_DIVISION_I_P = 29;
+    public static final int BARRETT_DIVISION_III_SIZE = 32;
+    public static final int BARRETT_DIVISION_III_SPEED = 32;
+    public static final int BARRETT_DIVISION_III_P = 34;
+
+    /**
+     * The Number of Blocks Requested in the First Extendable-Output Function Call
+     * for Heuristic qTESLA Security Category-1
+     */
+    public static final int GENERATOR_A_I = 19;
+
+    /**
+     * The Number of Blocks Requested in the First Extendable-Output Function Call
+     * for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int GENERATOR_A_I_P = 108;
+
+    /**
+     * The Number of Blocks Requested in the First Extendable-Output Function Call
+     * for Provably-Secure qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int GENERATOR_A_III_SIZE = 38;
+
+    /**
+     * The Number of Blocks Requested in the First Extendable-Output Function Call
+     * for Provably-Secure qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int GENERATOR_A_III_SPEED = 38;
+
+    /**
+     * The Number of Blocks Requested in the First Extendable-Output Function Call
+     * for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int GENERATOR_A_III_P = 180;
+
+    public static final int INVERSE_NUMBER_THEORETIC_TRANSFORM_I = 113307;
+    public static final int INVERSE_NUMBER_THEORETIC_TRANSFORM_I_P = 472064468;
+    public static final int INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SIZE = 1217638;
+    public static final int INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SPEED = 237839;
+    public static final int INVERSE_NUMBER_THEORETIC_TRANSFORM_III_P = 851423148;
+
+    public static final int R_I = 1081347;
+    public static final int R_III_SIZE = 35843;
+    public static final int R_III_SPEED = 15873;
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Polynomial.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Polynomial.java
new file mode 100644
index 0000000..cf81b3d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Polynomial.java
@@ -0,0 +1,1303 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import org.bouncycastle.util.Arrays;
+
+class Polynomial
+{
+
+    /**
+     * Size of A Random Number (in Byte)
+     */
+    public static final int RANDOM = 32;
+
+    /**
+     * Size of A Seed (in Byte)
+     */
+    public static final int SEED = 32;
+
+    /**
+     * Size of Hash Value C (in Byte) in the Signature Package
+     */
+    public static final int HASH = 32;
+
+    /**
+     * Size of Hashed Message
+     */
+    public static final int MESSAGE = 64;
+
+    /**
+     * Size of the Signature Package (Z, C) (in Byte) for Heuristic qTESLA Security Category-1.
+     * Z is A Polynomial Bounded by B and C is the Output of A Hashed String
+     */
+    public static final int SIGNATURE_I = (Parameter.N_I * Parameter.D_I + 7) / 8 + HASH;
+
+    /**
+     * Size of the Signature Package (Z, C) (in Byte) for Heuristic qTESLA Security Category-3 (Option for Size).
+     * Z is A Polynomial Bounded by B and C is the Output of A Hashed String
+     */
+    public static final int SIGNATURE_III_SIZE = (Parameter.N_III_SIZE * Parameter.D_III_SIZE + 7) / 8 + HASH;
+
+    /**
+     * Size of the Signature Package (Z, C) (in Byte) for Heuristic qTESLA Security Category-3 (Option for Speed).
+     * Z is A Polynomial Bounded by B and C is the Output of A Hashed String
+     */
+    public static final int SIGNATURE_III_SPEED = (Parameter.N_III_SPEED * Parameter.D_III_SPEED + 7) / 8 + HASH;
+
+    /**
+     * Size of the Signature Package (Z, C) (in Byte) for Provably-Secure qTESLA Security Category-1.
+     * Z is A Polynomial Bounded by B and C is the Output of A Hashed String
+     */
+    public static final int SIGNATURE_I_P = (Parameter.N_I_P * Parameter.D_I_P + 7) / 8 + HASH;
+
+    /**
+     * Size of the Signature Package (Z, C) (in Byte) for Provably-Secure qTESLA Security Category-3.
+     * Z is A Polynomial Bounded by B and C is the Output of A Hashed String
+     */
+    public static final int SIGNATURE_III_P = (Parameter.N_III_P * Parameter.D_III_P + 7) / 8 + HASH;
+
+    /**
+     * Size of the Public Key (in Byte) Containing seedA and Polynomial T for Heuristic qTESLA Security Category-1
+     */
+    public static final int PUBLIC_KEY_I = (Parameter.N_I * Parameter.K_I * Parameter.Q_LOGARITHM_I + 7) / 8 + SEED;
+
+    /**
+     * Size of the Public Key (in Byte) Containing seedA and Polynomial T for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int PUBLIC_KEY_III_SIZE = (Parameter.N_III_SIZE * Parameter.K_III_SIZE * Parameter.Q_LOGARITHM_III_SIZE + 7) / 8 + SEED;
+
+    /**
+     * Size of the Public Key (in Byte) Containing seedA and Polynomial T for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int PUBLIC_KEY_III_SPEED = (Parameter.N_III_SPEED * Parameter.K_III_SPEED * Parameter.Q_LOGARITHM_III_SPEED + 7) / 8 + SEED;
+
+    /**
+     * Size of the Public Key (in Byte) Containing seedA and Polynomial T for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int PUBLIC_KEY_I_P = (Parameter.N_I_P * Parameter.K_I_P * Parameter.Q_LOGARITHM_I_P + 7) / 8 + SEED;
+
+    /**
+     * Size of the Public Key (in Byte) Containing seedA and Polynomial T for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int PUBLIC_KEY_III_P = (Parameter.N_III_P * Parameter.K_III_P * Parameter.Q_LOGARITHM_III_P + 7) / 8 + SEED;
+
+    /**
+     * Size of the Private Key (in Byte) Containing Polynomials (Secret Polynomial and Error Polynomial) and Seeds (seedA and seedY)
+     * for Heuristic qTESLA Security Category-1
+     */
+    public static final int PRIVATE_KEY_I = Parameter.N_I * Parameter.S_BIT_I / Const.BYTE_SIZE * 2 + SEED * 2;
+
+    /**
+     * Size of the Private Key (in Byte) Containing Polynomials (Secret Polynomial and Error Polynomial) and Seeds (seedA and seedY)
+     * for Heuristic qTESLA Security Category-3 (Option for Size)
+     */
+    public static final int PRIVATE_KEY_III_SIZE = Parameter.N_III_SIZE * Parameter.S_BIT_III_SIZE / Const.BYTE_SIZE * 2 + SEED * 2;
+
+    /**
+     * Size of the Private Key (in Byte) Containing Polynomials (Secret Polynomial and Error Polynomial) and Seeds (seedA and seedY)
+     * for Heuristic qTESLA Security Category-3 (Option for Speed)
+     */
+    public static final int PRIVATE_KEY_III_SPEED = Parameter.N_III_SPEED * Parameter.S_BIT_III_SPEED / Const.BYTE_SIZE * 2 + SEED * 2;
+
+    /**
+     * Size of the Private Key (in Byte) Containing Polynomials (Secret Polynomial and Error Polynomial) and Seeds (seedA and seedY)
+     * for Provably-Secure qTESLA Security Category-1
+     */
+    public static final int PRIVATE_KEY_I_P = Parameter.N_I_P + Parameter.N_I_P * Parameter.K_I_P + SEED * 2;
+
+    /**
+     * Size of the Private Key (in Byte) Containing Polynomials (Secret Polynomial and Error Polynomial) and Seeds (seedA and seedY)
+     * for Provably-Secure qTESLA Security Category-3
+     */
+    public static final int PRIVATE_KEY_III_P = Parameter.N_III_P + Parameter.N_III_P * Parameter.K_III_P + SEED * 2;
+
+    /****************************************************************************
+     * Description:	Montgomery Reduction for Heuristic qTESLA Security Category 1
+     * 				and Security Category-3 (Option for Size and Speed)
+     *
+     * @param        number        Number to be Reduced
+     * @param        q            Modulus
+     * @param        qInverse
+     *
+     * @return Reduced Number
+     ****************************************************************************/
+    private static int montgomery(long number, int q, long qInverse)
+    {
+
+        return (int)((number + ((number * qInverse) & 0xFFFFFFFFL) * q) >> 32);
+
+    }
+
+    /****************************************************************************
+     * Description:	Montgomery Reduction for Provably-Secure qTESLA
+     * 				Security Category-1 and Security Category-3
+     *
+     * @param        number        Number to be Reduced
+     * @param        q            Modulus
+     * @param        qInverse
+     *
+     * @return Reduced Number
+     ****************************************************************************/
+    private static long montgomeryP(long number, int q, long qInverse)
+    {
+
+        return (number + ((number * qInverse) & 0xFFFFFFFFL) * q) >> 32;
+
+    }
+
+    /**********************************************************************************************
+     * Description:	Barrett Reduction for Heuristic qTESLA Security Category-3
+     * 				(Option for Size or Speed)
+     *
+     * @param        number                    Number to be Reduced
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     * @param        q                        Modulus
+     *
+     * @return Reduced Number
+     **********************************************************************************************/
+    public static int barrett(int number, int q, int barrettMultiplication, int barrettDivision)
+    {
+
+        return number - (int)(((long)number * barrettMultiplication) >> barrettDivision) * q;
+
+    }
+
+    /*************************************************************************************************
+     * Description:	Barrett Reduction for Provably-Secure qTESLA Security Category-1 and
+     * 				Security Category-3
+     *
+     * @param        number                    Number to be Reduced
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     * @param        q                        Modulus
+     *
+     * @return Reduced Number
+     *************************************************************************************************/
+    public static long barrett(long number, int q, int barrettMultiplication, int barrettDivision)
+    {
+
+        return number - ((number * barrettMultiplication) >> barrettDivision) * q;
+
+    }
+
+    /************************************************************************************************************
+     * Description:	Forward Number Theoretic Transform for Heuristic qTESLA Security Category-1,
+     * 				Security Category-3 (Option for Size and Speed)
+     *
+     * @param        destination        Destination of Transformation
+     * @param        source            Source of Transformation
+     * @param        n                Polynomial Degree
+     * @param        q                Modulus
+     * @param        qInverse
+     *
+     * @return none
+     ************************************************************************************************************/
+    private static void numberTheoreticTransform(int destination[], int source[], int n, int q, long qInverse)
+    {
+
+        int jTwiddle = 0;
+        int numberOfProblem = n >> 1;
+
+        for (; numberOfProblem > 0; numberOfProblem >>= 1)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < n; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[jTwiddle++];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    int temporary = montgomery(omega * destination[j + numberOfProblem], q, qInverse);
+
+                    destination[j + numberOfProblem] = destination[j] - temporary;
+                    destination[j] = destination[j] + temporary;
+
+                }
+
+            }
+
+        }
+
+    }
+
+    /**************************************************************************************************************
+     * Description:	Forward Number Theoretic Transform for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        destination        Destination of Transformation
+     * @param        source            Source of Transformation
+     *
+     * @return none
+     **************************************************************************************************************/
+    private static void numberTheoreticTransformIP(long destination[], long source[])
+    {
+
+        int numberOfProblem = Parameter.N_I_P >> 1;
+        int jTwiddle = 0;
+
+        for (; numberOfProblem > 0; numberOfProblem >>= 1)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < Parameter.N_I_P; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[jTwiddle++];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    long temporary = montgomeryP(
+                        omega * destination[j + numberOfProblem],
+                        Parameter.Q_I_P, Parameter.Q_INVERSE_I_P
+                    );
+
+                    destination[j + numberOfProblem] = destination[j] + (Parameter.Q_I_P - temporary);
+
+                    destination[j] = destination[j] + temporary;
+
+                }
+
+            }
+
+        }
+
+    }
+
+    /**************************************************************************************************************
+     * Description:	Forward Number Theoretic Transform for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        destination        Destination of Transformation
+     * @param        source            Source of Transformation
+     *
+     * @return none
+     **************************************************************************************************************/
+    private static void numberTheoreticTransformIIIP(long destination[], long source[])
+    {
+
+        int jTwiddle = 0;
+        int numberOfProblem = Parameter.N_III_P >> 1;
+
+        for (; numberOfProblem > 0; numberOfProblem >>= 1)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < Parameter.N_III_P; jFirst = j + numberOfProblem)
+            {
+
+                int omega = (int)source[jTwiddle++];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    long temporary = barrett(
+                        montgomeryP(
+                            omega * destination[j + numberOfProblem],
+                            Parameter.Q_III_P,
+                            Parameter.Q_INVERSE_III_P
+                        ),
+                        Parameter.Q_III_P,
+                        Parameter.BARRETT_MULTIPLICATION_III_P,
+                        Parameter.BARRETT_DIVISION_III_P
+                    );
+
+                    destination[j + numberOfProblem] = barrett(
+                        destination[j] + (2L * Parameter.Q_III_P - temporary),
+                        Parameter.Q_III_P,
+                        Parameter.BARRETT_MULTIPLICATION_III_P,
+                        Parameter.BARRETT_DIVISION_III_P
+                    );
+
+                    destination[j] = barrett(
+                        destination[j] + temporary,
+                        Parameter.Q_III_P,
+                        Parameter.BARRETT_MULTIPLICATION_III_P,
+                        Parameter.BARRETT_DIVISION_III_P
+                    );
+
+                }
+
+            }
+
+        }
+
+    }
+
+    /******************************************************************************************************************
+     * Description:	Inverse Number Theoretic Transform for Heuristic qTESLA Security Category-1
+     *
+     * @param        destination            Destination of Inverse Transformation
+     * @param        source                Source of Inverse Transformation
+     *
+     * @return none
+     ******************************************************************************************************************/
+    private static void inverseNumberTheoreticTransformI(int destination[], int source[])
+    {
+
+        int jTwiddle = 0;
+
+        for (int numberOfProblem = 1; numberOfProblem < Parameter.N_I; numberOfProblem *= 2)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < Parameter.N_I; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[jTwiddle++];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    int temporary = destination[j];
+
+                    destination[j] = temporary + destination[j + numberOfProblem];
+
+                    destination[j + numberOfProblem] = montgomery(
+                        omega * (temporary - destination[j + numberOfProblem]),
+                        Parameter.Q_I, Parameter.Q_INVERSE_I
+                    );
+
+                }
+
+            }
+
+        }
+
+        for (int i = 0; i < Parameter.N_I / 2; i++)
+        {
+
+            destination[i] = montgomery((long)Parameter.R_I * destination[i], Parameter.Q_I, Parameter.Q_INVERSE_I);
+
+        }
+
+    }
+
+    /**************************************************************************************************************************************************************************
+     * Description:	Inverse Number Theoretic Transform for Heuristic qTESLA Security Category-3 (Option for Size and Speed)
+     *
+     * @param        destination                    Destination of Inverse Transformation
+     * @param        source                        Source of Inverse Transformation
+     * @param        n                            Polynomial Degree
+     * @param        q                            Modulus
+     * @param        qInverse
+     * @param        r
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     *
+     * @return none
+     **************************************************************************************************************************************************************************/
+    private static void inverseNumberTheoreticTransform(int destination[], int source[], int n, int q, long qInverse, int r, int barrettMultiplication, int barrettDivision)
+    {
+
+        int jTwiddle = 0;
+
+        for (int numberOfProblem = 1; numberOfProblem < n; numberOfProblem *= 2)
+        {
+
+            int j = 0;
+
+            for (int jFirst = 0; jFirst < n; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[jTwiddle++];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    int temporary = destination[j];
+
+                    if (numberOfProblem == 16)
+                    {
+
+                        destination[j] = barrett(temporary + destination[j + numberOfProblem], q, barrettMultiplication, barrettDivision);
+
+                    }
+                    else
+                    {
+
+                        destination[j] = temporary + destination[j + numberOfProblem];
+
+                    }
+
+                    destination[j + numberOfProblem] = montgomery(omega * (temporary - destination[j + numberOfProblem]), q, qInverse);
+
+                }
+
+            }
+
+        }
+
+        for (int i = 0; i < n / 2; i++)
+        {
+
+            destination[i] = montgomery((long)r * destination[i], q, qInverse);
+
+        }
+
+    }
+
+    /***********************************************************************************************************************************************************************************
+     * Description:	Inverse Number Theoretic Transform for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        destination            Destination of Inverse Transformation
+     * @param        destinationOffset    Starting Point of the Destination
+     * @param        source                Source of Inverse Transformation
+     * @param        sourceOffset        Starting Point of the Source
+     *
+     * @return none
+     ***********************************************************************************************************************************************************************************/
+    private static void inverseNumberTheoreticTransformIP(long destination[], int destinationOffset, long source[], int sourceOffset)
+    {
+
+        int jTwiddle = 0;
+
+        for (int numberOfProblem = 1; numberOfProblem < Parameter.N_I_P; numberOfProblem *= 2)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < Parameter.N_I_P; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[sourceOffset + (jTwiddle++)];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    long temporary = destination[destinationOffset + j];
+
+                    destination[destinationOffset + j] = temporary + destination[destinationOffset + j + numberOfProblem];
+
+                    destination[destinationOffset + j + numberOfProblem] = montgomeryP(
+                        omega * (temporary + (2L * Parameter.Q_I_P - destination[destinationOffset + j + numberOfProblem])),
+                        Parameter.Q_I_P, Parameter.Q_INVERSE_I_P
+                    );
+
+                }
+
+            }
+
+            numberOfProblem *= 2;
+
+            for (jFirst = 0; jFirst < Parameter.N_I_P; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[sourceOffset + (jTwiddle++)];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    long temporary = destination[destinationOffset + j];
+
+                    destination[destinationOffset + j] = barrett(
+                        temporary + destination[destinationOffset + j + numberOfProblem],
+                        Parameter.Q_I_P, Parameter.BARRETT_MULTIPLICATION_I_P, Parameter.BARRETT_DIVISION_I_P
+                    );
+
+                    destination[destinationOffset + j + numberOfProblem] = montgomeryP(
+                        omega * (temporary + (2L * Parameter.Q_I_P - destination[destinationOffset + j + numberOfProblem])),
+                        Parameter.Q_I_P, Parameter.Q_INVERSE_I_P
+                    );
+
+                }
+
+            }
+
+        }
+
+    }
+
+    /******************************************************************************************************************************************************************************************
+     * Description:	Inverse Number Theoretic Transform for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        destination            Destination of Inverse Transformation
+     * @param        destinationOffset    Starting Point of the Destination
+     * @param        source                Source of Inverse Transformation
+     * @param        sourceOffset        Starting Point of the Source
+     *
+     * @return none
+     ******************************************************************************************************************************************************************************************/
+    private static void inverseNumberTheoreticTransformIIIP(long destination[], int destinationOffset, long source[], int sourceOffset)
+    {
+
+        int jTwiddle = 0;
+
+        for (int numberOfProblem = 1; numberOfProblem < Parameter.N_III_P; numberOfProblem *= 2)
+        {
+
+            int j = 0;
+            int jFirst;
+
+            for (jFirst = 0; jFirst < Parameter.N_III_P; jFirst = j + numberOfProblem)
+            {
+
+                long omega = source[sourceOffset + (jTwiddle++)];
+
+                for (j = jFirst; j < jFirst + numberOfProblem; j++)
+                {
+
+                    long temporary = destination[destinationOffset + j];
+
+                    destination[destinationOffset + j] = barrett(
+                        temporary + destination[destinationOffset + j + numberOfProblem],
+                        Parameter.Q_III_P, Parameter.BARRETT_MULTIPLICATION_III_P, Parameter.BARRETT_DIVISION_III_P
+                    );
+
+                    destination[destinationOffset + j + numberOfProblem] = barrett(
+                        montgomeryP(
+                            omega * (temporary + (2L * Parameter.Q_III_P - destination[destinationOffset + j + numberOfProblem])),
+                            Parameter.Q_III_P, Parameter.Q_INVERSE_III_P
+                        ),
+                        Parameter.Q_III_P, Parameter.BARRETT_MULTIPLICATION_III_P, Parameter.BARRETT_DIVISION_III_P
+                    );
+
+                }
+
+            }
+
+        }
+
+    }
+
+    /****************************************************************************************************************************************************
+     * Description:	Component Wise Polynomial Multiplication for Heuristic qTESLA Security Category-1 and Security Category-3 (Option for Size and Speed)
+     *
+     * @param        product                    Product = Multiplicand (*) Multiplier
+     * @param        multiplicand            Multiplicand Array
+     * @param        multiplier                Multiplier Array
+     * @param        n                        Polynomial Degree
+     * @param        q                        Modulus
+     * @param        qInverse
+     *
+     * @return none
+     ****************************************************************************************************************************************************/
+    private static void componentWisePolynomialMultiplication(int[] product, int[] multiplicand, int[] multiplier, int n, int q, long qInverse)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            product[i] = montgomery((long)multiplicand[i] * multiplier[i], q, qInverse);
+
+        }
+
+    }
+
+    /******************************************************************************************************************************************************************************************************************
+     * Description:	Component Wise Polynomial Multiplication for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        product                    Product = Multiplicand (*) Multiplier
+     * @param        productOffset            Starting Point of the Product Array
+     * @param        multiplicand            Multiplicand Array
+     * @param        multiplicandOffset        Starting Point of the Multiplicand Array
+     * @param        multiplier                Multiplier Array
+     * @param        multiplierOffset        Starting Point of the Multiplier Array
+     * @param        n                        Polynomial Degree
+     * @param        q                        Modulus
+     * @param        qInverse
+     *
+     * @return none
+     ******************************************************************************************************************************************************************************************************************/
+    private static void componentWisePolynomialMultiplication(long[] product, int productOffset, long[] multiplicand, int multiplicandOffset, long[] multiplier, int multiplierOffset, int n, int q, long qInverse)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            product[productOffset + i] = montgomeryP(multiplicand[multiplicandOffset + i] * multiplier[multiplierOffset + i], q, qInverse);
+
+        }
+
+    }
+
+    /***********************************************************************************************************************************************
+     * Description:	Polynomial Number Theoretic Transform for Provably-Secure qTESLA Security Category-1 and Category-3
+     *
+     * @param        arrayNumberTheoreticTransform        Transformed Array
+     * @param        array                                Array to be Transformed
+     * @param        n                                    Polynomial Degree
+     *
+     * @return none
+     ***********************************************************************************************************************************************/
+    public static void polynomialNumberTheoreticTransform(long[] arrayNumberTheoreticTransform, long[] array, int n)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            arrayNumberTheoreticTransform[i] = array[i];
+
+        }
+
+        if (n == Parameter.N_I_P)
+        {
+
+            numberTheoreticTransformIP(arrayNumberTheoreticTransform, PolynomialProvablySecure.ZETA_I_P);
+
+        }
+
+        if (n == Parameter.N_III_P)
+        {
+
+            numberTheoreticTransformIIIP(arrayNumberTheoreticTransform, PolynomialProvablySecure.ZETA_III_P);
+
+        }
+
+    }
+
+    /*******************************************************************************************************************************************
+     * Description:	Polynomial Multiplication for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size and Speed)
+     *
+     * @param        product                    Product = Multiplicand * Multiplier
+     * @param        multiplicand            Multiplicand Array
+     * @param        multiplier                Multiplier Array
+     * @param        n                        Polynomial Degree
+     * @param        q                        Modulus
+     * @param        qInverse
+     * @param        zeta
+     *
+     * @return none
+     *******************************************************************************************************************************************/
+    public static void polynomialMultiplication(int[] product, int[] multiplicand, int[] multiplier, int n, int q, long qInverse, int[] zeta)
+    {
+
+        int[] multiplierNumberTheoreticTransform = new int[n];
+
+        for (int i = 0; i < n; i++)
+        {
+
+            multiplierNumberTheoreticTransform[i] = multiplier[i];
+
+        }
+
+        numberTheoreticTransform(multiplierNumberTheoreticTransform, zeta, n, q, qInverse);
+
+        componentWisePolynomialMultiplication(product, multiplicand, multiplierNumberTheoreticTransform, n, q, qInverse);
+
+        if (q == Parameter.Q_I)
+        {
+
+            inverseNumberTheoreticTransformI(product, PolynomialHeuristic.ZETA_INVERSE_I);
+
+        }
+
+        if (q == Parameter.Q_III_SIZE)
+        {
+
+            inverseNumberTheoreticTransform(
+
+                product, PolynomialHeuristic.ZETA_INVERSE_III_SIZE,
+                Parameter.N_III_SIZE, Parameter.Q_III_SIZE, Parameter.Q_INVERSE_III_SIZE, Parameter.R_III_SIZE,
+                Parameter.BARRETT_MULTIPLICATION_III_SIZE, Parameter.BARRETT_DIVISION_III_SIZE
+
+            );
+
+        }
+
+        if (q == Parameter.Q_III_SPEED)
+        {
+
+            inverseNumberTheoreticTransform(
+
+                product, PolynomialHeuristic.ZETA_INVERSE_III_SPEED,
+                Parameter.N_III_SPEED, Parameter.Q_III_SPEED, Parameter.Q_INVERSE_III_SPEED, Parameter.R_III_SPEED,
+                Parameter.BARRETT_MULTIPLICATION_III_SPEED, Parameter.BARRETT_DIVISION_III_SPEED
+
+            );
+
+        }
+
+    }
+
+    /***************************************************************************************************************************************************************************************************
+     * Description:	Polynomial Multiplication for Provably-Secure qTESLA Security Category-1 and Category-3
+     *
+     * @param        product                    Product = Multiplicand * Multiplier
+     * @param        productOffset            Starting Point of the Product Array
+     * @param        multiplicand            Multiplicand Array
+     * @param        multiplicandOffset        Starting Point of the Multiplicand Array
+     * @param        multiplier                Multiplier Array
+     * @param        multiplierOffset        Starting Point of the Multiplier Array
+     * @param        n                        Polynomial Degree
+     * @param        q                        Modulus
+     * @param        qInverse
+     *
+     * @return none
+     ***************************************************************************************************************************************************************************************************/
+    public static void polynomialMultiplication(long[] product, int productOffset, long[] multiplicand, int multiplicandOffset, long[] multiplier, int multiplierOffset, int n, int q, long qInverse)
+    {
+
+        componentWisePolynomialMultiplication(product, productOffset, multiplicand, multiplicandOffset, multiplier, multiplierOffset, n, q, qInverse);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            inverseNumberTheoreticTransformIP(product, productOffset, PolynomialProvablySecure.ZETA_INVERSE_I_P, 0);
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            inverseNumberTheoreticTransformIIIP(product, productOffset, PolynomialProvablySecure.ZETA_INVERSE_III_P, 0);
+
+        }
+
+    }
+
+    /****************************************************************************************************************************************************
+     * Description:	Polynomial Addition for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size or Speed)
+     * 				Q + L_E < 2 ^ (CEIL (LOGARITHM (Q, 2)))
+     * 				No Necessary Reduction for Y + SC
+     *
+     * @param        summation            Summation = Augend + Addend
+     * @param        augend                Augend Array
+     * @param        addend                Addend Array
+     * @param        n                    Polynomial Degree
+     *
+     * @return none
+     ****************************************************************************************************************************************************/
+    public static void polynomialAddition(int[] summation, int[] augend, int[] addend, int n)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            summation[i] = augend[i] + addend[i];
+
+        }
+
+    }
+
+    /********************************************************************************************************************************************************
+     * Description:	Polynomial Addition for Provably-Secure qTESLA Security Category-1 and Category-3
+     * 				Q + L_E < 2 ^ (CEIL (LOGARITHM (Q, 2)))
+     * 				No Necessary Reduction for Y + SC
+     *
+     * @param        summation            Summation = Augend + Addend
+     * @param        summationOffset        Starting Point of the Summation Array
+     * @param        augend                Augend Array
+     * @param        augendOffset        Starting Point of the Augend Array
+     * @param        addend                Addend Array
+     * @param        addendOffset        Starting Point of the Addend Array
+     * @param        n                    Polynomial Degree
+     *
+     * @return none
+     ********************************************************************************************************************************************************/
+    public static void polynomialAddition(long[] summation, int summationOffset, long[] augend, int augendOffset, long[] addend, int addendOffset, int n)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            summation[summationOffset + i] = augend[augendOffset + i] + addend[addendOffset + i];
+
+        }
+
+    }
+
+    /*************************************************************************************************************
+     * Description:	Polynomial Addition with Correction for Heuristic qTESLA Security Category-1 and Category-3
+     * 				(Option for Size or Speed)
+     * 				Q + L_E < 2 ^ (CEIL (LOGARITHM (Q, 2)))
+     * 				No Necessary Reduction for Y + SC
+     *
+     * @param        summation            Summation = Augend + Addend
+     * @param        augend                Augend Array
+     * @param        addend                Addend Array
+     * @param        n                    Polynomial Degree
+     *
+     * @return none
+     ************************************************************************************************************/
+    public static void polynomialAdditionCorrection(int[] summation, int[] augend, int[] addend, int n, int q)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            summation[i] = augend[i] + addend[i];
+            /* If summation[i] < 0 Then Add Q */
+            summation[i] += (summation[i] >> 31) & q;
+            summation[i] -= q;
+            /* If summation[i] >= Q Then Subtract Q */
+            summation[i] += (summation[i] >> 31) & q;
+
+        }
+
+    }
+
+    /**********************************************************************************************************************
+     * Description:	Polynomial Subtraction with Correction for Heuristic qTESLA Security Category-1 and Security Category-3
+     *				(Option for Size or Speed)
+     *
+     * @param        difference                    Difference = Minuend (-) Subtrahend
+     * @param        minuend                        Minuend Array
+     * @param        subtrahend                    Subtrahend Array
+     * @param        n                            Polynomial Degree
+     * @param        q                            Modulus
+     *
+     * @return none
+     ***********************************************************************************************************************/
+    public static void polynomialSubtractionCorrection(int[] difference, int[] minuend, int[] subtrahend, int n, int q)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            difference[i] = minuend[i] - subtrahend[i];
+            /* If difference[i] < 0 Then Add Q */
+            difference[i] += (difference[i] >> 31) & q;
+
+        }
+
+    }
+
+    /*******************************************************************************************************************************************
+     * Description:	Polynomial Subtraction with Montgomery Reduction for Heuristic qTESLA Security Category-1 and Security Category-3
+     *				(Option for Size or Speed)
+     *
+     * @param        difference                    Difference = Minuend (-) Subtrahend
+     * @param        minuend                        Minuend Array
+     * @param        subtrahend                    Subtrahend Array
+     * @param        n                            Polynomial Degree
+     * @param        q                            Modulus
+     * @param        qInverse
+     * @param        r
+     *
+     * @return none
+     *******************************************************************************************************************************************/
+    public static void polynomialSubtractionMontgomery(int[] difference, int[] minuend, int[] subtrahend, int n, int q, long qInverse, int r)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            difference[i] = montgomery((long)r * (minuend[i] - subtrahend[i]), q, qInverse);
+
+        }
+
+    }
+
+    /******************************************************************************************************************************************************************************************************************************
+     * Description:	Polynomial Subtraction for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        difference                    Difference = Minuend (-) Subtrahend
+     * @param        differenceOffset            Starting Point of the Difference Array
+     * @param        minuend                        Minuend Array
+     * @param        minuendOffset                Starting Point of the Minuend Array
+     * @param        subtrahend                    Subtrahend Array
+     * @param        subtrahendOffset            Starting Point of the Subtrahend Array
+     * @param        n                            Polynomial Degree
+     * @param        q                            Modulus
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     *
+     * @return none
+     ******************************************************************************************************************************************************************************************************************************/
+    public static void polynomialSubtraction(long[] difference, int differenceOffset, long[] minuend, int minuendOffset, long[] subtrahend, int subtrahendOffset, int n, int q, int barrettMultiplication, int barrettDivision)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            difference[differenceOffset + i] = barrett(minuend[minuendOffset + i] - subtrahend[subtrahendOffset + i], q, barrettMultiplication, barrettDivision);
+
+        }
+
+    }
+
+    /******************************************************************************************************************************************************************************
+     * Description:	Generation of Polynomial A for Heuristic qTESLA Security Category-1 and Security Category-3 (Option for Size or Speed)
+     *
+     * @param        A                                    Polynomial to be Generated
+     * @param        seed                                Kappa-Bit Seed
+     * @param        seedOffset                            Starting Point of the Kappa-Bit Seed
+     * @param        n                                    Polynomial Degree
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     *
+     * @return none
+     ******************************************************************************************************************************************************************************/
+    public static void polynomialUniform(int[] A, byte[] seed, int seedOffset, int n, int q, long qInverse, int qLogarithm, int generatorA, int inverseNumberTheoreticTransform)
+    {
+
+        int position = 0;
+        int i = 0;
+        int numberOfByte = (qLogarithm + 7) / 8;
+        int numberOfBlock = generatorA;
+        short dualModeSampler = 0;
+        int value1;
+        int value2;
+        int value3;
+        int value4;
+        int mask = (1 << qLogarithm) - 1;
+
+        byte[] buffer = new byte[HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * generatorA];
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+            buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * generatorA,
+            dualModeSampler++,
+            seed, seedOffset, RANDOM
+        );
+
+        while (i < n)
+        {
+
+            if (position > (HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock - Const.INT_SIZE / Const.BYTE_SIZE * numberOfByte))
+            {
+
+                numberOfBlock = 1;
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                    buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock,
+                    dualModeSampler++,
+                    seed, seedOffset, RANDOM
+                );
+
+                position = 0;
+
+            }
+
+            value1 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value2 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value3 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value4 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            if (value1 < q && i < n)
+            {
+
+                A[i++] = montgomery((long)value1 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value2 < q && i < n)
+            {
+
+                A[i++] = montgomery((long)value2 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value3 < q && i < n)
+            {
+
+                A[i++] = montgomery((long)value3 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value4 < q && i < n)
+            {
+
+                A[i++] = montgomery((long)value4 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+        }
+
+    }
+
+    /**************************************************************************************************************************************************************************************
+     * Description:	Generation of Polynomial A for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        A                                    Polynomial to be Generated
+     * @param        seed                                Kappa-Bit Seed
+     * @param        seedOffset                            Starting Point of the Kappa-Bit Seed
+     * @param        n                                    Polynomial Degree
+     * @param        k                                    Number of Ring-Learning-With-Errors Samples
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     *
+     * @return none
+     **************************************************************************************************************************************************************************************/
+    public static void polynomialUniform(long[] A, byte[] seed, int seedOffset, int n, int k, int q, long qInverse, int qLogarithm, int generatorA, int inverseNumberTheoreticTransform)
+    {
+
+        int position = 0;
+        int i = 0;
+        int numberOfByte = (qLogarithm + 7) / 8;
+        int numberOfBlock = generatorA;
+        short dualModeSampler = 0;
+        int value1;
+        int value2;
+        int value3;
+        int value4;
+        int mask = (1 << qLogarithm) - 1;
+
+        byte[] buffer = new byte[HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock];
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+            buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock,
+            dualModeSampler++,
+            seed, seedOffset, RANDOM
+        );
+
+        while (i < n * k)
+        {
+
+            if (position > (HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock - Const.INT_SIZE / Const.BYTE_SIZE * numberOfByte))
+            {
+
+                numberOfBlock = 1;
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                    buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE * numberOfBlock,
+                    dualModeSampler++,
+                    seed, seedOffset, RANDOM
+                );
+
+                position = 0;
+
+            }
+
+            value1 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value2 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value3 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            value4 = CommonFunction.load32(buffer, position) & mask;
+            position += numberOfByte;
+
+            if (value1 < q && i < n * k)
+            {
+
+                A[i++] = montgomeryP((long)value1 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value2 < q && i < n * k)
+            {
+
+                A[i++] = montgomeryP((long)value2 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value3 < q && i < n * k)
+            {
+
+                A[i++] = montgomeryP((long)value3 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+            if (value4 < q && i < n * k)
+            {
+
+                A[i++] = montgomeryP((long)value4 * inverseNumberTheoreticTransform, q, qInverse);
+
+            }
+
+        }
+
+    }
+
+    /**************************************************************************************************************************************************************
+     * Description:	Performs Sparse Polynomial Multiplication for A Value Needed During Message Signification for Heuristic qTESLA Security Category-1 and
+     *				SecurityCategory-3 (Option for Size or Speed)
+     *
+     * @param        product                Product of Two Polynomials
+     * @param        privateKey            Part of the Private Key
+     * @param        positionList        List of Indices of Non-Zero Elements in C
+     * @param        signList            List of Signs of Non-Zero Elements in C
+     * @param        n                    Polynomial Degree
+     * @param        h                    Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return none
+     **************************************************************************************************************************************************************/
+    public static void sparsePolynomialMultiplication16(int[] product, final short[] privateKey, final int[] positionList, final short[] signList, int n, int h)
+    {
+
+        int position;
+
+        Arrays.fill(product, 0);
+
+        for (int i = 0; i < h; i++)
+        {
+
+            position = positionList[i];
+
+            for (int j = 0; j < position; j++)
+            {
+
+                product[j] -= signList[i] * privateKey[n + j - position];
+
+            }
+
+            for (int j = position; j < n; j++)
+            {
+
+                product[j] += signList[i] * privateKey[j - position];
+
+            }
+
+        }
+
+    }
+
+    /*****************************************************************************************************************************************************************************************************
+     * Description:	Performs Sparse Polynomial Multiplication for A Value Needed During Message Signification for Provably-Secure qTESLA Security Category-1 and Category-3
+     *
+     * @param        product                Product of Two Polynomials
+     * @param        productOffset        Starting Point of the Product of Two Polynomials
+     * @param        privateKey            Part of the Private Key
+     * @param        privateKeyOffset    Starting Point of the Private Key
+     * @param        positionList        List of Indices of Non-Zero Elements in C
+     * @param        signList            List of Signs of Non-Zero Elements in C
+     * @param        n                    Polynomial Degree
+     * @param        h                    Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return none
+     ******************************************************************************************************************************************************************************************************/
+    public static void sparsePolynomialMultiplication8(long[] product, int productOffset, final byte[] privateKey, int privateKeyOffset, final int[] positionList, final short[] signList, int n, int h)
+    {
+
+        int position;
+
+        Arrays.fill(product, 0L);
+
+        for (int i = 0; i < h; i++)
+        {
+
+            position = positionList[i];
+
+            for (int j = 0; j < position; j++)
+            {
+
+                product[productOffset + j] -= signList[i] * privateKey[privateKeyOffset + n + j - position];
+
+            }
+
+            for (int j = position; j < n; j++)
+            {
+
+                product[productOffset + j] += signList[i] * privateKey[privateKeyOffset + j - position];
+
+            }
+
+        }
+
+    }
+
+    /***********************************************************************************************************************************************************
+     * Description:	Performs Sparse Polynomial Multiplication for A Value Needed During Message Signification for Heuristic qTESLA Security Category-1 and
+     * 				Security Category-3 (Option for Size or Speed)
+     *
+     * @param        product                    Product of Two Polynomials
+     * @param        publicKey                Part of the Public Key
+     * @param        positionList            List of Indices of Non-Zero Elements in C
+     * @param        signList                List of Signs of Non-Zero Elements in C
+     * @param        n                        Polynomial Degree
+     * @param        h                        Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return none
+     ***********************************************************************************************************************************************************/
+    public static void sparsePolynomialMultiplication32(int[] product, final int[] publicKey, final int[] positionList, final short[] signList, int n, int h)
+    {
+
+        int position;
+
+        Arrays.fill(product, 0);
+
+        for (int i = 0; i < h; i++)
+        {
+
+            position = positionList[i];
+
+            for (int j = 0; j < position; j++)
+            {
+
+                product[j] -= signList[i] * publicKey[n + j - position];
+
+            }
+
+            for (int j = position; j < n; j++)
+            {
+
+                product[j] += signList[i] * publicKey[j - position];
+
+            }
+
+        }
+
+    }
+
+    /***********************************************************************************************************************************************************************************************************************************************************
+     * Description:	Performs Sparse Polynomial Multiplication for A Value Needed During Message Signification for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        product                    Product of Two Polynomials
+     * @param        productOffset            Starting Point of the Product of Two Polynomials
+     * @param        publicKey                Part of the Public Key
+     * @param        publicKeyOffset            Starting Point of the Public Key
+     * @param        positionList            List of Indices of Non-Zero Elements in C
+     * @param        signList                List of Signs of Non-Zero Elements in C
+     * @param        n                        Polynomial Degree
+     * @param        h                        Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                        Modulus
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     *
+     * @return none
+     ***********************************************************************************************************************************************************************************************************************************************************/
+    public static void sparsePolynomialMultiplication32(long[] product, int productOffset, final int[] publicKey, int publicKeyOffset, final int[] positionList, final short[] signList, int n, int h, int q, int barrettMultiplication, int barrettDivision)
+    {
+
+        int position;
+
+        Arrays.fill(product, 0L);
+
+        for (int i = 0; i < h; i++)
+        {
+
+            position = positionList[i];
+
+            for (int j = 0; j < position; j++)
+            {
+
+                product[productOffset + j] -= signList[i] * publicKey[publicKeyOffset + n + j - position];
+
+            }
+
+            for (int j = position; j < n; j++)
+            {
+
+                product[productOffset + j] += signList[i] * publicKey[publicKeyOffset + j - position];
+
+            }
+
+        }
+
+        for (int i = 0; i < n; i++)
+        {
+
+            product[productOffset + i] = barrett(product[productOffset + i], q, barrettMultiplication, barrettDivision);
+
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialHeuristic.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialHeuristic.java
new file mode 100644
index 0000000..5e16df5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialHeuristic.java
@@ -0,0 +1,682 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+final class PolynomialHeuristic
+{
+
+    /* Heuristic qTESLA Security Category-1 */
+
+    public static final int[] ZETA_I = {                    /* 512-Entry */
+
+        3359531, 2189080, 370173, 677362, 3132616, 2989204, 2362181, 1720831,
+        1203721, 3239574, 641414, 3932234, 3634017, 2251707, 355329, 4152265,
+        1356023, 4021436, 1465601, 4145892, 3348341, 675693, 1598775, 2799365,
+        3336234, 3856839, 603157, 1381183, 1069471, 2142038, 2877387, 2653969,
+        2055310, 3837123, 3141231, 1951522, 2375048, 445122, 1689285, 3664328,
+        676319, 3844199, 3669724, 1009639, 3666694, 1585701, 2102892, 966523,
+        4069555, 3246046, 846643, 2088895, 4068915, 3715722, 4119007, 230501,
+        1626667, 2119752, 1171284, 3153846, 17941, 1316589, 1814059, 3185686,
+        1183551, 2533671, 4152595, 2616162, 3015757, 194860, 1601807, 1271569,
+        139534, 2581874, 2183200, 2060697, 1036874, 646550, 2823563, 3312274,
+        391700, 99391, 638903, 2397164, 3924868, 3315551, 1170767, 422539,
+        1801679, 166402, 742283, 222557, 522210, 3415900, 177835, 3243355,
+        4196855, 1821376, 1290490, 3624896, 1546898, 1282351, 3960516, 835944,
+        2251927, 90910, 3034838, 4082965, 2311377, 3512216, 2652413, 2191140,
+        302935, 3866228, 2007511, 744185, 2801160, 3993630, 592962, 795067,
+        2822609, 3471782, 3710854, 1824985, 1495256, 3906591, 3111335, 3902620,
+        11234, 1586236, 3698245, 492808, 2729660, 3369937, 1869963, 7244,
+        1453951, 1757304, 1005437, 3668653, 1821321, 4203686, 1192473, 113408,
+        2904803, 1346735, 4161890, 711442, 4020959, 1164150, 2139014, 4134238,
+        731747, 3856202, 2351090, 3382729, 2644693, 617098, 2796766, 1911274,
+        552932, 2476095, 1801797, 1381577, 2338697, 1336590, 2798544, 459121,
+        3555631, 741068, 2302686, 1883916, 2148181, 2471691, 2174195, 1684042,
+        3266036, 227434, 4107207, 2910899, 3427718, 2011049, 2706372, 4182237,
+        1243355, 2908998, 15068, 1966206, 2157082, 4114100, 1846352, 230880,
+        1161075, 1259576, 1212857, 1697580, 39500, 3079648, 2529577, 2082167,
+        50282, 476606, 1494601, 1334236, 3349015, 1600445, 413060, 3104844,
+        139283, 1688398, 3230017, 1009712, 614253, 2973529, 2077610, 2218429,
+        4185344, 254428, 506799, 196179, 3310395, 4183346, 3897905, 2234639,
+        1859699, 3322900, 2151737, 1904476, 2457045, 383438, 2543045, 2985636,
+        731083, 1609871, 2171434, 535413, 2666041, 405934, 3303186, 802974,
+        3573046, 1760267, 2758359, 2102800, 1512274, 3981750, 1838169, 2101846,
+        1363757, 1342163, 3608830, 321523, 1072908, 855117, 1679204, 3624675,
+        3183259, 2438624, 407591, 1549799, 490068, 2769318, 3185950, 990968,
+        3700398, 2715638, 3672301, 3203080, 1775408, 2071611, 778637, 2335351,
+        3317014, 3768001, 571163, 2618746, 1028702, 3174131, 764504, 1386439,
+        4188876, 1131998, 1057083, 39651, 2588805, 2519763, 3838931, 4130059,
+        1893001, 2066802, 572208, 2529031, 220967, 3880345, 1820301, 2205978,
+        3036090, 1648541, 4012391, 1432533, 3068186, 1645476, 1397186, 2112498,
+        4168213, 1234734, 1648052, 1803157, 2011730, 1648875, 2547914, 437873,
+        2460774, 3403214, 2690605, 2567052, 739775, 1854855, 520305, 3661464,
+        1120944, 1245195, 1147367, 2571134, 696367, 3009976, 834907, 1691662,
+        1384090, 2795844, 1813845, 3425954, 4194068, 1317042, 2056507, 470026,
+        3097617, 2678203, 3077203, 2116013, 4155561, 2844478, 1467696, 4150754,
+        992951, 471101, 4062883, 1584992, 2252609, 3322854, 1597940, 3581574,
+        1115369, 4153697, 3236495, 4075586, 2066340, 1262360, 2730720, 3664692,
+        2681478, 2929295, 3831713, 3683420, 2511172, 3689552, 2645837, 2414330,
+        857564, 3703853, 468246, 1574274, 3590547, 2348366, 1565207, 1815326,
+        2508730, 1749217, 465029, 260794, 1630097, 3019607, 3872759, 1053481,
+        3958758, 3415305, 54348, 2516, 3045515, 3011542, 1951553, 1882613,
+        1729323, 801736, 3662451, 909634, 2949838, 2598628, 1652685, 1945350,
+        3221627, 2879417, 2732226, 3883548, 1891328, 3215710, 3159721, 1318941,
+        2153764, 1870381, 4039453, 3375151, 2655219, 4089723, 1388508, 3436490,
+        3956335, 2748982, 4111030, 328986, 1780674, 2570336, 2608795, 2600572,
+        2748827, 790335, 1988956, 3946950, 1789942, 710384, 3900335, 457139,
+        2550557, 3042298, 1952120, 1998308, 259999, 2361900, 119023, 3680445,
+        1893737, 4050016, 2696786, 567472, 3085466, 1580931, 1360307, 3075154,
+        904205, 1306381, 3257843, 2926984, 2065676, 3221598, 2551064, 1580354,
+        1636374, 699891, 1821560, 670885, 947258, 2908840, 3049868, 1038075,
+        1701447, 2439140, 2048478, 3183312, 2224644, 320592, 3304074, 2611056,
+        422256, 1752180, 2217951, 2900510, 1321050, 2797671, 312886, 2624042,
+        3166863, 908176, 24947, 152205, 2891981, 189908, 1959427, 1365987,
+        2071767, 1932065, 3185693, 3889374, 3644713, 79765, 969178, 11268,
+        1992233, 1579325, 1224905, 3741957, 1894871, 3060100, 1787540, 4194180,
+        1396587, 2745514, 26822, 695515, 2348201, 249698, 2988539, 1081347
+
+    };
+
+    public static final int[] ZETA_INVERSE_I = {            /* 512-Entry */
+
+        1217030, 3955871, 1857368, 3510054, 4178747, 1460055, 2808982, 11389,
+        2418029, 1145469, 2310698, 463612, 2980664, 2626244, 2213336, 4194301,
+        3236391, 4125804, 560856, 316195, 1019876, 2273504, 2133802, 2839582,
+        2246142, 4015661, 1313588, 4053364, 4180622, 3297393, 1038706, 1581527,
+        3892683, 1407898, 2884519, 1305059, 1987618, 2453389, 3783313, 1594513,
+        901495, 3884977, 1980925, 1022257, 2157091, 1766429, 2504122, 3167494,
+        1155701, 1296729, 3258311, 3534684, 2384009, 3505678, 2569195, 2625215,
+        1654505, 983971, 2139893, 1278585, 947726, 2899188, 3301364, 1130415,
+        2845262, 2624638, 1120103, 3638097, 1508783, 155553, 2311832, 525124,
+        4086546, 1843669, 3945570, 2207261, 2253449, 1163271, 1655012, 3748430,
+        305234, 3495185, 2415627, 258619, 2216613, 3415234, 1456742, 1604997,
+        1596774, 1635233, 2424895, 3876583, 94539, 1456587, 249234, 769079,
+        2817061, 115846, 1550350, 830418, 166116, 2335188, 2051805, 2886628,
+        1045848, 989859, 2314241, 322021, 1473343, 1326152, 983942, 2260219,
+        2552884, 1606941, 1255731, 3295935, 543118, 3403833, 2476246, 2322956,
+        2254016, 1194027, 1160054, 4203053, 4151221, 790264, 246811, 3152088,
+        332810, 1185962, 2575472, 3944775, 3740540, 2456352, 1696839, 2390243,
+        2640362, 1857203, 615022, 2631295, 3737323, 501716, 3348005, 1791239,
+        1559732, 516017, 1694397, 522149, 373856, 1276274, 1524091, 540877,
+        1474849, 2943209, 2139229, 129983, 969074, 51872, 3090200, 623995,
+        2607629, 882715, 1952960, 2620577, 142686, 3734468, 3212618, 54815,
+        2737873, 1361091, 50008, 2089556, 1128366, 1527366, 1107952, 3735543,
+        2149062, 2888527, 11501, 779615, 2391724, 1409725, 2821479, 2513907,
+        3370662, 1195593, 3509202, 1634435, 3058202, 2960374, 3084625, 544105,
+        3685264, 2350714, 3465794, 1638517, 1514964, 802355, 1744795, 3767696,
+        1657655, 2556694, 2193839, 2402412, 2557517, 2970835, 37356, 2093071,
+        2808383, 2560093, 1137383, 2773036, 193178, 2557028, 1169479, 1999591,
+        2385268, 325224, 3984602, 1676538, 3633361, 2138767, 2312568, 75510,
+        366638, 1685806, 1616764, 4165918, 3148486, 3073571, 16693, 2819130,
+        3441065, 1031438, 3176867, 1586823, 3634406, 437568, 888555, 1870218,
+        3426932, 2133958, 2430161, 1002489, 533268, 1489931, 505171, 3214601,
+        1019619, 1436251, 3715501, 2655770, 3797978, 1766945, 1022310, 580894,
+        2526365, 3350452, 3132661, 3884046, 596739, 2863406, 2841812, 2103723,
+        2367400, 223819, 2693295, 2102769, 1447210, 2445302, 632523, 3402595,
+        902383, 3799635, 1539528, 3670156, 2034135, 2595698, 3474486, 1219933,
+        1662524, 3822131, 1748524, 2301093, 2053832, 882669, 2345870, 1970930,
+        307664, 22223, 895174, 4009390, 3698770, 3951141, 20225, 1987140,
+        2127959, 1232040, 3591316, 3195857, 975552, 2517171, 4066286, 1100725,
+        3792509, 2605124, 856554, 2871333, 2710968, 3728963, 4155287, 2123402,
+        1675992, 1125921, 4166069, 2507989, 2992712, 2945993, 3044494, 3974689,
+        2359217, 91469, 2048487, 2239363, 4190501, 1296571, 2962214, 23332,
+        1499197, 2194520, 777851, 1294670, 98362, 3978135, 939533, 2521527,
+        2031374, 1733878, 2057388, 2321653, 1902883, 3464501, 649938, 3746448,
+        1407025, 2868979, 1866872, 2823992, 2403772, 1729474, 3652637, 2294295,
+        1408803, 3588471, 1560876, 822840, 1854479, 349367, 3473822, 71331,
+        2066555, 3041419, 184610, 3494127, 43679, 2858834, 1300766, 4092161,
+        3013096, 1883, 2384248, 536916, 3200132, 2448265, 2751618, 4198325,
+        2335606, 835632, 1475909, 3712761, 507324, 2619333, 4194335, 302949,
+        1094234, 298978, 2710313, 2380584, 494715, 733787, 1382960, 3410502,
+        3612607, 211939, 1404409, 3461384, 2198058, 339341, 3902634, 2014429,
+        1553156, 693353, 1894192, 122604, 1170731, 4114659, 1953642, 3369625,
+        245053, 2923218, 2658671, 580673, 2915079, 2384193, 8714, 962214,
+        4027734, 789669, 3683359, 3983012, 3463286, 4039167, 2403890, 3783030,
+        3034802, 890018, 280701, 1808405, 3566666, 4106178, 3813869, 893295,
+        1382006, 3559019, 3168695, 2144872, 2022369, 1623695, 4066035, 2934000,
+        2603762, 4010709, 1189812, 1589407, 52974, 1671898, 3022018, 1019883,
+        2391510, 2888980, 4187628, 1051723, 3034285, 2085817, 2578902, 3975068,
+        86562, 489847, 136654, 2116674, 3358926, 959523, 136014, 3239046,
+        2102677, 2619868, 538875, 3195930, 535845, 361370, 3529250, 541241,
+        2516284, 3760447, 1830521, 2254047, 1064338, 368446, 2150259, 1551600,
+        1328182, 2063531, 3136098, 2824386, 3602412, 348730, 869335, 1406204,
+        2606794, 3529876, 857228, 59677, 2739968, 184133, 2849546, 53304,
+        3850240, 1953862, 571552, 273335, 3564155, 965995, 3001848, 2484738,
+        1843388, 1216365, 1072953, 3528207, 3835396, 2016489, 846038, 3124222
+
+    };
+
+    /* Heuristic qTESLA Security Category-3 (Option for Size) */
+
+    public static final int[] ZETA_III_SIZE = {            /* 1024-Entry */
+
+        671800, 4181291, 975654, 970836, 1459996, 2949013, 1578790, 3375131,
+        177347, 2024971, 3299069, 2879655, 1061156, 3772041, 1726661, 2646527,
+        224962, 3106510, 1764167, 3790159, 110295, 277183, 2296602, 1995237,
+        1574725, 1473236, 1081285, 144829, 114244, 719647, 4114328, 917441,
+        4188270, 3805772, 261389, 52393, 2185303, 1021265, 2167874, 2986441,
+        3886274, 2191966, 284211, 3446813, 1389427, 2107810, 1173125, 1597161,
+        3753261, 1373052, 793684, 4091628, 1677907, 4164049, 1948749, 2758369,
+        1027640, 1118203, 891820, 1309242, 1810791, 1863364, 2587868, 1541007,
+        4104068, 675426, 1402433, 2557508, 1068970, 1940808, 3957823, 798456,
+        4092960, 3262467, 1793460, 658044, 1978921, 1367494, 3136736, 2360480,
+        941550, 37800, 1919065, 3032526, 581001, 3323192, 299785, 3114533,
+        545048, 2845265, 1891473, 102035, 2256179, 221259, 1796623, 504470,
+        377401, 3184337, 3107383, 606431, 200460, 3770995, 986925, 207500,
+        3712747, 1696453, 4158053, 3530443, 32005, 3222743, 3918763, 3574153,
+        2768592, 2608835, 1856937, 905294, 214652, 4154226, 2876170, 2651799,
+        1098009, 3905542, 3763042, 3055325, 1438567, 969841, 2397140, 3637385,
+        3779810, 644984, 1638607, 498549, 3404792, 4055115, 9472, 315805,
+        1796876, 972163, 3025826, 3334639, 2290368, 2552107, 160996, 3282568,
+        2279239, 1305163, 2304247, 603598, 3803059, 2582009, 3202587, 1094032,
+        1195417, 2879417, 1648902, 542294, 3085586, 3325229, 4177450, 150226,
+        890698, 503530, 3122945, 1929018, 3309179, 1075767, 2185016, 276011,
+        1620579, 1349757, 454010, 3835301, 3658519, 2369797, 203221, 2116132,
+        1371940, 3499700, 2991078, 3597638, 942280, 506271, 701979, 1853372,
+        2165162, 2830558, 2083508, 3582128, 4177826, 2623861, 3740436, 725559,
+        791017, 595361, 2192451, 878351, 1919935, 1730363, 165115, 3011415,
+        539166, 4049306, 2512830, 3633034, 3743092, 1721797, 356766, 3860922,
+        551806, 520752, 1492250, 3020875, 296084, 3951086, 3702654, 1541222,
+        2760082, 2967699, 1811892, 1913148, 3121111, 2583448, 37791, 2289197,
+        228811, 315449, 2711375, 2035264, 998876, 684125, 1377229, 1723513,
+        2093137, 1181754, 3978572, 1168111, 1295590, 1870157, 2992279, 1610031,
+        2052968, 3195982, 3195020, 2498826, 2430997, 1447298, 2178224, 1573739,
+        3444420, 2425537, 3066466, 1895376, 3494178, 2341084, 2603206, 2810264,
+        3665075, 4030046, 232980, 3770527, 2425457, 3193512, 1906687, 3838549,
+        1081341, 3385499, 343154, 3648238, 3066045, 3502707, 903006, 2216085,
+        2477447, 3769256, 2700907, 2899931, 3094342, 404354, 2325640, 4161594,
+        1153616, 2601633, 624385, 56418, 4122920, 303574, 3474524, 3047326,
+        3446806, 1755473, 1289687, 3030484, 745529, 2037059, 1126174, 3508536,
+        3263841, 1057863, 2424516, 3666380, 2238799, 1918076, 1096624, 666757,
+        2414037, 4105141, 86489, 236751, 2175830, 2842379, 3751432, 366978,
+        1727916, 627613, 2576775, 231383, 2352896, 1039386, 650148, 3849095,
+        2893195, 2813545, 2172937, 1389954, 1261168, 1470030, 832830, 3548304,
+        2585258, 3650945, 3733752, 797947, 4183412, 261772, 374082, 3717015,
+        1306771, 591941, 3320862, 3969254, 3730288, 4153963, 2641916, 706453,
+        3574687, 687011, 3723863, 518936, 674472, 2242626, 174183, 3560884,
+        3969544, 425417, 789235, 4183047, 4027225, 982625, 2075760, 2392513,
+        2538340, 3022462, 1997528, 356548, 3730142, 1536313, 1202696, 1344848,
+        3103217, 2383022, 1762142, 2994989, 3102783, 3072599, 1517632, 2024436,
+        2534641, 147328, 2356097, 190578, 2587663, 440306, 2374767, 3182600,
+        680532, 484370, 4131095, 3009332, 3562207, 976019, 3613316, 3033006,
+        3743622, 4136404, 1605237, 66645, 3859240, 908865, 4051121, 2726336,
+        3637443, 2340134, 813357, 3985220, 2868243, 3650243, 1684957, 3023114,
+        2402323, 1820096, 1764462, 1049670, 2260628, 4976, 3760346, 3157996,
+        3573461, 1006628, 454916, 4159906, 337885, 22277, 520578, 2607705,
+        2561874, 503606, 1415232, 3823408, 3829828, 554510, 914738, 3838536,
+        653901, 664244, 3918457, 361056, 515834, 2583400, 2666144, 1562200,
+        2635470, 3523620, 2847787, 281762, 1416774, 4047010, 2739024, 1492985,
+        2613083, 2116726, 4076288, 4141191, 3357856, 741301, 977038, 4028938,
+        1661277, 2769449, 3571042, 2601104, 57237, 3026729, 3478919, 87366,
+        3697654, 2676961, 3932341, 2883942, 3200147, 623723, 871365, 763732,
+        2354543, 661482, 1442350, 148821, 966821, 2154509, 1229800, 2252524,
+        1712762, 687319, 1231124, 225814, 127675, 2786959, 2996601, 1997279,
+        1410197, 2759369, 254896, 2633749, 743622, 2420984, 594581, 1359068,
+        3724994, 3338166, 473524, 3323698, 110693, 2630130, 3742099, 1392129,
+        1263087, 1474128, 964094, 3338617, 2682625, 2350723, 4039051, 2437147,
+        2003303, 1029372, 3710675, 1198388, 4047402, 337401, 959139, 3673320,
+        3269977, 1757086, 3846011, 3052386, 1555886, 1213798, 1730449, 574426,
+        3730903, 4058825, 3075, 3232877, 597243, 584901, 3208277, 423060,
+        3216342, 3727213, 89571, 709528, 3722455, 112585, 4199553, 578587,
+        1727014, 3010665, 1118724, 3088559, 458307, 695931, 2551953, 3462204,
+        2654347, 2908501, 3034211, 3511237, 3734268, 2443875, 270514, 776347,
+        683036, 1526569, 521044, 2352920, 557737, 4056083, 2391161, 2389563,
+        2293979, 2581739, 2738173, 2545480, 1008072, 3577574, 1673061, 4116273,
+        133058, 1222352, 1144238, 882222, 3000625, 4046931, 141504, 1904001,
+        1035854, 3807884, 2398461, 446181, 2041489, 1148183, 2291458, 3675915,
+        255124, 369448, 3016249, 2025225, 3237403, 2220199, 3134791, 2587255,
+        3220754, 3366174, 132697, 3383227, 1358468, 1158291, 2321651, 2869559,
+        1425523, 4054733, 69091, 3521561, 2453355, 2968118, 2968833, 3185424,
+        988606, 3025251, 1154802, 24092, 1305476, 3938667, 3405455, 2280837,
+        2987149, 1576181, 3812113, 481232, 1911887, 2305037, 3637072, 515558,
+        3183843, 3460525, 2134536, 3376047, 849276, 912675, 3126131, 3349335,
+        1736653, 247313, 307171, 2906949, 2483567, 3951115, 449581, 3211241,
+        119780, 3050685, 312715, 129516, 1413964, 3626707, 1834389, 739674,
+        2166987, 1898439, 3247386, 543470, 3893129, 1952324, 4010533, 2663329,
+        1611039, 4159354, 3221090, 4011118, 456104, 4128401, 3481956, 1341852,
+        1346376, 1373597, 1886912, 2289124, 2035164, 3802432, 4020200, 1440583,
+        131860, 2447356, 1147783, 3884191, 36600, 1417517, 3115113, 4106357,
+        2209232, 3913295, 2079509, 2915453, 253356, 2093028, 3105753, 420898,
+        3641863, 2237777, 589597, 3471638, 1556385, 1574364, 2961455, 2414774,
+        2532838, 3894119, 2561579, 1825751, 2610770, 4095615, 2366084, 1696032,
+        2935352, 1982899, 3940806, 962691, 2874348, 2295425, 3088987, 1724605,
+        138760, 2611152, 2321223, 3862854, 977071, 3373271, 2119442, 2444640,
+        184156, 2401204, 2250096, 3883423, 24318, 1799015, 2709027, 2477092,
+        2937887, 872546, 348220, 49520, 266109, 1166709, 1470353, 712356,
+        4162049, 2520023, 1093919, 3371334, 1529777, 3549597, 3033168, 3626405,
+        317815, 972428, 3325840, 1416192, 1615043, 3225312, 49030, 591050,
+        3470933, 533400, 905783, 2128579, 2589779, 1556207, 3295501, 3128246,
+        1323037, 2836289, 1222103, 2635029, 764092, 1785154, 1271391, 407326,
+        3293361, 697832, 1957938, 26925, 1909470, 921060, 1189793, 452905,
+        177180, 3986522, 3612073, 2634482, 3811697, 2155464, 3184049, 3773906,
+        4155559, 890604, 965647, 946702, 2980153, 2514794, 1634712, 1413135,
+        2059115, 1095948, 1094602, 3286386, 1617289, 2234906, 2942756, 2831603,
+        2790364, 4110867, 3267277, 2818115, 997189, 3975212, 1919457, 3294782,
+        42631, 3979780, 677949, 1074671, 4007873, 2013224, 4064265, 1404667,
+        1266413, 1753048, 1480954, 2251688, 3671233, 3337348, 3023835, 704482,
+        1867102, 2290506, 1202801, 3686892, 3618479, 3297031, 2477670, 3415258,
+        2889498, 2106378, 361488, 1478812, 3536666, 645275, 2793501, 2983604,
+        3150760, 1136423, 2629214, 3144871, 1095947, 2432448, 3144124, 1562104,
+        3685583, 2519659, 1745378, 275993, 1028739, 4053547, 3139341, 791685,
+        205316, 940435, 3044250, 3537550, 2347550, 2748749, 216515, 2376693,
+        3994272, 758809, 336837, 4138282, 254982, 2087732, 1443586, 2090448,
+        2407213, 2192231, 584225, 1528366, 714102, 2781015, 1061159, 144894,
+        2251444, 1706143, 3064185, 1082774, 1212561, 1964667, 1808852, 1281436,
+        380192, 1938622, 3594224, 2865093, 1814198, 3709791, 3557452, 641073,
+        3449310, 2797672, 1886229, 2374072, 1947652, 910530, 4110612, 3688785,
+        2761424, 2192378, 1210992, 432423, 3990493, 3710041, 3364266, 1402625,
+        1430941, 466915, 2307343, 3969361, 3041855, 1636011, 2336989, 4083954,
+        1752367, 1468975, 4003767, 2752277, 2639144, 2435428, 168292, 2731409,
+        2173963, 313121, 1885409, 1792411, 105750, 1595875, 3535511, 1917121,
+        3348968, 3600516, 3025874, 1234611, 387230, 254793, 2267177, 423073,
+        3643782, 2241875, 720861, 3996710, 2066073, 1031892, 2436415, 3356685,
+        3628852, 1131491, 315588, 3085726, 4060906, 3713538, 561022, 142143,
+        137017, 4091465, 525060, 523088, 2581256, 2546361, 529201, 1724592,
+        3917913, 4096490, 1689933, 575672, 2633453, 2453964, 3882580, 236313,
+        394169, 2731312, 3191196, 135139, 1208112, 2180950, 4051722, 330078,
+        4161293, 3314132, 1075088, 3797989, 958522, 1974573, 3610471, 3368492,
+        629863, 3712506, 281606, 4189621, 1437509, 2515187, 1936773, 3150875,
+        797596, 4050969, 2506561, 2023050, 3235484, 2216101, 3003527, 569898,
+        2081018, 3678953, 3392925, 857476, 1224594, 2996526, 3160227, 35843
+
+    };
+
+    public static final int[] ZETA_INVERSE_III_SIZE = {    /* 1024-Entry */
+
+        1046366, 1210067, 2981999, 3349117, 813668, 527640, 2125575, 3636695,
+        1203066, 1990492, 971109, 2183543, 1700032, 155624, 3408997, 1055718,
+        2269820, 1691406, 2769084, 16972, 3924987, 494087, 3576730, 838101,
+        596122, 2232020, 3248071, 408604, 3131505, 892461, 45300, 3876515,
+        154871, 2025643, 2998481, 4071454, 1015397, 1475281, 3812424, 3970280,
+        324013, 1752629, 1573140, 3630921, 2516660, 110103, 288680, 2482001,
+        3677392, 1660232, 1625337, 3683505, 3681533, 115128, 4069576, 4064450,
+        3645571, 493055, 145687, 1120867, 3891005, 3075102, 577741, 849908,
+        1770178, 3174701, 2140520, 209883, 3485732, 1964718, 562811, 3783520,
+        1939416, 3951800, 3819363, 2971982, 1180719, 606077, 857625, 2289472,
+        671082, 2610718, 4100843, 2414182, 2321184, 3893472, 2032630, 1475184,
+        4038301, 1771165, 1567449, 1454316, 202826, 2737618, 2454226, 122639,
+        1869604, 2570582, 1164738, 237232, 1899250, 3739678, 2775652, 2803968,
+        842327, 496552, 216100, 3774170, 2995601, 2014215, 1445169, 517808,
+        95981, 3296063, 2258941, 1832521, 2320364, 1408921, 757283, 3565520,
+        649141, 496802, 2392395, 1341500, 612369, 2267971, 3826401, 2925157,
+        2397741, 2241926, 2994032, 3123819, 1142408, 2500450, 1955149, 4061699,
+        3145434, 1425578, 3492491, 2678227, 3622368, 2014362, 1799380, 2116145,
+        2763007, 2118861, 3951611, 68311, 3869756, 3447784, 212321, 1829900,
+        3990078, 1457844, 1859043, 669043, 1162343, 3266158, 4001277, 3414908,
+        1067252, 153046, 3177854, 3930600, 2461215, 1686934, 521010, 2644489,
+        1062469, 1774145, 3110646, 1061722, 1577379, 3070170, 1055833, 1222989,
+        1413092, 3561318, 669927, 2727781, 3845105, 2100215, 1317095, 791335,
+        1728923, 909562, 588114, 519701, 3003792, 1916087, 2339491, 3502111,
+        1182758, 869245, 535360, 1954905, 2725639, 2453545, 2940180, 2801926,
+        142328, 2193369, 198720, 3131922, 3528644, 226813, 4163962, 911811,
+        2287136, 231381, 3209404, 1388478, 939316, 95726, 1416229, 1374990,
+        1263837, 1971687, 2589304, 920207, 3111991, 3110645, 2147478, 2793458,
+        2571881, 1691799, 1226440, 3259891, 3240946, 3315989, 51034, 432687,
+        1022544, 2051129, 394896, 1572111, 594520, 220071, 4029413, 3753688,
+        3016800, 3285533, 2297123, 4179668, 2248655, 3508761, 913232, 3799267,
+        2935202, 2421439, 3442501, 1571564, 2984490, 1370304, 2883556, 1078347,
+        911092, 2650386, 1616814, 2078014, 3300810, 3673193, 735660, 3615543,
+        4157563, 981281, 2591550, 2790401, 880753, 3234165, 3888778, 580188,
+        1173425, 656996, 2676816, 835259, 3112674, 1686570, 44544, 3494237,
+        2736240, 3039884, 3940484, 4157073, 3858373, 3334047, 1268706, 1729501,
+        1497566, 2407578, 4182275, 323170, 1956497, 1805389, 4022437, 1761953,
+        2087151, 833322, 3229522, 343739, 1885370, 1595441, 4067833, 2481988,
+        1117606, 1911168, 1332245, 3243902, 265787, 2223694, 1271241, 2510561,
+        1840509, 110978, 1595823, 2380842, 1645014, 312474, 1673755, 1791819,
+        1245138, 2632229, 2650208, 734955, 3616996, 1968816, 564730, 3785695,
+        1100840, 2113565, 3953237, 1291140, 2127084, 293298, 1997361, 100236,
+        1091480, 2789076, 4169993, 322402, 3058810, 1759237, 4074733, 2766010,
+        186393, 404161, 2171429, 1917469, 2319681, 2832996, 2860217, 2864741,
+        724637, 78192, 3750489, 195475, 985503, 47239, 2595554, 1543264,
+        196060, 2254269, 313464, 3663123, 959207, 2308154, 2039606, 3466919,
+        2372204, 579886, 2792629, 4077077, 3893878, 1155908, 4086813, 995352,
+        3757012, 255478, 1723026, 1299644, 3899422, 3959280, 2469940, 857258,
+        1080462, 3293918, 3357317, 830546, 2072057, 746068, 1022750, 3691035,
+        569521, 1901556, 2294706, 3725361, 394480, 2630412, 1219444, 1925756,
+        801138, 267926, 2901117, 4182501, 3051791, 1181342, 3217987, 1021169,
+        1237760, 1238475, 1753238, 685032, 4137502, 151860, 2781070, 1337034,
+        1884942, 3048302, 2848125, 823366, 4073896, 840419, 985839, 1619338,
+        1071802, 1986394, 969190, 2181368, 1190344, 3837145, 3951469, 530678,
+        1915135, 3058410, 2165104, 3760412, 1808132, 398709, 3170739, 2302592,
+        4065089, 159662, 1205968, 3324371, 3062355, 2984241, 4073535, 90320,
+        2533532, 629019, 3198521, 1661113, 1468420, 1624854, 1912614, 1817030,
+        1815432, 150510, 3648856, 1853673, 3685549, 2680024, 3523557, 3430246,
+        3936079, 1762718, 472325, 695356, 1172382, 1298092, 1552246, 744389,
+        1654640, 3510662, 3748286, 1118034, 3087869, 1195928, 2479579, 3628006,
+        7040, 4094008, 484138, 3497065, 4117022, 479380, 990251, 3783533,
+        998316, 3621692, 3609350, 973716, 4203518, 147768, 475690, 3632167,
+        2476144, 2992795, 2650707, 1154207, 360582, 2449507, 936616, 533273,
+        3247454, 3869192, 159191, 3008205, 495918, 3177221, 2203290, 1769446,
+        167542, 1855870, 1523968, 867976, 3242499, 2732465, 2943506, 2814464,
+        464494, 1576463, 4095900, 882895, 3733069, 868427, 481599, 2847525,
+        3612012, 1785609, 3462971, 1572844, 3951697, 1447224, 2796396, 2209314,
+        1209992, 1419634, 4078918, 3980779, 2975469, 3519274, 2493831, 1954069,
+        2976793, 2052084, 3239772, 4057772, 2764243, 3545111, 1852050, 3442861,
+        3335228, 3582870, 1006446, 1322651, 274252, 1529632, 508939, 4119227,
+        727674, 1179864, 4149356, 1605489, 635551, 1437144, 2545316, 177655,
+        3229555, 3465292, 848737, 65402, 130305, 2089867, 1593510, 2713608,
+        1467569, 159583, 2789819, 3924831, 1358806, 682973, 1571123, 2644393,
+        1540449, 1623193, 3690759, 3845537, 288136, 3542349, 3552692, 368057,
+        3291855, 3652083, 376765, 383185, 2791361, 3702987, 1644719, 1598888,
+        3686015, 4184316, 3868708, 46687, 3751677, 3199965, 633132, 1048597,
+        446247, 4201617, 1945965, 3156923, 2442131, 2386497, 1804270, 1183479,
+        2521636, 556350, 1338350, 221373, 3393236, 1866459, 569150, 1480257,
+        155472, 3297728, 347353, 4139948, 2601356, 70189, 462971, 1173587,
+        593277, 3230574, 644386, 1197261, 75498, 3722223, 3526061, 1023993,
+        1831826, 3766287, 1618930, 4016015, 1850496, 4059265, 1671952, 2182157,
+        2688961, 1133994, 1103810, 1211604, 2444451, 1823571, 1103376, 2861745,
+        3003897, 2670280, 476451, 3850045, 2209065, 1184131, 1668253, 1814080,
+        2130833, 3223968, 179368, 23546, 3417358, 3781176, 237049, 645709,
+        4032410, 1963967, 3532121, 3687657, 482730, 3519582, 631906, 3500140,
+        1564677, 52630, 476305, 237339, 885731, 3614652, 2899822, 489578,
+        3832511, 3944821, 23181, 3408646, 472841, 555648, 1621335, 658289,
+        3373763, 2736563, 2945425, 2816639, 2033656, 1393048, 1313398, 357498,
+        3556445, 3167207, 1853697, 3975210, 1629818, 3578980, 2478677, 3839615,
+        455161, 1364214, 2030763, 3969842, 4120104, 101452, 1792556, 3539836,
+        3109969, 2288517, 1967794, 540213, 1782077, 3148730, 942752, 698057,
+        3080419, 2169534, 3461064, 1176109, 2916906, 2451120, 759787, 1159267,
+        732069, 3903019, 83673, 4150175, 3582208, 1604960, 3052977, 44999,
+        1880953, 3802239, 1112251, 1306662, 1505686, 437337, 1729146, 1990508,
+        3303587, 703886, 1140548, 558355, 3863439, 821094, 3125252, 368044,
+        2299906, 1013081, 1781136, 436066, 3973613, 176547, 541518, 1396329,
+        1603387, 1865509, 712415, 2311217, 1140127, 1781056, 762173, 2632854,
+        2028369, 2759295, 1775596, 1707767, 1011573, 1010611, 2153625, 2596562,
+        1214314, 2336436, 2911003, 3038482, 228021, 3024839, 2113456, 2483080,
+        2829364, 3522468, 3207717, 2171329, 1495218, 3891144, 3977782, 1917396,
+        4168802, 1623145, 1085482, 2293445, 2394701, 1238894, 1446511, 2665371,
+        503939, 255507, 3910509, 1185718, 2714343, 3685841, 3654787, 345671,
+        3849827, 2484796, 463501, 573559, 1693763, 157287, 3667427, 1195178,
+        4041478, 2476230, 2286658, 3328242, 2014142, 3611232, 3415576, 3481034,
+        466157, 1582732, 28767, 624465, 2123085, 1376035, 2041431, 2353221,
+        3504614, 3700322, 3264313, 608955, 1215515, 706893, 2834653, 2090461,
+        4003372, 1836796, 548074, 371292, 3752583, 2856836, 2586014, 3930582,
+        2021577, 3130826, 897414, 2277575, 1083648, 3703063, 3315895, 4056367,
+        29143, 881364, 1121007, 3664299, 2557691, 1327176, 3011176, 3112561,
+        1004006, 1624584, 403534, 3602995, 1902346, 2901430, 1927354, 924025,
+        4045597, 1654486, 1916225, 871954, 1180767, 3234430, 2409717, 3890788,
+        4197121, 151478, 801801, 3708044, 2567986, 3561609, 426783, 569208,
+        1809453, 3236752, 2768026, 1151268, 443551, 301051, 3108584, 1554794,
+        1330423, 52367, 3991941, 3301299, 2349656, 1597758, 1438001, 632440,
+        287830, 983850, 4174588, 676150, 48540, 2510140, 493846, 3999093,
+        3219668, 435598, 4006133, 3600162, 1099210, 1022256, 3829192, 3702123,
+        2409970, 3985334, 1950414, 4104558, 2315120, 1361328, 3661545, 1092060,
+        3906808, 883401, 3625592, 1174067, 2287528, 4168793, 3265043, 1846113,
+        1069857, 2839099, 2227672, 3548549, 2413133, 944126, 113633, 3408137,
+        248770, 2265785, 3137623, 1649085, 2804160, 3531167, 102525, 2665586,
+        1618725, 2343229, 2395802, 2897351, 3314773, 3088390, 3178953, 1448224,
+        2257844, 42544, 2528686, 114965, 3412909, 2833541, 453332, 2609432,
+        3033468, 2098783, 2817166, 759780, 3922382, 2014627, 320319, 1220152,
+        2038719, 3185328, 2021290, 4154200, 3945204, 400821, 18323, 3289152,
+        92265, 3486946, 4092349, 4061764, 3125308, 2733357, 2631868, 2211356,
+        1909991, 3929410, 4096298, 416434, 2442426, 1100083, 3981631, 1560066,
+        2479932, 434552, 3145437, 1326938, 907524, 2181622, 4029246, 831462,
+        2627803, 1257580, 2746597, 3235757, 3230939, 25302, 3534793, 4170750
+
+    };
+
+    /* Heuristic qTESLA Security Category-3 (Option for Speed) */
+
+    public static final int[] ZETA_III_SPEED = {            /* 1024-Entry */
+
+        4751355, 3795849, 4203855, 2135008, 6005859, 8231721, 5028848, 2129664,
+        7697675, 4755217, 4725508, 3239612, 6448681, 1076080, 3836135, 157994,
+        5620931, 7886062, 2890907, 5218426, 5961785, 6266756, 6428554, 5190121,
+        4542230, 1731429, 2223635, 4784194, 3466184, 2050685, 6391390, 2917454,
+        2117568, 5724978, 3127077, 96284, 5251989, 3298678, 7201703, 432021,
+        540694, 6011377, 6511091, 6136825, 215125, 6152822, 4121955, 6320948,
+        4723419, 3116754, 3645529, 4643271, 3249093, 3697259, 965302, 3790255,
+        413429, 835404, 7555714, 4708344, 980578, 8245349, 3583234, 5891188,
+        510086, 5483952, 4214513, 7522675, 1382737, 8097349, 2423268, 1978286,
+        5820434, 2985005, 1002240, 3252040, 5584283, 4027445, 3761478, 571563,
+        7926529, 5265675, 4705738, 1136608, 2087977, 4856723, 7896505, 2504130,
+        4175968, 5245926, 3848909, 3723902, 2181242, 6476735, 5922041, 2555482,
+        6087709, 1106974, 975919, 978505, 666303, 510879, 5043449, 4402981,
+        4204183, 6947226, 3519239, 7237093, 2533941, 1259684, 4897608, 2422013,
+        5398162, 3551190, 6378523, 1066751, 5216741, 6557683, 7171180, 7736022,
+        7762004, 7816398, 434930, 5685531, 7776512, 2136107, 4689096, 2604202,
+        981324, 6730872, 7113462, 4313227, 5315069, 2687514, 6464663, 6622027,
+        4919554, 3137828, 6662263, 180027, 1225049, 993103, 6035200, 4768729,
+        7594608, 7982166, 1506084, 1412996, 7294988, 6493396, 3679803, 707143,
+        5016089, 5893370, 7746168, 8284307, 2196442, 4506697, 5744441, 8155374,
+        2335696, 3358969, 4559736, 8378847, 7396599, 5613912, 5146767, 5609330,
+        7478110, 7007768, 1540167, 2082109, 4395136, 2443, 1730472, 6785605,
+        3689430, 7862069, 5994777, 2079150, 1569788, 3575961, 2449565, 7637802,
+        3223577, 7636917, 7014221, 3206599, 1033702, 2788915, 2962522, 5785994,
+        2935623, 8040165, 646639, 6994735, 1576929, 4976182, 1923760, 7349612,
+        7767222, 5695773, 2143434, 1957445, 6164911, 6325092, 3612819, 5415356,
+        6956649, 426840, 6748291, 3530533, 2487417, 7851363, 3671732, 7201581,
+        4717139, 4328822, 5046010, 6056773, 997476, 3549222, 1616322, 3335537,
+        2012692, 7475529, 2140630, 167787, 3791455, 3347958, 1263751, 2818658,
+        8280316, 4667491, 7207537, 6918648, 6602507, 1441518, 5135376, 1610367,
+        1016061, 6841480, 1841539, 2809077, 5412381, 1240493, 4442913, 2092171,
+        530239, 4308557, 3094305, 18124, 7247221, 5096652, 6892131, 3384856,
+        1435046, 3360331, 4908655, 1534384, 724138, 4632705, 7021901, 776577,
+        6462800, 658408, 6767742, 6323190, 163450, 3090558, 8264681, 6113406,
+        2256514, 2002774, 4157811, 1055743, 4272701, 50782, 506820, 2425609,
+        6121916, 7048118, 7211758, 7436523, 7921267, 8219231, 2113012, 5561555,
+        5402793, 6238877, 2785469, 6601179, 5379488, 8247469, 7039082, 8122949,
+        4032315, 7719142, 6412117, 1765474, 1423373, 3702073, 6814517, 4539579,
+        3714409, 3581714, 4818181, 4950204, 6549391, 6746510, 1332314, 6200219,
+        4026957, 4085009, 6474721, 6328096, 8097370, 821395, 4479068, 6408341,
+        1730278, 3865108, 131998, 3351400, 4527559, 4193606, 1952686, 3339484,
+        549242, 3890467, 3314638, 2970830, 7867854, 235273, 4948941, 3082658,
+        6487691, 6448908, 3822702, 5156099, 3762420, 359477, 1160634, 2545064,
+        2616573, 343964, 1277910, 8113538, 5564704, 6944366, 2663139, 2516714,
+        7942808, 5983628, 6727371, 7839582, 4671024, 5279277, 2488251, 5096610,
+        7584873, 1054559, 3389302, 5410955, 2700464, 7113783, 1182446, 681631,
+        7959149, 1325409, 3882231, 4559, 7653722, 2600940, 4354328, 6155200,
+        8125241, 629708, 1056841, 5566349, 6779032, 7067599, 5284661, 693330,
+        7111640, 6054764, 513663, 3702555, 3667331, 8053262, 3156410, 3207020,
+        6096921, 3226364, 425916, 1538990, 398369, 155207, 5962206, 3895624,
+        3506460, 4470299, 8132637, 7313282, 1829556, 3023684, 7819374, 258083,
+        4547318, 6274233, 4376026, 630321, 5354666, 3252030, 3347795, 5465400,
+        424523, 5150532, 4329513, 5096843, 2541091, 4590781, 5698802, 5642789,
+        6737542, 6616826, 6654175, 1804189, 2843875, 5019191, 2161010, 1581450,
+        7289320, 4525144, 2861293, 1184843, 6181826, 3408399, 6954125, 5897789,
+        899044, 983144, 996950, 2489509, 387143, 6743865, 6535741, 5316114,
+        2464958, 6452453, 6588356, 7749117, 3100483, 5451553, 6683005, 6153131,
+        2627493, 3750459, 6016367, 1854681, 5732487, 5756336, 8222984, 6286073,
+        4805023, 4851345, 5346259, 2571898, 7886466, 915166, 750162, 7786158,
+        3314583, 3691654, 8162728, 2964553, 934173, 2078087, 5894912, 103974,
+        2087696, 8074875, 5687948, 6220280, 5741269, 3408244, 5908533, 5524440,
+        405672, 2691224, 4107404, 1478212, 5069278, 1307718, 2701016, 5020579,
+        3281802, 173449, 7661012, 6737317, 2871182, 7099172, 8035102, 4259638,
+        676548, 1986555, 2711904, 72486, 1537417, 4903843, 6366739, 2767425,
+        4405806, 3939004, 6029482, 7042007, 7145803, 4423846, 5865280, 6985580,
+        4890457, 5525519, 3020926, 5286410, 2507470, 164000, 7418741, 3860720,
+        464246, 7532333, 2980645, 2208123, 1971493, 7819798, 4391480, 3562255,
+        1579560, 3499404, 3220123, 5084410, 2287245, 6505523, 7780056, 63470,
+        7361152, 6336292, 3791465, 1891129, 6121738, 6215206, 578317, 2487175,
+        2862407, 7213504, 175006, 850302, 4374314, 3335934, 5070337, 6404001,
+        6073674, 8339498, 2024643, 590809, 3871922, 7695009, 3420134, 2417242,
+        2441851, 510359, 6415975, 5966400, 1511640, 3278055, 7552659, 7251424,
+        299565, 4631182, 923500, 4565103, 1774136, 1514014, 504288, 7611114,
+        348434, 6179273, 6103289, 5512004, 6204874, 4163633, 4030619, 5442160,
+        5517078, 4979705, 1450139, 4955279, 6892996, 1378796, 7049658, 7482552,
+        2773460, 3700106, 5637962, 1306126, 1342831, 2876039, 2004802, 7911534,
+        7811881, 7092783, 2948733, 3985770, 4334975, 2089910, 4202878, 5889243,
+        2572806, 7276150, 3116397, 4610309, 4197207, 4524762, 3158380, 712800,
+        7767402, 2288328, 3468435, 4486610, 3258256, 4642983, 4397184, 3897150,
+        3551813, 5337205, 7602723, 882125, 4202004, 8272524, 4429435, 2663261,
+        1849803, 4271342, 947283, 5290695, 2673376, 7191822, 2383684, 5913757,
+        1682056, 4777883, 695434, 2390033, 5758581, 3482483, 6148624, 4372212,
+        1400527, 3325425, 3100142, 7502466, 6912369, 6564003, 187779, 6691298,
+        1850484, 1886083, 496958, 4353128, 6385269, 7972087, 3249950, 1216051,
+        2945392, 1716654, 974864, 2073675, 4221586, 3197564, 5970107, 2900682,
+        1387771, 1054897, 5018658, 4773826, 2772495, 3056563, 3437397, 1151634,
+        3999643, 4238788, 2370183, 7770204, 2300657, 4762800, 4526771, 4778855,
+        6276244, 2113067, 4081453, 4596532, 896126, 1589159, 5012357, 1884715,
+        3849142, 1866468, 1032092, 4847686, 5661618, 424095, 6396824, 6683872,
+        1861112, 2415218, 7008299, 7999725, 61635, 7831813, 1934052, 1635771,
+        5857904, 3890446, 3661009, 4427825, 1472600, 2675631, 3142762, 2889031,
+        6246428, 3402870, 890885, 300722, 6084442, 6218398, 6764876, 198422,
+        5203148, 1220540, 3348691, 7973167, 1045857, 5930282, 7686549, 4965437,
+        2645402, 3029580, 1825802, 4844838, 5808305, 3553948, 2913430, 4640069,
+        3231039, 5061776, 3388089, 5793791, 5210767, 6179591, 3259669, 208882,
+        7981542, 8176212, 8044267, 4362411, 8164807, 3882310, 4575856, 1654989,
+        2265125, 4993003, 1845358, 876204, 4604356, 6572234, 3709582, 2495322,
+        7769694, 925160, 5211127, 3071339, 8271814, 1715113, 5979772, 8317560,
+        2452182, 4560530, 2756321, 7541123, 2085235, 2131014, 7043807, 945796,
+        1043611, 2220162, 1519639, 1881321, 4596482, 136652, 8225264, 4442429,
+        3140394, 4274467, 2235364, 3403641, 181254, 2256107, 5402447, 274444,
+        2450384, 774651, 1007857, 488393, 7305342, 8006923, 6213033, 6045031,
+        7357074, 3686418, 2069641, 4827274, 6134510, 8182850, 6622239, 4957796,
+        5001496, 2676491, 2165891, 4258696, 1805202, 5978874, 5892091, 3081817,
+        8351892, 2919790, 6813596, 1226235, 7874664, 1597662, 445691, 7699844,
+        517985, 5142533, 583275, 2942212, 2005170, 2781492, 5325095, 5840485,
+        5904531, 1652696, 462001, 488382, 1483208, 3583371, 3903552, 4338246,
+        8102054, 4607027, 3097235, 5461223, 878707, 5641705, 3004044, 6269117,
+        8310809, 1068150, 3754674, 7419023, 2389012, 2672920, 7033357, 3299934,
+        5696831, 8333890, 6870124, 4248890, 7451238, 2996873, 969409, 4748469,
+        6761147, 7334152, 4608897, 6381766, 593469, 5060352, 7663929, 3490012,
+        5639842, 804562, 3554683, 1275119, 2464713, 584123, 1282802, 869246,
+        1698712, 460380, 2270294, 1082297, 5755881, 4164200, 3083595, 7232256,
+        6669791, 5297513, 7888160, 305159, 4943995, 4839451, 4607217, 4607189,
+        4027370, 6773057, 2930951, 7228212, 4930292, 5787132, 6979166, 3679768,
+        2488882, 7434774, 6751191, 4901863, 601751, 3772392, 4470584, 3139313,
+        2581011, 5344571, 2578160, 126930, 4080823, 5594812, 3191131, 2170321,
+        4703512, 3837804, 7417071, 2996858, 6019670, 5239573, 1770901, 7113857,
+        5965467, 8008016, 192380, 7790747, 867783, 2310931, 477474, 723267,
+        2025346, 7474446, 1992778, 5665730, 5375937, 1925098, 1772156, 7957977,
+        7842750, 4780661, 1703317, 4165961, 5256458, 4850569, 4937646, 1616991,
+        8229940, 128563, 1160620, 5109082, 4794032, 4890146, 4147576, 7912097,
+        927266, 450684, 7302719, 2598976, 5529718, 1041149, 4841395, 6276135,
+        7825395, 7621671, 6329777, 1955851, 6040427, 1035300, 2855476, 3258870,
+        3396861, 5274746, 2777694, 3359337, 4493563, 561924, 7215951, 2907115,
+        4547697, 5403413, 7025806, 2453538, 4137455, 971005, 4298903, 1271923,
+        7150549, 2833306, 8021667, 7587207, 833119, 2919663, 5306176, 659188,
+        4708953, 3799478, 5584025, 7305039, 5709959, 3562365, 965949, 15873
+
+    };
+
+    public static final int[] ZETA_INVERSE_III_SPEED = {    /* 1024-Entry */
+
+        7439044, 4842628, 2695034, 1099954, 2820968, 4605515, 3696040, 7745805,
+        3098817, 5485330, 7571874, 817786, 383326, 5571687, 1254444, 7133070,
+        4106090, 7433988, 4267538, 5951455, 1379187, 3001580, 3857296, 5497878,
+        1189042, 7843069, 3911430, 5045656, 5627299, 3130247, 5008132, 5146123,
+        5549517, 7369693, 2364566, 6449142, 2075216, 783322, 579598, 2128858,
+        3563598, 7363844, 2875275, 5806017, 1102274, 7954309, 7477727, 492896,
+        4257417, 3514847, 3610961, 3295911, 7244373, 8276430, 175053, 6788002,
+        3467347, 3554424, 3148535, 4239032, 6701676, 3624332, 562243, 447016,
+        6632837, 6479895, 3029056, 2739263, 6412215, 930547, 6379647, 7681726,
+        7927519, 6094062, 7537210, 614246, 8212613, 396977, 2439526, 1291136,
+        6634092, 3165420, 2385323, 5408135, 987922, 4567189, 3701481, 6234672,
+        5213862, 2810181, 4324170, 8278063, 5826833, 3060422, 5823982, 5265680,
+        3934409, 4632601, 7803242, 3503130, 1653802, 970219, 5916111, 4725225,
+        1425827, 2617861, 3474701, 1176781, 5474042, 1631936, 4377623, 3797804,
+        3797776, 3565542, 3460998, 8099834, 516833, 3107480, 1735202, 1172737,
+        5321398, 4240793, 2649112, 7322696, 6134699, 7944613, 6706281, 7535747,
+        7122191, 7820870, 5940280, 7129874, 4850310, 7600431, 2765151, 4914981,
+        741064, 3344641, 7811524, 2023227, 3796096, 1070841, 1643846, 3656524,
+        7435584, 5408120, 953755, 4156103, 1534869, 71103, 2708162, 5105059,
+        1371636, 5732073, 6015981, 985970, 4650319, 7336843, 94184, 2135876,
+        5400949, 2763288, 7526286, 2943770, 5307758, 3797966, 302939, 4066747,
+        4501441, 4821622, 6921785, 7916611, 7942992, 6752297, 2500462, 2564508,
+        3079898, 5623501, 6399823, 5462781, 7821718, 3262460, 7887008, 705149,
+        7959302, 6807331, 530329, 7178758, 1591397, 5485203, 53101, 5323176,
+        2512902, 2426119, 6599791, 4146297, 6239102, 5728502, 3403497, 3447197,
+        1782754, 222143, 2270483, 3577719, 6335352, 4718575, 1047919, 2359962,
+        2191960, 398070, 1099651, 7916600, 7397136, 7630342, 5954609, 8130549,
+        3002546, 6148886, 8223739, 5001352, 6169629, 4130526, 5264599, 3962564,
+        179729, 8268341, 3808511, 6523672, 6885354, 6184831, 7361382, 7459197,
+        1361186, 6273979, 6319758, 863870, 5648672, 3844463, 5952811, 87433,
+        2425221, 6689880, 133179, 5333654, 3193866, 7479833, 635299, 5909671,
+        4695411, 1832759, 3800637, 7528789, 6559635, 3411990, 6139868, 6750004,
+        3829137, 4522683, 240186, 4042582, 360726, 228781, 423451, 8196111,
+        5145324, 2225402, 3194226, 2611202, 5016904, 3343217, 5173954, 3764924,
+        5491563, 4851045, 2596688, 3560155, 6579191, 5375413, 5759591, 3439556,
+        718444, 2474711, 7359136, 431826, 5056302, 7184453, 3201845, 8206571,
+        1640117, 2186595, 2320551, 8104271, 7514108, 5002123, 2158565, 5515962,
+        5262231, 5729362, 6932393, 3977168, 4743984, 4514547, 2547089, 6769222,
+        6470941, 573180, 8343358, 405268, 1396694, 5989775, 6543881, 1721121,
+        2008169, 7980898, 2743375, 3557307, 7372901, 6538525, 4555851, 6520278,
+        3392636, 6815834, 7508867, 3808461, 4323540, 6291926, 2128749, 3626138,
+        3878222, 3642193, 6104336, 634789, 6034810, 4166205, 4405350, 7253359,
+        4967596, 5348430, 5632498, 3631167, 3386335, 7350096, 7017222, 5504311,
+        2434886, 5207429, 4183407, 6331318, 7430129, 6688339, 5459601, 7188942,
+        5155043, 432906, 2019724, 4051865, 7908035, 6518910, 6554509, 1713695,
+        8217214, 1840990, 1492624, 902527, 5304851, 5079568, 7004466, 4032781,
+        2256369, 4922510, 2646412, 6014960, 7709559, 3627110, 6722937, 2491236,
+        6021309, 1213171, 5731617, 3114298, 7457710, 4133651, 6555190, 5741732,
+        3975558, 132469, 4202989, 7522868, 802270, 3067788, 4853180, 4507843,
+        4007809, 3762010, 5146737, 3918383, 4936558, 6116665, 637591, 7692193,
+        5246613, 3880231, 4207786, 3794684, 5288596, 1128843, 5832187, 2515750,
+        4202115, 6315083, 4070018, 4419223, 5456260, 1312210, 593112, 493459,
+        6400191, 5528954, 7062162, 7098867, 2767031, 4704887, 5631533, 922441,
+        1355335, 7026197, 1511997, 3449714, 6954854, 3425288, 2887915, 2962833,
+        4374374, 4241360, 2200119, 2892989, 2301704, 2225720, 8056559, 793879,
+        7900705, 6890979, 6630857, 3839890, 7481493, 3773811, 8105428, 1153569,
+        852334, 5126938, 6893353, 2438593, 1989018, 7894634, 5963142, 5987751,
+        4984859, 709984, 4533071, 7814184, 6380350, 65495, 2331319, 2000992,
+        3334656, 5069059, 4030679, 7554691, 8229987, 1191489, 5542586, 5917818,
+        7826676, 2189787, 2283255, 6513864, 4613528, 2068701, 1043841, 8341523,
+        624937, 1899470, 6117748, 3320583, 5184870, 4905589, 6825433, 4842738,
+        4013513, 585195, 6433500, 6196870, 5424348, 872660, 7940747, 4544273,
+        986252, 8240993, 5897523, 3118583, 5384067, 2879474, 3514536, 1419413,
+        2539713, 3981147, 1259190, 1362986, 2375511, 4465989, 3999187, 5637568,
+        2038254, 3501150, 6867576, 8332507, 5693089, 6418438, 7728445, 4145355,
+        369891, 1305821, 5533811, 1667676, 743981, 8231544, 5123191, 3384414,
+        5703977, 7097275, 3335715, 6926781, 4297589, 5713769, 7999321, 2880553,
+        2496460, 4996749, 2663724, 2184713, 2717045, 330118, 6317297, 8301019,
+        2510081, 6326906, 7470820, 5440440, 242265, 4713339, 5090410, 618835,
+        7654831, 7489827, 518527, 5833095, 3058734, 3553648, 3599970, 2118920,
+        182009, 2648657, 2672506, 6550312, 2388626, 4654534, 5777500, 2251862,
+        1721988, 2953440, 5304510, 655876, 1816637, 1952540, 5940035, 3088879,
+        1869252, 1661128, 8017850, 5915484, 7408043, 7421849, 7505949, 2507204,
+        1450868, 4996594, 2223167, 7220150, 5543700, 3879849, 1115673, 6823543,
+        6243983, 3385802, 5561118, 6600804, 1750818, 1788167, 1667451, 2762204,
+        2706191, 3814212, 5863902, 3308150, 4075480, 3254461, 7980470, 2939593,
+        5057198, 5152963, 3050327, 7774672, 4028967, 2130760, 3857675, 8146910,
+        585619, 5381309, 6575437, 1091711, 272356, 3934694, 4898533, 4509369,
+        2442787, 8249786, 8006624, 6866003, 7979077, 5178629, 2308072, 5197973,
+        5248583, 351731, 4737662, 4702438, 7891330, 2350229, 1293353, 7711663,
+        3120332, 1337394, 1625961, 2838644, 7348152, 7775285, 279752, 2249793,
+        4050665, 5804053, 751271, 8400434, 4522762, 7079584, 445844, 7723362,
+        7222547, 1291210, 5704529, 2994038, 5015691, 7350434, 820120, 3308383,
+        5916742, 3125716, 3733969, 565411, 1677622, 2421365, 462185, 5888279,
+        5741854, 1460627, 2840289, 291455, 7127083, 8061029, 5788420, 5859929,
+        7244359, 8045516, 4642573, 3248894, 4582291, 1956085, 1917302, 5322335,
+        3456052, 8169720, 537139, 5434163, 5090355, 4514526, 7855751, 5065509,
+        6452307, 4211387, 3877434, 5053593, 8272995, 4539885, 6674715, 1996652,
+        3925925, 7583598, 307623, 2076897, 1930272, 4319984, 4378036, 2204774,
+        7072679, 1658483, 1855602, 3454789, 3586812, 4823279, 4690584, 3865414,
+        1590476, 4702920, 6981620, 6639519, 1992876, 685851, 4372678, 282044,
+        1365911, 157524, 3025505, 1803814, 5619524, 2166116, 3002200, 2843438,
+        6291981, 185762, 483726, 968470, 1193235, 1356875, 2283077, 5979384,
+        7898173, 8354211, 4132292, 7349250, 4247182, 6402219, 6148479, 2291587,
+        140312, 5314435, 8241543, 2081803, 1637251, 7746585, 1942193, 7628416,
+        1383092, 3772288, 7680855, 6870609, 3496338, 5044662, 6969947, 5020137,
+        1512862, 3308341, 1157772, 8386869, 5310688, 4096436, 7874754, 6312822,
+        3962080, 7164500, 2992612, 5595916, 6563454, 1563513, 7388932, 6794626,
+        3269617, 6963475, 1802486, 1486345, 1197456, 3737502, 124677, 5586335,
+        7141242, 5057035, 4613538, 8237206, 6264363, 929464, 6392301, 5069456,
+        6788671, 4855771, 7407517, 2348220, 3358983, 4076171, 3687854, 1203412,
+        4733261, 553630, 5917576, 4874460, 1656702, 7978153, 1448344, 2989637,
+        4792174, 2079901, 2240082, 6447548, 6261559, 2709220, 637771, 1055381,
+        6481233, 3428811, 6828064, 1410258, 7758354, 364828, 5469370, 2618999,
+        5442471, 5616078, 7371291, 5198394, 1390772, 768076, 5181416, 767191,
+        5955428, 4829032, 6835205, 6325843, 2410216, 542924, 4715563, 1619388,
+        6674521, 8402550, 4009857, 6322884, 6864826, 1397225, 926883, 2795663,
+        3258226, 2791081, 1008394, 26146, 3845257, 5046024, 6069297, 249619,
+        2660552, 3898296, 6208551, 120686, 658825, 2511623, 3388904, 7697850,
+        4725190, 1911597, 1110005, 6991997, 6898909, 422827, 810385, 3636264,
+        2369793, 7411890, 7179944, 8224966, 1742730, 5267165, 3485439, 1782966,
+        1940330, 5717479, 3089924, 4091766, 1291531, 1674121, 7423669, 5800791,
+        3715897, 6268886, 628481, 2719462, 7970063, 588595, 642989, 668971,
+        1233813, 1847310, 3188252, 7338242, 2026470, 4853803, 3006831, 5982980,
+        3507385, 7145309, 5871052, 1167900, 4885754, 1457767, 4200810, 4002012,
+        3361544, 7894114, 7738690, 7426488, 7429074, 7298019, 2317284, 5849511,
+        2482952, 1928258, 6223751, 4681091, 4556084, 3159067, 4229025, 5900863,
+        508488, 3548270, 6317016, 7268385, 3699255, 3139318, 478464, 7833430,
+        4643515, 4377548, 2820710, 5152953, 7402753, 5419988, 2584559, 6426707,
+        5981725, 307644, 7022256, 882318, 4190480, 2921041, 7894907, 2513805,
+        4821759, 159644, 7424415, 3696649, 849279, 7569589, 7991564, 4614738,
+        7439691, 4707734, 5155900, 3761722, 4759464, 5288239, 3681574, 2084045,
+        4283038, 2252171, 8189868, 2268168, 1893902, 2393616, 7864299, 7972972,
+        1203290, 5106315, 3153004, 8308709, 5277916, 2680015, 6287425, 5487539,
+        2013603, 6354308, 4938809, 3620799, 6181358, 6673564, 3862763, 3214872,
+        1976439, 2138237, 2443208, 3186567, 5514086, 518931, 2784062, 8246999,
+        4568858, 7328913, 1956312, 5165381, 3679485, 3649776, 707318, 6275329,
+        3376145, 173272, 2399134, 6269985, 4201138, 4609144, 3653638, 8389120
+
+    };
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialProvablySecure.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialProvablySecure.java
new file mode 100644
index 0000000..100812c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/PolynomialProvablySecure.java
@@ -0,0 +1,798 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+final class PolynomialProvablySecure
+{
+
+    /* Provably-Secure qTESLA Security Category-1 */
+
+    public static final long[] ZETA_I_P = {                    /* 1024-Entry */
+
+        152487987, 362708180, 151084668, 393585500, 285507339, 301982154, 215753424, 31155376,
+        233716852, 465705615, 128512487, 219324660, 50229578, 153034376, 215476824, 300536066,
+        335024578, 289187646, 164713195, 268061318, 189759369, 377168836, 116435874, 176113053,
+        431517196, 190900734, 338220498, 464878555, 173361289, 232250039, 454096337, 436656394,
+        195787662, 168565616, 114027909, 101482211, 122304260, 245339257, 270992315, 4286310,
+        284182633, 281862863, 467931137, 7603533, 87296419, 467325730, 121905925, 68965750,
+        7622021, 337743349, 393551614, 47124528, 159423746, 411556895, 272161997, 437233591,
+        145848369, 293644567, 483816172, 151632492, 404105953, 50166550, 146739314, 412145078,
+        15864543, 70750806, 129965017, 341088149, 316683907, 350635104, 130053135, 163148141,
+        121505175, 321911633, 208679484, 294431751, 453314132, 108343178, 347335911, 164705047,
+        162721550, 207020617, 292545544, 470584726, 172724538, 305647837, 149867383, 362637208,
+        452440748, 260522302, 286994319, 24740874, 202044444, 119470626, 478104674, 80911411,
+        414592412, 2221974, 246379318, 129955789, 28799678, 36003616, 152368173, 339611023,
+        223510693, 291221765, 193132933, 463217793, 331552134, 325636488, 203402489, 295759306,
+        248357734, 68229047, 81897305, 240159122, 118053748, 317468757, 422053445, 366729953,
+        437649988, 255661703, 483132783, 73941682, 473070031, 266941830, 273715645, 293305752,
+        97728261, 387416830, 466510576, 359483415, 350097663, 303812937, 61983368, 110765849,
+        174098312, 288799426, 21108638, 467492225, 174686783, 248408233, 45473451, 384747267,
+        405224388, 88952814, 320950556, 7845265, 96262921, 194785009, 345346400, 283984375,
+        141922183, 197095618, 350922677, 11272999, 89297173, 32109806, 274225692, 315879286,
+        447761615, 162801357, 472516440, 273754932, 433704551, 12277378, 129604129, 130765873,
+        341928374, 97220107, 381055612, 81197393, 281134974, 179347952, 39761035, 34072176,
+        156107747, 160521566, 445615052, 382999431, 114761104, 217307868, 361522659, 154828064,
+        108616610, 483961979, 7594235, 235284403, 243224032, 389934638, 100293270, 300649913,
+        252603238, 156952794, 476122165, 413580514, 276718826, 361292064, 361798877, 469444666,
+        478187612, 366087489, 58364362, 377641792, 464279854, 238407425, 414624898, 95101086,
+        321027398, 429694956, 464583265, 437187651, 60881585, 385486792, 312091683, 66834498,
+        233738788, 348037644, 233604363, 107253653, 131016429, 73947604, 169670982, 290195637,
+        119921194, 271135954, 190934119, 290478001, 458387655, 30555018, 243654544, 445824897,
+        428159318, 284210623, 380070942, 43667456, 346591135, 469657107, 229110312, 89859525,
+        279253247, 439931225, 229136222, 321034403, 246424219, 100693825, 214223676, 217294437,
+        256157960, 295265349, 79251464, 286824915, 454734790, 151846826, 92265815, 248302338,
+        324806913, 107169154, 198491413, 107136390, 135693521, 363861623, 129717151, 385117374,
+        341459776, 364530058, 331840615, 338653108, 179941421, 145194323, 232142444, 310117875,
+        245100240, 236651455, 321450096, 408132892, 459643284, 120999848, 331994388, 466033192,
+        444350281, 46244984, 349366459, 47312667, 210852290, 461907423, 473508756, 243941159,
+        334095768, 484392307, 87495054, 79707111, 10325325, 200821060, 83579595, 150963302,
+        117650353, 241599951, 324099496, 449053341, 36312493, 53120594, 297622605, 80558972,
+        378548649, 444726854, 381040843, 435710377, 437798831, 409728224, 337424109, 119488440,
+        361132895, 56096923, 105509808, 62508901, 220866000, 465933834, 134955292, 165152430,
+        461194453, 94958388, 126965781, 461323542, 383847206, 317476902, 163556720, 256017938,
+        72928042, 371345649, 72741296, 1132229, 282268470, 212115745, 379618538, 106905134,
+        239955357, 192105800, 279633365, 261297633, 471571564, 90968104, 459175544, 256276478,
+        349475049, 94611089, 471946846, 339973147, 179509745, 334215655, 348294589, 246487412,
+        338690430, 257410736, 386561992, 130193167, 10375815, 352823358, 66528931, 326800610,
+        5109748, 350056529, 248785466, 71808467, 91140754, 240803382, 46545554, 148468313,
+        217897756, 178220810, 477609665, 208790805, 132277867, 140742422, 254913327, 28667006,
+        128372915, 79301382, 407186666, 89552874, 246186501, 285455129, 391678316, 275180688,
+        128264018, 310322712, 204235493, 392483801, 82342680, 216812163, 464479351, 180360499,
+        444647478, 203271968, 245690827, 184373912, 109600517, 224566820, 28243984, 271488557,
+        21295878, 5023179, 37276402, 156173856, 216282463, 114627758, 400597776, 384792625,
+        105917732, 345779139, 120948814, 379908150, 352765486, 416168777, 56042296, 349589484,
+        144728793, 388255614, 261690821, 484844902, 12918385, 60552278, 302475143, 407425208,
+        184843522, 76793749, 43262192, 318572606, 192724017, 307990251, 164548393, 289980843,
+        119689917, 192248504, 86870040, 113677230, 207612114, 48663937, 453434248, 377339887,
+        132917063, 336896801, 106411814, 268777744, 109251604, 359574042, 467613823, 362251552,
+        400195434, 397227950, 447715984, 156476326, 407995653, 323955098, 143648649, 6572585,
+        61447842, 60823848, 132376333, 156552612, 447237682, 3352830, 437079412, 293474358,
+        288464703, 15328503, 138579659, 221291483, 380584037, 357161755, 205687364, 479414291,
+        173269786, 295661628, 1098184, 377981714, 433970464, 385767379, 200391723, 463310671,
+        470707303, 280545651, 289143978, 1390137, 320221838, 213802405, 52847233, 440579585,
+        361337171, 467447061, 278099177, 130391690, 112618414, 24013500, 144924227, 296806074,
+        251842387, 345992943, 284162718, 360902180, 420467337, 110656360, 350012131, 202820082,
+        130403434, 353559391, 97646151, 331494212, 207981153, 178821640, 6093504, 174655523,
+        25199097, 350851018, 171708038, 460816830, 103136904, 80524000, 275742304, 458552752,
+        472541551, 190172218, 362093141, 38733526, 9645818, 67304688, 67935446, 108377230,
+        311977539, 474309436, 444125532, 103441699, 3468252, 349723698, 97903160, 300076184,
+        267658035, 238838767, 242177661, 26606481, 135193080, 123327558, 344360834, 367595829,
+        356606282, 264791463, 257139528, 162368489, 211890949, 55564884, 438861649, 229441471,
+        237272101, 233185135, 277657399, 407149255, 91300697, 479685681, 430422637, 119519807,
+        421727840, 192635510, 93585631, 135381498, 18652441, 397908973, 188581729, 232327608,
+        69565403, 391762415, 326530182, 441347692, 230134217, 64984929, 478944650, 351581551,
+        190325639, 193670224, 149376075, 211003745, 282313407, 401282565, 142157967, 358980062,
+        420121767, 23490157, 334750456, 333888004, 267502025, 165819942, 232587558, 86202250,
+        302042598, 410443459, 257125477, 225677323, 414705770, 25685059, 301886182, 108044670,
+        22721383, 270234733, 79875762, 146613845, 419895311, 369960062, 103044731, 31676484,
+        286660070, 419993402, 364984450, 18133974, 446533052, 445715499, 406972835, 163019061,
+        450202465, 241240654, 188881609, 340298195, 253414624, 384263117, 228275432, 255596801,
+        10999354, 326104131, 470162945, 345212724, 481147036, 355238387, 445490028, 455415009,
+        260881193, 427862585, 132654052, 18007865, 59818933, 68142229, 461931681, 213097607,
+        330845761, 345671100, 445951421, 379951938, 351197187, 411141392, 7263182, 70699592,
+        365421264, 376472585, 461484665, 162243510, 31855741, 293625046, 335452299, 133496651,
+        449999480, 91675672, 274948339, 231546888, 5898807, 374453904, 415664585, 187816557,
+        366875998, 16795888, 358324214, 67223590, 208260807, 81751761, 331910299, 126163909,
+        197637544, 425142827, 380092754, 432163910, 455820426, 254980283, 256358636, 238549963,
+        394492520, 207988901, 382035000, 58514991, 406817321, 338951326, 85955701, 385883363,
+        199611595, 193846573, 242046917, 445871847, 194675932, 153430125, 126399473, 212399985,
+        131399120, 150281168, 241101682, 424624124, 463829369, 220399539, 250758923, 286498206,
+        389690457, 295269249, 379701698, 289778634, 337591505, 299401947, 408084588, 222627582,
+        436090373, 268654172, 96674628, 63939283, 63956567, 362372935, 443759942, 75655989,
+        451148002, 74813563, 245300859, 92113977, 235932323, 24871709, 445930061, 112311252,
+        382591591, 341672267, 192081751, 227249338, 104393113, 338268986, 206994950, 319724787,
+        213198392, 170478760, 37605185, 158287930, 411833228, 74675173, 290411522, 234950252,
+        147659191, 477676082, 302215477, 236037294, 396241956, 90037635, 71703941, 189694446,
+        127714437, 72581761, 388695298, 324058182, 307942478, 25250803, 430776557, 98441916,
+        244856801, 54851167, 334050668, 381066122, 91898087, 55879503, 447270860, 473167653,
+        219844498, 187193519, 266952433, 391729403, 53226126, 51780224, 371399487, 333787358,
+        56263300, 280390944, 162069765, 126088217, 254997184, 219544659, 290699753, 91930445,
+        262038894, 420981566, 460776098, 376255854, 282871085, 189434060, 447641051, 348987773,
+        393608679, 229566111, 403901447, 471446519, 411894730, 215726740, 484920069, 409829517,
+        306172231, 65880808, 272668213, 300554215, 165356547, 127919123, 432943738, 184462319,
+        474779995, 312870826, 103228518, 118983222, 470656522, 71417412, 480528917, 63072121,
+        276207594, 101611809, 92202812, 400978338, 316767039, 228776701, 96935521, 229917830,
+        205039402, 397476586, 344074695, 398615665, 107334948, 321477097, 84224896, 289704056,
+        337221600, 298175575, 221915312, 193369885, 426129039, 447853316, 54842415, 451086232,
+        182463979, 451831604, 247440983, 302608727, 173057620, 189163830, 406954853, 263838955,
+        390119120, 323062166, 209857219, 233847360, 397630721, 465926004, 10245195, 426687348,
+        106435173, 177694955, 463447907, 18002511, 30155910, 224512318, 357311179, 265219392,
+        299513685, 30291602, 60997329, 10326191, 138035293, 231111502, 414088384, 77026622,
+        174972782, 4116187, 52092276, 228872080, 15308807, 286405235, 414584533, 228392331,
+        399460999, 219125940, 337293712, 187192073, 183965759, 229399789, 51179285, 419512139,
+        28392143, 424986565, 69672656, 202415584, 216120556, 455470549, 76078626, 278191677,
+        228751917, 246802857, 389799018, 342470047, 313959017, 69383448, 340686475, 61470385,
+        26192776, 409901437, 119717553, 174793260, 123425866, 250300643, 429290586, 387248439,
+        183926914, 285901994, 391555377, 468207735, 22116415, 13034202, 399447558, 317569778,
+        269660832, 44359608, 57347786, 225367797, 253610604, 205790505, 20120029, 285335246,
+        351522795, 256987219, 358102627, 167191360, 50532216, 327731504, 369248325, 460123994,
+        266415358, 194297404, 175124754, 431803266, 172719791, 347394909, 478670700, 198769524,
+        172061925, 145181173, 153659180, 261113394, 234615141, 174150073, 188625016, 395363348,
+        356478592, 350375496, 185431871, 422914531, 144616554, 328948119, 417580486, 132620154,
+        208514724, 323125199, 169409944, 209437217, 147304756, 313577692, 467533131, 102365927,
+        353952516, 47854485, 378899191, 136952166, 46435149, 470355414, 328058320, 292689847,
+        24707084, 114822297, 272180086, 182640159, 7628028, 408073454, 377046674, 214221064,
+        472655903, 91461227, 453378209, 459785635, 126296748, 7369666, 52466342, 269961618,
+        231883972, 233439994, 228009066, 436059177, 355741338, 103186287, 391072528, 386169816,
+        317017329, 449528664, 378844336, 330396197, 137599363, 20911303, 288139426, 164788880,
+        266741184, 89618721, 54677577, 457226242, 208474107, 240521207, 158524358, 480614438,
+        226941889, 385643536, 326188597, 389903564, 264886102, 226747931, 258540220, 407142392
+
+    };
+
+    public static final long[] ZETA_INVERSE_I_P = {            /* 1024-Entry */
+
+        227437893, 259230182, 221092011, 96074549, 159789516, 100334577, 259036224, 5363675,
+        327453755, 245456906, 277504006, 28751871, 431300536, 396359392, 219236929, 321189233,
+        197838687, 465066810, 348378750, 155581916, 107133777, 36449449, 168960784, 99808297,
+        94905585, 382791826, 130236775, 49918936, 257969047, 252538119, 254094141, 216016495,
+        433511771, 478608447, 359681365, 26192478, 32599904, 394516886, 13322210, 271757049,
+        108931439, 77904659, 478350085, 303337954, 213798027, 371155816, 461271029, 193288266,
+        157919793, 15622699, 439542964, 349025947, 107078922, 438123628, 132025597, 383612186,
+        18444982, 172400421, 338673357, 276540896, 316568169, 162852914, 277463389, 353357959,
+        68397627, 157029994, 341361559, 63063582, 300546242, 135602617, 129499521, 90614765,
+        297353097, 311828040, 251362972, 224864719, 332318933, 340796940, 313916188, 287208589,
+        7307413, 138583204, 313258322, 54174847, 310853359, 291680709, 219562755, 25854119,
+        116729788, 158246609, 435445897, 318786753, 127875486, 228990894, 134455318, 200642867,
+        465858084, 280187608, 232367509, 260610316, 428630327, 441618505, 216317281, 168408335,
+        86530555, 472943911, 463861698, 17770378, 94422736, 200076119, 302051199, 98729674,
+        56687527, 235677470, 362552247, 311184853, 366260560, 76076676, 459785337, 424507728,
+        145291638, 416594665, 172019096, 143508066, 96179095, 239175256, 257226196, 207786436,
+        409899487, 30507564, 269857557, 283562529, 416305457, 60991548, 457585970, 66465974,
+        434798828, 256578324, 302012354, 298786040, 148684401, 266852173, 86517114, 257585782,
+        71393580, 199572878, 470669306, 257106033, 433885837, 481861926, 311005331, 408951491,
+        71889729, 254866611, 347942820, 475651922, 424980784, 455686511, 186464428, 220758721,
+        128666934, 261465795, 455822203, 467975602, 22530206, 308283158, 379542940, 59290765,
+        475732918, 20052109, 88347392, 252130753, 276120894, 162915947, 95858993, 222139158,
+        79023260, 296814283, 312920493, 183369386, 238537130, 34146509, 303514134, 34891881,
+        431135698, 38124797, 59849074, 292608228, 264062801, 187802538, 148756513, 196274057,
+        401753217, 164501016, 378643165, 87362448, 141903418, 88501527, 280938711, 256060283,
+        389042592, 257201412, 169211074, 84999775, 393775301, 384366304, 209770519, 422905992,
+        5449196, 414560701, 15321591, 366994891, 382749595, 173107287, 11198118, 301515794,
+        53034375, 358058990, 320621566, 185423898, 213309900, 420097305, 179805882, 76148596,
+        1058044, 270251373, 74083383, 14531594, 82076666, 256412002, 92369434, 136990340,
+        38337062, 296544053, 203107028, 109722259, 25202015, 64996547, 223939219, 394047668,
+        195278360, 266433454, 230980929, 359889896, 323908348, 205587169, 429714813, 152190755,
+        114578626, 434197889, 432751987, 94248710, 219025680, 298784594, 266133615, 12810460,
+        38707253, 430098610, 394080026, 104911991, 151927445, 431126946, 241121312, 387536197,
+        55201556, 460727310, 178035635, 161919931, 97282815, 413396352, 358263676, 296283667,
+        414274172, 395940478, 89736157, 249940819, 183762636, 8302031, 338318922, 251027861,
+        195566591, 411302940, 74144885, 327690183, 448372928, 315499353, 272779721, 166253326,
+        278983163, 147709127, 381585000, 258728775, 293896362, 144305846, 103386522, 373666861,
+        40048052, 461106404, 250045790, 393864136, 240677254, 411164550, 34830111, 410322124,
+        42218171, 123605178, 422021546, 422038830, 389303485, 217323941, 49887740, 263350531,
+        77893525, 186576166, 148386608, 196199479, 106276415, 190708864, 96287656, 199479907,
+        235219190, 265578574, 22148744, 61353989, 244876431, 335696945, 354578993, 273578128,
+        359578640, 332547988, 291302181, 40106266, 243931196, 292131540, 286366518, 100094750,
+        400022412, 147026787, 79160792, 427463122, 103943113, 277989212, 91485593, 247428150,
+        229619477, 230997830, 30157687, 53814203, 105885359, 60835286, 288340569, 359814204,
+        154067814, 404226352, 277717306, 418754523, 127653899, 469182225, 119102115, 298161556,
+        70313528, 111524209, 480079306, 254431225, 211029774, 394302441, 35978633, 352481462,
+        150525814, 192353067, 454122372, 323734603, 24493448, 109505528, 120556849, 415278521,
+        478714931, 74836721, 134780926, 106026175, 40026692, 140307013, 155132352, 272880506,
+        24046432, 417835884, 426159180, 467970248, 353324061, 58115528, 225096920, 30563104,
+        40488085, 130739726, 4831077, 140765389, 15815168, 159873982, 474978759, 230381312,
+        257702681, 101714996, 232563489, 145679918, 297096504, 244737459, 35775648, 322959052,
+        79005278, 40262614, 39445061, 467844139, 120993663, 65984711, 199318043, 454301629,
+        382933382, 116018051, 66082802, 339364268, 406102351, 215743380, 463256730, 377933443,
+        184091931, 460293054, 71272343, 260300790, 228852636, 75534654, 183935515, 399775863,
+        253390555, 320158171, 218476088, 152090109, 151227657, 462487956, 65856346, 126998051,
+        343820146, 84695548, 203664706, 274974368, 336602038, 292307889, 295652474, 134396562,
+        7033463, 420993184, 255843896, 44630421, 159447931, 94215698, 416412710, 253650505,
+        297396384, 88069140, 467325672, 350596615, 392392482, 293342603, 64250273, 366458306,
+        55555476, 6292432, 394677416, 78828858, 208320714, 252792978, 248706012, 256536642,
+        47116464, 430413229, 274087164, 323609624, 228838585, 221186650, 129371831, 118382284,
+        141617279, 362650555, 350785033, 459371632, 243800452, 247139346, 218320078, 185901929,
+        388074953, 136254415, 482509861, 382536414, 41852581, 11668677, 174000574, 377600883,
+        418042667, 418673425, 476332295, 447244587, 123884972, 295805895, 13436562, 27425361,
+        210235809, 405454113, 382841209, 25161283, 314270075, 135127095, 460779016, 311322590,
+        479884609, 307156473, 277996960, 154483901, 388331962, 132418722, 355574679, 283158031,
+        135965982, 375321753, 65510776, 125075933, 201815395, 139985170, 234135726, 189172039,
+        341053886, 461964613, 373359699, 355586423, 207878936, 18531052, 124640942, 45398528,
+        433130880, 272175708, 165756275, 484587976, 196834135, 205432462, 15270810, 22667442,
+        285586390, 100210734, 52007649, 107996399, 484879929, 190316485, 312708327, 6563822,
+        280290749, 128816358, 105394076, 264686630, 347398454, 470649610, 197513410, 192503755,
+        48898701, 482625283, 38740431, 329425501, 353601780, 425154265, 424530271, 479405528,
+        342329464, 162023015, 77982460, 329501787, 38262129, 88750163, 85782679, 123726561,
+        18364290, 126404071, 376726509, 217200369, 379566299, 149081312, 353061050, 108638226,
+        32543865, 437314176, 278365999, 372300883, 399108073, 293729609, 366288196, 195997270,
+        321429720, 177987862, 293254096, 167405507, 442715921, 409184364, 301134591, 78552905,
+        183502970, 425425835, 473059728, 1133211, 224287292, 97722499, 341249320, 136388629,
+        429935817, 69809336, 133212627, 106069963, 365029299, 140198974, 380060381, 101185488,
+        85380337, 371350355, 269695650, 329804257, 448701711, 480954934, 464682235, 214489556,
+        457734129, 261411293, 376377596, 301604201, 240287286, 282706145, 41330635, 305617614,
+        21498762, 269165950, 403635433, 93494312, 281742620, 175655401, 357714095, 210797425,
+        94299797, 200522984, 239791612, 396425239, 78791447, 406676731, 357605198, 457311107,
+        231064786, 345235691, 353700246, 277187308, 8368448, 307757303, 268080357, 337509800,
+        439432559, 245174731, 394837359, 414169646, 237192647, 135921584, 480868365, 159177503,
+        419449182, 133154755, 475602298, 355784946, 99416121, 228567377, 147287683, 239490701,
+        137683524, 151762458, 306468368, 146004966, 14031267, 391367024, 136503064, 229701635,
+        26802569, 395010009, 14406549, 224680480, 206344748, 293872313, 246022756, 379072979,
+        106359575, 273862368, 203709643, 484845884, 413236817, 114632464, 413050071, 229960175,
+        322421393, 168501211, 102130907, 24654571, 359012332, 391019725, 24783660, 320825683,
+        351022821, 20044279, 265112113, 423469212, 380468305, 429881190, 124845218, 366489673,
+        148554004, 76249889, 48179282, 50267736, 104937270, 41251259, 107429464, 405419141,
+        188355508, 432857519, 449665620, 36924772, 161878617, 244378162, 368327760, 335014811,
+        402398518, 285157053, 475652788, 406271002, 398483059, 1585806, 151882345, 242036954,
+        12469357, 24070690, 275125823, 438665446, 136611654, 439733129, 41627832, 19944921,
+        153983725, 364978265, 26334829, 77845221, 164528017, 249326658, 240877873, 175860238,
+        253835669, 340783790, 306036692, 147325005, 154137498, 121448055, 144518337, 100860739,
+        356260962, 122116490, 350284592, 378841723, 287486700, 378808959, 161171200, 237675775,
+        393712298, 334131287, 31243323, 199153198, 406726649, 190712764, 229820153, 268683676,
+        271754437, 385284288, 239553894, 164943710, 256841891, 46046888, 206724866, 396118588,
+        256867801, 16321006, 139386978, 442310657, 105907171, 201767490, 57818795, 40153216,
+        242323569, 455423095, 27590458, 195500112, 295043994, 214842159, 366056919, 195782476,
+        316307131, 412030509, 354961684, 378724460, 252373750, 137940469, 252239325, 419143615,
+        173886430, 100491321, 425096528, 48790462, 21394848, 56283157, 164950715, 390877027,
+        71353215, 247570688, 21698259, 108336321, 427613751, 119890624, 7790501, 16533447,
+        124179236, 124686049, 209259287, 72397599, 9855948, 329025319, 233374875, 185328200,
+        385684843, 96043475, 242754081, 250693710, 478383878, 2016134, 377361503, 331150049,
+        124455454, 268670245, 371217009, 102978682, 40363061, 325456547, 329870366, 451905937,
+        446217078, 306630161, 204843139, 404780720, 104922501, 388758006, 144049739, 355212240,
+        356373984, 473700735, 52273562, 212223181, 13461673, 323176756, 38216498, 170098827,
+        211752421, 453868307, 396680940, 474705114, 135055436, 288882495, 344055930, 201993738,
+        140631713, 291193104, 389715192, 478132848, 165027557, 397025299, 80753725, 101230846,
+        440504662, 237569880, 311291330, 18485888, 464869475, 197178687, 311879801, 375212264,
+        423994745, 182165176, 135880450, 126494698, 19467537, 98561283, 388249852, 192672361,
+        212262468, 219036283, 12908082, 412036431, 2845330, 230316410, 48328125, 119248160,
+        63924668, 168509356, 367924365, 245818991, 404080808, 417749066, 237620379, 190218807,
+        282575624, 160341625, 154425979, 22760320, 292845180, 194756348, 262467420, 146367090,
+        333609940, 449974497, 457178435, 356022324, 239598795, 483756139, 71385701, 405066702,
+        7873439, 366507487, 283933669, 461237239, 198983794, 225455811, 33537365, 123340905,
+        336110730, 180330276, 313253575, 15393387, 193432569, 278957496, 323256563, 321273066,
+        138642202, 377634935, 32663981, 191546362, 277298629, 164066480, 364472938, 322829972,
+        355924978, 135343009, 169294206, 144889964, 356013096, 415227307, 470113570, 73833035,
+        339238799, 435811563, 81872160, 334345621, 2161941, 192333546, 340129744, 48744522,
+        213816116, 74421218, 326554367, 438853585, 92426499, 148234764, 478356092, 417012363,
+        364072188, 18652383, 398681694, 478374580, 18046976, 204115250, 201795480, 481691803,
+        214985798, 240638856, 363673853, 384495902, 371950204, 317412497, 290190451, 49321719,
+        31881776, 253728074, 312616824, 21099558, 147757615, 295077379, 54460917, 309865060,
+        369542239, 108809277, 296218744, 217916795, 321264918, 196790467, 150953535, 185442047,
+        270501289, 332943737, 435748535, 266653453, 357465626, 20272498, 252261261, 454822737,
+        270224689, 183995959, 200470774, 92392613, 334893445, 123269933, 333490126, 78835721
+
+    };
+
+    /* Provably-Secure qTESLA Security Category-3 */
+
+    public static final long[] ZETA_III_P = {                /* 2048-Entry */
+
+        663045521, 592864312, 129934065, 177469925, 343322489, 76003277, 514174562, 190066344,
+        383213768, 787962888, 658937726, 1080673671, 1119184179, 711485619, 895353292, 741830559,
+        449748398, 882352834, 210564246, 513517678, 792274530, 1106148000, 447255681, 838374925,
+        817124617, 494866507, 801735296, 793510817, 270693858, 301435475, 260117255, 10836133,
+        1090140236, 870237322, 519619509, 74028645, 223438873, 115296996, 731671863, 711767285,
+        756344742, 432270821, 921367017, 743782190, 756600516, 403820997, 571909291, 809934428,
+        12306094, 1080752961, 1103853276, 848006190, 651623616, 1066528111, 991262313, 90376233,
+        1031639319, 16581402, 195692585, 764838834, 443651394, 397689573, 855192181, 464522040,
+        992536780, 1065244154, 280383996, 468397118, 363648899, 30284833, 136693103, 866826704,
+        684264872, 369300261, 147123393, 425272346, 947975751, 200848998, 207227922, 949139594,
+        347812886, 312530409, 270384680, 1035127685, 844540200, 354908220, 399963784, 771423554,
+        168982346, 111911899, 891254450, 606094727, 997954525, 531583148, 617069803, 183958096,
+        133356782, 708062096, 808380329, 644973028, 1122831312, 519547216, 764125907, 320659143,
+        557529771, 1016802360, 1043273438, 1045802164, 1034628659, 450715977, 741225144, 605149405,
+        1012579499, 651882116, 904529967, 300080999, 629210435, 173034527, 989077200, 1078174711,
+        110168090, 256388598, 610617659, 881033840, 608872901, 50037154, 640116890, 769511414,
+        738741272, 92678917, 442704510, 821794500, 142894162, 370261366, 899737750, 578322223,
+        983445865, 278101088, 320548969, 960437988, 847057645, 535264585, 1031091774, 384531787,
+        839679409, 791760948, 937608754, 64047905, 315242491, 690211684, 304945377, 127139900,
+        55322681, 615105519, 1057531084, 720717234, 1061452791, 1095429367, 975341664, 483335114,
+        346985754, 486624908, 954236263, 606609353, 374493515, 1026296717, 325660087, 901013385,
+        53959974, 212660347, 959289131, 1056916487, 1125908681, 840084518, 527647955, 143827226,
+        1054554933, 861883427, 114992875, 332264810, 1071780030, 502142509, 205074888, 328230624,
+        728399298, 6590787, 412294778, 821072942, 186833297, 86543585, 380686974, 684478338,
+        685483832, 170880254, 1049509911, 299404976, 355091877, 952129776, 345588044, 357652764,
+        1076734604, 924340986, 262332782, 562093746, 674846863, 869200402, 167429450, 230595565,
+        379519977, 64054402, 667864494, 252591944, 174034622, 45520823, 784274107, 752467928,
+        436711302, 1004905853, 329220738, 721523796, 169192713, 551217408, 907929323, 194805106,
+        62131815, 68055725, 545662009, 726339771, 10612113, 878659984, 675814719, 365783377,
+        951164276, 866711073, 684096262, 413270603, 702997992, 778608769, 127639044, 47216120,
+        744109445, 430463358, 1117777586, 424051168, 656374939, 1103725866, 14621181, 919164722,
+        915751418, 976196867, 260519617, 802701264, 977088018, 632721351, 20944803, 435584923,
+        672284530, 530447301, 1080372403, 969633414, 858453527, 467086140, 377506201, 201043187,
+        624606245, 356437339, 392100995, 997346375, 344147086, 1086111956, 718030456, 110621087,
+        452912731, 336746546, 621211472, 795046, 794852591, 919732385, 714562897, 692647493,
+        521856561, 892141810, 929164476, 178245765, 949027329, 511109867, 501815810, 855826969,
+        240246307, 1090455392, 871247558, 612287417, 200668298, 658636844, 62944986, 482777398,
+        714192478, 867140496, 199314763, 780516102, 571032048, 817693014, 607138429, 912533516,
+        774019587, 835671643, 168024255, 449898784, 964159534, 486792883, 1119449268, 6657041,
+        86939725, 764245347, 273343215, 777897095, 930426166, 788914763, 469974947, 178163195,
+        519599146, 886003889, 859825363, 1024222142, 888997684, 402918616, 146815556, 742952999,
+        908078470, 687435577, 647696655, 693593765, 766931890, 944985585, 394729082, 816013317,
+        392951623, 65907852, 96202750, 645391372, 795224730, 402710723, 863608137, 818970673,
+        545310222, 970699745, 913314065, 344644471, 195858177, 414360679, 357670948, 683445481,
+        833938949, 1027656845, 191164920, 78809923, 1041045426, 73270196, 101525688, 185383095,
+        583325181, 265134328, 467801210, 1096264575, 367433146, 474798672, 41386419, 8344607,
+        170218721, 635545135, 668716283, 348890991, 155290659, 123249926, 769501318, 244415064,
+        1050203259, 859568787, 237251392, 995643980, 522636185, 829867059, 324850649, 618298124,
+        650748178, 467147593, 524674833, 341426504, 402629779, 519829789, 805279247, 494226863,
+        508315225, 319600904, 48615828, 165396124, 328305874, 183239019, 878118280, 161754975,
+        715041085, 746274821, 961841131, 357826469, 97008202, 885304034, 352059292, 197879468,
+        25678065, 1028070136, 447949253, 962673193, 813464159, 17237594, 674670906, 959137288,
+        10286769, 182827874, 439769615, 875041932, 304463252, 374894049, 749497333, 38938778,
+        95615605, 323023572, 918611876, 207681758, 859164726, 97118122, 769881035, 360249017,
+        507250246, 223795463, 24153203, 927058118, 178686381, 765072252, 491103160, 30816153,
+        100214484, 554568910, 715304135, 93510037, 1021905527, 1045723816, 900771572, 1011589617,
+        558930760, 228903927, 41675196, 857737037, 1092385029, 1067405169, 526576034, 25638952,
+        1017160549, 1052660585, 364103167, 228705333, 566586884, 1126549926, 148651837, 217709216,
+        614537001, 773273483, 1097591846, 480172084, 365704498, 262321659, 937989155, 1036392392,
+        811974084, 132591218, 799370207, 255702709, 215218285, 632314426, 58376649, 483086198,
+        401012915, 839724879, 70449719, 502867571, 757507513, 1073691794, 215293647, 725537396,
+        398067214, 584143318, 392011063, 554158763, 730533625, 632408477, 259767996, 1124249847,
+        432958856, 1026767843, 237644370, 29926518, 131813155, 898001983, 581765490, 215126607,
+        937229093, 1030360940, 452462053, 645808548, 645402879, 379809569, 577012489, 1082786079,
+        572510912, 475330285, 152788398, 690858296, 876393456, 1002942486, 67723832, 132689262,
+        1030380537, 529311082, 41121650, 626066149, 866026053, 240011600, 574505003, 565608154,
+        414049009, 123706126, 80739723, 326160835, 476194893, 1126100881, 646887456, 987296393,
+        1119394953, 994979769, 133560363, 725327130, 850262599, 132796686, 1114247361, 1040894619,
+        562914322, 641645573, 817070125, 57537779, 59054681, 878878988, 307674440, 1001013577,
+        594860688, 879718195, 318482613, 698792051, 756144110, 204900318, 1031928100, 926221256,
+        695955308, 662601363, 408725276, 424272249, 110402404, 535365123, 226700293, 372920839,
+        1118626221, 1070521569, 639548808, 621533107, 260391977, 316354318, 362523650, 733090366,
+        456807669, 113808372, 399369653, 647539514, 165899708, 312376469, 213806296, 208149196,
+        937880087, 289692684, 580756218, 963782721, 1086045881, 705999228, 1115084349, 508210869,
+        893248141, 1109740411, 338975294, 752813515, 622466015, 372202503, 55041759, 338412385,
+        29530795, 689413481, 974303137, 605274799, 812143197, 121492617, 758223932, 200993249,
+        575018581, 1039577172, 1090395670, 612284976, 414561447, 886969, 685649488, 667914411,
+        183143190, 272811635, 104938162, 64626548, 404300448, 333824065, 399917080, 105374124,
+        329534966, 930231830, 187994078, 772453163, 81595362, 135380677, 536704334, 787124497,
+        554875178, 60344266, 790999625, 581578940, 72453408, 872892444, 204321645, 885844343,
+        942736233, 12824064, 23018086, 996569940, 818486394, 1484060, 349940348, 981882207,
+        855621323, 1034617940, 565456643, 745822751, 68273335, 578729865, 170455086, 173683666,
+        375983003, 128070235, 982298151, 650454518, 218561113, 1063878953, 541912322, 695738038,
+        232263685, 671557037, 584280432, 126161631, 394804150, 149748923, 670383088, 150214459,
+        409907258, 730673023, 452696559, 556617060, 271416278, 398913344, 793695740, 603610724,
+        620753264, 760650912, 867673149, 32169440, 106515963, 802984660, 859117007, 854436840,
+        736728629, 789197380, 1065424969, 689713946, 980964818, 605711034, 1105012640, 552261321,
+        980118632, 868407647, 247404352, 182421983, 1076188005, 324001412, 258504546, 973008126,
+        458306267, 216381363, 1099063604, 172774993, 897006405, 662034826, 827348922, 213283635,
+        712936407, 586286121, 133326382, 771279651, 77080161, 692598228, 272139235, 156805228,
+        184634865, 216660729, 331306952, 180435518, 1098019508, 642364354, 759605128, 444205747,
+        818119290, 697748488, 779633860, 1123069299, 628905992, 1093632915, 363341708, 896455329,
+        653929021, 348858884, 483831705, 248783457, 50018777, 527261991, 1108955766, 683741144,
+        838366046, 1040212608, 956504693, 441862460, 181267978, 415765896, 551409278, 866054366,
+        855141207, 141967451, 462022373, 903388714, 5583486, 624006881, 1027131584, 127103419,
+        227496735, 876597209, 1075725589, 13097576, 275925041, 46896293, 1047922947, 480370214,
+        979242782, 304620097, 88645237, 157252101, 1080960020, 1019514352, 910182857, 344499049,
+        841029348, 586201316, 561562804, 877577189, 774682172, 420802584, 243335866, 818233882,
+        1102088786, 910997938, 394459347, 114606856, 959148981, 304978043, 15246639, 410347623,
+        819777499, 304889937, 196333903, 294201539, 742724283, 136229739, 1095082129, 710673795,
+        303958022, 730529672, 330937766, 290549001, 975944911, 1012630530, 940739802, 428357654,
+        876150675, 270868991, 605451019, 692162505, 164423625, 414486664, 567335281, 60501334,
+        301815246, 988181982, 749960008, 971794624, 1044979874, 846257184, 944403720, 1028849636,
+        20676966, 402436874, 1038249855, 636703483, 52612884, 457266419, 404964279, 764097082,
+        73543501, 850137726, 543909567, 478253061, 341753973, 145701153, 816141937, 150699764,
+        1012159718, 996158125, 874816245, 140152812, 1046568846, 731360631, 165696325, 923351799,
+        373705673, 295885033, 571437494, 323580072, 535312031, 261690954, 128330254, 678037418,
+        730169337, 800223155, 493938662, 1035925088, 1086213401, 1029948847, 703929623, 1067237691,
+        203498545, 260171642, 489884813, 595714326, 717921496, 379455421, 739695761, 991791615,
+        1101866478, 768413486, 248059075, 641256446, 252045193, 383431807, 1098526663, 655518597,
+        868514850, 975851412, 607928997, 1015436331, 219860723, 248644475, 1100013490, 1059262194,
+        616750819, 706912112, 186194896, 371412253, 932777950, 605371544, 32806481, 41915372,
+        942949174, 657520226, 337575994, 767026353, 795340494, 602216057, 1051603235, 38167611,
+        383256063, 961974657, 643676937, 850900578, 1033468109, 791020067, 602254362, 742154500,
+        5280825, 557072845, 260014823, 274231000, 957875719, 620840911, 468091423, 844999033,
+        1097180885, 635575570, 741694428, 564249983, 1015743243, 179096376, 326642449, 737067396,
+        404230544, 164870855, 233971733, 25568965, 480191729, 304346683, 43284571, 600295394,
+        805273167, 1081440213, 132076275, 660273218, 115344223, 436181422, 581442439, 521590088,
+        278258022, 1016124223, 326820478, 833856285, 578613123, 981790341, 912622887, 677878660,
+        983305736, 582146018, 50261960, 421861087, 688852042, 1053015424, 638857404, 13988561,
+        613374169, 246804474, 354822706, 755945338, 48594230, 1112837032, 490266853, 331336539,
+        573470525, 482415512, 1066189384, 480749813, 340470541, 1087888528, 866254054, 328905420,
+        282987588, 772186429, 280721545, 827109021, 1065603359, 319039186, 632282960, 368180744,
+        103743324, 1039700039, 944775691, 35416271, 876336840, 387995308, 744351857, 1024873450,
+        846245765, 488549456, 793960147, 360822038, 1047746824, 70006457, 1081240530, 845224049,
+        158270603, 1040065115, 109153922, 842769347, 357450022, 745529551, 127101430, 1013053569,
+        653900906, 1066692138, 1026185333, 40127076, 888550251, 1074046165, 859190733, 172044729,
+        232991700, 856026028, 181368869, 11312168, 965826001, 718641942, 140897415, 300107020,
+        760832029, 454871649, 538869148, 209839945, 603404196, 1070516093, 362685617, 594598150,
+        706680144, 248566695, 624350428, 687354425, 108825926, 996763990, 768570600, 192652503,
+        1030980703, 285993613, 678456384, 666912839, 636939881, 724986064, 567331335, 1109690969,
+        786490830, 1041275289, 13569330, 310803565, 1018122073, 236560398, 625689685, 227532759,
+        773302523, 275485190, 151580837, 1097590370, 43338286, 709601963, 530263051, 696952520,
+        591327015, 779208675, 900380693, 779322099, 365008051, 816626432, 9177437, 715814813,
+        951132001, 978726879, 539618044, 330049876, 794850808, 75659606, 535871088, 1117571277,
+        1035668574, 737539534, 638053030, 1067376479, 817653745, 608323193, 952285144, 489088626,
+        643974231, 1100308604, 80188084, 1009055308, 528621558, 845717932, 574893980, 185963698,
+        304405106, 254075674, 885653667, 125272317, 310803515, 431578311, 49069840, 734649900,
+        64565269, 68004786, 877673289, 927546004, 379372125, 179774076, 313581255, 48547291,
+        669540902, 805507558, 458763285, 808407028, 467495228, 200042934, 286655832, 204624339,
+        1076184783, 580415818, 422842319, 130214533, 1019327141, 196299734, 544501682, 1039357725,
+        139924087, 1079672537, 517801387, 1103773407, 832398676, 971715180, 249998343, 17982966,
+        226812113, 1056456541, 429118671, 1115368643, 731996995, 114969826, 90081103, 503306049,
+        601600644, 221645888, 419902454, 366845662, 668188540, 595203705, 289744999, 311876332,
+        708499030, 814702169, 1038171811, 305413430, 1025269191, 401153805, 524933941, 244137773,
+        690611117, 1006016675, 368084114, 695256047, 400388115, 315582560, 240063473, 316625045,
+        193016450, 444532677, 490320790, 1007751505, 809310640, 335632874, 932973140, 879894932,
+        664689650, 582802605, 524248338, 381467584, 15568620, 102853205, 344510143, 244997234,
+        228087680, 804700227, 33671368, 202173391, 810352194, 941527027, 107007723, 250295846,
+        439491161, 385750316, 881611329, 305721499, 489662560, 253590441, 574217985, 187749275,
+        767834897, 577560416, 666585061, 536277466, 31104595, 459368906, 738728273, 395846485,
+        915511988, 152055976, 123475832, 434555954, 708682317, 873350950, 733405860, 594180445,
+        508539260, 411702537, 525230789, 245498406, 807139759, 421829267, 576175978, 671688378,
+        586113326, 717964200, 549178465, 340455344, 177498005, 185915353, 758077402, 35174925,
+        62477885, 432126554, 865525725, 94991243, 953163796, 64489669, 63466463, 1027121609,
+        603180773, 102927367, 328204523, 849484682, 1106252192, 166218129, 336284946, 115841434,
+        394987097, 1071354740, 1124188058, 756569276, 1109657140, 693362574, 582653179, 356054504,
+        10684879, 43556307, 683161419, 43248060, 311516690, 801263962, 259313122, 1076460000,
+        627348913, 723691023, 1029014449, 1100617454, 910402911, 804049172, 439314326, 277694586,
+        89508968, 45373766, 191679221, 573131182, 602365665, 620254956, 594424107, 763083130,
+        984103431, 746523686, 419387264, 1030279302, 774892955, 242854266, 102748733, 633218020,
+        205014651, 835462852, 458575555, 34062189, 765679808, 999547926, 509921953, 785947219,
+        966902522, 639941920, 244960006, 581685840, 1052422889, 674992133, 61665140, 451012255,
+        122965849, 190085617, 748838858, 349331162, 529980624, 16193587, 34516478, 42504381,
+        160246364, 173535194, 187379517, 111011381, 365394663, 102220644, 699473271, 366177836,
+        81692890, 659930501, 36322222, 896361190, 473670263, 1039987458, 412734344, 864218684,
+        138309539, 146538307, 1101721292, 898028054, 727055521, 601140835, 577133426, 451632863,
+        725601236, 388561051, 696130927, 299179408, 194132814, 104549843, 919472671, 900644523,
+        848766532, 557407329, 506005527, 529295823, 637231568, 840578154, 40752048, 1043021730,
+        940132528, 1635875, 390473136, 36409813, 938731454, 1005273567, 169556667, 456362115,
+        968015234, 385125443, 1033592386, 622268544, 824710397, 811874465, 1126356395, 1030647864,
+        1027801728, 1032615842, 990780079, 340414618, 431920306, 797512392, 36624143, 1039964580,
+        78368304, 725419125, 1031876274, 545277986, 400681087, 824700406, 916868477, 979226643,
+        327065594, 666022464, 692190875, 241031536, 486380927, 271722557, 903843361, 141586809,
+        747887461, 634534510, 613428929, 35052951, 519999706, 907994356, 122720932, 47771789,
+        111045658, 101409844, 478385667, 160271513, 709331396, 407803014, 233222181, 446853856,
+        325464420, 902760210, 272094082, 341360162, 749117421, 560701701, 963877946, 787641615,
+        205947725, 590645393, 232274153, 623635766, 912309623, 1120569701, 620395638, 104547179,
+        326785462, 462547537, 940416330, 25839620, 19867751, 37744827, 852776583, 515439522,
+        254321646, 368159144, 977334258, 457474804, 545693728, 590432747, 485742318, 929078943,
+        737942351, 275516935, 808617222, 720700675, 172457394, 549776288, 773237950, 425343903,
+        106571968, 735846275, 60428193, 122914286, 749764808, 987165321, 38777778, 78068884,
+        255797430, 533180457, 948660956, 48180746, 1126178511, 529625145, 556629751, 638195644,
+        832146389, 336969603, 525037491, 33022927, 179961503, 1111138669, 8606225, 744603862,
+        831709431, 936738778, 976276214, 628380488, 186998645, 1058798754, 681993880, 653924606,
+        946881757, 648359538, 739466740, 91286181, 294298084, 874448181, 197552840, 1095208028,
+        1044083721, 391228026, 525623627, 1098872860, 195023028, 787479821, 623295468, 953049875,
+        598245883, 53206653, 107774100, 1118437046, 444804586, 736921099, 245341091, 386750976,
+        133313309, 679691423, 1106023536, 311728621, 298633293, 401684808, 243635798, 110079800,
+        995741344, 323549381, 934619946, 561564543, 318482785, 437414717, 954436048, 120567824,
+        939556676, 96791826, 457592630, 938550164, 338458435, 235369986, 520146590, 367137669,
+        215447981, 816673608, 388257556, 367337840, 505745620, 16797457, 625598745, 670629880,
+        254936589, 910270385, 42251434, 163010455, 35446687, 313308255, 882198755, 1020005685,
+        629715947, 45305629, 160442570, 751826615, 839359276, 665790727, 1126866480, 206409968,
+        243347643, 601811321, 987009162, 748255207, 204576339, 60528047, 215906665, 956509919,
+        298034351, 23754044, 490568798, 458549410, 38365707, 66017494, 289322020, 23546127,
+        1110422453, 430589196, 451259509, 452129088, 246026906, 479959331, 657705765, 154141097,
+        545145366, 756619911, 1056054068, 305614614, 982437349, 733683110, 356219317, 138617825,
+        1077109241, 367006768, 107952142, 792745355, 1101149620, 190430549, 642564451, 1027811321,
+        529836063, 856677079, 136144741, 193585315, 759745460, 929992250, 670307004, 1127656802,
+        1006741766, 845736365, 936834519, 1017230852, 281391123, 580173947, 30327752, 1121201269,
+        1055919747, 182909919, 160816233, 602222114, 380162230, 868238592, 238843489, 626593576,
+        827265325, 474165116, 237320246, 663594943, 436318864, 1049025048, 70720879, 971089740,
+        88817691, 635640003, 121885571, 243941023, 2431313, 941718254, 799204753, 612476838,
+        941429841, 218425031, 62583588, 843195067, 826107237, 360474616, 699278259, 879607149,
+        103065937, 68048433, 88684453, 368247280, 352557675, 958077844, 188716852, 142753212,
+        187289952, 1075600752, 538047877, 79021856, 72991411, 855788038, 872392901, 230685283,
+        684292160, 1006193052, 389933214, 858196548, 879451470, 275454910, 233947442, 798039506,
+        522351014, 1028810225, 973496946, 459028464, 716757179, 299354781, 303032004, 378782749,
+        726394798, 822245773, 1112315820, 572181708, 792445736, 354854796, 391571531, 1120313604,
+        948219264, 683224878, 109721560, 210780537, 877361416, 837335205, 829228039, 710692263,
+        336548681, 1113948080, 500533488, 561629043, 1004877220, 538880595, 121199664, 933983782,
+        1012189335, 466644222, 948695748, 1030237630, 409196123, 831874921, 209931563, 472212875,
+        667493959, 374083498, 121993645, 135468879, 743372774, 445381601, 1056162737, 535313411,
+        731504361, 106330271, 449811720, 1025526021, 538706550, 384586636, 672759140, 214035474,
+        1061388936, 1083928033, 581161697, 343521559, 467089784, 918062822, 197840310, 276992034,
+        267922106, 626045501, 689381922, 1031751565, 453082931, 703374420, 543907459, 1101013667,
+        158907990, 531648771, 96624556, 1107736421, 653786417, 1020275778, 1100295652, 1080331394,
+        934499233, 1103133235, 665774139, 760309263, 583529543, 927074230, 109488746, 759686422,
+        658349346, 1121943871, 528665116, 800822916, 210288486, 526666123, 265162098, 797138998,
+        225170105, 773601444, 1074231036, 358050025, 792486275, 559918288, 931510368, 283561253,
+        929909084, 643871325, 165045098, 236482168, 27010690, 947252425, 762156229, 403915775,
+        226420583, 111771291, 457403715, 390913388, 417791477, 1051596236, 218412938, 231310973,
+        913814997, 1010678562, 1061436357, 402561769, 46161499, 194906871, 1106996555, 1044025883,
+        84769121, 958130074, 458164325, 75716319, 742943036, 662710618, 1015724782, 541305935,
+        887985887, 374173602, 667853276, 1118405527, 888897413, 806335907, 483810195, 94681964,
+        932049061, 419630532, 863884216, 258267823, 871542447, 748276380, 1089602035, 956286084,
+        452661681, 297101315, 292838603, 602202013, 59322052, 868600049, 695946675, 1119453722,
+        369279180, 571163895, 1076824823, 613633165, 1105924843, 970753160, 38059756, 230436803,
+        993075206, 825136859, 772252886, 665426090, 252341082, 81169114, 15411109, 1109435384,
+        169742336, 879343295, 637603710, 1050610062, 543393828, 360888011, 622980636, 538625088,
+        384365656, 954674344, 672395534, 822045489, 271453575, 379517678, 745140555, 64423305,
+        132805764, 730783710, 818513567, 1062263522, 633828399, 609441394, 952235218, 443525262,
+        408766329, 154648340, 321016488, 39971097, 975003097, 115001826, 929799112, 1079586908,
+        630403891, 883673758, 1017692831, 982184607, 1052333730, 732655912, 673402471, 144580658,
+        776541469, 599807837, 897876410, 750409126, 408883399, 800706291, 525848844, 1043397101,
+        546417436, 927622216, 923472422, 126273770, 976796416, 301390002, 640312848, 64953087,
+        850801014, 80099312, 344764299, 166353727, 304339056, 563820482, 620610256, 95176064,
+        436429135, 450761768, 1080607344, 554114394, 1051016002, 1094965858, 1044316801, 90082309,
+        802170370, 785939405, 96092255, 481999812, 399399466, 798786453, 1069910963, 1075397160,
+        645632403, 721614722, 81169724, 16732546, 256145620, 1032181185, 858152279, 261506136,
+        939613769, 1109259166, 1072545109, 893701664, 1076290454, 94376212, 1129486743, 496007349,
+        461211004, 215869774, 903345586, 185658758, 769007243, 973105742, 843449005, 531620573,
+        115498344, 118893063, 1080559132, 289993254, 729094212, 100228973, 970141524, 88018590,
+        401752692, 490667846, 120206196, 1053373040, 927137503, 109156130, 1099960359, 852051778,
+        542020710, 890761046, 935484975, 503476669, 247175418, 137646228, 122463313, 1098625172,
+        313612773, 1045687932, 942418016, 223296002, 613265129, 618070459, 210263615, 258537008,
+        687201993, 1069972265, 1092070505, 209044958, 705866604, 959480062, 37107231, 530334549,
+        940857114, 888162459, 550603427, 833686010, 914377969, 777858517, 823074720, 257780639,
+        682005406, 79684712, 1108405948, 546085914, 695245394, 296369184, 90008780, 876596576,
+        1060844810, 971909901, 180162302, 155422312, 913147941, 572392352, 172449506, 707256652,
+        379461286, 396142844, 341990080, 626969834, 156354367, 614031487, 682424885, 1092771510,
+        881893810, 1087158911, 85380176, 1044830162, 986021749, 619772632, 829678380, 1095084953,
+        726141659, 941134286, 949047185, 1013348374, 17076281, 979953556, 730262704, 692286875,
+        723405610, 879517616, 639675138, 319980056, 711102461, 564387868, 1089317723, 659400607,
+        372246588, 1035423048, 766956675, 186444638, 504211183, 871579167, 64189114, 905789437
+
+    };
+
+    public static final long[] ZETA_INVERSE_III_P = {        /* 2048-Entry */
+
+        1065536839, 258146786, 625514770, 943281315, 362769278, 94302905, 757479365, 470325346,
+        40408230, 565338085, 418623492, 809745897, 490050815, 250208337, 406320343, 437439078,
+        399463249, 149772397, 1112649672, 116377579, 180678768, 188591667, 403584294, 34641000,
+        300047573, 509953321, 143704204, 84895791, 1044345777, 42567042, 247832143, 36954443,
+        447301068, 515694466, 973371586, 502756119, 787735873, 733583109, 750264667, 422469301,
+        957276447, 557333601, 216578012, 974303641, 949563651, 157816052, 68881143, 253129377,
+        1039717173, 833356769, 434480559, 583640039, 21320005, 1050041241, 447720547, 871945314,
+        306651233, 351867436, 215347984, 296039943, 579122526, 241563494, 188868839, 599391404,
+        1092618722, 170245891, 423859349, 920680995, 37655448, 59753688, 442523960, 871188945,
+        919462338, 511655494, 516460824, 906429951, 187307937, 84038021, 816113180, 31100781,
+        1007262640, 992079725, 882550535, 626249284, 194240978, 238964907, 587705243, 277674175,
+        29765594, 1020569823, 202588450, 76352913, 1009519757, 639058107, 727973261, 1041707363,
+        159584429, 1029496980, 400631741, 839732699, 49166821, 1010832890, 1014227609, 598105380,
+        286276948, 156620211, 360718710, 944067195, 226380367, 913856179, 668514949, 633718604,
+        239210, 1035349741, 53435499, 236024289, 57180844, 20466787, 190112184, 868219817,
+        271573674, 97544768, 873580333, 1112993407, 1048556229, 408111231, 484093550, 54328793,
+        59814990, 330939500, 730326487, 647726141, 1033633698, 343786548, 327555583, 1039643644,
+        85409152, 34760095, 78709951, 575611559, 49118609, 678964185, 693296818, 1034549889,
+        509115697, 565905471, 825386897, 963372226, 784961654, 1049626641, 278924939, 1064772866,
+        489413105, 828335951, 152929537, 1003452183, 206253531, 202103737, 583308517, 86328852,
+        603877109, 329019662, 720842554, 379316827, 231849543, 529918116, 353184484, 985145295,
+        456323482, 397070041, 77392223, 147541346, 112033122, 246052195, 499322062, 50139045,
+        199926841, 1014724127, 154722856, 1089754856, 808709465, 975077613, 720959624, 686200691,
+        177490735, 520284559, 495897554, 67462431, 311212386, 398942243, 996920189, 1065302648,
+        384585398, 750208275, 858272378, 307680464, 457330419, 175051609, 745360297, 591100865,
+        506745317, 768837942, 586332125, 79115891, 492122243, 250382658, 959983617, 20290569,
+        1114314844, 1048556839, 877384871, 464299863, 357473067, 304589094, 136650747, 899289150,
+        1091666197, 158972793, 23801110, 516092788, 52901130, 558562058, 760446773, 10272231,
+        433779278, 261125904, 1070403901, 527523940, 836887350, 832624638, 677064272, 173439869,
+        40123918, 381449573, 258183506, 871458130, 265841737, 710095421, 197676892, 1035043989,
+        645915758, 323390046, 240828540, 11320426, 461872677, 755552351, 241740066, 588420018,
+        114001171, 467015335, 386782917, 1054009634, 671561628, 171595879, 1044956832, 85700070,
+        22729398, 934819082, 1083564454, 727164184, 68289596, 119047391, 215910956, 898414980,
+        911313015, 78129717, 711934476, 738812565, 672322238, 1017954662, 903305370, 725810178,
+        367569724, 182473528, 1102715263, 893243785, 964680855, 485854628, 199816869, 846164700,
+        198215585, 569807665, 337239678, 771675928, 55494917, 356124509, 904555848, 332586955,
+        864563855, 603059830, 919437467, 328903037, 601060837, 7782082, 471376607, 370039531,
+        1020237207, 202651723, 546196410, 369416690, 463951814, 26592718, 195226720, 49394559,
+        29430301, 109450175, 475939536, 21989532, 1033101397, 598077182, 970817963, 28712286,
+        585818494, 426351533, 676643022, 97974388, 440344031, 503680452, 861803847, 852733919,
+        931885643, 211663131, 662636169, 786204394, 548564256, 45797920, 68337017, 915690479,
+        456966813, 745139317, 591019403, 104199932, 679914233, 1023395682, 398221592, 594412542,
+        73563216, 684344352, 386353179, 994257074, 1007732308, 755642455, 462231994, 657513078,
+        919794390, 297851032, 720529830, 99488323, 181030205, 663081731, 117536618, 195742171,
+        1008526289, 590845358, 124848733, 568096910, 629192465, 15777873, 793177272, 419033690,
+        300497914, 292390748, 252364537, 918945416, 1020004393, 446501075, 181506689, 9412349,
+        738154422, 774871157, 337280217, 557544245, 17410133, 307480180, 403331155, 750943204,
+        826693949, 830371172, 412968774, 670697489, 156229007, 100915728, 607374939, 331686447,
+        895778511, 854271043, 250274483, 271529405, 739792739, 123532901, 445433793, 899040670,
+        257333052, 273937915, 1056734542, 1050704097, 591678076, 54125201, 942436001, 986972741,
+        941009101, 171648109, 777168278, 761478673, 1041041500, 1061677520, 1026660016, 250118804,
+        430447694, 769251337, 303618716, 286530886, 1067142365, 911300922, 188296112, 517249115,
+        330521200, 188007699, 1127294640, 885784930, 1007840382, 494085950, 1040908262, 158636213,
+        1059005074, 80700905, 693407089, 466131010, 892405707, 655560837, 302460628, 503132377,
+        890882464, 261487361, 749563723, 527503839, 968909720, 946816034, 73806206, 8524684,
+        1099398201, 549552006, 848334830, 112495101, 192891434, 283989588, 122984187, 2069151,
+        459418949, 199733703, 369980493, 936140638, 993581212, 273048874, 599889890, 101914632,
+        487161502, 939295404, 28576333, 336980598, 1021773811, 762719185, 52616712, 991108128,
+        773506636, 396042843, 147288604, 824111339, 73671885, 373106042, 584580587, 975584856,
+        472020188, 649766622, 883699047, 677596865, 678466444, 699136757, 19303500, 1106179826,
+        840403933, 1063708459, 1091360246, 671176543, 639157155, 1105971909, 831691602, 173216034,
+        913819288, 1069197906, 925149614, 381470746, 142716791, 527914632, 886378310, 923315985,
+        2859473, 463935226, 290366677, 377899338, 969283383, 1084420324, 500010006, 109720268,
+        247527198, 816417698, 1094279266, 966715498, 1087474519, 219455568, 874789364, 459096073,
+        504127208, 1112928496, 623980333, 762388113, 741468397, 313052345, 914277972, 762588284,
+        609579363, 894355967, 791267518, 191175789, 672133323, 1032934127, 190169277, 1009158129,
+        175289905, 692311236, 811243168, 568161410, 195106007, 806176572, 133984609, 1019646153,
+        886090155, 728041145, 831092660, 817997332, 23702417, 450034530, 996412644, 742974977,
+        884384862, 392804854, 684921367, 11288907, 1021951853, 1076519300, 531480070, 176676078,
+        506430485, 342246132, 934702925, 30853093, 604102326, 738497927, 85642232, 34517925,
+        932173113, 255277772, 835427869, 1038439772, 390259213, 481366415, 182844196, 475801347,
+        447732073, 70927199, 942727308, 501345465, 153449739, 192987175, 298016522, 385122091,
+        1121119728, 18587284, 949764450, 1096703026, 604688462, 792756350, 297579564, 491530309,
+        573096202, 600100808, 3547442, 1081545207, 181064997, 596545496, 873928523, 1051657069,
+        1090948175, 142560632, 379961145, 1006811667, 1069297760, 393879678, 1023153985, 704382050,
+        356488003, 579949665, 957268559, 409025278, 321108731, 854209018, 391783602, 200647010,
+        643983635, 539293206, 584032225, 672251149, 152391695, 761566809, 875404307, 614286431,
+        276949370, 1091981126, 1109858202, 1103886333, 189309623, 667178416, 802940491, 1025178774,
+        509330315, 9156252, 217416330, 506090187, 897451800, 539080560, 923778228, 342084338,
+        165848007, 569024252, 380608532, 788365791, 857631871, 226965743, 804261533, 682872097,
+        896503772, 721922939, 420394557, 969454440, 651340286, 1028316109, 1018680295, 1081954164,
+        1007005021, 221731597, 609726247, 1094673002, 516297024, 495191443, 381838492, 988139144,
+        225882592, 858003396, 643345026, 888694417, 437535078, 463703489, 802660359, 150499310,
+        212857476, 305025547, 729044866, 584447967, 97849679, 404306828, 1051357649, 89761373,
+        1093101810, 332213561, 697805647, 789311335, 138945874, 97110111, 101924225, 99078089,
+        3369558, 317851488, 305015556, 507457409, 96133567, 744600510, 161710719, 673363838,
+        960169286, 124452386, 190994499, 1093316140, 739252817, 1128090078, 189593425, 86704223,
+        1088973905, 289147799, 492494385, 600430130, 623720426, 572318624, 280959421, 229081430,
+        210253282, 1025176110, 935593139, 830546545, 433595026, 741164902, 404124717, 678093090,
+        552592527, 528585118, 402670432, 231697899, 28004661, 983187646, 991416414, 265507269,
+        716991609, 89738495, 656055690, 233364763, 1093403731, 469795452, 1048033063, 763548117,
+        430252682, 1027505309, 764331290, 1018714572, 942346436, 956190759, 969479589, 1087221572,
+        1095209475, 1113532366, 599745329, 780394791, 380887095, 939640336, 1006760104, 678713698,
+        1068060813, 454733820, 77303064, 548040113, 884765947, 489784033, 162823431, 343778734,
+        619804000, 130178027, 364046145, 1095663764, 671150398, 294263101, 924711302, 496507933,
+        1026977220, 886871687, 354832998, 99446651, 710338689, 383202267, 145622522, 366642823,
+        535301846, 509470997, 527360288, 556594771, 938046732, 1084352187, 1040216985, 852031367,
+        690411627, 325676781, 219323042, 29108499, 100711504, 406034930, 502377040, 53265953,
+        870412831, 328461991, 818209263, 1086477893, 446564534, 1086169646, 1119041074, 773671449,
+        547072774, 436363379, 20068813, 373156677, 5537895, 58371213, 734738856, 1013884519,
+        793441007, 963507824, 23473761, 280241271, 801521430, 1026798586, 526545180, 102604344,
+        1066259490, 1065236284, 176562157, 1034734710, 264200228, 697599399, 1067248068, 1094551028,
+        371648551, 943810600, 952227948, 789270609, 580547488, 411761753, 543612627, 458037575,
+        553549975, 707896686, 322586194, 884227547, 604495164, 718023416, 621186693, 535545508,
+        396320093, 256375003, 421043636, 695169999, 1006250121, 977669977, 214213965, 733879468,
+        390997680, 670357047, 1098621358, 593448487, 463140892, 552165537, 361891056, 941976678,
+        555507968, 876135512, 640063393, 824004454, 248114624, 743975637, 690234792, 879430107,
+        1022718230, 188198926, 319373759, 927552562, 1096054585, 325025726, 901638273, 884728719,
+        785215810, 1026872748, 1114157333, 748258369, 605477615, 546923348, 465036303, 249831021,
+        196752813, 794093079, 320415313, 121974448, 639405163, 685193276, 936709503, 813100908,
+        889662480, 814143393, 729337838, 434469906, 761641839, 123709278, 439114836, 885588180,
+        604792012, 728572148, 104456762, 824312523, 91554142, 315023784, 421226923, 817849621,
+        839980954, 534522248, 461537413, 762880291, 709823499, 908080065, 528125309, 626419904,
+        1039644850, 1014756127, 397728958, 14357310, 700607282, 73269412, 902913840, 1111742987,
+        879727610, 158010773, 297327277, 25952546, 611924566, 50053416, 989801866, 90368228,
+        585224271, 933426219, 110398812, 999511420, 706883634, 549310135, 53541170, 925101614,
+        843070121, 929683019, 662230725, 321318925, 670962668, 324218395, 460185051, 1081178662,
+        816144698, 949951877, 750353828, 202179949, 252052664, 1061721167, 1065160684, 395076053,
+        1080656113, 698147642, 818922438, 1004453636, 244072286, 875650279, 825320847, 943762255,
+        554831973, 284008021, 601104395, 120670645, 1049537869, 29417349, 485751722, 640637327,
+        177440809, 521402760, 312072208, 62349474, 491672923, 392186419, 94057379, 12154676,
+        593854865, 1054066347, 334875145, 799676077, 590107909, 150999074, 178593952, 413911140,
+        1120548516, 313099521, 764717902, 350403854, 229345260, 350517278, 538398938, 432773433,
+        599462902, 420123990, 1086387667, 32135583, 978145116, 854240763, 356423430, 902193194,
+        504036268, 893165555, 111603880, 818922388, 1116156623, 88450664, 343235123, 20034984,
+        562394618, 404739889, 492786072, 462813114, 451269569, 843732340, 98745250, 937073450,
+        361155353, 132961963, 1020900027, 442371528, 505375525, 881159258, 423045809, 535127803,
+        767040336, 59209860, 526321757, 919886008, 590856805, 674854304, 368893924, 829618933,
+        988828538, 411084011, 163899952, 1118413785, 948357084, 273699925, 896734253, 957681224,
+        270535220, 55679788, 241175702, 1089598877, 103540620, 63033815, 475825047, 116672384,
+        1002624523, 384196402, 772275931, 286956606, 1020572031, 89660838, 971455350, 284501904,
+        48485423, 1059719496, 81979129, 768903915, 335765806, 641176497, 283480188, 104852503,
+        385374096, 741730645, 253389113, 1094309682, 184950262, 90025914, 1025982629, 761545209,
+        497442993, 810686767, 64122594, 302616932, 849004408, 357539524, 846738365, 800820533,
+        263471899, 41837425, 789255412, 648976140, 63536569, 647310441, 556255428, 798389414,
+        639459100, 16888921, 1081131723, 373780615, 774903247, 882921479, 516351784, 1115737392,
+        490868549, 76710529, 440873911, 707864866, 1079463993, 547579935, 146420217, 451847293,
+        217103066, 147935612, 551112830, 295869668, 802905475, 113601730, 851467931, 608135865,
+        548283514, 693544531, 1014381730, 469452735, 997649678, 48285740, 324452786, 529430559,
+        1086441382, 825379270, 649534224, 1104156988, 895754220, 964855098, 725495409, 392658557,
+        803083504, 950629577, 113982710, 565475970, 388031525, 494150383, 32545068, 284726920,
+        661634530, 508885042, 171850234, 855494953, 869711130, 572653108, 1124445128, 387571453,
+        527471591, 338705886, 96257844, 278825375, 486049016, 167751296, 746469890, 1091558342,
+        78122718, 527509896, 334385459, 362699600, 792149959, 472205727, 186776779, 1087810581,
+        1096919472, 524354409, 196948003, 758313700, 943531057, 422813841, 512975134, 70463759,
+        29712463, 881081478, 909865230, 114289622, 521796956, 153874541, 261211103, 474207356,
+        31199290, 746294146, 877680760, 488469507, 881666878, 361312467, 27859475, 137934338,
+        390030192, 750270532, 411804457, 534011627, 639841140, 869554311, 926227408, 62488262,
+        425796330, 99777106, 43512552, 93800865, 635787291, 329502798, 399556616, 451688535,
+        1001395699, 868034999, 594413922, 806145881, 558288459, 833840920, 756020280, 206374154,
+        964029628, 398365322, 83157107, 989573141, 254909708, 133567828, 117566235, 979026189,
+        313584016, 984024800, 787971980, 651472892, 585816386, 279588227, 1056182452, 365628871,
+        724761674, 672459534, 1077113069, 493022470, 91476098, 727289079, 1109048987, 100876317,
+        185322233, 283468769, 84746079, 157931329, 379765945, 141543971, 827910707, 1069224619,
+        562390672, 715239289, 965302328, 437563448, 524274934, 858856962, 253575278, 701368299,
+        188986151, 117095423, 153781042, 839176952, 798788187, 399196281, 825767931, 419052158,
+        34643824, 993496214, 387001670, 835524414, 933392050, 824836016, 309948454, 719378330,
+        1114479314, 824747910, 170576972, 1015119097, 735266606, 218728015, 27637167, 311492071,
+        886390087, 708923369, 355043781, 252148764, 568163149, 543524637, 288696605, 785226904,
+        219543096, 110211601, 48765933, 972473852, 1041080716, 825105856, 150483171, 649355739,
+        81803006, 1082829660, 853800912, 1116628377, 54000364, 253128744, 902229218, 1002622534,
+        102594369, 505719072, 1124142467, 226337239, 667703580, 987758502, 274584746, 263671587,
+        578316675, 713960057, 948457975, 687863493, 173221260, 89513345, 291359907, 445984809,
+        20770187, 602463962, 1079707176, 880942496, 645894248, 780867069, 475796932, 233270624,
+        766384245, 36093038, 500819961, 6656654, 350092093, 431977465, 311606663, 685520206,
+        370120825, 487361599, 31706445, 949290435, 798419001, 913065224, 945091088, 972920725,
+        857586718, 437127725, 1052645792, 358446302, 996399571, 543439832, 416789546, 916442318,
+        302377031, 467691127, 232719548, 956950960, 30662349, 913344590, 671419686, 156717827,
+        871221407, 805724541, 53537948, 947303970, 882321601, 261318306, 149607321, 577464632,
+        24713313, 524014919, 148761135, 440012007, 64300984, 340528573, 392997324, 275289113,
+        270608946, 326741293, 1023209990, 1097556513, 262052804, 369075041, 508972689, 526115229,
+        336030213, 730812609, 858309675, 573108893, 677029394, 399052930, 719818695, 979511494,
+        459342865, 979977030, 734921803, 1003564322, 545445521, 458168916, 897462268, 433987915,
+        587813631, 65847000, 911164840, 479271435, 147427802, 1001655718, 753742950, 956042287,
+        959270867, 550996088, 1061452618, 383903202, 564269310, 95108013, 274104630, 147843746,
+        779785605, 1128241893, 311239559, 133156013, 1106707867, 1116901889, 186989720, 243881610,
+        925404308, 256833509, 1057272545, 548147013, 338726328, 1069381687, 574850775, 342601456,
+        593021619, 994345276, 1048130591, 357272790, 941731875, 199494123, 800190987, 1024351829,
+        729808873, 795901888, 725425505, 1065099405, 1024787791, 856914318, 946582763, 461811542,
+        444076465, 1128838984, 715164506, 517440977, 39330283, 90148781, 554707372, 928732704,
+        371502021, 1008233336, 317582756, 524451154, 155422816, 440312472, 1100195158, 791313568,
+        1074684194, 757523450, 507259938, 376912438, 790750659, 19985542, 236477812, 621515084,
+        14641604, 423726725, 43680072, 165943232, 548969735, 840033269, 191845866, 921576757,
+        915919657, 817349484, 963826245, 482186439, 730356300, 1015917581, 672918284, 396635587,
+        767202303, 813371635, 869333976, 508192846, 490177145, 59204384, 11099732, 756805114,
+        903025660, 594360830, 1019323549, 705453704, 721000677, 467124590, 433770645, 203504697,
+        97797853, 924825635, 373581843, 430933902, 811243340, 250007758, 534865265, 128712376,
+        822051513, 250846965, 1070671272, 1072188174, 312655828, 488080380, 566811631, 88831334,
+        15478592, 996929267, 279463354, 404398823, 996165590, 134746184, 10331000, 142429560,
+        482838497, 3625072, 653531060, 803565118, 1048986230, 1006019827, 715676944, 564117799,
+        555220950, 889714353, 263699900, 503659804, 1088604303, 600414871, 99345416, 997036691,
+        1062002121, 126783467, 253332497, 438867657, 976937555, 654395668, 557215041, 46939874,
+        552713464, 749916384, 484323074, 483917405, 677263900, 99365013, 192496860, 914599346,
+        547960463, 231723970, 997912798, 1099799435, 892081583, 102958110, 696767097, 5476106,
+        869957957, 497317476, 399192328, 575567190, 737714890, 545582635, 731658739, 404188557,
+        914432306, 56034159, 372218440, 626858382, 1059276234, 290001074, 728713038, 646639755,
+        1071349304, 497411527, 914507668, 874023244, 330355746, 997134735, 317751869, 93333561,
+        191736798, 867404294, 764021455, 649553869, 32134107, 356452470, 515188952, 912016737,
+        981074116, 3176027, 563139069, 901020620, 765622786, 77065368, 112565404, 1104087001,
+        603149919, 62320784, 37340924, 271988916, 1088050757, 900822026, 570795193, 118136336,
+        228954381, 84002137, 107820426, 1036215916, 414421818, 575157043, 1029511469, 1098909800,
+        638622793, 364653701, 951039572, 202667835, 1105572750, 905930490, 622475707, 769476936,
+        359844918, 1032607831, 270561227, 922044195, 211114077, 806702381, 1034110348, 1090787175,
+        380228620, 754831904, 825262701, 254684021, 689956338, 946898079, 1119439184, 170588665,
+        455055047, 1112488359, 316261794, 167052760, 681776700, 101655817, 1104047888, 931846485,
+        777666661, 244421919, 1032717751, 771899484, 167884822, 383451132, 414684868, 967970978,
+        251607673, 946486934, 801420079, 964329829, 1081110125, 810125049, 621410728, 635499090,
+        324446706, 609896164, 727096174, 788299449, 605051120, 662578360, 478977775, 511427829,
+        804875304, 299858894, 607089768, 134081973, 892474561, 270157166, 79522694, 885310889,
+        360224635, 1006476027, 974435294, 780834962, 461009670, 494180818, 959507232, 1121381346,
+        1088339534, 654927281, 762292807, 33461378, 661924743, 864591625, 546400772, 944342858,
+        1028200265, 1056455757, 88680527, 1050916030, 938561033, 102069108, 295787004, 446280472,
+        772055005, 715365274, 933867776, 785081482, 216411888, 159026208, 584415731, 310755280,
+        266117816, 727015230, 334501223, 484334581, 1033523203, 1063818101, 736774330, 313712636,
+        734996871, 184740368, 362794063, 436132188, 482029298, 442290376, 221647483, 386772954,
+        982910397, 726807337, 240728269, 105503811, 269900590, 243722064, 610126807, 951562758,
+        659751006, 340811190, 199299787, 351828858, 856382738, 365480606, 1042786228, 1123068912,
+        10276685, 642933070, 165566419, 679827169, 961701698, 294054310, 355706366, 217192437,
+        522587524, 312032939, 558693905, 349209851, 930411190, 262585457, 415533475, 646948555,
+        1066780967, 471089109, 929057655, 517438536, 258478395, 39270561, 889479646, 273898984,
+        627910143, 618616086, 180698624, 951480188, 200561477, 237584143, 607869392, 437078460,
+        415163056, 209993568, 334873362, 1128930907, 508514481, 792979407, 676813222, 1019104866,
+        411695497, 43613997, 785578867, 132379578, 737624958, 773288614, 505119708, 928682766,
+        752219752, 662639813, 271272426, 160092539, 49353550, 599278652, 457441423, 694141030,
+        1108781150, 497004602, 152637935, 327024689, 869206336, 153529086, 213974535, 210561231,
+        1115104772, 26000087, 473351014, 705674785, 11948367, 699262595, 385616508, 1082509833,
+        1002086909, 351117184, 426727961, 716455350, 445629691, 263014880, 178561677, 763942576,
+        453911234, 251065969, 1119113840, 403386182, 584063944, 1061670228, 1067594138, 934920847,
+        221796630, 578508545, 960533240, 408202157, 800505215, 124820100, 693014651, 377258025,
+        345451846, 1084205130, 955691331, 877134009, 461861459, 1065671551, 750205976, 899130388,
+        962296503, 260525551, 454879090, 567632207, 867393171, 205384967, 52991349, 772073189,
+        784137909, 177596177, 774634076, 830320977, 80216042, 958845699, 444242121, 445247615,
+        749038979, 1043182368, 942892656, 308653011, 717431175, 1123135166, 401326655, 801495329,
+        924651065, 627583444, 57945923, 797461143, 1014733078, 267842526, 75171020, 985898727,
+        602077998, 289641435, 3817272, 72809466, 170436822, 917065606, 1075765979, 228712568,
+        804065866, 103429236, 755232438, 523116600, 175489690, 643101045, 782740199, 646390839,
+        154384289, 34296586, 68273162, 409008719, 72194869, 514620434, 1074403272, 1002586053,
+        824780576, 439514269, 814483462, 1065678048, 192117199, 337965005, 290046544, 745194166,
+        98634179, 594461368, 282668308, 169287965, 809176984, 851624865, 146280088, 551403730,
+        229988203, 759464587, 986831791, 307931453, 687021443, 1037047036, 390984681, 360214539,
+        489609063, 1079688799, 520853052, 248692113, 519108294, 873337355, 1019557863, 51551242,
+        140648753, 956691426, 500515518, 829644954, 225195986, 477843837, 117146454, 524576548,
+        388500809, 679009976, 95097294, 83923789, 86452515, 112923593, 572196182, 809066810,
+        365600046, 610178737, 6894641, 484752925, 321345624, 421663857, 996369171, 945767857,
+        512656150, 598142805, 131771428, 523631226, 238471503, 1017814054, 960743607, 358302399,
+        729762169, 774817733, 285185753, 94598268, 859341273, 817195544, 781913067, 180586359,
+        922498031, 928876955, 181750202, 704453607, 982602560, 760425692, 445461081, 262899249,
+        993032850, 1099441120, 766077054, 661328835, 849341957, 64481799, 137189173, 665203913,
+        274533772, 732036380, 686074559, 364887119, 934033368, 1113144551, 98086634, 1039349720,
+        138463640, 63197842, 478102337, 281719763, 25872677, 48972992, 1117419859, 319791525,
+        557816662, 725904956, 373125437, 385943763, 208358936, 697455132, 373381211, 417958668,
+        398054090, 1014428957, 906287080, 1055697308, 610106444, 259488631, 39585717, 1118889820,
+        869608698, 828290478, 859032095, 336215136, 327990657, 634859446, 312601336, 291351028,
+        682470272, 23577953, 337451423, 616208275, 919161707, 247373119, 679977555, 387895394,
+        234372661, 418240334, 10541774, 49052282, 470788227, 341763065, 746512185, 939659609,
+        615551391, 1053722676, 786403464, 952256028, 999791888, 536861641, 466680432, 223936516
+
+    };
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLA.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLA.java
new file mode 100644
index 0000000..5d7fc94
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLA.java
@@ -0,0 +1,1948 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import java.security.SecureRandom;
+
+public class QTESLA
+{
+
+    /******************************************************************************************************************************************
+     * Description:	Hash Function to Generate C' for Heuristic qTESLA Security Category-1 and Category-3 (Option for Size or Speed)
+     ******************************************************************************************************************************************/
+    private static void hashFunction(byte[] output, int outputOffset, int[] V, final byte[] message, int messageOffset, int n, int d, int q)
+    {
+
+        int mask;
+        int cL;
+
+        byte[] T = new byte[n + Polynomial.MESSAGE];
+
+        for (int i = 0; i < n; i++)
+        {
+            /* If V[i] > Q / 2 Then V[i] = V[i] - Q */
+            mask = (q / 2 - V[i]) >> 31;
+            V[i] = ((V[i] - q) & mask) | (V[i] & (~mask));
+            cL = V[i] & ((1 << d) - 1);
+            /* If cL > 2 ^ (d - 1) Then cL = cL - 2 ^ d */
+            mask = ((1 << (d - 1)) - cL) >> 31;
+            cL = ((cL - (1 << d)) & mask) | (cL & (~mask));
+            T[i] = (byte)((V[i] - cL) >> d);
+
+        }
+
+        System.arraycopy(message, messageOffset, T, n, Polynomial.MESSAGE);
+
+        if (q == Parameter.Q_I)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(output, outputOffset, Polynomial.HASH, T, 0, n + Polynomial.MESSAGE);
+
+        }
+
+        if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(output, outputOffset, Polynomial.HASH, T, 0, n + Polynomial.MESSAGE);
+
+        }
+
+    }
+
+    /**************************************************************************************************************************************************
+     * Description:	Hash Function to Generate C' for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     **************************************************************************************************************************************************/
+    private static void hashFunction(byte[] output, int outputOffset, long[] V, final byte[] message, int messageOffset, int n, int k, int d, int q)
+    {
+
+        int index;
+        long mask;
+        long cL;
+        long temporary;
+
+        byte[] T = new byte[n * k + Polynomial.MESSAGE];
+
+        for (int j = 0; j < k; j++)
+        {
+
+            index = n * j;
+
+            for (int i = 0; i < n; i++)
+            {
+
+                temporary = V[index];
+                /* If V[i] > Q / 2 Then V[i] = V[i] - Q */
+                mask = (q / 2 - temporary) >> 63;
+                temporary = ((temporary - q) & mask) | (temporary & (~mask));
+                cL = temporary & ((1 << d) - 1);
+                /* If cL > 2 ^ (d - 1) Then cL = cL - 2 ^ d */
+                mask = ((1 << (d - 1)) - cL) >> 63;
+                cL = ((cL - (1 << d)) & mask) | (cL & (~mask));
+                T[index++] = (byte)((temporary - cL) >> d);
+
+            }
+
+        }
+
+        System.arraycopy(message, messageOffset, T, n * k, Polynomial.MESSAGE);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(output, outputOffset, Polynomial.HASH, T, 0, n * k + Polynomial.MESSAGE);
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(output, outputOffset, Polynomial.HASH, T, 0, n * k + Polynomial.MESSAGE);
+
+        }
+
+    }
+
+    /**************************************************************************************************************************************
+     * Description:	Computes Absolute Value for for Heuristic qTESLA Security Category-1 and Security Category-3 (Option for Size or Speed)
+     **************************************************************************************************************************************/
+    private static int absolute(int value)
+    {
+
+        return ((value >> 31) ^ value) - (value >> 31);
+
+    }
+
+    /*****************************************************************************************************************
+     * Description:	Computes Absolute Value for for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *****************************************************************************************************************/
+    private static long absolute(long value)
+    {
+
+        return ((value >> 63) ^ value) - (value >> 63);
+
+    }
+
+    /*********************************************************************************
+     * Description:	Checks Bounds for Signature Vector Z During Signification.
+     * 				Leaks the Position of the Coefficient that Fails the Test.
+     * 				The Position of the Coefficient is Independent of the Secret Data.
+     * 				Does not Leak the Signature of the Coefficients.
+     * 				For Heuristic qTESLA Security Category-1 and Security Category-3
+     * 				(Option for Size or Speed)
+     *
+     * @param        Z        Signature Vector
+     * @param        n        Polynomial Degree
+     * @param        b        Interval the Randomness is Chosen in During Signification
+     * @param        u        Bound in Checking Secret Polynomial
+     *
+     * @return false    Valid / Accepted
+     * 				true	Invalid / Rejected
+     ********************************************************************************/
+    private static boolean testRejection(int[] Z, int n, int b, int u)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            if (absolute(Z[i]) > (b - u))
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /*************************************************************************************
+     * Description:	Checks Bounds for Signature Vector Z During Signification.
+     * 				Leaks the Position of the Coefficient that Fails the Test.
+     * 				The Position of the Coefficient is Independent of the Secret Data.
+     * 				Does not Leak the Signature of the Coefficients.
+     * 				For Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        Z        Signature Vector
+     * @param        n        Polynomial Degree
+     * @param        b        Interval the Randomness is Chosen in During Signification
+     * @param        u        Bound in Checking Secret Polynomial
+     *
+     * @return false    Valid / Accepted
+     * 				true	Invalid / Rejected
+     *************************************************************************************/
+    private static boolean testRejection(long[] Z, int n, int b, int u)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            if (absolute(Z[i]) > (b - u))
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /**********************************************************************************
+     * Description:	Checks Bounds for Signature Vector Z During Signature Verification
+     * 				for Heuristic qTESLA Security Category-1 and Security Category-3
+     * 				(Option of Size of Speed)
+     *
+     * @param        Z        Signature Vector
+     * @param        n        Polynomial Degree
+     * @param        b        Interval the Randomness is Chosen in During Signification
+     * @param        u        Bound in Checking Secret Polynomial
+     *
+     * @return false    Valid / Accepted
+     * 				true	Invalid / Rejected
+     *********************************************************************************/
+    private static boolean testZ(int[] Z, int n, int b, int u)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            if ((Z[i] < -(b - u)) || (Z[i] > b - u))
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /*************************************************************************************
+     * Description:	Checks Bounds for Signature Vector Z During Signature Verification
+     * 				for Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        Z        Signature Vector
+     * @param        n        Polynomial Degree
+     * @param        b        Interval the Randomness is Chosen in During Signification
+     * @param        u        Bound in Checking Secret Polynomial
+     *
+     * @return false    Valid / Accepted
+     * 				true	Invalid / Rejected
+     *************************************************************************************/
+    private static boolean testZ(long[] Z, int n, int b, int u)
+    {
+
+        for (int i = 0; i < n; i++)
+        {
+
+            if ((Z[i] < -(b - u)) || (Z[i] > b - u))
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /*********************************************************************************
+     * Description:	Checks Bounds for W = V - EC During Signature Verification.
+     * 				Leaks the Position of the Coefficient that Fails the Test.
+     * 				The Position of the Coefficient is Independent of the Secret Data.
+     * 				Does not Leak the Signature of the Coefficients.
+     * 				For Heuristic qTESLA Security Category-1 and Security Category-3
+     * 				(Option for Size or Speed)
+     *
+     * @param        V            Parameter to be Checked
+     * @param        n            Polynomial Degree
+     * @param        d            Number of Rounded Bits
+     * @param        q            Modulus
+     * @param        rejection    Bound in Checking Error Polynomial
+     *
+     * @return false        Valid / Accepted
+     * 				true		Invalid / Rejected
+     *********************************************************************************/
+    private static boolean testV(int[] V, int n, int d, int q, int rejection)
+    {
+
+        int mask;
+        int left;
+        int right;
+        int test1;
+        int test2;
+
+        for (int i = 0; i < n; i++)
+        {
+
+            mask = (q / 2 - V[i]) >> 31;
+            right = ((V[i] - q) & mask) | (V[i] & (~mask));
+            test1 = (~(absolute(right) - (q / 2 - rejection))) >>> 31;
+            left = right;
+            right = (right + (1 << (d - 1)) - 1) >> d;
+            right = left - (right << d);
+            test2 = (~(absolute(right) - ((1 << (d - 1)) - rejection))) >>> 31;
+
+            /* Two Tests Fail */
+            if ((test1 | test2) == 1)
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /****************************************************************************************
+     * Description:	Checks Bounds for W = V - EC During Signature Verification.
+     * 				Leaks the Position of the Coefficient that Fails the Test.
+     * 				The Position of the Coefficient is Independent of the Secret Data.
+     * 				Does not Leak the Signature of the Coefficients.
+     * 				For Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        V            Parameter to be Checked
+     * @param        vOffset        Starting Point of V
+     * @param        n            Polynomial Degree
+     * @param        d            Number of Rounded Bits
+     * @param        q            Modulus
+     * @param        rejection    Bound in Checking Error Polynomial
+     *
+     * @return false        Valid / Accepted
+     * 				true		Invalid / Rejected
+     ****************************************************************************************/
+    private static boolean testV(long[] V, int vOffset, int n, int d, int q, int rejection)
+    {
+
+        long mask;
+        long left;
+        long right;
+        long test1;
+        long test2;
+
+        for (int i = 0; i < n; i++)
+        {
+
+            mask = (q / 2 - V[vOffset + i]) >> 63;
+            right = ((V[vOffset + i] - q) & mask) | (V[vOffset + i] & (~mask));
+            test1 = (~(absolute(right) - (q / 2 - rejection))) >>> 63;
+
+            left = right;
+            right = (int)((right + (1 << (d - 1)) - 1) >> d);
+            right = left - (right << d);
+            test2 = (~(absolute(right) - ((1 << (d - 1)) - rejection))) >>> 63;
+
+            /* Two Tests Fail */
+            if ((test1 | test2) == 1L)
+            {
+
+                return true;
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+    /**********************************************************************************************************
+     * Description:	Checks Whether the Generated Error Polynomial or the Generated Secret Polynomial
+     *				Fulfills Certain Properties Needed in Key Generation Algorithm
+     *				For Heuristic qTESLA Security Category-1 and Security Category-3 (Option for Size or Speed)
+     *
+     * @param        polynomial        Parameter to be Checked
+     * @param        bound            Threshold of Summation
+     * @param        n                Polynomial Degree
+     * @param        h                Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return false            Fulfillment
+     * 				true			No Fulfillment
+     **********************************************************************************************************/
+    private static boolean checkPolynomial(int[] polynomial, int bound, int n, int h)
+    {
+
+        int summation = 0;
+        int limit = n;
+        int temporary;
+        int mask;
+        int[] list = new int[n];
+
+        for (int i = 0; i < n; i++)
+        {
+
+            list[i] = absolute(polynomial[i]);
+
+        }
+
+        for (int i = 0; i < h; i++)
+        {
+
+            for (int j = 0; j < limit - 1; j++)
+            {
+                /* If list[j + 1] > list[j] Then Exchanges Contents */
+                mask = (list[j + 1] - list[j]) >> 31;
+                temporary = (list[j + 1] & mask) | (list[j] & (~mask));
+                list[j + 1] = (list[j] & mask) | (list[j + 1] & (~mask));
+                list[j] = temporary;
+
+            }
+
+            summation += list[limit - 1];
+            limit--;
+
+        }
+
+        if (summation > bound)
+        {
+
+            return true;
+
+        }
+
+        return false;
+
+    }
+
+    /**********************************************************************************************************
+     * Description:	Checks Whether the Generated Error Polynomial or the Generated Secret Polynomial
+     *				Fulfills Certain Properties Needed in Key Generation Algorithm
+     *				For Provably-Secure qTESLA Security Category-1 and Security Category-3
+     *
+     * @param        polynomial        Parameter to be Checked
+     * @param        offset            Starting Point of the Polynomial to be Checked
+     * @param        bound            Threshold of Summation
+     * @param        n                Polynomial Degree
+     * @param        h                Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return false            Fulfillment
+     * 				true			No Fulfillment
+     **********************************************************************************************************/
+    private static boolean checkPolynomial(long[] polynomial, int offset, int bound, int n, int h)
+    {
+
+        int summation = 0;
+        int limit = n;
+        short temporary;
+        short mask;
+        short[] list = new short[n];
+
+        for (int i = 0; i < n; i++)
+        {
+
+            list[i] = (short)(absolute(polynomial[offset + i]));
+
+        }
+
+        for (int i = 0; i < h; i++)
+        {
+
+            for (int j = 0; j < limit - 1; j++)
+            {
+                /* If list[j + 1] > list[j] Then Exchanges Contents */
+                mask = (short)((list[j + 1] - list[j]) >> 15);
+                temporary = (short)((list[j + 1] & mask) | (list[j] & (~mask)));
+                list[j + 1] = (short)((list[j] & mask) | (list[j + 1] & (~mask)));
+                list[j] = temporary;
+
+            }
+
+            summation += (int)list[limit - 1];
+            limit--;
+
+        }
+
+        if (summation > bound)
+        {
+
+            return true;
+
+        }
+
+        return false;
+
+    }
+
+    /************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Heuristic qTESLA Security Category-1 and Security Category-3
+     *				(Option for Size or Speed)
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     * @param        n                                    Polynomial Degree
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        xi
+     * @param        zeta
+     * @param        errorBound                            Bound in Checking Error Polynomial
+     * @param        secretBound                            Bound in Checking Secret Polynomial
+     *
+     * @return 0                                    Successful Execution
+     ************************************************************************************************************************************************************/
+    private static int generateKeyPair(
+
+        byte[] publicKey, byte[] privateKey, SecureRandom secureRandom,
+        int n, int h, int q, long qInverse, int qLogarithm, int generatorA, int inverseNumberTheoreticTransform, double xi,
+        int[] zeta,
+        int errorBound, int secretBound)
+    {
+
+        /* Initialize Domain Separator for Error Polynomial and Secret Polynomial */
+        int nonce = 0;
+
+        byte[] randomness = new byte[Polynomial.RANDOM];
+
+        /* Extend Random Bytes to Seed Generation of Error Polynomial and Secret Polynomial */
+        byte[] randomnessExtended = new byte[Polynomial.SEED * 4];
+
+        int[] secretPolynomial = new int[n];
+        int[] errorPolynomial = new int[n];
+        int[] A = new int[n];
+        int[] T = new int[n];
+
+        /* Get randomnessExtended <- seedErrorPolynomial, seedSecretPolynomial, seedA, seedY */
+        // this.rng.randomByte (randomness, (short) 0, Polynomial.RANDOM);
+        secureRandom.nextBytes(randomness);
+
+        if (q == Parameter.Q_I)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(randomnessExtended, 0, Polynomial.SEED * 4, randomness, 0, Polynomial.RANDOM);
+
+        }
+
+        if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(randomnessExtended, 0, Polynomial.SEED * 4, randomness, 0, Polynomial.RANDOM);
+
+        }
+
+        /*
+         * Sample the Error Polynomial Fulfilling the Criteria
+         * Choose All Error Polynomial in R with Entries from D_SIGMA
+         * Repeat Step at Iteration if the h Largest Entries of Error Polynomial Summation to L_E
+         */
+        do
+        {
+
+            if (q == Parameter.Q_I)
+            {
+
+                Sample.polynomialGaussSamplerI(errorPolynomial, 0, randomnessExtended, 0, ++nonce);
+
+            }
+
+            if (q == Parameter.Q_III_SIZE)
+            {
+
+                Sample.polynomialGaussSamplerIII(errorPolynomial, 0, randomnessExtended, 0, ++nonce, n, xi, Sample.EXPONENTIAL_DISTRIBUTION_III_SIZE);
+
+            }
+
+            if (q == Parameter.Q_III_SPEED)
+            {
+
+                Sample.polynomialGaussSamplerIII(errorPolynomial, 0, randomnessExtended, 0, ++nonce, n, xi, Sample.EXPONENTIAL_DISTRIBUTION_III_SPEED);
+
+            }
+
+        }
+        while (checkPolynomial(errorPolynomial, errorBound, n, h) == true);
+
+        /*
+         * Sample the Secret Polynomial Fulfilling the Criteria
+         * Choose Secret Polynomial in R with Entries from D_SIGMA
+         * Repeat Step if the h Largest Entries of Secret Polynomial Summation to L_S
+         */
+        do
+        {
+
+            if (q == Parameter.Q_I)
+            {
+
+                Sample.polynomialGaussSamplerI(secretPolynomial, 0, randomnessExtended, Polynomial.SEED, ++nonce);
+
+            }
+
+            if (q == Parameter.Q_III_SIZE)
+            {
+
+                Sample.polynomialGaussSamplerIII(secretPolynomial, 0, randomnessExtended, Polynomial.SEED, ++nonce, n, xi, Sample.EXPONENTIAL_DISTRIBUTION_III_SIZE);
+
+            }
+
+            if (q == Parameter.Q_III_SPEED)
+            {
+
+                Sample.polynomialGaussSamplerIII(secretPolynomial, 0, randomnessExtended, Polynomial.SEED, ++nonce, n, xi, Sample.EXPONENTIAL_DISTRIBUTION_III_SPEED);
+
+            }
+
+        }
+        while (checkPolynomial(secretPolynomial, secretBound, n, h) == true);
+
+        /* Generate Uniform Polynomial A */
+        Polynomial.polynomialUniform(A, randomnessExtended, Polynomial.SEED * 2, n, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform);
+
+        /* Compute the Public Key T = A * secretPolynomial + errorPolynomial */
+        Polynomial.polynomialMultiplication(T, A, secretPolynomial, n, q, qInverse, zeta);
+        Polynomial.polynomialAdditionCorrection(T, T, errorPolynomial, n, q);
+
+        /* Pack Public and Private Keys */
+        if (q == Parameter.Q_I)
+        {
+
+            Pack.encodePrivateKeyI(privateKey, secretPolynomial, errorPolynomial, randomnessExtended, Polynomial.SEED * 2);
+            Pack.encodePublicKey(publicKey, T, randomnessExtended, Polynomial.SEED * 2, Parameter.N_I, Parameter.Q_LOGARITHM_I);
+
+        }
+
+        if (q == Parameter.Q_III_SIZE)
+        {
+
+            Pack.encodePrivateKeyIIISize(privateKey, secretPolynomial, errorPolynomial, randomnessExtended, Polynomial.SEED * 2);
+            Pack.encodePublicKey(publicKey, T, randomnessExtended, Polynomial.SEED * 2, Parameter.N_III_SIZE, Parameter.Q_LOGARITHM_III_SIZE);
+
+        }
+
+        if (q == Parameter.Q_III_SPEED)
+        {
+
+            Pack.encodePrivateKeyIIISpeed(privateKey, secretPolynomial, errorPolynomial, randomnessExtended, Polynomial.SEED * 2);
+            Pack.encodePublicKeyIIISpeed(publicKey, T, randomnessExtended, Polynomial.SEED * 2);
+
+        }
+
+        return 0;
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for
+     * 				Heuristic qTESLA Security Category-1
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     *
+     ****************************************************************************************************************************************************************/
+    public static int generateKeyPairI(byte[] publicKey, byte[] privateKey, SecureRandom secureRandom)
+    {
+
+        return generateKeyPair(
+
+            publicKey, privateKey, secureRandom,
+            Parameter.N_I, Parameter.H_I, Parameter.Q_I, Parameter.Q_INVERSE_I, Parameter.Q_LOGARITHM_I,
+            Parameter.GENERATOR_A_I, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I,
+            Parameter.XI_I,
+            PolynomialHeuristic.ZETA_I,
+            Parameter.KEY_GENERATOR_BOUND_E_I, Parameter.KEY_GENERATOR_BOUND_S_I
+
+        );
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Heuristic qTESLA Security Category-3 (Option for Size)
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     ****************************************************************************************************************************************************************/
+    public static int generateKeyPairIIISize(byte[] publicKey, byte[] privateKey, SecureRandom secureRandom)
+    {
+
+        return generateKeyPair(
+
+            publicKey, privateKey, secureRandom,
+            Parameter.N_III_SIZE, Parameter.H_III_SIZE, Parameter.Q_III_SIZE, Parameter.Q_INVERSE_III_SIZE, Parameter.Q_LOGARITHM_III_SIZE,
+            Parameter.GENERATOR_A_III_SIZE, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SIZE,
+            Parameter.XI_III_SIZE,
+            PolynomialHeuristic.ZETA_III_SIZE,
+            Parameter.KEY_GENERATOR_BOUND_E_III_SIZE, Parameter.KEY_GENERATOR_BOUND_S_III_SIZE
+
+        );
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Heuristic qTESLA Security Category-3
+     * 				(Option for Speed)
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     *
+     ****************************************************************************************************************************************************************/
+    public static int generateKeyPairIIISpeed(byte[] publicKey, byte[] privateKey, SecureRandom secureRandom)
+    {
+
+        return generateKeyPair(
+
+            publicKey, privateKey, secureRandom,
+            Parameter.N_III_SPEED, Parameter.H_III_SPEED, Parameter.Q_III_SPEED, Parameter.Q_INVERSE_III_SPEED, Parameter.Q_LOGARITHM_III_SPEED,
+            Parameter.GENERATOR_A_III_SPEED, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SPEED,
+            Parameter.XI_III_SPEED,
+            PolynomialHeuristic.ZETA_III_SPEED,
+            Parameter.KEY_GENERATOR_BOUND_E_III_SPEED, Parameter.KEY_GENERATOR_BOUND_S_III_SPEED
+
+        );
+
+    }
+
+    /*******************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Provably-Secure qTESLA Security Category-1
+     * 				and Category-3
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     * @param        n                                    Polynomial Degree
+     * @param        k                                    Number of Ring-Learning-With-Errors Samples
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        xi
+     * @param        zeta
+     * @param        errorBound                            Bound in Checking Error Polynomial
+     * @param        secretBound                            Bound in Checking Secret Polynomial
+     *
+     * @return 0                                    Successful Execution
+     *******************************************************************************************************************************************************/
+    private static int generateKeyPair(
+
+        byte[] publicKey, byte[] privateKey, SecureRandom secureRandom,
+        int n, int k, int h, int q, long qInverse, int qLogarithm, int generatorA, int inverseNumberTheoreticTransform, double xi,
+        long[] zeta,
+        int errorBound, int secretBound
+
+    )
+    {
+
+        /* Initialize Domain Separator for Error Polynomial and Secret Polynomial */
+        int nonce = 0;
+
+        long mask;
+
+        byte[] randomness = new byte[Polynomial.RANDOM];
+
+        /* Extend Random Bytes to Seed Generation of Error Polynomial and Secret Polynomial */
+        byte[] randomnessExtended = new byte[Polynomial.SEED * (k + 3)];
+
+        long[] secretPolynomial = new long[n];
+        long[] secretPolynomialNumberTheoreticTransform = new long[n];
+        long[] errorPolynomial = new long[n * k];
+        long[] A = new long[n * k];
+        long[] T = new long[n * k];
+
+        /* Get randomnessExtended <- seedErrorPolynomial, seedSecretPolynomial, seedA, seedY */
+//        rng.randomByte(randomness, 0, Polynomial.RANDOM);
+        secureRandom.nextBytes (randomness);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                randomnessExtended, 0, Polynomial.SEED * (k + 3), randomness, 0, Polynomial.RANDOM
+            );
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                randomnessExtended, 0, Polynomial.SEED * (k + 3), randomness, 0, Polynomial.RANDOM
+            );
+
+        }
+
+        /*
+         * Sample the Error Polynomial Fulfilling the Criteria
+         * Choose All Error Polynomial_i in R with Entries from D_SIGMA
+         * Repeat Step at Iteration if the h Largest Entries of Error Polynomial_k Summation to L_E
+         */
+        for (int i = 0; i < k; i++)
+        {
+
+            do
+            {
+
+                if (q == Parameter.Q_I_P)
+                {
+
+                    Sample.polynomialGaussSamplerIP(errorPolynomial, n * i, randomnessExtended, Polynomial.SEED * i, ++nonce);
+
+                }
+
+                if (q == Parameter.Q_III_P)
+                {
+
+                    Sample.polynomialGaussSamplerIIIP(errorPolynomial, n * i, randomnessExtended, Polynomial.SEED * i, ++nonce);
+
+                }
+
+            }
+            while (checkPolynomial(errorPolynomial, n * i, errorBound, n, h) == true);
+
+        }
+
+        /*
+         * Sample the Secret Polynomial Fulfilling the Criteria
+         * Choose Secret Polynomial in R with Entries from D_SIGMA
+         * Repeat Step if the h Largest Entries of Secret Polynomial Summation to L_S
+         */
+        do
+        {
+
+            if (q == Parameter.Q_I_P)
+            {
+
+                Sample.polynomialGaussSamplerIP(secretPolynomial, 0, randomnessExtended, Polynomial.SEED * k, ++nonce);
+
+            }
+
+            if (q == Parameter.Q_III_P)
+            {
+
+                Sample.polynomialGaussSamplerIIIP(secretPolynomial, 0, randomnessExtended, Polynomial.SEED * k, ++nonce);
+
+            }
+
+        }
+        while (checkPolynomial(secretPolynomial, 0, secretBound, n, h) == true);
+
+        /* Generate Uniform Polynomial A */
+        Polynomial.polynomialUniform(
+            A, randomnessExtended, Polynomial.SEED * (k + 1), n, k, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform
+        );
+
+        Polynomial.polynomialNumberTheoreticTransform(secretPolynomialNumberTheoreticTransform, secretPolynomial, n);
+
+        /* Compute the Public Key T = A * secretPolynomial + errorPolynomial */
+        for (int i = 0; i < k; i++)
+        {
+
+            Polynomial.polynomialMultiplication(T, n * i, A, n * i, secretPolynomialNumberTheoreticTransform, 0, n, q, qInverse);
+            Polynomial.polynomialAddition(T, n * i, T, n * i, errorPolynomial, n * i, n);
+
+            for (int j = 0; j < n; j++)
+            {
+
+                mask = (q - T[n * i + j]) >> 63;
+                T[n * i + j] -= (q & mask);
+
+            }
+
+        }
+
+        /* Pack Public and Private Keys */
+        Pack.packPrivateKey(privateKey, secretPolynomial, errorPolynomial, randomnessExtended, Polynomial.SEED * (k + 1), n, k);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            Pack.encodePublicKeyIP(publicKey, T, randomnessExtended, Polynomial.SEED * (k + 1));
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            Pack.encodePublicKeyIIIP(publicKey, T, randomnessExtended, Polynomial.SEED * (k + 1));
+
+        }
+
+        return 0;
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     ****************************************************************************************************************************************************************/
+    public static int generateKeyPairIP(byte[] publicKey, byte[] privateKey, SecureRandom secureRandom)
+    {
+
+        return generateKeyPair(
+
+            publicKey, privateKey, secureRandom,
+            Parameter.N_I_P, Parameter.K_I_P, Parameter.H_I_P, Parameter.Q_I_P, Parameter.Q_INVERSE_I_P, Parameter.Q_LOGARITHM_I_P,
+            Parameter.GENERATOR_A_I_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I_P,
+            Parameter.XI_I_P,
+            PolynomialProvablySecure.ZETA_I_P,
+            Parameter.KEY_GENERATOR_BOUND_E_I_P, Parameter.KEY_GENERATOR_BOUND_S_I_P
+
+        );
+
+    }
+
+    /****************************************************************************************************************************************************************
+     * Description:	Generates A Pair of Public Key and Private Key for qTESLA Signature Scheme for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        publicKey                            Contains Public Key
+     * @param        privateKey                            Contains Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     ****************************************************************************************************************************************************************/
+    public static int generateKeyPairIIIP(byte[] publicKey, byte[] privateKey, SecureRandom secureRandom)
+    {
+
+        return generateKeyPair(
+
+            publicKey, privateKey, secureRandom,
+            Parameter.N_III_P, Parameter.K_III_P, Parameter.H_III_P, Parameter.Q_III_P, Parameter.Q_INVERSE_III_P, Parameter.Q_LOGARITHM_III_P,
+            Parameter.GENERATOR_A_III_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_P,
+            Parameter.XI_III_P,
+            PolynomialProvablySecure.ZETA_III_P,
+            Parameter.KEY_GENERATOR_BOUND_E_III_P, Parameter.KEY_GENERATOR_BOUND_S_III_P
+
+        );
+
+    }
+
+    /******************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Heuristic qTESLA Security Category-1 and
+     * 				Security Category-3 (Option for Size or Speed)
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     * @param        n                                    Polynomial Degree
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        b                                    Determines the Interval the Randomness is Chosen in During Signing
+     * @param        bBit                                b = (2 ^ bBit) - 1
+     * @param        d                                    Number of Rounded Bits
+     * @param        u                                    Bound in Checking Secret Polynomial
+     * @param        rejection                            Bound in Checking Error Polynomial
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     * @param        zeta
+     *
+     * @return 0                                    Successful Execution
+     ******************************************************************************************************************************************************/
+    private static int signing(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom,
+        int n, int h, int q, long qInverse, int qLogarithm, int b, int bBit, int d, int u, int rejection,
+        int generatorA, int inverseNumberTheoreticTransform,
+        int barrettMultiplication, int barrettDivision,
+        int[] zeta
+
+    )
+    {
+
+        byte[] C = new byte[Polynomial.HASH];
+        byte[] randomness = new byte[Polynomial.SEED];
+        byte[] randomnessInput = new byte[Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE];
+        byte[] seed = new byte[Polynomial.SEED * 2];
+        byte[] temporaryRandomnessInput	= new byte[Polynomial.RANDOM];
+        int[] positionList = new int[h];
+        short[] signList = new short[h];
+        short[] secretPolynomial = new short[n];
+        short[] errorPolynomial = new short[n];
+
+        int[] A = new int[n];
+        int[] V = new int[n];
+        int[] Y = new int[n];
+        int[] Z = new int[n];
+        int[] SC = new int[n];
+        int[] EC = new int[n];
+
+        /* Domain Separator for Sampling Y */
+        int nonce = 0;
+
+        if (q == Parameter.Q_I)
+        {
+
+            Pack.decodePrivateKeyI(seed, secretPolynomial, errorPolynomial, privateKey);
+
+        }
+
+        if (q == Parameter.Q_III_SIZE)
+        {
+
+            Pack.decodePrivateKeyIIISize(seed, secretPolynomial, errorPolynomial, privateKey);
+
+        }
+
+        if (q == Parameter.Q_III_SPEED)
+        {
+
+            Pack.decodePrivateKeyIIISpeed(seed, secretPolynomial, errorPolynomial, privateKey);
+
+        }
+
+//        rng.randomByte(randomnessInput, Polynomial.RANDOM, Polynomial.RANDOM);
+         secureRandom.nextBytes (temporaryRandomnessInput);
+         System.arraycopy (temporaryRandomnessInput, 0, randomnessInput, Polynomial.RANDOM, Polynomial.RANDOM);
+
+        System.arraycopy(seed, Polynomial.SEED, randomnessInput, 0, Polynomial.SEED);
+
+        if (q == Parameter.Q_I)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                randomnessInput, Polynomial.RANDOM + Polynomial.SEED, Polynomial.MESSAGE, message, 0, messageLength
+            );
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                randomness, 0, Polynomial.SEED, randomnessInput, 0, Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE
+            );
+
+        }
+
+        if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                randomnessInput, Polynomial.RANDOM + Polynomial.SEED, Polynomial.MESSAGE, message, 0, messageLength
+            );
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                randomness, 0, Polynomial.SEED, randomnessInput, 0, Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE
+            );
+
+        }
+
+        Polynomial.polynomialUniform(A, seed, 0, n, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform);
+
+        /* Loop Due to Possible Rejection */
+        while (true)
+        {
+
+            /* Sample Y Uniformly Random from -B to B */
+            Sample.sampleY(Y, randomness, 0, ++nonce, n, q, b, bBit);
+
+            /* V = A * Y Modulo Q */
+            Polynomial.polynomialMultiplication(V, A, Y, n, q, qInverse, zeta);
+
+            hashFunction(C, 0, V, randomnessInput, Polynomial.RANDOM + Polynomial.SEED, n, d, q);
+
+            /* Generate C = EncodeC (C') Where C' is the Hashing of V Together with Message */
+            Sample.encodeC(positionList, signList, C, 0, n, h);
+
+            Polynomial.sparsePolynomialMultiplication16(SC, secretPolynomial, positionList, signList, n, h);
+
+            /* Z = Y + EC Modulo Q */
+            Polynomial.polynomialAddition(Z, Y, SC, n);
+
+            /* Rejection Sampling */
+            if (testRejection(Z, n, b, u) == true)
+            {
+
+                continue;
+
+            }
+
+            Polynomial.sparsePolynomialMultiplication16(EC, errorPolynomial, positionList, signList, n, h);
+
+            /* V = V - EC modulo Q */
+            Polynomial.polynomialSubtractionCorrection(V, V, EC, n, q);
+
+            if (testV(V, n, d, q, rejection) == true)
+            {
+                continue;
+            }
+
+            if (q == Parameter.Q_I)
+            {
+                /* Pack Signature */
+                Pack.encodeSignature(signature, 0, C, 0, Z, n, d);
+            }
+
+            if (q == Parameter.Q_III_SIZE)
+            {
+                /* Pack Signature */
+                Pack.encodeSignature(signature, 0, C, 0, Z, n, d);
+            }
+
+            if (q == Parameter.Q_III_SPEED)
+            {
+                /* Pack Signature */
+                Pack.encodeSignatureIIISpeed(signature, 0, C, 0, Z);
+            }
+
+            return 0;
+
+        }
+
+    }
+
+    /*****************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Heuristic qTESLA Security Category-1
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     *****************************************************************************************************************************************************/
+    static int signingI(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom
+
+    )
+    {
+
+        return signing(
+
+            signature,
+            message, messageOffset, messageLength,
+            privateKey, secureRandom,
+            Parameter.N_I, Parameter.H_I, Parameter.Q_I, Parameter.Q_INVERSE_I, Parameter.Q_LOGARITHM_I,
+            Parameter.B_I, Parameter.B_BIT_I, Parameter.D_I, Parameter.U_I, Parameter.REJECTION_I,
+            Parameter.GENERATOR_A_I, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I,
+            Parameter.BARRETT_MULTIPLICATION_I, Parameter.BARRETT_DIVISION_I,
+            PolynomialHeuristic.ZETA_I
+
+        );
+
+    }
+
+    /*****************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Heuristic qTESLA Security Category-3
+     * 				(Option for Size)
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     *****************************************************************************************************************************************************/
+    static int signingIIISize(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom
+
+    )
+    {
+
+        return signing(
+
+            signature,
+            message, messageOffset, messageLength,
+            privateKey, secureRandom,
+            Parameter.N_III_SIZE, Parameter.H_III_SIZE, Parameter.Q_III_SIZE, Parameter.Q_INVERSE_III_SIZE, Parameter.Q_LOGARITHM_III_SIZE,
+            Parameter.B_III_SIZE, Parameter.B_BIT_III_SIZE, Parameter.D_III_SIZE, Parameter.U_III_SIZE, Parameter.REJECTION_III_SIZE,
+            Parameter.GENERATOR_A_III_SIZE, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SIZE,
+            Parameter.BARRETT_MULTIPLICATION_III_SIZE, Parameter.BARRETT_DIVISION_III_SIZE,
+            PolynomialHeuristic.ZETA_III_SIZE
+
+        );
+
+    }
+
+    /****************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Heuristic qTESLA Security Category-3
+     *				(Option for Speed)
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     ****************************************************************************************************************************************************/
+    static int signingIIISpeed(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom
+
+    )
+    {
+
+        return signing(
+
+            signature,
+            message, messageOffset, messageLength,
+            privateKey, secureRandom,
+            Parameter.N_III_SPEED, Parameter.H_III_SPEED, Parameter.Q_III_SPEED, Parameter.Q_INVERSE_III_SPEED, Parameter.Q_LOGARITHM_III_SPEED,
+            Parameter.B_III_SPEED, Parameter.B_BIT_III_SPEED, Parameter.D_III_SPEED, Parameter.U_III_SPEED, Parameter.REJECTION_III_SPEED,
+            Parameter.GENERATOR_A_III_SPEED, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SPEED,
+            Parameter.BARRETT_MULTIPLICATION_III_SPEED, Parameter.BARRETT_DIVISION_III_SPEED,
+            PolynomialHeuristic.ZETA_III_SPEED
+
+        );
+
+    }
+
+    /*****************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Provably-Secure qTESLA Security Category-1
+     *				and Category-3
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     * @param        n                                    Polynomial Degree
+     * @param        k                                    Number of Ring-Learning-With-Errors Samples
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        b                                    Determines the Interval the Randomness is Chosen in During Signing
+     * @param        bBit                                b = (2 ^ bBit) - 1
+     * @param        d                                    Number of Rounded Bits
+     * @param        u                                    Bound in Checking Secret Polynomial
+     * @param        rejection                            Bound in Checking Error Polynomial
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        privateKeySize                        Size of the Private Key
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     *
+     * @return 0                                    Successful Execution
+     *****************************************************************************************************************************************************/
+    private static int signing(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom,
+        int n, int k, int h, int q, long qInverse, int qLogarithm, int b, int bBit, int d, int u, int rejection,
+        int generatorA, int inverseNumberTheoreticTransform, int privateKeySize,
+        int barrettMultiplication, int barrettDivision
+
+    )
+    {
+
+        byte[] C = new byte[Polynomial.HASH];
+        byte[] randomness = new byte[Polynomial.SEED];
+        byte[] randomnessInput = new byte[Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE];
+        byte[] temporaryRandomnessInput	= new byte[Polynomial.RANDOM];
+        int[] positionList = new int[h];
+        short[] signList = new short[h];
+
+        long[] A = new long[n * k];
+        long[] V = new long[n * k];
+        long[] Y = new long[n];
+        long[] numberTheoreticTransformY = new long[n];
+        long[] Z = new long[n];
+        long[] SC = new long[n];
+        long[] EC = new long[n * k];
+
+        boolean response = false;
+
+        /* Domain Separator for Sampling Y */
+        int nonce = 0;
+
+//        rng.randomByte(randomnessInput, Polynomial.RANDOM, Polynomial.RANDOM);
+        secureRandom.nextBytes (temporaryRandomnessInput);
+        System.arraycopy (temporaryRandomnessInput, 0, randomnessInput, Polynomial.RANDOM, Polynomial.RANDOM);
+        System.arraycopy(privateKey, privateKeySize - Polynomial.SEED, randomnessInput, 0, Polynomial.SEED);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                randomnessInput, Polynomial.RANDOM + Polynomial.SEED, Polynomial.MESSAGE, message, 0, messageLength
+            );
+
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                randomness, 0, Polynomial.SEED, randomnessInput, 0, Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE
+            );
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                randomnessInput, Polynomial.RANDOM + Polynomial.SEED, Polynomial.MESSAGE, message, 0, messageLength
+            );
+
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                randomness, 0, Polynomial.SEED, randomnessInput, 0, Polynomial.RANDOM + Polynomial.SEED + Polynomial.MESSAGE
+            );
+
+        }
+
+        Polynomial.polynomialUniform(
+            A, privateKey, privateKeySize - 2 * Polynomial.SEED, n, k, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform
+        );
+
+        /* Loop Due to Possible Rejection */
+        while (true)
+        {
+
+            /* Sample Y Uniformly Random from -B to B */
+            Sample.sampleY(Y, randomness, 0, ++nonce, n, q, b, bBit);
+
+            Polynomial.polynomialNumberTheoreticTransform(numberTheoreticTransformY, Y, n);
+
+            /* V_i = A_i * Y Modulo Q for All i */
+            for (int i = 0; i < k; i++)
+            {
+
+                Polynomial.polynomialMultiplication(V, n * i, A, n * i, numberTheoreticTransformY, 0, n, q, qInverse);
+
+            }
+
+            hashFunction(C, 0, V, randomnessInput, Polynomial.RANDOM + Polynomial.SEED, n, k, d, q);
+
+            /* Generate C = EncodeC (C') Where C' is the Hashing of V Together with Message */
+            Sample.encodeC(positionList, signList, C, 0, n, h);
+
+            Polynomial.sparsePolynomialMultiplication8(SC, 0, privateKey, 0, positionList, signList, n, h);
+
+            /* Z = Y + EC modulo Q */
+            Polynomial.polynomialAddition(Z, 0, Y, 0, SC, 0, n);
+
+            /* Rejection Sampling */
+            if (testRejection(Z, n, b, u) == true)
+            {
+
+                continue;
+
+            }
+
+            for (int i = 0; i < k; i++)
+            {
+
+                Polynomial.sparsePolynomialMultiplication8(EC, n * i, privateKey, n * (i + 1), positionList, signList, n, h);
+
+                /* V_i = V_i - EC_i Modulo Q for All k */
+                Polynomial.polynomialSubtraction(V, n * i, V, n * i, EC, n * i, n, q, barrettMultiplication, barrettDivision);
+
+                response = testV(V, n * i, n, d, q, rejection);
+
+                if (response == true)
+                {
+
+                    break;
+
+                }
+
+            }
+
+            if (response == true)
+            {
+
+                continue;
+
+            }
+
+            if (q == Parameter.Q_I_P)
+            {
+                /* Pack Signature */
+                Pack.encodeSignatureIP(signature, 0, C, 0, Z);
+
+            }
+
+            if (q == Parameter.Q_III_P)
+            {
+                /* Pack Signature */
+                Pack.encodeSignatureIIIP(signature, 0, C, 0, Z);
+            }
+
+            return 0;
+
+        }
+
+    }
+
+    /*****************************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     *****************************************************************************************************************************************************/
+    public static int signingIP(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom
+
+    )
+    {
+
+        return signing(
+
+            signature,
+            message, messageOffset, messageLength,
+            privateKey, secureRandom,
+            Parameter.N_I_P, Parameter.K_I_P, Parameter.H_I_P, Parameter.Q_I_P, Parameter.Q_INVERSE_I_P, Parameter.Q_LOGARITHM_I_P,
+            Parameter.B_I_P, Parameter.B_BIT_I_P, Parameter.D_I_P, Parameter.U_I_P, Parameter.REJECTION_I_P,
+            Parameter.GENERATOR_A_I_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I_P, Polynomial.PRIVATE_KEY_I_P,
+            Parameter.BARRETT_MULTIPLICATION_I_P, Parameter.BARRETT_DIVISION_I_P
+
+        );
+
+    }
+
+    /**********************************************************************************************************************************************
+     * Description:	Generates A Signature for A Given Message According to the Ring-TESLA Signature Scheme for Provably-Secure
+     * 				qTESLA Security Category-3
+     *
+     * @param        message                                Message to be Signed
+     * @param        messageOffset                        Starting Point of the Message to be Signed
+     * @param        messageLength                        Length of the Message to be Signed
+     * @param        signature                            Output Package Containing Signature
+     * @param        privateKey                            Private Key
+     * @param        secureRandom                        Source of Randomness
+     *
+     * @return 0                                    Successful Execution
+     **********************************************************************************************************************************************/
+    public static int signingIIIP(
+
+        byte[] signature,
+        final byte[] message, int messageOffset, int messageLength,
+        final byte[] privateKey, SecureRandom secureRandom
+
+    )
+    {
+
+        return signing(
+
+            signature,
+            message, messageOffset, messageLength,
+            privateKey, secureRandom,
+            Parameter.N_III_P, Parameter.K_III_P, Parameter.H_III_P, Parameter.Q_III_P, Parameter.Q_INVERSE_III_P, Parameter.Q_LOGARITHM_III_P,
+            Parameter.B_III_P, Parameter.B_BIT_III_P, Parameter.D_III_P, Parameter.U_III_P, Parameter.REJECTION_III_P,
+            Parameter.GENERATOR_A_III_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_P, Polynomial.PRIVATE_KEY_III_P,
+            Parameter.BARRETT_MULTIPLICATION_III_P, Parameter.BARRETT_DIVISION_III_P
+
+        );
+
+    }
+
+    /*********************************************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for A Given Signature Package
+     * 				for Heuristic qTESLA Security Category-1 and Security Category-3 (Option for Size of Speed)
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     * @param        n                                    Polynomial Degree
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        b                                    Determines the Interval the Randomness is Chosen in During Signing
+     * @param        d                                    Number of Rounded Bits
+     * @param        u                                    Bound in Checking Secret Polynomial
+     * @param        r
+     * @param        signatureSize                        Size of the Given Signature Package
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     * @param        zeta
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     *********************************************************************************************************************************/
+    private static int verifying(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey,
+        int n, int h, int q, long qInverse, int qLogarithm, int b, int d, int u, int r, int signatureSize,
+        int generatorA, int inverseNumberTheoreticTransform,
+        int barrettMultiplication, int barrettDivision,
+        int[] zeta
+
+    )
+    {
+
+        byte[] C = new byte[Polynomial.HASH];
+        byte[] cSignature = new byte[Polynomial.HASH];
+        byte[] seed = new byte[Polynomial.SEED];
+        byte[] hashMessage = new byte[Polynomial.MESSAGE];
+        int[] newPublicKey = new int[n];
+
+        int[] positionList = new int[h];
+        short[] signList = new short[h];
+
+        int[] W = new int[n];
+        int[] Z = new int[n];
+        int[] TC = new int[n];
+        int[] A = new int[n];
+
+        if (signatureLength < signatureSize)
+        {
+
+            return -1;
+
+        }
+
+        if (q == Parameter.Q_I || q == Parameter.Q_III_SIZE)
+        {
+
+            Pack.decodeSignature(C, Z, signature, signatureOffset, n, d);
+
+        }
+
+        if (q == Parameter.Q_III_SPEED)
+        {
+
+            Pack.decodeSignatureIIISpeed(C, Z, signature, signatureOffset);
+
+        }
+
+        /* Check Norm of Z */
+        if (testZ(Z, n, b, u) == true)
+        {
+
+            return -2;
+
+        }
+
+        if (q == Parameter.Q_I || q == Parameter.Q_III_SIZE)
+        {
+
+            Pack.decodePublicKey(newPublicKey, seed, 0, publicKey, n, qLogarithm);
+
+        }
+
+        if (q == Parameter.Q_III_SPEED)
+        {
+
+            Pack.decodePublicKeyIIISpeed(newPublicKey, seed, 0, publicKey);
+
+        }
+
+        /* Generate A Polynomial */
+        Polynomial.polynomialUniform(A, seed, 0, n, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform);
+
+        Sample.encodeC(positionList, signList, C, 0, n, h);
+
+        /* W = A * Z - TC */
+        Polynomial.sparsePolynomialMultiplication32(TC, newPublicKey, positionList, signList, n, h);
+
+        Polynomial.polynomialMultiplication(W, A, Z, n, q, qInverse, zeta);
+
+        Polynomial.polynomialSubtractionMontgomery(W, W, TC, n, q, qInverse, r);
+
+        if (q == Parameter.Q_I)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                hashMessage, 0, Polynomial.MESSAGE, message, 0, message.length
+            );
+
+        }
+
+        if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                hashMessage, 0, Polynomial.MESSAGE, message, 0, message.length
+            );
+
+        }
+
+        /* Obtain the Hash Symbol */
+        hashFunction(cSignature, 0, W, hashMessage, 0, n, d, q);
+
+        /* Check if Same With One from Signature */
+        if (CommonFunction.memoryEqual(C, 0, cSignature, 0, Polynomial.HASH) == false)
+        {
+            return -3;
+        }
+
+        return 0;
+
+    }
+
+    /*******************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for
+     * 				A Given Signature Package for Heuristic qTESLA Security Category-1
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     *******************************************************************************************************/
+    static int verifyingI(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey
+
+    )
+    {
+
+        return verifying(
+
+            message,
+            signature, signatureOffset, signatureLength,
+            publicKey,
+            Parameter.N_I, Parameter.H_I, Parameter.Q_I, Parameter.Q_INVERSE_I, Parameter.Q_LOGARITHM_I,
+            Parameter.B_I, Parameter.D_I, Parameter.U_I, Parameter.R_I,
+            Polynomial.SIGNATURE_I,
+            Parameter.GENERATOR_A_I, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I,
+            Parameter.BARRETT_MULTIPLICATION_I, Parameter.BARRETT_DIVISION_I,
+            PolynomialHeuristic.ZETA_I
+
+        );
+
+    }
+
+    /******************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for
+     *				A Given Signature Package for Heuristic qTESLA Security Category-3 (Option for Size)
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     ******************************************************************************************************/
+    static int verifyingIIISize(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey
+
+    )
+    {
+
+        return verifying(
+
+            message,
+            signature, signatureOffset, signatureLength,
+            publicKey,
+            Parameter.N_III_SIZE, Parameter.H_III_SIZE,
+            Parameter.Q_III_SIZE, Parameter.Q_INVERSE_III_SIZE, Parameter.Q_LOGARITHM_III_SIZE,
+            Parameter.B_III_SIZE, Parameter.D_III_SIZE, Parameter.U_III_SIZE, Parameter.R_III_SIZE,
+            Polynomial.SIGNATURE_III_SIZE,
+            Parameter.GENERATOR_A_III_SIZE, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SIZE,
+            Parameter.BARRETT_MULTIPLICATION_III_SIZE, Parameter.BARRETT_DIVISION_III_SIZE,
+            PolynomialHeuristic.ZETA_III_SIZE
+
+        );
+
+    }
+
+    /**********************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for
+     * 				A Given Signature Package for Heuristic qTESLA Security Category-3 (Option for Speed)
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     **********************************************************************************************************/
+    static int verifyingIIISpeed(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey
+
+    )
+    {
+
+        return verifying(
+
+            message,
+            signature, signatureOffset, signatureLength,
+            publicKey,
+            Parameter.N_III_SPEED, Parameter.H_III_SPEED,
+            Parameter.Q_III_SPEED, Parameter.Q_INVERSE_III_SPEED, Parameter.Q_LOGARITHM_III_SPEED,
+            Parameter.B_III_SPEED, Parameter.D_III_SPEED, Parameter.U_III_SPEED, Parameter.R_III_SPEED,
+            Polynomial.SIGNATURE_III_SPEED,
+            Parameter.GENERATOR_A_III_SPEED, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_SPEED,
+            Parameter.BARRETT_MULTIPLICATION_III_SPEED, Parameter.BARRETT_DIVISION_III_SPEED,
+            PolynomialHeuristic.ZETA_III_SPEED
+
+        );
+
+    }
+
+    /**************************************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for A Given Signature
+     * 				Package for Provably-Secure qTESLA Security Category-1 and Category-3
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     * @param        n                                    Polynomial Degree
+     * @param        k                                    Number of Ring-Learning-With-Errors Samples
+     * @param        h                                    Number of Non-Zero Entries of Output Elements of Encryption
+     * @param        q                                    Modulus
+     * @param        qInverse
+     * @param        qLogarithm                            q <= 2 ^ qLogarithm
+     * @param        b                                    Determines the Interval the Randomness is Chosen in During Signing
+     * @param        d                                    Number of Rounded Bits
+     * @param        u                                    Bound in Checking Secret Polynomial
+     * @param        generatorA
+     * @param        inverseNumberTheoreticTransform
+     * @param        barrettMultiplication
+     * @param        barrettDivision
+     * @param        zeta
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     *************************************************************************************************************************/
+    private static int verifying(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey,
+        int n, int k, int h, int q, long qInverse, int qLogarithm, int b, int d, int u, int signatureSize,
+        int generatorA, int inverseNumberTheoreticTransform,
+        int barrettMultiplication, int barrettDivision,
+        long[] zeta
+
+    )
+    {
+
+        byte[] C = new byte[Polynomial.HASH];
+        byte[] cSignature = new byte[Polynomial.HASH];
+        byte[] seed = new byte[Polynomial.SEED];
+        byte[] hashMessage = new byte[Polynomial.MESSAGE];
+        int[] newPublicKey = new int[n * k];
+
+        int[] positionList = new int[h];
+        short[] signList = new short[h];
+
+        long[] W = new long[n * k];
+        long[] Z = new long[n];
+        long[] numberTheoreticTransformZ = new long[n];
+        long[] TC = new long[n * k];
+        long[] A = new long[n * k];
+
+        if (signatureLength < signatureSize)
+        {
+
+            return -1;
+
+        }
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            Pack.decodeSignatureIP(C, Z, signature, signatureOffset);
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            Pack.decodeSignatureIIIP(C, Z, signature, signatureOffset);
+
+        }
+
+        /* Check Norm of Z */
+        if (testZ(Z, n, b, u) == true)
+        {
+
+            return -2;
+
+        }
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            Pack.decodePublicKeyIP(newPublicKey, seed, 0, publicKey);
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            Pack.decodePublicKeyIIIP(newPublicKey, seed, 0, publicKey);
+
+        }
+
+        /* Generate A Polynomial */
+        Polynomial.polynomialUniform(A, seed, 0, n, k, q, qInverse, qLogarithm, generatorA, inverseNumberTheoreticTransform);
+
+        Sample.encodeC(positionList, signList, C, 0, n, h);
+
+        Polynomial.polynomialNumberTheoreticTransform(numberTheoreticTransformZ, Z, n);
+
+        /* W_i = A_i * Z_i - TC_i for All i */
+        for (int i = 0; i < k; i++)
+        {
+
+            Polynomial.polynomialMultiplication(W, n * i, A, n * i, numberTheoreticTransformZ, 0, n, q, qInverse);
+
+            Polynomial.sparsePolynomialMultiplication32(
+                TC, n * i, newPublicKey, n * i, positionList, signList, n, h, q, barrettMultiplication, barrettDivision
+            );
+
+            Polynomial.polynomialSubtraction(W, n * i, W, n * i, TC, n * i, n, q, barrettMultiplication, barrettDivision);
+
+        }
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK128(
+                hashMessage, 0, Polynomial.MESSAGE, message, 0, message.length
+            );
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            HashUtils.secureHashAlgorithmKECCAK256(
+                hashMessage, 0, Polynomial.MESSAGE, message, 0, message.length
+            );
+
+        }
+
+        /* Obtain the Hash Symbol */
+        hashFunction(cSignature, 0, W, hashMessage, 0, n, k, d, q);
+
+        /* Check if Same with One from Signature */
+        if (CommonFunction.memoryEqual(C, 0, cSignature, 0, Polynomial.HASH) == false)
+        {
+            return -3;
+        }
+
+        return 0;
+
+    }
+
+    /*****************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for
+     * 				A Given Signature Package for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     *****************************************************************************************************/
+    static int verifyingPI(
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey
+    )
+    {
+
+        return verifying(
+
+            message,
+            signature, signatureOffset, signatureLength,
+            publicKey,
+            Parameter.N_I_P, Parameter.K_I_P, Parameter.H_I_P,
+            Parameter.Q_I_P, Parameter.Q_INVERSE_I_P, Parameter.Q_LOGARITHM_I_P,
+            Parameter.B_I_P, Parameter.D_I_P, Parameter.U_I_P, Polynomial.SIGNATURE_I_P,
+            Parameter.GENERATOR_A_I_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_I_P,
+            Parameter.BARRETT_MULTIPLICATION_I_P, Parameter.BARRETT_DIVISION_I_P,
+            PolynomialProvablySecure.ZETA_I_P
+
+        );
+
+    }
+
+    /*****************************************************************************************************
+     * Description:	Extracts the Original Message and Checks Whether the Generated Signature is Valid for
+     * 				A Given Signature Package for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        signature                            Given Signature Package
+     * @param        signatureOffset                        Starting Point of the Given Signature Package
+     * @param        signatureLength                        Length of the Given Signature Package
+     * @param        message                                Original (Signed) Message
+     * @param        publicKey                            Public Key
+     *
+     * @return 0                                    Valid Signature
+     * 				< 0									Invalid Signature
+     *****************************************************************************************************/
+    static int verifyingPIII(
+
+        byte[] message,
+        final byte[] signature, int signatureOffset, int signatureLength,
+        final byte[] publicKey
+
+    )
+    {
+        return verifying(
+            message,
+            signature, signatureOffset, signatureLength,
+            publicKey,
+            Parameter.N_III_P, Parameter.K_III_P, Parameter.H_III_P,
+            Parameter.Q_III_P, Parameter.Q_INVERSE_III_P, Parameter.Q_LOGARITHM_III_P,
+            Parameter.B_III_P, Parameter.D_III_P, Parameter.U_III_P, Polynomial.SIGNATURE_III_P,
+            Parameter.GENERATOR_A_III_P, Parameter.INVERSE_NUMBER_THEORETIC_TRANSFORM_III_P,
+            Parameter.BARRETT_MULTIPLICATION_III_P, Parameter.BARRETT_DIVISION_III_P,
+            PolynomialProvablySecure.ZETA_III_P
+        );
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyGenerationParameters.java
new file mode 100644
index 0000000..7992330
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyGenerationParameters.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * qTESLA key-pair generation parameters.
+ */
+public class QTESLAKeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    private final int securityCategory;
+
+    /**
+     * Base constructor - provide the qTESLA security category and a source of randomness.
+     *
+     * @param securityCategory the security category to generate the parameters for.
+     * @param random           the random byte source.
+     */
+    public QTESLAKeyGenerationParameters(int securityCategory, SecureRandom random)
+    {
+        super(random, -1);
+
+        QTESLASecurityCategory.getPrivateSize(securityCategory);  // check the category is valid
+
+        this.securityCategory = securityCategory;
+    }
+
+    /**
+      * Return the security category for these parameters.
+      *
+      * @return the security category for keys generated using these parameters.
+      */
+    public int getSecurityCategory()
+    {
+        return securityCategory;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyPairGenerator.java
new file mode 100644
index 0000000..b3b5dc3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAKeyPairGenerator.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * Key-pair generator for qTESLA keys.
+ */
+public final class QTESLAKeyPairGenerator
+    implements AsymmetricCipherKeyPairGenerator
+{
+    /**
+     * qTESLA Security Category
+     */
+    private int securityCategory;
+    private SecureRandom secureRandom;
+
+    /**
+     * Initialize the generator with a security category and a source of randomness.
+     *
+     * @param param a {@link QTESLAKeyGenerationParameters} object.
+     */
+    public void init(
+        KeyGenerationParameters param)
+    {
+        QTESLAKeyGenerationParameters parameters = (QTESLAKeyGenerationParameters)param;
+
+        this.secureRandom = parameters.getRandom();
+        this.securityCategory = parameters.getSecurityCategory();
+    }
+
+    /**
+     * Generate a key-pair.
+     *
+     * @return a matching key-pair consisting of (QTESLAPublicKeyParameters, QTESLAPrivateKeyParameters).
+     */
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        byte[] privateKey = allocatePrivate(securityCategory);
+        byte[] publicKey = allocatePublic(securityCategory);
+
+        switch (securityCategory)
+        {
+        case QTESLASecurityCategory.HEURISTIC_I:
+            QTESLA.generateKeyPairI(publicKey, privateKey, secureRandom);
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SIZE:
+            QTESLA.generateKeyPairIIISize(publicKey, privateKey, secureRandom);
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SPEED:
+            QTESLA.generateKeyPairIIISpeed(publicKey, privateKey, secureRandom);
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_I:
+            QTESLA.generateKeyPairIP(publicKey, privateKey, secureRandom);
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_III:
+            QTESLA.generateKeyPairIIIP(publicKey, privateKey, secureRandom);
+            break;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+
+        return new AsymmetricCipherKeyPair(new QTESLAPublicKeyParameters(securityCategory, publicKey), new QTESLAPrivateKeyParameters(securityCategory, privateKey));
+    }
+
+    private byte[] allocatePrivate(int securityCategory)
+    {
+        return new byte[QTESLASecurityCategory.getPrivateSize(securityCategory)];
+    }
+
+    private byte[] allocatePublic(int securityCategory)
+    {
+        return new byte[QTESLASecurityCategory.getPublicSize(securityCategory)];
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPrivateKeyParameters.java
new file mode 100644
index 0000000..38c1ecb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPrivateKeyParameters.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * qTESLA private key
+ */
+public final class QTESLAPrivateKeyParameters
+    extends AsymmetricKeyParameter
+{
+    /**
+     * qTESLA Security Category (From 4 To 8)
+     */
+    private int securityCategory;
+
+    /**
+     * Text of the qTESLA Private Key
+     */
+    private byte[] privateKey;
+
+    /**
+     * Base constructor.
+     *
+     * @param securityCategory the security category for the passed in public key data.
+     * @param privateKey the private key data.
+     */
+    public QTESLAPrivateKeyParameters(int securityCategory, byte[] privateKey)
+    {
+        super(true);
+
+        if (privateKey.length != QTESLASecurityCategory.getPrivateSize(securityCategory))
+        {
+            throw new IllegalArgumentException("invalid key size for security category");
+        }
+
+        this.securityCategory = securityCategory;
+        this.privateKey = Arrays.clone(privateKey);
+    }
+
+    /**
+     * Return the security category for this key.
+     *
+     * @return the key's security category.
+     */
+    public int getSecurityCategory()
+    {
+        return this.securityCategory;
+    }
+
+    /**
+     * Return the key's secret value.
+     *
+     * @return key private data.
+     */
+    public byte[] getSecret()
+    {
+        return Arrays.clone(privateKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPublicKeyParameters.java
new file mode 100644
index 0000000..6be4c6b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLAPublicKeyParameters.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * qTESLA public key
+ */
+public final class QTESLAPublicKeyParameters
+    extends AsymmetricKeyParameter
+{
+    /**
+     * qTESLA Security Category
+     */
+    private int securityCategory;
+
+    /**
+     * Text of the qTESLA Public Key
+     */
+    private byte[] publicKey;
+
+    /**
+     * Base constructor.
+     *
+     * @param securityCategory the security category for the passed in public key data.
+     * @param publicKey the public key data.
+     */
+    public QTESLAPublicKeyParameters(int securityCategory, byte[] publicKey)
+    {
+        super(false);
+
+        if (publicKey.length != QTESLASecurityCategory.getPublicSize(securityCategory))
+        {
+            throw new IllegalArgumentException("invalid key size for security category");
+        }
+
+        this.securityCategory = securityCategory;
+        this.publicKey = Arrays.clone(publicKey);
+
+    }
+
+    /**
+     * Return the security category for this key.
+     *
+     * @return the key's security category.
+     */
+    public int getSecurityCategory()
+    {
+        return this.securityCategory;
+    }
+
+    /**
+     * Return the key's public value.
+     *
+     * @return key public data.
+     */
+    public byte[] getPublicData()
+    {
+        return Arrays.clone(publicKey);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASecurityCategory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASecurityCategory.java
new file mode 100644
index 0000000..7460741
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASecurityCategory.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+/**
+ * The qTESLA security categories.
+ */
+public class QTESLASecurityCategory
+{
+    public static final int HEURISTIC_I = 0;
+    public static final int HEURISTIC_III_SIZE = 1;
+    public static final int HEURISTIC_III_SPEED = 2;
+    public static final int PROVABLY_SECURE_I = 3;
+    public static final int PROVABLY_SECURE_III = 4;
+
+    private QTESLASecurityCategory()
+    {
+    }
+
+    static void validate(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case HEURISTIC_I:
+        case HEURISTIC_III_SIZE:
+        case HEURISTIC_III_SPEED:
+        case PROVABLY_SECURE_I:
+        case PROVABLY_SECURE_III:
+            break;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+
+    static int getPrivateSize(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case HEURISTIC_I:
+            return Polynomial.PRIVATE_KEY_I;
+        case HEURISTIC_III_SIZE:
+            return Polynomial.PRIVATE_KEY_III_SIZE;
+        case HEURISTIC_III_SPEED:
+            return Polynomial.PRIVATE_KEY_III_SPEED;
+        case PROVABLY_SECURE_I:
+            return Polynomial.PRIVATE_KEY_I_P;
+        case PROVABLY_SECURE_III:
+            return Polynomial.PRIVATE_KEY_III_P;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+
+    static int getPublicSize(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case HEURISTIC_I:
+            return Polynomial.PUBLIC_KEY_I;
+        case HEURISTIC_III_SIZE:
+            return Polynomial.PUBLIC_KEY_III_SIZE;
+        case HEURISTIC_III_SPEED:
+            return Polynomial.PUBLIC_KEY_III_SPEED;
+        case PROVABLY_SECURE_I:
+            return Polynomial.PUBLIC_KEY_I_P;
+        case PROVABLY_SECURE_III:
+            return Polynomial.PUBLIC_KEY_III_P;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+
+    static int getSignatureSize(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case HEURISTIC_I:
+            return Polynomial.SIGNATURE_I;
+        case HEURISTIC_III_SIZE:
+            return Polynomial.SIGNATURE_III_SIZE;
+        case HEURISTIC_III_SPEED:
+            return Polynomial.SIGNATURE_III_SPEED;
+        case PROVABLY_SECURE_I:
+            return Polynomial.SIGNATURE_I_P;
+        case PROVABLY_SECURE_III:
+            return Polynomial.SIGNATURE_III_P;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+
+    /**
+     * Return a standard name for the security category.
+     *
+     * @param securityCategory the category of interest.
+     * @return the name for the category.
+     */
+    public static String getName(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case HEURISTIC_I:
+            return "qTESLA-I";
+        case HEURISTIC_III_SIZE:
+            return "qTESLA-III-size";
+        case HEURISTIC_III_SPEED:
+            return "qTESLA-III-speed";
+        case PROVABLY_SECURE_I:
+            return "qTESLA-p-I";
+        case PROVABLY_SECURE_III:
+            return "qTESLA-p-III";
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASigner.java
new file mode 100644
index 0000000..2e64510
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/QTESLASigner.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.MessageSigner;
+
+/**
+ * Signer for the qTESLA algorithm (https://qtesla.org/)
+ */
+public class QTESLASigner
+    implements MessageSigner
+{
+    /**
+     * The Public Key of the Identity Whose Signature Will be Generated
+     */
+    private QTESLAPublicKeyParameters publicKey;
+
+    /**
+     * The Private Key of the Identity Whose Signature Will be Generated
+     */
+    private QTESLAPrivateKeyParameters privateKey;
+
+    /**
+     * The Source of Randomness for private key operations
+     */
+    private SecureRandom secureRandom;
+
+    public QTESLASigner()
+    {
+    }
+
+    /**
+     * Initialise the signer.
+     *
+     * @param forSigning true if we are generating a signature, false
+     *                   otherwise.
+     * @param param      ParametersWithRandom containing a private key for signature generation, public key otherwise.
+     */
+    public void init(boolean forSigning, CipherParameters param)
+    {
+         if (forSigning)
+         {
+             if (param instanceof ParametersWithRandom)
+             {
+                 this.secureRandom = ((ParametersWithRandom)param).getRandom();
+                 privateKey = (QTESLAPrivateKeyParameters)((ParametersWithRandom)param).getParameters();
+             }
+             else
+             {
+                 this.secureRandom = CryptoServicesRegistrar.getSecureRandom();
+                 privateKey = (QTESLAPrivateKeyParameters)param;
+             }
+             publicKey = null;
+             QTESLASecurityCategory.validate(privateKey.getSecurityCategory());
+         }
+         else
+         {
+             privateKey = null;
+             publicKey = (QTESLAPublicKeyParameters)param;
+             QTESLASecurityCategory.validate(publicKey.getSecurityCategory());
+         }
+    }
+
+    /**
+     * Generate a signature directly for the passed in message.
+     *
+     * @param message the message to be signed.
+     * @return the signature generated.
+     */
+    public byte[] generateSignature(byte[] message)
+    {
+        byte[] sig = new byte[QTESLASecurityCategory.getSignatureSize(privateKey.getSecurityCategory())];
+
+        switch (privateKey.getSecurityCategory())
+        {
+        case QTESLASecurityCategory.HEURISTIC_I:
+            QTESLA.signingI(sig, message, 0, message.length, privateKey.getSecret(), secureRandom);
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SIZE:
+            QTESLA.signingIIISize(sig, message, 0, message.length, privateKey.getSecret(), secureRandom);
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SPEED:
+            QTESLA.signingIIISpeed(sig, message, 0, message.length, privateKey.getSecret(), secureRandom);
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_I:
+            QTESLA.signingIP(sig, message, 0, message.length, privateKey.getSecret(), secureRandom);
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_III:
+            QTESLA.signingIIIP(sig, message, 0, message.length, privateKey.getSecret(), secureRandom);
+            break;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + privateKey.getSecurityCategory());
+        }
+
+        return sig;
+    }
+
+    /**
+     * Verify the signature against the passed in message.
+     *
+     * @param message the message that was supposed to have been signed.
+     * @param signature the signature of the message
+     * @return true if the signature passes, false otherwise.
+     */
+    public boolean verifySignature(byte[] message, byte[] signature)
+    {
+        int status;
+
+        switch (publicKey.getSecurityCategory())
+        {
+        case QTESLASecurityCategory.HEURISTIC_I:
+            status = QTESLA.verifyingI(message, signature, 0, signature.length, publicKey.getPublicData());
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SIZE:
+            status = QTESLA.verifyingIIISize(message, signature, 0, signature.length, publicKey.getPublicData());
+            break;
+        case QTESLASecurityCategory.HEURISTIC_III_SPEED:
+            status = QTESLA.verifyingIIISpeed(message, signature, 0, signature.length, publicKey.getPublicData());
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_I:
+            status = QTESLA.verifyingPI(message, signature, 0, signature.length, publicKey.getPublicData());
+            break;
+        case QTESLASecurityCategory.PROVABLY_SECURE_III:
+            status = QTESLA.verifyingPIII(message, signature, 0, signature.length, publicKey.getPublicData());
+            break;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + publicKey.getSecurityCategory());
+        }
+
+        return 0 == status;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Sample.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Sample.java
new file mode 100644
index 0000000..3ccd485
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/qtesla/Sample.java
@@ -0,0 +1,1373 @@
+package org.bouncycastle.pqc.crypto.qtesla;
+
+import org.bouncycastle.util.Arrays;
+
+class Sample
+{
+
+    static final double[][] EXPONENTIAL_DISTRIBUTION_I = {
+        /* [3][32] */
+        {
+            1.0000000000000000000000000000000000000000, 0.9990496327075997720621566739241504871513,
+            0.9981001686131900082646604498429491608001, 0.9971516068584008799087793737854343387385,
+            0.9962039465856783249057599531380206128030, 0.9952571869382832724989228009014122394200,
+            0.9943113270602908687225570427678069689363, 0.9933663660965897025969132575731249565771,
+            0.9924223031928810330585953871541593536283, 0.9914791374956780166256527164832613053574,
+            0.9905368681523049357966736891640434381216, 0.9895954943108964281831839869512129866330,
+            0.9886550151203967163746519649066284074237, 0.9877154297305588385354051961226109899227,
+            0.9867767372919438797327625416330343864518, 0.9858389369559202039956868221933583419625,
+            0.9849020278746626871032638290431658501235, 0.9839660092011519501023140705695025630520,
+            0.9830308800891735935534443109670000387768, 0.9820966396933174325048466155419862577528,
+            0.9811632871689767321931532752331431453491, 0.9802308216723474444706566402213564033800,
+            0.9792992423604274449582035491768120172661, 0.9783685483910157709230746967427200384407,
+            0.9774387389227118598811599372828827520575, 0.9765098131149147889227411777252636721429,
+            0.9755817701278225147611951665163479411869, 0.9746546091224311145039291392620050672727,
+            0.9737283292605340271448629345703623656609, 0.9728029297047212957777718459314622781631,
+            0.9718784096183788105298051271677986565965, 0.9709547681656875522144957200697952895280
+        },
+
+        {
+            1.0000000000000000000000000000000000000000, 0.9700320045116228367035774232914930379400,
+            0.9409620897768370674212298508058219852849, 0.9127633421156708668942503744059309052528,
+            0.8854096543971923811501043960464255901147, 0.8588757018688517364879932717859212289637,
+            0.8331369187101692180902460141030849026557, 0.8081694752890624155161689578277768341910,
+            0.7839502560997556536888618983783791053116, 0.7604568383618460545183896873859249753543,
+            0.7376674712607126902372883387750345338472, 0.7155610558100490615694685434237323547987,
+            0.6941171253178751117406951384261687867164, 0.6733158264379437043232142381368341940533,
+            0.6531379007889984662634253213819854052726, 0.6335646671248656289427239706049936967143,
+            0.6145780040388724765036124496076447154217, 0.5961603331865797040852326968966728810261,
+            0.5782946030112948570545930362131434268247, 0.5609642729572995100665682618108293115511,
+            0.5441532981561743827978648643747061873131, 0.5278461145720445955231454653404664082188,
+            0.5120276245919921478529972155927751107378, 0.4966831830482948512984566287866591847562,
+            0.4817985836595507424420546966358580262381, 0.4673600458781348185224193866260805625424,
+            0.4533542021318111302275642196084653301628, 0.4397680854476881857303133336231578259611,
+            0.4265891174470596033395475021945475958821, 0.4138050967000153253100465421861800587782,
+            0.4014041874290417902572763743098032661210, 0.3893749085511525646401543103372782315254
+        },
+
+        {
+            1.0000000000000000000000000000000000000000000, 0.3777061230484043540417651455683576466650000,
+            0.1426619153882563708052119679085105421822000, 0.0538842789679578114076165050703859298545800,
+            0.0203524221022460198907862721163275696205800, 0.0076872344468839996101743286763347159496400,
+            0.0029035155198967005412614828182250511400990, 0.0010966755902310549151227158892825815595730,
+            0.0004142210854279922997296008273437255752956, 0.0001564538402619088712753422615493598849163,
+            5.909357344135995142394679824207999201121E-5, 2.232000452161222135025591154935960584027E-5,
+            8.430402374281007236700902260035220289887E-6, 3.184214596527742337148476455363347356131E-6,
+            1.202697350208632670782595114365065060885E-6, 4.542661533478916755570208360842380811059E-7,
+            1.715791076131440947144583312662638239090E-7, 6.480647953266561572601959656715022445021E-8,
+            2.447780413269889735078224512008720987199E-8, 9.245416499699910342143072277116651273927E-9,
+            3.492050422069402212293514861017928736701E-9, 1.318968826409377991494549187659977485249E-9,
+            4.981826018447900060525041555590742055479E-10, 1.881666191129624879723808164319051826703E-10,
+            7.107168419228284402686789774896404982106E-11, 2.684421029478771850078976357840397379201E-11,
+            1.013922259674033292202917547107956246173E-11, 3.829646457739566105989785588606755995719E-12,
+            1.446480916198866420590826731657500079699E-12, 5.463446989209777070985952848270039796153E-13,
+            2.063577380774902353530525926195322827410E-13, 7.794258121028692337871970872695782456164E-14
+        }
+
+    };
+
+    static final double[][] EXPONENTIAL_DISTRIBUTION_III_SIZE = {
+        /* [3][32] */
+        {
+            1.0000000000000000000000000000000000000000, 0.9914791374956780166256527164832613053571,
+            0.9830308800891735935534443109670000387763, 0.9746546091224311145039291392620050672719,
+            0.9663497112088951922951613058690022829314, 0.9581155781885929401990530331782558043141,
+            0.9499516070835989810875119461809064028436, 0.9418572000538799331122753584612083652659,
+            0.9338317643535151384510743106138183393464, 0.9258747122872904292046909607697858626681,
+            0.9179854611676617518466375609653990674902, 0.9101634332720854987115840832838713554612,
+            0.9024080558007124218622779514513692802555, 0.8947187608344420312994997523024746481561,
+            0.8870949852933344058775329566907233056474, 0.8795361708953763714606266672461444022383,
+            0.8720417641155990268059148554652540437481, 0.8646112161455436233871237462566364157436,
+            0.8572439828530728308830350554160731167048, 0.8499395247425244453469447315612369857573,
+            0.8426973069152046221501168284377584096225, 0.8355167990302177406553164840946716839800,
+            0.8283974752656300322277354108287439566785, 0.8213388142799641276318029906853001579399,
+            0.8143402991740217040952958306017837324709, 0.8074014174530314363485930132316684297705,
+            0.8005216609891194797686327175999898485396, 0.7937005259840997373758528196362056425534,
+            0.7869375129325811858498730937766509221324, 0.7802321265853895589476145632070372895529,
+            0.7735838759133007097276526159890746982448, 0.7669922740710829958085504579386416555178
+        },
+
+        {
+            1.0000000000000000000000000000000000000000000, 0.7604568383618460545183896873859249753475000,
+            0.5782946030112948570545930362131434268144000, 0.4397680854476881857303133336231578259493000,
+            0.3344246478719911187527828322027724928608000, 0.2543155103910080342970055083858543302085000,
+            0.1933959689783251774319131539973439846964000, 0.1470692871211828233002294902623869285186000,
+            0.1118398451043052539124374690378746581867000, 0.0850493750108985602659800234578596968909400,
+            0.0646763788254389154655681775607238774758000, 0.0491835945582863241156170683610789877042500,
+            0.0374020008170653143735533498156831735514100, 0.0284426072897526718347370026026486693779300,
+            0.0216293752143329118535087528015400204365400, 0.0164482062912336825104561988130156459132400,
+            0.0125081509529549918713848862125144537439400, 0.0095119089274368649464762531606995319241100,
+            0.0072333961897444564781616481310238106761860, 0.0055006855970716932734388078251246759697760,
+            0.0041830339779716833064716700383503988117580, 0.0031810167936485222816222493382931725715270,
+            0.0024190259736738921137934575940109408536480, 0.0018395648438552342443599128094977711325760,
+            0.0013989096651197544439950127319659303796870, 0.0010638104210907972987681203969116907947710,
+            0.0008089819094390918283473978621390203944673, 0.0006151958251439810374839895543363501050953,
+            0.0004678298721623988965815688638575721914033, 0.0003557644254758444808169155363704398225966,
+            0.0002705434901989792929582192527080744710506, 0.0002057366471960948784546183663182928059645
+        },
+
+        {
+            1.000000000000000000000000000000000000000, 0.0001564538402619088712753422615493598848717,
+            2.447780413269889735078224512008720985804E-8, 3.829646457739566105989785588606755992447E-12,
+            5.991628951587712183461314435723455239107E-16, 9.374133589003324437283071562544462897124E-20,
+            1.466619199127720628458909574032394007656E-23, 2.294582059053771218038974267927533833163E-27,
+            3.589961749350406706790987553377863179812E-31, 5.616633020792314645332222710264644857908E-35,
+            8.787438054448034835939954112296077697602E-39, 1.374828429682032112779050229478845154715E-42,
+            2.150971875250036652628677686695580621313E-46, 3.365278101782278104362461212648493483965E-50,
+            5.265106825731444425408506379787751403098E-54, 8.237461822748734749731771711361450782154E-58,
+            1.288782536179903234906819256928735424052E-61, 2.016349770478283712998453222332343703812E-65,
+            3.154656649025460159903286438614052035760E-69, 4.935581474477980619913950088312373997931E-73,
+            7.721906756076146364991353446502442654680E-77, 1.208121966132492313782281967679468463452E-80,
+            1.890153211061962317744312705074835436919E-84, 2.957217285540223765931001823632869648146E-88,
+            4.626680008116659240109379372702265238809E-92, 7.238618549328510447646200176448777448608E-96,
+            1.132509670233533294861752560334068442100E-99, 1.771854870417843101882482426208692422294E-103,
+            2.772134988636384669818807165401424398102E-107, 4.337111646965654912069237407317707678694E-111,
+            6.785577728124290751600099215097500773985E-115, 1.061629693960724289088455922591023103484E-118
+        }
+
+    };
+
+    static final double[][] EXPONENTIAL_DISTRIBUTION_III_SPEED = {
+        /* [3][32] */
+        {
+            1.0000000000000000000000000000000000000000, 0.9951980443443537316500388424172839303752,
+            0.9904191474668262564830185894967173613892, 0.9856631986401875746675941557587114196642,
+            0.9809300876689149347041557365309129923940, 0.9762197048866395987965541168345276706016,
+            0.9715319411536058687432894158212596709598, 0.9668666878541423134736924881553750396380,
+            0.9622238368941451396373408016639000521875, 0.9576032806985736469363056351479270970296,
+            0.9530049122089577101698314104664824876542, 0.9484286248809172302397073765744987564880,
+            0.9438743126816934966419131566675496907225, 0.9393418700876924042461092785035073150884,
+            0.9348311920820394674392081270253399758265, 0.9303421741521465749826061515830447550861,
+            0.9258747122872904292046909607697858626672, 0.9214287029762026134209634491584644007645,
+            0.9170040432046712317435415947941667461407, 0.9126006304531540657099452867877830194818,
+            0.9082183626944031924279067014123113094560, 0.9038571383911010091985145255388756529519,
+            0.8995168564935076098442888811876009946339, 0.8951974164371194582318032579854959087286,
+            0.8908987181403393047402262055905414183192, 0.8866206620021572916876550405654798379036,
+            0.8823631488998431939863624175501337704454, 0.8781260801866497415560803096876886684788,
+            0.8739093576895269702812107160640808580937, 0.8697128837068475485533842136704059167642,
+            0.8655365610061430266950922187780245940470, 0.8613802928218509568132024098758678171240
+        },
+
+        {
+            1.000000000000000000000000000000000000000000, 0.857243982853072830883035055416073116703300,
+            0.734867246137799425692104349091725698937400, 0.629960524947436582383605303639195946052600,
+            0.540029869446153084936465415644391919699900, 0.462937356143645214602345480384983067321600,
+            0.396850262992049868687926409818180089809000, 0.340197500043594241063920093831306311583500,
+            0.291632259894029145223423158665267961335500, 0.250000000000000000000000000000097352251700,
+            0.214310995713268207720758763854101733807800, 0.183716811534449856423026087273002965715400,
+            0.157490131236859145595901325909860314588700, 0.135007467361538271234116353911150553048700,
+            0.115734339035911303650586370096290834824400, 0.099212565748012467171981602454583656718920,
+            0.085049375010898560265980023457859696888520, 0.072908064973507286305855789666345381391030,
+            0.062500000000000000000000000000048676125830, 0.053577748928317051930189690963546297109930,
+            0.045929202883612464105756521818268626674130, 0.039372532809214786398975331477480410666060,
+            0.033751866840384567808529088477800781543110, 0.028933584758977825912646592524083975704600,
+            0.024803141437003116792995400613655572746400, 0.021262343752724640066495005864473203970290,
+            0.018227016243376821576463947416593443112050, 0.015625000000000000000000000000018253547190,
+            0.013394437232079262982547422740891790191980, 0.011482300720903116026439130454571627979850,
+            0.009843133202303696599743832869373935671238, 0.008437966710096141952132272119453481206014
+        },
+
+        {
+            1.000000000000000000000000000000000000000, 0.007233396189744456478161648131023810675775,
+            5.232202043780962102557587008169005410143E-5, 3.784659032745836912993682954976324658164E-7,
+            2.737593822694567686662466634421542264066E-9, 1.980210072614684707158711353745069372717E-11,
+            1.432364399414465384287735340977513952565E-13, 1.036085918905020069841154248521752033776E-15,
+            7.494419938055456100418425186702743722723E-18, 5.421010862427522170037264004417260251684E-20,
+            3.921231931684654880817938739668273317360E-22, 2.836382411375207747860568187463889509638E-24,
+            2.051667772709962123314993704273413823620E-26, 1.484052584974173558955043468582713624191E-28,
+            1.073474031353259824558654154333806911547E-30, 7.764862968180290824468612020607860317513E-33,
+            5.616633020792314645332222710264644852793E-35, 4.062733189179202535382045195211707654781E-37,
+            2.938735877055718769921841343128853888538E-39, 2.125704089576016965228859756656407540404E-41,
+            1.537605986206336992222535387300608525931E-43, 1.112211328195318530448364746285024038827E-45,
+            8.045065183558638234146057828832053516826E-48, 5.819314384499884015403474144560288801662E-50,
+            4.209340649576656799996170991423257963815E-52, 3.044782861598424467581974062513986546956E-54,
+            2.202412074968526631812431321732133496007E-56, 1.593091911132452277028880397827266782094E-58,
+            1.152346495989819456843455045622426762614E-60, 8.335378753358135655955994470664225877261E-63,
+            6.029309691461763611680553229574282672923E-65, 4.361238574900884540660050746922306538111E-67,
+        }
+
+    };
+
+    static final double[][] EXPONENTIAL_DISTRIBUTION_P = {
+        /* [3][32] */
+        {
+            1.0000000000000000000000000000000000000000, 0.9930924954370359015332102168880765048173,
+            0.9862327044933591729073804985266878802443, 0.9794202975869268710835182321094224250961,
+            0.9726549474122855185227020947295413763023, 0.9659363289248455510651443129204733029988,
+            0.9592641193252643901322834293949397264660, 0.9526379980439373889289005948680289570903,
+            0.9460576467255959075051119972754354254470, 0.9395227492140117766851490088262829075331,
+            0.9330329915368074159813432661499603336007, 0.9265880618903708756879317851202732375877,
+            0.9201876506248750783904312382017973974891, 0.9138314502294005401326428921359892449876,
+            0.9075191553171608564550809482180658363403, 0.9012504626108302434560060155923701020040,
+            0.8950250709279724289295293992056807493013, 0.8888426811665701935046683790031660959701,
+            0.8827029962906548665450116490541232503509, 0.8766057213160350863710299119436526437378,
+            0.8705505632961241391362700174797799990040, 0.8645372313078651954249311342751209858410,
+            0.8585654364377537683418658040230197384022, 0.8526348917679567215371033354114150564474,
+            0.8467453123625271602457822707284519309456, 0.8408964152537145430311254762332558266219,
+            0.8350879194283693564930171007187976000468, 0.8293195458144416997480650199452263126561,
+            0.8235910172675731299989737240342361894393, 0.8179020585577811249918276889374069238047,
+            0.8122523963562355226097093827753290960475, 0.8066417592221263022701629871861700330324
+        },
+
+        {
+            1.000000000000000000000000000000000000000000, 0.801069877589622077182576980035615205902700,
+            0.641712948781452099037917089781420222618900, 0.514056913328033254673172479396413573907600,
+            0.411795508633786564999486862017198273163900, 0.329876977693223564843500492807512798916800,
+            0.264254510140345093624873553521627364440900, 0.211686328090631790061445567682195415303500,
+            0.169575540930958985396762834141244920349500, 0.135841857815757262606900740466230788178000,
+            0.108818820412015517392033752185036062422000, 0.087171479146900338767218235365499637556050,
+            0.069830446129513747913186914700207350540810, 0.055939066932998276808095587450398613186620,
+            0.044811101500494605684562734558780780465070, 0.035896823593657343962457092086843785683310,
+            0.028755864082027346199700976193834163154190, 0.023035456520173456442055699495851578765410,
+            0.018453010334836412492976026695119689178670, 0.014782150730087436054767374957445947431420,
+            0.011841535675862485018337967197721359270050, 0.009485897534336303604787967133085469399049,
+            0.007598866776658480613458610115084898737984, 0.006087223278597655149117219956228990612855,
+            0.004876291206646921576592633968279399063782, 0.003906250000000000000000000000006084516053,
+            0.003129179209334461238994441328268996020587, 0.002506691206177547261866863631962577257343,
+            0.002008034817687629901067079997645368310618, 0.001608576205600728769529245554757186330929,
+            0.001288581944114154550169923800031354012535, 0.001032244180235723021972162318445464753156
+        },
+
+        {
+            1.000000000000000000000000000000000000000, 0.0008268997191040304299275217487598638498908,
+            6.837631454543244275598561791827450446268E-7, 5.654035529098691704742888887601969318770E-10,
+            4.675320390815916240837145591289455678271E-13, 3.866021117887026910581260785663924052584E-16,
+            3.196811776431032265107748321378670183434E-19, 2.643442759959277106397015416454182808165E-22,
+            2.185862075677909177530183421677021601630E-25, 1.807488736378216004902267757945329990433E-28,
+            1.494611928394845722509566662381681852231E-31, 1.235894183759231170477230799378805483584E-34,
+            1.021960553392813221805059629881904702629E-37, 8.450588945359167454685108853553438401193E-41,
+            6.987789625181120323479538530531788834637E-44, 5.778201278220326478541087516212630539830E-47,
+            4.777993013886937548374901071454718579294E-50, 3.950921081064128423947108109095179681258E-53,
+            3.267015532134120033414586853048549151733E-56, 2.701494225830208356330596231491229575841E-59,
+            2.233864816500159437321055999997722887780E-62, 1.847182189280358319436455385107649366142E-65,
+            1.527434433449896263866613728025637317872E-68, 1.263035103969543081968346060350962609985E-71,
+            1.044403372690945043917523022329044283453E-74, 8.636168555094444625386351863230863826745E-78,
+            7.141245352342656606906053992842560076147E-81, 5.905093775905105564186232605424573035226E-84,
+            4.882920384578890205960673105845289217904E-87, 4.037685494415628551550334502904113261957E-90,
+            3.338761001162701476381524668052565130775E-93, 2.760820534016929266476966660680800456743E-96
+        }
+
+    };
+
+    static final long[][] CUMULATIVE_DISTRIBUTION_TABLE_I = {
+        /* [12][2] */
+        {0x0200000000000000L, 0x0000000000000000L}, {0x0300000000000000L, 0x0000000000000000L},
+        {0x0320000000000000L, 0x0000000000000000L}, {0x0321000000000000L, 0x0000000000000000L},
+        {0x0321020000000000L, 0x0000000000000000L}, {0x0321020100000000L, 0x0000000000000000L},
+        {0x0321020100200000L, 0x0000000000000000L}, {0x0321020100200100L, 0x0000000000000000L},
+        {0x0321020100200100L, 0x0200000000000000L}, {0x0321020100200100L, 0x0200010000000000L},
+        {0x0321020100200100L, 0x0200010000200000L}, {0x0321020100200100L, 0x0200010000200001L},
+
+    };
+
+    static final long[][] CUMULATIVE_DISTRIBUTION_TABLE_III = {
+        /* [14][3] */
+        {0x0000020000000000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000030000000000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032000000000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032100000000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032102000000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032102010000L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0000000000000000L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0100000000000000L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0100020000000000L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0100020001000000L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0100020001000020L, 0x0000000000000000L},
+        {0x0000032102010020L, 0x0100020001000020L, 0x0001000000000000L},
+        {0x0000032102010020L, 0x0100020001000020L, 0x0001000002000000L},
+        {0x0000032102010020L, 0x0100020001000020L, 0x0001000002000001L}
+
+    };
+
+    private static long modulus7(long number)
+    {
+
+        long temporary = number;
+
+        for (int i = 0; i < 2; i++)
+        {
+
+            temporary = (temporary & 7) + (temporary >> 3);
+
+        }
+
+        return ((temporary - 7) >> 3) & temporary;
+
+    }
+
+    /******************************************************************************************************************
+     * Description:	Samples Polynomial Y, Such That Each Coefficient is in the Range [-B, B], for Heuristic qTESLA
+     * 				Security Category-1 and Security Category-3 (Option for Size or Speed)
+     *
+     * @param        Y                Polynomial Y
+     * @param        seed            Kappa-Bit Seed
+     * @param        seedOffset        Starting Point of the Kappa-Bit Seed
+     * @param        nonce            Domain Separator for Error Polynomial and Secret Polynomial
+     * @param        n                Polynomial Degree
+     * @param        q                Modulus
+     * @param        b                Determines the Interval the Randomness is Chosen in During Signing
+     * @param        bBit            b = 2 ^ bBit - 1
+     *
+     * @return none
+     ******************************************************************************************************************/
+    public static void sampleY(int[] Y, final byte[] seed, int seedOffset, int nonce, int n, int q, int b, int bBit)
+    {
+
+        int i = 0;
+        int position = 0;
+        int numberOfByte = (bBit + 1 + 7) / 8;
+        int numberOfBlock = n;
+        byte[] buffer = new byte[n * numberOfByte];
+        int[] y = new int[4];
+
+        short dualModeSampler = (short)(nonce << 8);
+
+        if (q == Parameter.Q_I)
+        {
+
+            HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                buffer, 0, n * numberOfByte, dualModeSampler++, seed, seedOffset, Polynomial.RANDOM
+            );
+
+        }
+
+        if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+        {
+
+            HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                buffer, 0, n * numberOfByte, dualModeSampler++, seed, seedOffset, Polynomial.RANDOM
+            );
+
+        }
+
+        while (i < n)
+        {
+
+            if (position > numberOfBlock * numberOfByte * 4)
+            {
+
+                if (q == Parameter.Q_I)
+                {
+
+                    numberOfBlock =
+                        HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE /
+                            ((bBit + 1 + 7) / 8);
+
+                    HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                        buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE,
+                        dualModeSampler++,
+                        seed, seedOffset, Polynomial.RANDOM
+                    );
+
+                }
+
+                if (q == Parameter.Q_III_SIZE || q == Parameter.Q_III_SPEED)
+                {
+
+                    numberOfBlock =
+                        HashUtils.SECURE_HASH_ALGORITHM_KECCAK_256_RATE /
+                            ((bBit + 1 + 7) / 8);
+
+                    HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                        buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_256_RATE,
+                        dualModeSampler++,
+                        seed, seedOffset, Polynomial.RANDOM
+                    );
+
+                }
+
+                position = 0;
+
+            }
+
+            y[0] = (CommonFunction.load32(buffer, position) & ((1 << (bBit + 1)) - 1)) - b;
+            y[1] = (CommonFunction.load32(buffer, position + numberOfByte) & ((1 << (bBit + 1)) - 1)) - b;
+            y[2] = (CommonFunction.load32(buffer, position + numberOfByte * 2) & ((1 << (bBit + 1)) - 1)) - b;
+            y[3] = (CommonFunction.load32(buffer, position + numberOfByte * 3) & ((1 << (bBit + 1)) - 1)) - b;
+
+            if (i < n && y[0] != (1 << bBit))
+            {
+
+                Y[i++] = y[0];
+
+            }
+
+            if (i < n && y[1] != (1 << bBit))
+            {
+
+                Y[i++] = y[1];
+
+            }
+
+            if (i < n && y[2] != (1 << bBit))
+            {
+
+                Y[i++] = y[2];
+
+            }
+
+            if (i < n && y[3] != (1 << bBit))
+            {
+
+                Y[i++] = y[3];
+
+            }
+
+            position += numberOfByte * 4;
+
+        }
+
+    }
+
+    /*******************************************************************************************************************
+     * Description:	Samples Polynomial Y, Such That Each Coefficient is in the Range [-B, B], for Provably-Secure qTESLA
+     *				Security Category-1 and Security Category-3
+     *
+     * @param        Y                Polynomial Y
+     * @param        seed            Kappa-Bit Seed
+     * @param        seedOffset        Starting Point of the Kappa-Bit Seed
+     * @param        nonce            Domain Separator for Error Polynomial and Secret Polynomial
+     * @param        n                Polynomial Degree
+     * @param        q                Modulus
+     * @param        b                Determines the Interval the Randomness is Chosen in During Signing
+     * @param        bBit            b = 2 ^ bBit - 1
+     *
+     * @return none
+     *******************************************************************************************************************/
+    public static void sampleY(long[] Y, final byte[] seed, int seedOffset, int nonce, int n, int q, int b, int bBit)
+    {
+
+        int i = 0;
+        int position = 0;
+        int numberOfByte = (bBit + 1 + 7) / 8;
+        int numberOfBlock = n;
+        byte[] buffer = new byte[n * numberOfByte];
+
+        short dualModeSampler = (short)(nonce << 8);
+
+        if (q == Parameter.Q_I_P)
+        {
+
+            HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                buffer, 0, n * numberOfByte, dualModeSampler++, seed, seedOffset, Polynomial.RANDOM
+            );
+
+        }
+
+        if (q == Parameter.Q_III_P)
+        {
+
+            HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                buffer, 0, n * numberOfByte, dualModeSampler++, seed, seedOffset, Polynomial.RANDOM
+            );
+
+        }
+
+        while (i < n)
+        {
+
+            if (position > numberOfBlock * numberOfByte)
+            {
+
+                if (q == Parameter.Q_I_P)
+                {
+
+                    numberOfBlock =
+                        HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE /
+                            ((bBit + 1 + 7) / 8);
+
+                    HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                        buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE,
+                        dualModeSampler++,
+                        seed, seedOffset, Polynomial.RANDOM
+                    );
+
+                }
+
+                if (q == Parameter.Q_III_P)
+                {
+
+                    numberOfBlock =
+                        HashUtils.SECURE_HASH_ALGORITHM_KECCAK_256_RATE /
+                            ((bBit + 1 + 7) / 8);
+
+                    HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                        buffer, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_256_RATE,
+                        dualModeSampler++,
+                        seed, seedOffset, Polynomial.RANDOM
+                    );
+
+                }
+
+                position = 0;
+
+            }
+
+            Y[i] = (CommonFunction.load32(buffer, position) & ((1 << (bBit + 1)) - 1)) - b;
+
+            if (Y[i] != (1 << bBit))
+            {
+
+                i++;
+
+            }
+
+            position += numberOfByte;
+
+        }
+
+    }
+
+    /*****************************************************************************************************************
+     * Description:	Samples A Bit from Bernoulli with Restriction of 20-Bit Exponent
+     *****************************************************************************************************************/
+    private static int bernoulli(long result, long fractionOfExponent, double[][] exponentialDistribution)
+    {
+
+        /* *
+         * Computes the Actual Bernoulli Parameter = exp (-t / f)
+         * Yields A Fraction of 2^62, to Keep Only 62 Bits of Precision in This Implementation
+         * */
+        double bernoulliParameter = 4611686018427387904.0;
+
+        for (long i = 0, j = fractionOfExponent; i < 3; i++, j >>= 5)
+        {
+
+            bernoulliParameter *= exponentialDistribution[(int)i][(int)(j & 31)];
+
+        }
+
+        /* Sample from Bernoulli of bernoulliParameter */
+        return (int)(((result & 0x3FFFFFFFFFFFFFFFL) - round(bernoulliParameter)) >>> 63);
+
+    }
+
+    /**********************************************************************************************************************
+     * Description:	Gaussian Sampler for Heuristic qTESLA Security Category-1
+     *
+     * @param        data                        Data to be Sampled
+     * @param        dataOffset                    Starting Point of the Data to be Sampled
+     * @param        seed                        Kappa-Bit Seed
+     * @param        seedOffset                    Starting Point of the Kappa-Bit Seed
+     * @param        nonce                        Domain Separator for Error Polynomial and Secret Polynomial
+     *
+     * @return none
+     **********************************************************************************************************************/
+    public static void polynomialGaussSamplerI(int[] data, int dataOffset, final byte[] seed, int seedOffset, int nonce)
+    {
+
+        byte[] seedExpander = new byte[Parameter.N_I * Const.LONG_SIZE / Const.INT_SIZE];
+        short domainSeparator = (short)(nonce << 8);
+        int index;
+        int j = 0;
+        long k;
+        long sign;
+        long r;
+        long s;
+        long randomBit;
+        long bitRemained;
+        long y;
+        long z;
+        long buffer;
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+            seedExpander, 0, Parameter.N_I * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+        );
+
+        for (index = 0; index < Parameter.N_I; index++)
+        {
+
+            if (j + 46 > Parameter.N_I)
+            {
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                    seedExpander, 0, Parameter.N_I * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+                );
+
+                j = 0;
+
+            }
+
+            do
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+                do
+                {
+
+                    /* Sample x from D^+_{\SIGMA_2} and y from U ({0, ..., k - 1}) */
+                    do
+                    {
+
+                        r = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        s = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                        if (bitRemained <= 64 - 6)
+                        {
+
+                            randomBit = (randomBit << 6) ^ ((r >>> 58) & 63L);
+                            bitRemained += 6;
+
+                        }
+
+                        r &= 0x03FFFFFFFFFFFFFFL;
+
+                        /*
+                         * Checks If r Exceeds A Maximum Value
+                         * Variation is Random ad Does not Depend on Private Data
+                         */
+                    }
+                    while (r > 0x0321020100200100L);
+
+                    y = 0;
+
+                    for (int i = 0; i < 12; i++)
+                    {
+
+                        long c = s - CUMULATIVE_DISTRIBUTION_TABLE_I[i][1];
+
+                        long b = (((c & CUMULATIVE_DISTRIBUTION_TABLE_I[i][1]) & 1) + (CUMULATIVE_DISTRIBUTION_TABLE_I[i][1] >> 1) + (c >>> 1)) >>> 63;
+
+                        c = r - (CUMULATIVE_DISTRIBUTION_TABLE_I[i][0] + b);
+
+                        y += ~(c >>> 63) & 1L;
+
+                    }
+
+                    /* The Next Sampler Works Exclusively for xi <= 28 */
+                    do
+                    {
+
+                        do
+                        {
+
+                            if (bitRemained < 6)
+                            {
+
+                                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                                bitRemained = 64;
+
+                            }
+
+                            z = randomBit & 63L;
+                            randomBit >>= 6;
+                            bitRemained -= 6;
+
+                        }
+                        while (z == 63);
+
+                        if (bitRemained < 2)
+                        {
+
+                            randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                            bitRemained = 64;
+
+                        }
+
+                        z = (modulus7(z) << 2) + (randomBit & 3L);
+                        randomBit >>= 2;
+                        bitRemained -= 2;
+
+                        /*
+                         * Making Sure Random z Does not Exceed A Certain Limit
+                         * No Private Data is Leaked
+                         * It Varies Uniformly
+                         */
+                    }
+                    while (z >= Parameter.XI_I);
+
+                    /* Sample A Bit from Bernoulli_{exp (- y * (y + 2 * k * x) / (2 * k^2 * SIGMA_2^2))} */
+                    k = (long)(Parameter.XI_I * y + z);
+
+                    buffer = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                }
+                while (bernoulli(buffer, z * ((k << 1) - z), EXPONENTIAL_DISTRIBUTION_I) == 0);
+
+                /* Put Last Random Bits into Sign Bit */
+                randomBit <<= (int)(64 - bitRemained);
+
+                if (bitRemained == 0)
+                {
+
+                    randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                    bitRemained = 64;
+
+                }
+
+                sign = randomBit >> 63;
+                randomBit <<= 1;
+                bitRemained--;
+
+            }
+            while ((k | (sign & 1L)) == 0);
+
+            if (bitRemained == 0)
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+            }
+
+            sign = randomBit >> 63;
+            randomBit <<= 1;
+            bitRemained--;
+            k = ((k << 1) & sign) - k;
+            data[dataOffset + index] = (int)((k << 48) >> 48);
+
+        }
+
+    }
+
+    /**********************************************************************************************************************
+     * Description:	Gaussian Sampler for Provably-Secure qTESLA Security Category-1
+     *
+     * @param        data                        Data to be Sampled
+     * @param        dataOffset                    Starting Point of the Data to be Sampled
+     * @param        seed                        Kappa-Bit Seed
+     * @param        seedOffset                    Starting Point of the Kappa-Bit Seed
+     * @param        nonce                        Domain Separator for Error Polynomial and Secret Polynomial
+     *
+     * @return none
+     **********************************************************************************************************************/
+    public static void polynomialGaussSamplerIP(long[] data, int dataOffset, final byte[] seed, int seedOffset, int nonce)
+    {
+
+        byte[] seedExpander = new byte[Parameter.N_I_P * Const.LONG_SIZE / Const.INT_SIZE];
+        short domainSeparator = (short)(nonce << 8);
+        int index;
+        int j = 0;
+        long k;
+        long sign;
+        long r;
+        long s;
+        long randomBit;
+        long bitRemained;
+        long y;
+        long z;
+        long buffer;
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+            seedExpander, 0, Parameter.N_I_P * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+        );
+
+        for (index = 0; index < Parameter.N_I_P; index++)
+        {
+
+            if (j + 46 > Parameter.N_I_P)
+            {
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                    seedExpander, 0, Parameter.N_I_P * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+                );
+
+                j = 0;
+
+            }
+
+            do
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+                do
+                {
+
+                    /* Sample x from D^+_{\SIGMA_2} and y from U ({0, ..., k - 1}) */
+                    do
+                    {
+
+                        r = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        s = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                        if (bitRemained <= 64 - 6)
+                        {
+
+                            randomBit = (randomBit << 6) ^ ((r >>> 58) & 63L);
+                            bitRemained += 6;
+
+                        }
+
+                        r &= 0x03FFFFFFFFFFFFFFL;
+
+                        /*
+                         * Checks If r Exceeds A Maximum Value
+                         * Variation is Random ad Does not Depend on Private Data
+                         */
+                    }
+                    while (r > 0x0321020100200100L);
+
+                    y = 0;
+
+                    for (int i = 0; i < 12; i++)
+                    {
+
+                        long c = s - CUMULATIVE_DISTRIBUTION_TABLE_I[i][1];
+
+                        long b = (((c & CUMULATIVE_DISTRIBUTION_TABLE_I[i][1]) & 1) + (CUMULATIVE_DISTRIBUTION_TABLE_I[i][1] >> 1) + (c >>> 1)) >>> 63;
+
+                        c = r - (CUMULATIVE_DISTRIBUTION_TABLE_I[i][0] + b);
+
+                        y += ~(c >>> 63) & 1L;
+
+                    }
+
+                    /* The Next Sampler Works Exclusively for xi <= 28 */
+                    do
+                    {
+
+                        do
+                        {
+
+                            if (bitRemained < 6)
+                            {
+
+                                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                                bitRemained = 64;
+
+                            }
+
+                            z = randomBit & 63L;
+                            randomBit >>= 6;
+                            bitRemained -= 6;
+
+                        }
+                        while (z == 63);
+
+                        if (bitRemained < 2)
+                        {
+
+                            randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                            bitRemained = 64;
+
+                        }
+
+                        z = (modulus7(z) << 2) + (randomBit & 3L);
+                        randomBit >>= 2;
+                        bitRemained -= 2;
+
+                        /*
+                         * Making Sure Random z Does not Exceed A Certain Limit
+                         * No Private Data is Leaked
+                         * It Varies Uniformly
+                         */
+                    }
+                    while (z >= Parameter.XI_I_P);
+
+                    /* Sample A Bit from Bernoulli_{exp (- y * (y + 2 * k * x) / (2 * k^2 * SIGMA_2^2))} */
+                    k = (long)(Parameter.XI_I_P * y + z);
+
+                    buffer = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                }
+                while (bernoulli(buffer, z * ((k << 1) - z), EXPONENTIAL_DISTRIBUTION_P) == 0);
+
+                /* Put Last Random Bits into Sign Bit */
+                randomBit <<= (int)(64 - bitRemained);
+
+                if (bitRemained == 0)
+                {
+
+                    randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                    bitRemained = 64;
+
+                }
+
+                sign = randomBit >> 63;
+                randomBit <<= 1;
+                bitRemained--;
+
+            }
+            while ((k | (sign & 1L)) == 0);
+
+            if (bitRemained == 0)
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+            }
+
+            sign = randomBit >> 63;
+            randomBit <<= 1;
+            bitRemained--;
+            k = ((k << 1) & sign) - k;
+            data[dataOffset + index] = (k << 48) >> 48;
+
+        }
+
+    }
+
+    /*******************************************************************************************************************************************************************************
+     * Description:	Gaussian Sampler for Heuristic qTESLA Security Category-3 (Option for Size or Speed)
+     *
+     * @param        data                        Data to be Sampled
+     * @param        dataOffset                    Starting Point of the Data to be Sampled
+     * @param        seed                        Kappa-Bit Seed
+     * @param        seedOffset                    Starting Point of the Kappa-Bit Seed
+     * @param        nonce                        Domain Separator for Error Polynomial and Secret Polynomial
+     * @param        n                            Polynomial Degree
+     * @param        xi
+     * @param        exponentialDistribution        Exponential Distribution Table
+     *
+     * @return none
+     *******************************************************************************************************************************************************************************/
+    public static void polynomialGaussSamplerIII(int[] data, int dataOffset, final byte[] seed, int seedOffset, int nonce, int n, double xi, double[][] exponentialDistribution)
+    {
+
+        byte[] seedExpander = new byte[n * Const.LONG_SIZE / Const.INT_SIZE];
+        short domainSeparator = (short)(nonce << 8);
+        int index;
+        int j = 0;
+        long k;
+        long sign;
+        long r;
+        long s;
+        long t;
+        long randomBit;
+        long bitRemained;
+        long y;
+        long z;
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+            seedExpander, 0, n * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+        );
+
+        for (index = 0; index < n; index++)
+        {
+
+            if (j + 46 > n)
+            {
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                    seedExpander, 0, n * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+                );
+
+                j = 0;
+
+            }
+
+            do
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+                do
+                {
+
+                    /* Sample x from D^+_{\SIGMA_2} and y from U ({0, ..., k - 1}) */
+                    do
+                    {
+
+                        r = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        s = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        t = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                        if (bitRemained <= 64 - 6)
+                        {
+
+                            randomBit = (randomBit << 6) ^ ((r >>> 58) & 63L);
+                            bitRemained += 6;
+
+                        }
+
+                        r &= 0x000003FFFFFFFFFFL;
+
+                        /*
+                         * Checks If r Exceeds A Maximum Value
+                         * Variation is Random ad Does not Depend on Private Data
+                         */
+                    }
+                    while (r > 0x0000032102010020L);
+
+                    y = 0;
+
+                    for (int i = 0; i < 14; i++)
+                    {
+
+                        long c = t - CUMULATIVE_DISTRIBUTION_TABLE_III[i][2];
+
+                        long b = ((c & CUMULATIVE_DISTRIBUTION_TABLE_III[i][2] & 1L) + (CUMULATIVE_DISTRIBUTION_TABLE_III[i][2] >> 1) + (c >>> 1)) >> 63;
+
+                        /* Least significant Bits of All CUMULATIVE_DISTRIBUTION_TABLE[i][1] are Zero: Overflow Cannot Occur at This Point */
+                        c = s - (CUMULATIVE_DISTRIBUTION_TABLE_III[i][1] + b);
+
+                        b = (((c & b) & 1L) + (CUMULATIVE_DISTRIBUTION_TABLE_III[i][1] >> 1) + (c >>> 1)) >> 63;
+
+                        /* Least significant Bits of All CUMULATIVE_DISTRIBUTION_TABLE[i][0] are Zero: Overflow Cannot Occur at This Point */
+                        c = r - (CUMULATIVE_DISTRIBUTION_TABLE_III[i][0] + b);
+
+                        y += ~(c >>> 63) & 1L;
+
+                    }
+
+                    /* The Next Sampler Works Exclusively for xi <= 28 */
+                    do
+                    {
+
+                        do
+                        {
+
+                            if (bitRemained < 6)
+                            {
+
+                                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                                bitRemained = 64;
+
+                            }
+
+                            z = randomBit & 63L;
+                            randomBit >>= 6;
+                            bitRemained -= 6;
+
+                        }
+                        while (z == 63L);
+
+                        if (bitRemained < 2)
+                        {
+
+                            randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                            bitRemained = 64;
+
+                        }
+
+                        z = (modulus7(z) << 2) + (randomBit & 3L);
+                        randomBit >>= 2;
+                        bitRemained -= 2;
+
+                        /*
+                         * Making Sure Random z Does not Exceed A Certain Limit
+                         * No Private Data is Leaked
+                         * It Varies Uniformly
+                         */
+                    }
+                    while (z >= xi);
+
+                    /* Sample A Bit from Bernoulli_{exp (- y * (y + 2 * k * x) / (2 * k^2 * SIGMA_2^2))} */
+                    k = (long)(xi * y + z);
+
+                }
+                while (bernoulli(CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE), z * ((k << 1) - z), exponentialDistribution) == 0);
+
+                /* Put Last Random Bits into Sign Bit */
+                randomBit <<= (int)(64 - bitRemained);
+
+                if (bitRemained == 0L)
+                {
+
+                    randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                    bitRemained = 64;
+
+                }
+
+                sign = randomBit >> 63;
+                randomBit <<= 1;
+                bitRemained--;
+
+            }
+            while ((k | (sign & 1L)) == 0);
+
+            if (bitRemained == 0)
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+            }
+
+            sign = randomBit >> 63;
+            randomBit <<= 1;
+            bitRemained--;
+            k = ((k << 1) & sign) - k;
+            data[dataOffset + index] = (int)((k << 48) >> 48);
+
+        }
+
+    }
+
+    /**************************************************************************************************************************
+     * Description:	Gaussian Sampler for Provably-Secure qTESLA Security Category-3
+     *
+     * @param        data                        Data to be Sampled
+     * @param        dataOffset                    Starting Point of the Data to be Sampled
+     * @param        seed                        Kappa-Bit Seed
+     * @param        seedOffset                    Starting Point of the Kappa-Bit Seed
+     * @param        nonce                        Domain Separator for Error Polynomial and Secret Polynomial
+     *
+     * @return none
+     **************************************************************************************************************************/
+    public static void polynomialGaussSamplerIIIP(long[] data, int dataOffset, final byte[] seed, int seedOffset, int nonce)
+    {
+
+        byte[] seedExpander = new byte[Parameter.N_III_P * Const.LONG_SIZE / Const.INT_SIZE];
+        short domainSeparator = (short)(nonce << 8);
+        int index;
+        int j = 0;
+        long k;
+        long sign;
+        long r;
+        long s;
+        long t;
+        long randomBit;
+        long bitRemained;
+        long y;
+        long z;
+
+        HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+            seedExpander, 0, Parameter.N_III_P * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+        );
+
+        for (index = 0; index < Parameter.N_III_P; index++)
+        {
+
+            if (j + 46 > Parameter.N_III_P)
+            {
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK256Simple(
+                    seedExpander, 0, Parameter.N_III_P * Const.LONG_SIZE / Const.INT_SIZE, domainSeparator++, seed, seedOffset, Polynomial.RANDOM
+                );
+
+                j = 0;
+
+            }
+
+            do
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+                do
+                {
+
+                    /* Sample x from D^+_{\SIGMA_2} and y from U ({0, ..., k - 1}) */
+                    do
+                    {
+
+                        r = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        s = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                        t = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+
+                        if (bitRemained <= 64 - 6)
+                        {
+
+                            randomBit = (randomBit << 6) ^ ((r >>> 58) & 63L);
+                            bitRemained += 6;
+
+                        }
+
+                        r &= 0x000003FFFFFFFFFFL;
+
+                        /*
+                         * Checks If r Exceeds A Maximum Value
+                         * Variation is Random ad Does not Depend on Private Data
+                         */
+                    }
+                    while (r > 0x0000032102010020L);
+
+                    y = 0;
+
+                    for (int i = 0; i < 14; i++)
+                    {
+
+                        long c = t - CUMULATIVE_DISTRIBUTION_TABLE_III[i][2];
+
+                        long b = ((c & CUMULATIVE_DISTRIBUTION_TABLE_III[i][2] & 1L) + (CUMULATIVE_DISTRIBUTION_TABLE_III[i][2] >> 1) + (c >>> 1)) >> 63;
+
+                        /* Least significant Bits of All CUMULATIVE_DISTRIBUTION_TABLE[i][1] are Zero: Overflow Cannot Occur at This Point */
+                        c = s - (CUMULATIVE_DISTRIBUTION_TABLE_III[i][1] + b);
+
+                        b = (((c & b) & 1L) + (CUMULATIVE_DISTRIBUTION_TABLE_III[i][1] >> 1) + (c >>> 1)) >> 63;
+
+                        /* Least significant Bits of All CUMULATIVE_DISTRIBUTION_TABLE[i][0] are Zero: Overflow Cannot Occur at This Point */
+                        c = r - (CUMULATIVE_DISTRIBUTION_TABLE_III[i][0] + b);
+
+                        y += ~(c >>> 63) & 1L;
+
+                    }
+
+                    /* The Next Sampler Works Exclusively for xi <= 28 */
+                    do
+                    {
+
+                        do
+                        {
+
+                            if (bitRemained < 6)
+                            {
+
+                                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                                bitRemained = 64;
+
+                            }
+
+                            z = randomBit & 63L;
+                            randomBit >>= 6;
+                            bitRemained -= 6;
+
+                        }
+                        while (z == 63L);
+
+                        if (bitRemained < 2)
+                        {
+
+                            randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                            bitRemained = 64;
+
+                        }
+
+                        z = (modulus7(z) << 2) + (randomBit & 3L);
+                        randomBit >>= 2;
+                        bitRemained -= 2;
+
+                        /*
+                         * Making Sure Random z Does not Exceed A Certain Limit
+                         * No Private Data is Leaked
+                         * It Varies Uniformly
+                         */
+                    }
+                    while (z >= Parameter.XI_III_P);
+
+                    /* Sample A Bit from Bernoulli_{exp (- y * (y + 2 * k * x) / (2 * k^2 * SIGMA_2^2))} */
+                    k = (long)(Parameter.XI_III_P * y + z);
+
+                }
+                while (bernoulli(CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE), z * ((k << 1) - z), EXPONENTIAL_DISTRIBUTION_P) == 0);
+
+                /* Put Last Random Bits into Sign Bit */
+                randomBit <<= (int)(64 - bitRemained);
+
+                if (bitRemained == 0L)
+                {
+
+                    randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                    bitRemained = 64;
+
+                }
+
+                sign = randomBit >> 63;
+                randomBit <<= 1;
+                bitRemained--;
+
+            }
+            while ((k | (sign & 1L)) == 0);
+
+            if (bitRemained == 0)
+            {
+
+                randomBit = CommonFunction.load64(seedExpander, (j++) * Const.LONG_SIZE / Const.INT_SIZE);
+                bitRemained = 64;
+
+            }
+
+            sign = randomBit >> 63;
+            randomBit <<= 1;
+            bitRemained--;
+            k = ((k << 1) & sign) - k;
+            data[dataOffset + index] = (k << 48) >> 48;
+
+        }
+
+    }
+
+    /*************************************************************************************************************************
+     * Description:	Encoding of C' by Mapping the Output of the Hash Function H to An N-Element Vector with Entries {-1, 0, 1}
+     *
+     * @param        positionList            {0, ..., n - 1} ^ h
+     * @param        signList            {-1, +1} ^ h
+     * @param        output                Result of the Hash Function H
+     * @param        outputOffset        Starting Point of the Result of the Hash Function H
+     * @param        n                    Polynomial Degree
+     * @param        h                    Number of Non-Zero Entries of Output Elements of Encryption
+     *
+     * @return none
+     *************************************************************************************************************************/
+    public static void encodeC(int[] positionList, short[] signList, byte[] output, int outputOffset, int n, int h)
+    {
+
+        int count = 0;
+        int position;
+        short domainSeparator = 0;
+        short[] C = new short[n];
+        byte[] randomness = new byte[HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE];
+
+        /* Use the Hash Value as Key to Generate Some Randomness */
+        HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+            randomness, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE,
+            domainSeparator++,
+            output, outputOffset, Polynomial.RANDOM
+        );
+
+        /* Use Rejection Sampling to Determine Positions to be Set in the New Vector */
+        Arrays.fill(C, (short)0);
+
+        /* Sample A Unique Position k times.
+         * Use Two Bytes
+         */
+        for (int i = 0; i < h; )
+        {
+
+            if (count > HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE - 3)
+            {
+
+                HashUtils.customizableSecureHashAlgorithmKECCAK128Simple(
+                    randomness, 0, HashUtils.SECURE_HASH_ALGORITHM_KECCAK_128_RATE,
+                    domainSeparator++,
+                    output, outputOffset, Polynomial.RANDOM
+                );
+
+                count = 0;
+
+            }
+
+            position = (randomness[count] << 8) | (randomness[count + 1] & 0xFF);
+            position &= (n - 1);
+
+            /* Position is between [0, n - 1] and Has not Been Set Yet
+             * Determine Signature
+             */
+            if (C[position] == 0)
+            {
+
+                if ((randomness[count + 2] & 1) == 1)
+                {
+
+                    C[position] = -1;
+
+                }
+                else
+                {
+
+                    C[position] = 1;
+
+                }
+
+                positionList[i] = position;
+                signList[i] = C[position];
+                i++;
+
+            }
+
+            count += 3;
+
+        }
+
+    }
+
+    private static long round(double v)
+    {
+        if (v < 0)
+        {
+            return (long)(v - 0.5);
+        }
+        else
+        {
+            return (long)(v + 0.5);
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
index 6b041cf..e537336 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowKeyPairGenerator.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField;
 import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field;
@@ -90,7 +91,7 @@
         this.rainbowParams = (RainbowKeyGenerationParameters)param;
 
         // set source of randomness
-        this.sr = new SecureRandom();
+        this.sr = rainbowParams.getRandom();
 
         // unmarshalling:
         this.vi = this.rainbowParams.getParameters().getVi();
@@ -101,7 +102,7 @@
 
     private void initializeDefault()
     {
-        RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(new SecureRandom(), new RainbowParameters());
+        RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new RainbowParameters());
         initialize(rbKGParams);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java
index 147c55e..926f1ba 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowParameters.java
@@ -44,22 +44,15 @@
     public RainbowParameters(int[] vi)
     {
         this.vi = vi;
-        try
-        {
-            checkParams();
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
+
+        checkParams();
     }
 
     private void checkParams()
-        throws Exception
     {
         if (vi == null)
         {
-            throw new Exception("no layers defined.");
+            throw new IllegalArgumentException("no layers defined.");
         }
         if (vi.length > 1)
         {
@@ -67,14 +60,14 @@
             {
                 if (vi[i] >= vi[i + 1])
                 {
-                    throw new Exception(
+                    throw new IllegalArgumentException(
                         "v[i] has to be smaller than v[i+1]");
                 }
             }
         }
         else
         {
-            throw new Exception(
+            throw new IllegalArgumentException(
                 "Rainbow needs at least 1 layer, such that v1 < v2.");
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java
index 979e759..fe0cd98 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/RainbowSigner.java
@@ -3,6 +3,7 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.MessageSigner;
 import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField;
@@ -21,6 +22,8 @@
 public class RainbowSigner
     implements MessageSigner
 {
+    private static final int MAXITS = 65536;
+    
     // Source of randomness
     private SecureRandom random;
 
@@ -50,7 +53,7 @@
             else
             {
 
-                this.random = new SecureRandom();
+                this.random = CryptoServicesRegistrar.getSecureRandom();
                 this.key = (RainbowPrivateKeyParameters)param;
             }
         }
@@ -69,7 +72,7 @@
      * @param layer the current layer for which a LES is to be solved.
      * @param msg   the message that should be signed.
      * @return Y_ the modified document needed for solving LES, (Y_ =
-     *         A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
+     * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
      */
     private short[] initSign(Layer[] layer, short[] msg)
     {
@@ -125,6 +128,7 @@
         byte[] S = new byte[layer[numberOfLayers - 1].getViNext()];
 
         short[] msgHashVals = makeMessageRepresentative(message);
+        int itCount = 0;
 
         // shows if an exception is caught
         boolean ok;
@@ -150,9 +154,9 @@
                     }
 
                     /*
-                          * plug in the vars of the previous layer in order to get
-                          * the vars of the current layer
-                          */
+                     * plug in the vars of the previous layer in order to get
+                     * the vars of the current layer
+                     */
                     solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i);
 
                     if (solVec == null)
@@ -183,8 +187,14 @@
                 ok = false;
             }
         }
-        while (!ok);
+        while (!ok && ++itCount < MAXITS);
         /* return the signature in bytes */
+
+        if (itCount == MAXITS)
+        {
+            throw new IllegalStateException("unable to generate signature - LES not solvable");
+        }
+
         return S;
     }
 
@@ -192,7 +202,7 @@
      * This function verifies the signature of the message that has been
      * updated, with the aid of the public key.
      *
-     * @param message the message
+     * @param message   the message
      * @param signature the signature of the message
      * @return true if the signature has been verified, false otherwise.
      */
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java
index 5bf2573..c44a2de 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/rainbow/util/ComputeInField.java
@@ -32,19 +32,19 @@
      * @param b the right part of the equation
      *          (b in the equation above)
      * @return x  the solution of the equation if it is solvable
-     *         null otherwise
+     * null otherwise
      * @throws RuntimeException if LES is not solvable
      */
     public short[] solveEquation(short[][] B, short[] b)
     {
+        if (B.length != b.length)
+        {
+            return null;   // not solvable in this form
+        }
+
         try
         {
 
-            if (B.length != b.length)
-            {
-                throw new RuntimeException(
-                    "The equation system is not solvable");
-            }
 
             /** initialize **/
             // this matrix stores B and b from the equation B*x = b
@@ -93,7 +93,7 @@
      *
      * @param coef the matrix which inverse matrix is needed
      * @return inverse matrix of the input matrix.
-     *         If the matrix is singular, null is returned.
+     * If the matrix is singular, null is returned.
      * @throws RuntimeException if the given matrix is not invertible
      */
     public short[][] inverse(short[][] coef)
@@ -173,6 +173,7 @@
      * </p><p>
      * The result is stored in the global matrix A
      * </p>
+     *
      * @param usedForInverse This parameter shows if the function is used by the
      *                       solveEquation-function or by the inverse-function and according
      *                       to this creates matrices of different sizes.
@@ -209,7 +210,7 @@
                 //in this case is the input matrix not invertible
                 if (factor2 == 0)
                 {
-                    throw new RuntimeException("Matrix not invertible! We have to choose another one!");
+                    throw new IllegalStateException("Matrix not invertible! We have to choose another one!");
                 }
 
                 for (int j = k; j < length; j++)
@@ -233,6 +234,7 @@
      * It is used in the inverse-function
      * The result is stored in the global matrix A
      * </p>
+     *
      * @throws RuntimeException in case a multiplicative inverse of 0 is needed
      */
     private void computeZerosAbove()
@@ -272,10 +274,11 @@
      * If the multiplicative inverse of 0 is needed, an exception is thrown.
      * In this case is the LES not solvable
      * </p>
+     *
      * @throws RuntimeException in case a multiplicative inverse of 0 is needed
      */
     private void substitute()
-        throws RuntimeException
+        throws IllegalStateException
     {
 
         // for the temporary results of the operations in field
@@ -284,7 +287,7 @@
         temp = GF2Field.invElem(A[A.length - 1][A.length - 1]);
         if (temp == 0)
         {
-            throw new RuntimeException("The equation system is not solvable");
+            throw new IllegalStateException("The equation system is not solvable");
         }
 
         /** backward substitution **/
@@ -301,7 +304,7 @@
             temp = GF2Field.invElem(A[i][i]);
             if (temp == 0)
             {
-                throw new RuntimeException("Not solvable equation system");
+                throw new IllegalStateException("Not solvable equation system");
             }
             x[i] = GF2Field.multElem(tmp, temp);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256KeyPairGenerator.java
index 9186c04..1e79f97 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256KeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256KeyPairGenerator.java
@@ -42,6 +42,7 @@
         // Construct top subtree
         Tree.treehash(hs, pk, (Horst.N_MASKS * SPHINCS256Config.HASH_BYTES), SPHINCS256Config.SUBTREE_HEIGHT, sk, a, pk, 0);
 
-        return new AsymmetricCipherKeyPair(new SPHINCSPublicKeyParameters(pk), new SPHINCSPrivateKeyParameters(sk));
+        return new AsymmetricCipherKeyPair(new SPHINCSPublicKeyParameters(pk, treeDigest.getAlgorithmName()),
+                            new SPHINCSPrivateKeyParameters(sk, treeDigest.getAlgorithmName()));
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256Signer.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256Signer.java
index c15f48a..eb4ad23 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCS256Signer.java
@@ -2,6 +2,7 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.MessageSigner;
 import org.bouncycastle.util.Pack;
 
@@ -12,6 +13,10 @@
  * for message hashing and tree construction are now configurable (within limits...) and that the implementation produces
  * detached signatures.
  * </p>
+ * <p>
+ * The SPHINCS reference implementation is public domain, as per the statement in the second last paragraph of
+ * section 1 in https://eprint.iacr.org/2014/795.pdf
+ * </p>
  */
 public class SPHINCS256Signer
     implements MessageSigner
@@ -44,7 +49,12 @@
     {
          if (forSigning)
          {
-             keyData = ((SPHINCSPrivateKeyParameters)param).getKeyData();
+             if (param instanceof ParametersWithRandom) {
+                 // SPHINCS-256 signatures are deterministic, RNG is not required.
+                 keyData = ((SPHINCSPrivateKeyParameters)((ParametersWithRandom) param).getParameters()).getKeyData();
+             } else {
+                 keyData = ((SPHINCSPrivateKeyParameters) param).getKeyData();
+             }
          }
          else
          {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSKeyParameters.java
new file mode 100644
index 0000000..0bcfe31
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSKeyParameters.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.pqc.crypto.sphincs;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class SPHINCSKeyParameters
+    extends AsymmetricKeyParameter
+{
+    /**
+     * Use SHA512-256 for the tree generation function.
+     */
+    public static final String SHA512_256 = "SHA-512/256";
+
+    /**
+     * Use SHA3-256 for the tree generation function.
+     */
+    public static final String SHA3_256 = "SHA3-256";
+
+    private final String treeDigest;
+
+    protected SPHINCSKeyParameters(boolean isPrivateKey, String treeDigest)
+    {
+        super(isPrivateKey);
+        this.treeDigest = treeDigest;
+    }
+
+    public String getTreeDigest()
+    {
+        return treeDigest;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPrivateKeyParameters.java
index f0a5a82..d79d774 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPrivateKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPrivateKeyParameters.java
@@ -1,16 +1,21 @@
 package org.bouncycastle.pqc.crypto.sphincs;
 
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.util.Arrays;
 
 public class SPHINCSPrivateKeyParameters
-    extends AsymmetricKeyParameter
+    extends SPHINCSKeyParameters
 {
     private final byte[] keyData;
 
     public SPHINCSPrivateKeyParameters(byte[] keyData)
     {
-        super(true);
+        super(true, null);
+        this.keyData = Arrays.clone(keyData);
+    }
+
+    public SPHINCSPrivateKeyParameters(byte[] keyData, String treeDigest)
+    {
+        super(true, treeDigest);
         this.keyData = Arrays.clone(keyData);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPublicKeyParameters.java
index 62f458f..3aadea4 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/SPHINCSPublicKeyParameters.java
@@ -1,16 +1,22 @@
 package org.bouncycastle.pqc.crypto.sphincs;
 
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.util.Arrays;
 
 public class SPHINCSPublicKeyParameters
-    extends AsymmetricKeyParameter
+    extends SPHINCSKeyParameters
 {
     private final byte[] keyData;
 
     public SPHINCSPublicKeyParameters(byte[] keyData)
     {
-        super(false);
+        super(false, null);
+        this.keyData = Arrays.clone(keyData);
+    }
+
+    public SPHINCSPublicKeyParameters(byte[] keyData, String treeDigest)
+    {
+
+        super(false, treeDigest);
         this.keyData = Arrays.clone(keyData);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/package.html b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/package.html
new file mode 100644
index 0000000..7b6c7f9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/sphincs/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Low level implementation of the SPHINCS-256 signature algorithm.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/GMSSSignerTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/GMSSSignerTest.java
index 8b0cad3..b7f9acc 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/GMSSSignerTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/GMSSSignerTest.java
@@ -4,18 +4,23 @@
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
 import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.DigestingMessageSigner;
+import org.bouncycastle.pqc.crypto.DigestingStateAwareMessageSigner;
 import org.bouncycastle.pqc.crypto.gmss.GMSSDigestProvider;
 import org.bouncycastle.pqc.crypto.gmss.GMSSKeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.gmss.GMSSKeyPairGenerator;
 import org.bouncycastle.pqc.crypto.gmss.GMSSParameters;
 import org.bouncycastle.pqc.crypto.gmss.GMSSPrivateKeyParameters;
 import org.bouncycastle.pqc.crypto.gmss.GMSSSigner;
+import org.bouncycastle.pqc.crypto.gmss.GMSSStateAwareSigner;
 import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.FixedSecureRandom;
 import org.bouncycastle.util.test.SimpleTest;
@@ -27,7 +32,7 @@
     byte[] keyData = Hex.decode("b5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
 
     SecureRandom keyRandom = new FixedSecureRandom(
-        new FixedSecureRandom.Source[] { new FixedSecureRandom.Data(keyData), new FixedSecureRandom.Data(keyData) });
+        new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(keyData), new FixedSecureRandom.Data(keyData)});
 
     public String getName()
     {
@@ -57,7 +62,9 @@
 
         AsymmetricCipherKeyPair pair = gmssKeyGen.generateKeyPair();
 
-        ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), keyRandom);
+        GMSSPrivateKeyParameters privKey = (GMSSPrivateKeyParameters)pair.getPrivate();
+
+        ParametersWithRandom param = new ParametersWithRandom(privKey, keyRandom);
 
         // TODO
         Signer gmssSigner = new DigestingMessageSigner(new GMSSSigner(digProvider), new SHA224Digest());
@@ -79,6 +86,50 @@
         {
             fail("private key not marked as used");
         }
+
+        stateAwareTest(privKey.nextKey(), pair.getPublic());
+    }
+
+    private void stateAwareTest(GMSSPrivateKeyParameters privKey, AsymmetricKeyParameter pub)
+    {
+        DigestingStateAwareMessageSigner statefulSigner = new DigestingStateAwareMessageSigner(new GMSSStateAwareSigner(new SHA224Digest()), new SHA224Digest());
+        statefulSigner.init(true, new ParametersWithRandom(privKey, CryptoServicesRegistrar.getSecureRandom()));
+
+        byte[] mes1 = Strings.toByteArray("Message One");
+        statefulSigner.update(mes1, 0, mes1.length);
+        byte[] sig1 = statefulSigner.generateSignature();
+
+        isTrue(privKey.isUsed());
+
+        byte[] mes2 = Strings.toByteArray("Message Two");
+        statefulSigner.update(mes2, 0, mes2.length);
+        byte[] sig2 = statefulSigner.generateSignature();
+
+        GMSSPrivateKeyParameters recoveredKey = (GMSSPrivateKeyParameters)statefulSigner.getUpdatedPrivateKey();
+
+        isTrue(recoveredKey.isUsed() == false);
+
+        try
+        {
+            statefulSigner.generateSignature();
+        }
+        catch (IllegalStateException e)
+        {
+            isEquals("signing key no longer usable", e.getMessage());
+        }
+
+        statefulSigner.init(false, pub);
+        statefulSigner.update(mes2, 0, mes2.length);
+        if (!statefulSigner.verifySignature(sig2))
+        {
+            fail("verification two fails");
+        }
+
+        statefulSigner.update(mes1, 0, mes1.length);
+        if (!statefulSigner.verifySignature(sig1))
+        {
+            fail("verification one fails");
+        }
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NewHopeTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NewHopeTest.java
index d039f50..c8ce567 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NewHopeTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NewHopeTest.java
@@ -1,14 +1,20 @@
 package org.bouncycastle.pqc.crypto.test;
 
+import java.io.IOException;
 import java.security.SecureRandom;
 
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.util.DEROtherInfo;
 import org.bouncycastle.pqc.crypto.ExchangePair;
 import org.bouncycastle.pqc.crypto.newhope.NHAgreement;
 import org.bouncycastle.pqc.crypto.newhope.NHExchangePairGenerator;
 import org.bouncycastle.pqc.crypto.newhope.NHKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.newhope.NHOtherInfoGenerator;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
 public class NewHopeTest
@@ -112,7 +118,65 @@
             isTrue("value mismatch", Arrays.areEqual(aliceSharedKey, bobExchPair.getSharedValue()));
         }
     }
-    
+
+    private void testPrivInfoGeneration()
+        throws IOException
+    {
+        SecureRandom random = new SecureRandom();
+        NHOtherInfoGenerator.PartyU partyU = new NHOtherInfoGenerator.PartyU(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), Hex.decode("beef"), Hex.decode("cafe"), random);
+
+        byte[] partA = partyU.getSuppPrivInfoPartA();
+
+        NHOtherInfoGenerator.PartyV partyV = new NHOtherInfoGenerator.PartyV(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), Hex.decode("beef"), Hex.decode("cafe"), random);
+
+        byte[] partB = partyV.getSuppPrivInfoPartB(partA);
+
+        DEROtherInfo otherInfoU = partyU.generate(partB);
+
+        DEROtherInfo otherInfoV = partyV.generate();
+
+        areEqual(otherInfoU.getEncoded(), otherInfoV.getEncoded());
+    }
+
+    private void testReuse()
+        throws IOException
+    {
+        SecureRandom random = new SecureRandom();
+        NHOtherInfoGenerator.PartyU partyU = new NHOtherInfoGenerator.PartyU(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), Hex.decode("beef"), Hex.decode("cafe"), random);
+
+        byte[] partA = partyU.getSuppPrivInfoPartA();
+
+        NHOtherInfoGenerator.PartyV partyV = new NHOtherInfoGenerator.PartyV(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), Hex.decode("beef"), Hex.decode("cafe"), random);
+
+        byte[] partB = partyV.getSuppPrivInfoPartB(partA);
+
+        DEROtherInfo otherInfoU = partyU.generate(partB);
+
+        DEROtherInfo otherInfoV = partyV.generate();
+
+        areEqual(otherInfoU.getEncoded(), otherInfoV.getEncoded());
+
+        try
+        {
+            partyV.generate();
+            fail("no exception");
+        }
+        catch (IllegalStateException e)
+        {
+            isEquals("builder already used", e.getMessage());
+        }
+
+        try
+        {
+            partyU.generate(partB);
+            fail("no exception");
+        }
+        catch (IllegalStateException e)
+        {
+            isEquals("builder already used", e.getMessage());
+        }
+    }
+
     private void testInterop()
     {
         /*
@@ -151,6 +215,8 @@
     {
         testKeyExchange();
         testInterop();
+        testPrivInfoGeneration();
+        testReuse();
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NullPRNG.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NullPRNG.java
new file mode 100644
index 0000000..627ae73
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/NullPRNG.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.pqc.crypto.test;
+
+import java.security.SecureRandom;
+
+/**
+ * Implementation of null PRNG returning zeroes only. For testing purposes
+ * only(!).
+ */
+public final class NullPRNG
+    extends SecureRandom
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public NullPRNG()
+    {
+        super();
+    }
+    
+    public void nextBytes(byte[] bytes)
+    {
+        for (int i = 0; i < bytes.length; i++)
+        {
+            bytes[i] = 0x00;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLASecureRandomFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLASecureRandomFactory.java
new file mode 100644
index 0000000..1532d91
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLASecureRandomFactory.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.pqc.crypto.test;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.util.test.FixedSecureRandom;
+
+/**
+ * Factory for producing FixedSecureRandom objects for use with testsing
+ */
+class QTESLASecureRandomFactory
+{
+    private byte[] seed;
+    private byte[] personalization;
+    private byte[] key;
+    private byte[] v;
+    int reseed_counuter = 1;
+
+
+    /**
+     * Return a seeded FixedSecureRandom representing the result of processing a
+     * qTESLA test seed with the qTESLA RandomNumberGenerator.
+     *
+     * @param seed original qTESLA seed
+     * @param strength bit-strength of the RNG required.
+     * @return a FixedSecureRandom containing the correct amount of seed material for use with Java.
+     */
+    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)
+    {
+        QTESLASecureRandomFactory teslaRNG = new QTESLASecureRandomFactory(seed, personalization);
+        teslaRNG.init(strength);
+        byte[] burn = new byte[discard];
+        teslaRNG.nextBytes(burn);
+        if (discard != size)
+        {
+            burn = new byte[size];
+        }
+        teslaRNG.nextBytes(burn);
+        return new FixedSecureRandom(burn);
+    }
+
+
+    private QTESLASecureRandomFactory(byte[] seed, byte[] personalization)
+    {
+        this.seed = seed;
+        this.personalization = personalization;
+    }
+
+
+    private void init(int strength)
+    {
+        randombytes_init(seed, personalization, strength);
+        reseed_counuter = 1;
+    }
+
+    private 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)
+            {
+                System.arraycopy(block, 0, x, i, block.length);
+                i += 16;
+                xlen -= 16;
+            }
+            else
+            {
+                System.arraycopy(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
+        {
+            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
+
+            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
+
+            cipher.doFinal(ctr, 0, ctr.length, buffer, startPosition);
+        }
+        catch (Throwable ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+
+
+    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];
+            }
+        }
+
+        System.arraycopy(tmp, 0, key, 0, key.length);
+        System.arraycopy(tmp, 32, v, 0, v.length);
+
+
+    }
+
+
+    private void randombytes_init(byte[] entropyInput, byte[] personalization, int strength)
+    {
+        byte[] seedMaterial = new byte[48];
+
+        System.arraycopy(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;
+
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLATest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLATest.java
new file mode 100644
index 0000000..d35d56e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/QTESLATest.java
@@ -0,0 +1,207 @@
+package org.bouncycastle.pqc.crypto.test;
+
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAKeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASigner;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class QTESLATest
+    extends TestCase
+{
+    static SecureRandom secureRandom = new SecureRandom();
+
+    private void doTestSig(AsymmetricCipherKeyPair kp)
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        QTESLAPublicKeyParameters qPub = (QTESLAPublicKeyParameters)kp.getPublic();
+        QTESLAPrivateKeyParameters qPriv = (QTESLAPrivateKeyParameters)kp.getPrivate();
+
+        QTESLASigner signer = new QTESLASigner();
+
+        signer.init(true, new ParametersWithRandom(qPriv, QTESLASecureRandomFactory.getFixed(seed, 256)));
+
+        byte[] sig = signer.generateSignature(msg);
+
+        signer.init(false, qPub);
+
+        assertTrue(signer.verifySignature(msg, sig));
+    }
+
+    public void testGenerateKeyPairSigningVerifyingI()
+    {
+        QTESLAKeyPairGenerator kpGen = new QTESLAKeyPairGenerator();
+
+        kpGen.init(new QTESLAKeyGenerationParameters(QTESLASecurityCategory.HEURISTIC_I, secureRandom));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        doTestSig(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingIIISize()
+    {
+        QTESLAKeyPairGenerator kpGen = new QTESLAKeyPairGenerator();
+
+        kpGen.init(new QTESLAKeyGenerationParameters(QTESLASecurityCategory.HEURISTIC_III_SIZE, secureRandom));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        doTestSig(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingIIISpeed()
+    {
+        QTESLAKeyPairGenerator kpGen = new QTESLAKeyPairGenerator();
+
+        kpGen.init(new QTESLAKeyGenerationParameters(QTESLASecurityCategory.HEURISTIC_III_SPEED, secureRandom));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        doTestSig(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingPI()
+    {
+        QTESLAKeyPairGenerator kpGen = new QTESLAKeyPairGenerator();
+
+        kpGen.init(new QTESLAKeyGenerationParameters(QTESLASecurityCategory.PROVABLY_SECURE_I, secureRandom));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        doTestSig(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingPIII()
+    {
+        QTESLAKeyPairGenerator kpGen = new QTESLAKeyPairGenerator();
+
+        kpGen.init(new QTESLAKeyGenerationParameters(QTESLASecurityCategory.PROVABLY_SECURE_III, secureRandom));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+        
+        doTestSig(kp);
+    }
+
+    private void doTestKAT(int securityCategory, byte[] pubKey, byte[] privKey, byte[] seed, byte[] msg, byte[] expected)
+    {
+        QTESLAPublicKeyParameters qPub = new QTESLAPublicKeyParameters(securityCategory, pubKey);
+        QTESLAPrivateKeyParameters qPriv = new QTESLAPrivateKeyParameters(securityCategory, privKey);
+
+        QTESLASigner signer = new QTESLASigner();
+
+        signer.init(true, new ParametersWithRandom(qPriv, QTESLASecureRandomFactory.getFixed(seed,256)));
+
+        byte[] sig = signer.generateSignature(msg);
+
+        assertTrue(Arrays.areEqual(expected, org.bouncycastle.util.Arrays.concatenate(sig, msg)));
+
+        signer.init(false, qPub);
+
+        assertTrue(signer.verifySignature(msg, sig));
+    }
+
+    /**
+     * # qTesla-I
+     */
+    public void testCatIVector0()
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+
+        byte[] publicKey = Hex.decode("D2F21F7B398701A369A10AB5CA5752324D01D2E4E85D7ADE9C21F2F41CAFDF25B15F173504C315EA250E1EFC243557E90C23509E7D2AE486518448EF18BA837DDE8DD6A30271E793D50DC7999485AE30A649636257E71DE6D65C9803A8FBB384181C6C28604C2B201C938A5198C01704B0F56ACEFD0CF58BC98F81E20CD233C6B5523C8A00F5DB1C934D2B4FB3609C0CB37543A42C4145CAA283C269B49C8EF323CE941F5FCEDB172CA9B5DD116DD6B2B6284DE55C2CC033426C84BD91D74837C6140E12D0C6B05765FD269BD23200FBE110D61856F8C5CE55FDF7269D6BE7D9FC213C885D74F2311A46BD5E32C06308C7C2E7F26BDDBDD10DD1F6DA004B8D28990D9A62276C4FFA90BF6D734DEAF116000CE004EAA0BB25640F9342E9C2FD40774DE360FA1560D478985BE9639F1E5BDD5B3000B364E12AEF3AAFECA99E4DD24530BB223663F095E3254E42A9E0D88639A946388331A4123CCB71A4D8925DF3EF6B6EADD257788B92E8C5E6596AEB77E0FBB86BCD03D0EE68D9B5C08E0E4B67273CCAA8544871276C9375AA0D0CE5681EBA576EF02A617879E2C27E137C8EC106B544D727CEC5B93F67CD4CB2016640B8FCE631243EB08FAD4F1237F3039376061B1AC3FE724C939CD00000EFC693C6D94828428E4CF1103B6BA8139724322ACC8832119F16528056E7D8CCCCFFE683E91AD84BBDC4D492E0215C45B5B75D1C01114DF952C731DB41D3673D80C21E901D06E898E4E6CC2CF3686ADB6760DBD0076ACD31F610C16B291EA1FB595B912ECC5F3E57CDE588CC14046D1D5AF625B0D52D61075082EF052F5F00B66C429E2B2977665E8719F4E4BE4D0EDFF5E6A793DEE09AC0589A6FECF7732684226E03A9520A472DE129A7EE74956FBA661610804F22C235BF5EA9B83D05C634215473309AC5E0BBCC30929AF45F6669340216DD0DCA2E04FA1697546EC1C93BE569068ADC39790435DB82DB3F3C21F16015CF17D1BAB83DACE254A37BC6E4871ED6031B9BA44EF7A71C1E5A709255A9CD226C0DE6733F1615403820C4B3DB50AAB082C04D0D922550DF4A940C9F20710D7B8208E0E97648F02B330EF436F436E011338523718D9DCFA2C6459DB1920A38E978D57D67D8D163313093C58E5A55F96D0DD7A170F3F255CEC4CCAAFA5044D1B21B2B5D44EA76CFC8F5B1A03A99D5DFD04A0ACE897CF480A008819FDBB7216313829CD14A9A1C9596F95ABB48D6F88AFB2B852F2E2CE687B0623D52A81001589EC05D7F6E582B79F2D036030BC6D1573618A83860A8A77C144D7B2DB988F84B16AF61E0931C27478C99B9A1C15801D0A01464DA0611318334238B2745653F14690E37699BAF5A7576FE451F5EAD46DEF2694711E018E37B24EDA8EBB9364553C5EB976DF38D9E4D21C2D174153ACBB0E2644C9C1EC56C3DB51514AED57AE8653C8361262CC21EFD6CF410160F8070753C59439465E62EAF8A34C17E8853C9817F327E273C2D2911C77C1BF9090D30C4243A39F9865545A83D089C5CB23880450E18A4B0036531164B004072E2EDCDF1A015323256BADBE8C1548F2A4CC00241B18825187015D322CE160BA48C4FAF070969A6F6CA9495E6A1959F0923394E7E5E4820B7C6358C61F4471128C67ACA4900728452BD64F6598DB9A421C8D4D33CA8C077C468DDF8FE8F387E950ED979EC2A1AF8FC473529EA069C21A9F26AA5811D343B0E3373183DB3CA9F10E63BA3FC7F81F4999BD0FBD0CA5D6C5546E9B7ABE2AC4D7A5C0FDC8C33A257A8F09281DE4C38D1273B5B37C88F8611CACD58C4E7F9BB916169104CCFA6FB7087FDA4150D5B84F4837C0DB5C6DB321FC7DC79FD90F707456330B37E0109F0ADE25433D3637112E2D96901E22C734407BD988AD203CA805AE8F757EEA09F327DD49710B471CFC197724948A011E597F3A4564CE00FF701B9B240347F227F2BE01582E07680AC993699D2E1536A155B96AE4E461E3D019F0350CBC52EC12186069382FDBD19CE7D70734FD72F8E61361D6BF9EEDEFEA6D44B6B50E1612ADA4E42033329B098318DD9CF695A2921A332204044994F244C0944993B08009265B8004398CF119F95FCC217D38228F1D1F14BCFC5B7160986C339");
+        byte[] secretKey = Hex.decode("2074F000FEEE3B804206EEEB4FFE07FBEF2FC006FB839F4101076400BFFA09AC6F00FE2228C080FE0944CF7FFEE16BD0FEF80D20A0BFF912102080FBEDF7BF0000FE5F9FBD05CCBF2F3FFFDEAB3F00001F48E000032A648F8104F5EBBFBFFFF92FB0400DF78380C1FAF53FD07FFEDC27C03D00D3FB5F8002F153C080030FD88FFEFA139C5F410CFF5FF000081F38D07D01E137A08002120050FF08D4EF1F42010BBC6FFFFE0EE4AF4103D23F0042FE061C60C2F8F5BF8FFDF9111C604200270010BDF8025CB04107F387C07F06EC0770C201FC53003FF60B5070C1011BE0BF800518485000F9F8FF9F81061E6410BCFCE42F60BFFAF4DF5F80FA020450C0EEB0DBFFBDF9DFDBFF8100F42F00FF091068A04101F2DFCF41F90B28B07FF10FCC2F80FFE73BD03FF9F4F35F3F07FF6BC0C2031AECCF7D04F467E03FF2EDDBDFFF0C02804F3F0608D02F3F060C28407DFD2F54AFFFF905D02FBEFCFB8340FE040C885F8103EA1FA001F8F2F36FBCFF1FDC2F42012D1020C0FEF8DBAFC1061E2CB00103F7CF20BE032E5810420417AC1FBEF3F537F0FC05F807603E02FDFFBF7DF2EA13A07FFDF84F40FE070908A0C1FBE523A0810A04703080FEFEDFFF3D01FB4730020A033440BE0AF86F8F42F6E99BA0C1FFF5BF1001F9F15FC0FEFE06E4AF81FDFA4F90FEFC27B02FBFF702DCEF00F7FB83807F04F0CB3F80FDE09F8F3F01E553A0C006016CA0FFF438983F3E05F5D7DF3FF71A10F03E030BBC8F7FFB19C84F81FCFF03D0BEF9EEDBF0BE02E9C71F3E05201830FF03EC17C0800028741F0000D97B60C004F34F60FE0206788042FD04CCDF00081A8CAF800401708F7F10EE0B70C1F411D0AF00011CDCEF3EFA1654C081FCDC7F907FFB0104508208FDAFBFC0F815B0307FFED417B0C0FFF0471081FFD8ABFF8205134C90FD03EC4FA07FFF1434F08003FE9B507F01DD932F3FF6DC833F81FAF6EBCFC1FC0274F07C0201F4DF3EFB203850FEFF0BE4EF4008F71B20FF04E3FBDF7E030A608FC0FF1904E0BFF5FC0B50BFFF18AC3F3FFA08C8BFFF04EED31F41FD0B78CFBE030B38507F0302DC4FBE00E85B300000ECD78F410105C02FFFFAF393CF8102EF278040F20A288080FF0FD03F7FFCFCF78F8009104070FEFDDF2F80FFFD1DE4CFC0FEEE57D03EFA06E48F400103E89F010228DCFF7F07FC9F3F41FEF4235040F309A4E03F041D980FC1FFF85BD07E00C88BDF0008F5EB7FFEF7DFDB9FBFF7F43B307EF60B688042052B982F83040AE89FBEF2E51BF0C00621CC0FC40817BCF0FD00F3D3EF00FBE75B2001F8170C3041FBFEBFBFFEFDE487C0FEFD318C3FC0FEEE6B8F80FAF4834000F8FDF37FC0051A0C00C200FCB78F0000F2DB1F42F812649F3FF9266CD0C1FC0B8CB0C2F8E5B7CFC0F6062440FE0304F8EF4009145CE03FF5F73350BFF80C9480BEFAEAFB2F02FEF9CF6040FC0E6C403F09016040BE0109A0403F01FFDFCFFE072C48CF00F8FFBFCFBFF80AA04F0103E6A3FF40F706ACEFC0032AE09FBF0C0948CF3E012914303EFD1518904106C917907EF9F56BC000FF1650D0C0FF008CE07F050F28913D0126B44F4205FAC3AFC0F40134D07D0CE93BB03E04EC735FC202035C1F40FE0810A08103E8DF0FBDFD22D8AFFE080A5050C10007186000FE06DC5FBDFA0DC8EF020300EC6F3F071248B080FEF9D71F800B0D3C208008F9975F41F902BCAF01FDF06F9000FC17A0B0C1FA17A42F41F8FABBB03D090108707FFCF5BF2FC107ECB390FE05E25FD03DFF11706040FDEFE34F80F62F30E0FF060E3060FFFC993B08009265B8004398CF119F95FCC217D38228F1D1F14BCFC5B7160986C339F23EB15423271EF1CF476289657DBBB1460665D3944B78BEE92D15AA609768F9");
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] sm = Hex.decode("AF8718AB747CEFA75F4A5F26B5392113BDE8CAF1DDDF1D45B473CB64DFFC255B2E211588DAB5D5D2C5407EBBB684E7D8F0C6AB8EB3AB874CEEB87C9CE32568D0ADA6A3B9716EF9AB92BB9F02424D91DD5C3768E4598D2DA954D0664EA3D61070FA4304983B44D6098FB74B55FE40F156F07D0E39F11DBDE5E0549854ACBD65B2F6EFDBF3CEFB27DB44FA18EAF9BDA9F4409FE020B8FB0C7E9A1C17F5A1D59D6996FC1EC43AE246AAE1A8FC777EF5912EEC95A6F9A2B32A91C2EEBD733E0EAA43C3A33E989D1DEF0B1F412C6299628DF5AEEB599391C411942873FDCE0FA701C58FD2FB383A7FEF455AE12F415496BB0F6E75441BB0F670910F207CFC372B823A099EA84105E1B316CA33587FE990017BA0C52C1EA85C9E0F030B63168B093EE83D81C47EE70D5EEE3F5603E00C3FD306DF8D58C594B4E2047397FAE4BB83FCAC0D3A8BF7896770017860E9612BC4FE5E416AD961C277836C89E29B22C9C6A1F2F8A55FD84D57F4C3550EC14E9B30BFD18A3E479217DB48B65B9E3ACF731C08F8655D2D2F689EB6547C5241887CF017FF9F0EFC49D3448D623353C6846F2C547090873903DC281D2D327C1DD8B49BF87CDF28DDAB0BE1D48B8874D8693DACFEE07E51D5137B57F58D1EDFA374C5FC2A9C42FCA63393C8F24160BF54110D8721BADFA288F304E345FBDF179AC43FE45DFE55D98683A8B7E0A7B48BA4193B01294D6666688EA4099E842F78402874FB1CE5A52FB14B6C6BA3343941E0012556C1BC8184DB46B1E49161E5504815ADB45987E7EABC1C5657D5D8F23847B728F5AD51290995BB742EFB99528147D423FB56EE1118E4C170523E4CE2734219902FB54457BDC111F54497002C0D06D4EB11FED96683DD3F5E1A938F4D141DB638BEC5C8A5BBD5E7F4BDCFB8D03C4D003BC0DEA33119015FC6D2468C0A5635049A04055B542463B4AC17FF4AEB339E674A1E5EEE7C0FACB7E6CE7D2B990C4E5EAA29030F5CB76BD83CB59D215875439F68BD8731A4D5F54B1D27B905AC42D381AC8A1C6BF1F785CCAC2CCA0D08411A2845A920DBC51C82105205F1772DBB0914030BF06115CAC3596E0BE6F97ABBEEB6FC10A84819C84507348F2C887366A82B6FA5DB6B34111066DA4A41A709AA7EE8622A3B3AD5C854381440D08C49BBAA025E7F1D459A2606368285B12E50330DA0FDFB9B50D34CC3664B9F13A4B9E0E65F7F5390A4D8D25353CA2DDC5BFF4A1238321AFE33BBA3318D5984AE57799ACBAB7B5A0C52120CA6014B89B3BEE993183703BC717871A1D080E57ED5E4FB5C64AA4620B5F78DD4144C1005ED3D4D2F1E94FF01DBDF0107B52D5FD3B212A72AAE32BD32846E0B31AEB2064A30361E8F58A82937AAE4C3E74A61C9953406B946809B11379411C910A9E9B7E2384D0E9C09A78763CB2DFF356941B2D863C35FCF0D5735B478D62D1D90DC21E833C871A77E533EACDB08081105BA3B65E8953EFECB9C2096DEEEEDACAD630133A6D35AAADF07019EEBE7D1C9EA1CD25F55454D861C1A4A17BDE58FAEE5696FA4DD496A4C0AE443524BB33B98D8F254ACB9CCB852CA4561BB4E3830A24E5EDB9233A20B37F42B323DFA6DC1A891F0C306A121EA94AD8071D107C4B4CDB294FD524014339CBB821F60CE298EABABFD3D64F6F5237DBC14BA7867740F047325691DEC988E401E8CF1427CD040AFA8A33B8BFFC2AC080ECFD7BD4F22646CC7013210B80A99B1F7D6031FE4F50768531C604E7AE9349E87A0006566B61EFE18DF34C385B1B5E8D35946D8E53A75A7412DEC7E8A28A06E8DA2BA3B860AF01455359C01EEDBFB96D6881E5EEAEA7E1B2663A18410218B1588DE897E8E01DFCD72D08757FE1CAEAEDF71F7303CB91E91EE7ED7C7BC3BA6F72A3C8382FC3ED355C6F56CDC8630B2C3A06EA9DF197823448E70C01F349FB0E6D69958AF7347D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(QTESLASecurityCategory.HEURISTIC_I, publicKey, secretKey, seed, msg, sm);
+    }
+
+    /**
+     * # qTesla-I
+     */
+    public void testCatIVector1()
+    {
+        byte[] seed = Hex.decode("64335BF29E5DE62842C941766BA129B0643B5E7121CA26CFC190EC7DC3543830557FDD5C03CF123A456D48EFEA43C868");
+
+        byte[] publicKey = Hex.decode("6B8495B1C393966CE20EE722515892480FDDA8F7848245573308D4F1C3C93C4F31B262002E32BCBFBCE58F58C95322F9158CB10F30ED2DE4E1F202DDE2C4F68143F354A52F2C27B12B8E434E14EABC9C61B421708B809CDB3908B75106A11B09AC4EFC19662DDC818D4D2A50827D6197B8405C3C9D1AED8A513D3BED08CC60BD04B028D50CD55D9ECD3640EF81047F505B3705AFBDE294DF42EE9E488D647E9C7D532724ECF6501B18680F4376EEE1E0ED1CB4DAF5861F19AFFCA2092C8A6845224008B437F352695B7827E55C7F0BE5A49B464B4654280FE67A137F69924F8529FD947C5B1E9953BC86808243FECE6E03682FE46396D5B174148059703694A74D9DD399B64714EFD666874AE771E1C8A7C2E73D03EBB82731DE05324BFDC8356D5809A40835393B78CF577778B6DA061B2E640A083E5748A6D88CA3A1B106BE566B86C3293886CAF38BA93E16267BE96A0E91E08FD8683406779C1528298FC98101B1ABE625200B1296C50170388B5A5432A020E2D6DD7B40524B52FB960B8FF3FDDE3526842D355AB860B78F9A4AAE588F548F7EA05F94ED711646D82D4BBF8A22244FD9C466DD71E4C314B98235D819321052039F5996C216DA9A1B0BC897A1DA04F2DB48944CAA0C0F5BDCDC10498FD1E3B6A76EC8158ADB9280F7C0F43E1E835C178B114C939F94DE20974301D9AFB3D5D4CD9D3C24152F891E8019BBDF5B74E7212B85E5C6C90614219F54E674334D1D3A57EE5889F2E6C334D2475FF899FC50DA49C0B106F05810868F850A95411E6B616F56397221E45A363A2A4066AFB5A56CCFCFC3419F99B5CF87C977B6AD926504062CCEAF1EFB578EE1A2EC6CD6D1244EB20604E94D37C283238105B0F4834FB2946B8523F308D0020B1C8557403EF85235CF9F547842D6D94A8DD380BB06732EFF51FD315CF111BE138A7D118643B5A8921FE03EF309169539D7779CBA09C6079BCBBDCF84C383939E55C9F042FE5130E4E31EA33E2412A9F3B308F781E8353F7261DE83B20A5DD66210F05B71B49BFB499D60D8C67E2CC72B67D25E6B550BAF745F0E11F29A185406752B0109EEE7CB503B6F785DBEBA42270E9AAE1991FA41B5758A9D0CD74C6823EF23D4D62ECE67370E0732C43A1D54DC61F54CF507C251758E6C78D6DAA5120E202CAFEBD58A25A933B4B48E1A78ECF36104467A7167231918E93AC356C4AE2FCDF284A3B0E9F19038F8985204BC35839804484D3D4194E1E72A7B22FBBFE03F00072A3C05689FBC6E11C71DE2748826A40261A6357904B76ABA722AB91DE9B3C9FC66034B2C06AE45625E8071552A80A807F20A3ABD1D094512C64B57212941D3190638CE3FE69E5CC8759FEBEDDFA46FCE02B822DD32F1751BC41F4EDCDA63BE023154FE075275A28548450494D9743AC52DAB26971C78371AAA3D87B8F000E95FD79EA6F17B5F614FD0626C236F87072C56D8F7F7EEC167323F79A26E32048BF7C43C00904697F1E4C93D018D7F0845EACC01D3989D3502E2891B776B11CB349AF9C462E83AB1039B30688E153D7C1AA62C46B8889E404936912BCCA2517C059B89EFFC1FE1488C2EE3473C27805715ADE619BD3645D540C84D187441A46C0D904C3C9E2A92A92FCDD6D86380395A934DB93979AEA13C428EAFD9259CB3E2478DB643D0D6913AC275ACD2387808669D2FC40FC6011A87AA1F03639D02692BB18322D6417808A902A403068A7A45DC62A5ADBEC18458AD4F91BEBF7FF60494AA7C0A02AA6E067A86430CAAA3536056EE44944CBA38B7AE9016D66621703A77BCD36BD104BC2A040A12577DBA2FBF4B89AE4F49D202C27681B05BAA8C4026E0A60D44140A2213CAF78BEE9047A10569ABE5C568FBEBEA276DFDCF905F8398432129A82AF45CBD10BE668D0C60F4B64327388B830B1EDFE4ADB67EC5472541AD29C16B732A5F2F6B8EB1DA671B6F1ACC4AB510694F2AAE8055127734591BCEDC1077B757C036ECA8E4A114A649EA4730EAC4309B07DF98A5D8EA838C84675A7D10EC93E1D36704B23BD04E27F635AC6DA31959BEA5A10CA37B7193664419926D965E7F1916EC44B52B4BA993C5FBE705800B2C070B09CF36E0908FB89F71E2F7AD9448");
+        byte[] secretKey = Hex.decode("E2FB4F00FFFF37F07F1016B87FFE00DAD38F3D08E9BFBF7F040634B0C2FF340490FF0205F87F8106F3075080FE0F105080FEE9679F0006FA4720C110F4EBFF7FFD13840080FFE60B80BC04003C307F00F827807F05EFD77F800217CC5FFDFF0AC0B0BE0500701F80F4D48B9FFEFF184C70FF00E9DF7F4110180810FEFFE1F31F3E03F79B3F3E00FAEBBF7E0508D0AF80F52BF0807EFFEC3F203EF61CC0F07E04EEE30FC1FF02E42FBF00E65300000A2674E03FFD1460607F0714F08F3E050E9CF07E03FB7B7F7EFBF4B76F3FFCFF6B303DFB0D981F0202E56F60C000E37BB08106FFFB1F80020808F0BFF6CD43D081032354A040F8CE63C081FC070820800401E8CF7DFA106480C000F5DB507DFD0C7400400515D81FC1F8E32B10BE02F06B9F3FFDE947908002F41F50FF07E44FD0FDFDDE2F91C2F9DB23F0BCFD020850BF05E4DFFFFE00F5B76F020DED573043FAEFC72F80FF0B4000BE04C8A77082FC22A45040F9E7570040FBF713D04000160050C0040E884F81FEDF9F0081FB12D02FFF0211980FBFFD1768F080F8E313803EFEEA6B2002FEF9B3BFBFF10B7820C001EA0FE00001F5D37FC1FA04A0EFFEFC3CE4EF7E02D9234080FC198070C100E417504203057860BF07F68360FFFFF82380C1FD1C90B0BF051328203F02F9F70F8107EC435080F9FDAB2F40FEF21B20C10302F02F4104F27BDFC0010258F0BFFB01986F7E02E7AFEF7F0418C4CF3FF4EE0F10C007CED36F4209FE5B10C0FB02F47FBEFEF867E07F00F9BB1F41F40540407D01ED6F0F41FEE48B8F01FFE43BC0820CEFA38040FB01AC2F40020F54E080F412D4CFBF0118A44F0305EE67A03F010B04A03F0819B80F8004F09350C1FC10544FBF00FA7F800107FC9BCFC00724F82FC10A2F74C0BFFB0AE05F8103C9D78F3F0601F02F7DFEF66F107F00E85F70FFFB1678CFC1FAFC338F7FF701E86FC1FFEA27C081FAE81FD03FFCEF87C07E080BE4AF81FDEE63208002F41BF00201F83BC0FFFA0BE4CFBE0416E8BF4004109C408008F5A72F7E00DE835FC0FEDE93B03CFFC7E7AF81FFF03B500101EC5FA03F01E0F33F3E0600B44FBF00F01F200001211C40FC010D8C200005F7DB9F7F09FBA3FFC002070C7080F80388AFBFFEF297C0BE0AF92F507EFBE3F79FC0F7071C5040FE00E0DF02FA1FB85FC0FE0F504001FE15846F8003036830C0F7DE6FB0FEF3FFAB4F40060FDC7F010DE80B7081F70D741080F813CCAF7FFEFFCF3F81FCEFEF2FBFFE07E0DF7CFE017C30C1F9DF673FFE02F11F900004203C7000F9E04B40C205F107B0BE0225EC9F41F8F9D77FBFFDFE13D0FEFEF5CFDF4000F433C0FEFDE3CB9FC2FBED4740FFF608E80F01FF022C8040FAEF13A07D02F4DFBF800331548FFEF9FD03903F0312B46FBFFE10C0EF010224F0BF7EF90834D0FFF620705000F4F55F10420021DC6F41000D2C5080FFF11BB07E021CA0CF42FE2FF08F8012FEB3807E07CE571080FFEF9F5F3EFEE30B80C00512CC7F41FAF96BE000FBFB13B07EFF0BA0DF8202E00B9041FDED4F30C001F2CB60BE01EC0B20800102E07FC2FE1710D0BF0B231C903F03FF1FE03F0533F0DFBFF807EC2F000917445040001A58EF0001FFF36F40FFE627B0BC030E3C907EF5F2AF4FFF08F2D72F40020E683F7DFD049C5F80FC13F42F7C0006949080013118207DF6FCE36FC0FD0D348FC002E4B7F040F9076490BFF7E56FB0BE0903B04FC1FEF623208408F06F303FFC128C4F7E01F967603E14F447908009D7E70F830BF1F30F7F07F9B31F42FDF57FF04004E3D3BF81FB1916EC44B52B4BA993C5FBE705800B2C070B09CF36E0908FB89F71E2F7AD944898B305452F9EFEA0237F6BACBE4022FC80E5DE2D66D398814A7C835419435744");
+        byte[] msg = Hex.decode("225D5CE2CEAC61930A07503FB59F7C2F936A3E075481DA3CA299A80F8C5DF9223A073E7B90E02EBF98CA2227EBA38C1AB2568209E46DBA961869C6F83983B17DCD49");
+        byte[] sm = Hex.decode("553082050F6A8BD54C40E296C67011C2765199D81304F22C3CE306AE9C05235FC8EE08D851E2397C2956795D02F1AF944E54E2D270ED89FD6F89FE9BEC269B5CBE7F0184B244F89455876E181F39E1221F962B34FAE1A8F6E4EEFBE882DCD5BE4941EAC3ADAE263EBFE5AA9E0BCE6D2AEF44A12710B4301F07545EE129974B27536C2DD6C38FE9D5F7B725DA66C8B7A92F439C6721AD2CFC389176BC6A39FF44483C06B9D1A554164F5FCEAB5BF317983E8525ABF7EF291DE0FA281791680F61012A06742836CC6E0CAA87BDEC0C7D451801979D3A993068DE6F601DA81615FA56CB0A4B315CA2CE06F21925D088DE4DF0CAAC3DD33E51FDBE8C0DD187BAA82216009DD2819D524BCFF70012A734FE9D936A413AED15B3D652258CF6C3B0FD5BF4F64B7EC9BDBC9251C01A34AFBA707B35BA4EB6C392DFD6F93EEA454DCD2BDFDD689B5A980C21EEF124843F5571E31A69AE097A25B3CB1F04ABE269E78E098B658201B8BECC456147F759B14C3A3CBC00D1D7E3344F05E433C1C82DBD4001C6AFA8D948E92D1686C64F57436F6DD9ADFA82F35CB3814DDAD7AD0912B62B6B412A40D70F49E05B305BB5DD319C5C89775F27D3DD1884B5936A54738F816AC81FE328AB344FB39CDA6EC0B64D47556733815D2D83818D88D65094933E6E9A9FEB7E433D3EFE0B4C98D7ACCB9EA851AC72134283116513EDE0802BF959ECD5A8AFA9407D8EBBBD8ECB48B24CD034C3FA763E83782D6B0B2115929F94645F9378411251692C8858BE725B9D8AB880054956E71A75D5DD3A88D1476C36064A3C62EFD861C4A25943037ABFCA2DA9363F34371E868ED76D48E068C3A078757FD8E8588B356530F098ED81E3C21652E1071A6DE3F8610B94BEF7EFFA5C5A955EA3987B0EEFD16E6996BBDB563BFAE6C769AAF95E4CC4E42634A0CA641297642C92A57AA8A704E35CD8A759366E1DFD2006C173D31BC853A1089284F043E1648DB047E944EE26AED13A07DFE7C6893998A43CDBF9520A9E077DE27B36F03FFF08FCF4C4F6F4076C54EE6CD7F93C7C1297A4917B3E430981B312C2E1ACD923A419175C735F80F3957418BBACFF5F09425F047564E2F9FE4A9445FDEA63657FD25D2177A9C5A2BA11E54B3D0C06B2323735622AA23A69C14B4AA78BC4310481067E68C0225CBAC0C40A21E8676B5D2E82DE06BB5BC95A3A4269747E9BD3895C0C29A4B25BF85AE0C46959B6AC8BCEBF6CC3039FA0AD7E4698CDCB73C44A10A3469520C5967C2B491777711DFF1F311FF16639A5C35F10FF7DE14EAB5A786E7CCF152BC45600CB3565BF69477EE7BFB508F3259C75EC87B7F57561ECBBC8178497F6B77D5C8D6C2D3DCADBD7B2C21B59333FAAED20C5152E35886B1C672F6AC4BE7FB0DC6D9F4478428141FD35E30691677A1E6B866CB91BB9962D4BD5462275D5932820FD104BF0E1D4E08F0CC2B7B1FF36D2A694B848F1B84475FB647A828CEE4DB1566CA35A908562DCD45AFD107453E4FD18B571B2F64B077DB9AE3BFA15F0C10BAEC410805EDBD34B6638138651F24F91F9D82D8E7CC07F1F5AD93B1017A375A4A9EACC85A96403442BF1D5D9BF18204011E666569B6F280B268BBF339B607D7C33BB59EC7E12E7F20B14BE770547F797E990E70A8F4F1B738B4EFC7CE124B919537CCDEB5F0CA8DC2EA9649640EEEED11489C0A1C110DEB64BDA50C756A3EAF34C4E1059560D3788FDACBC2E352F2B93BAE31DE89C0D19277BB8AAAF0A419C2C4C1B265BBA1B41A9719AE53A7DB5F051E3B08836E5D3087A94315C8B83E21B1217E1276FB90252F56E72CF9C406A9539F782B4BCF95CE4E55340A00899CEDA7A0C020AFBBA1DEF8C394CE2354E86B7752DC57D1CC94ED32A27F12A06AF8C72AD2BB0A4D619C7F1569EA066B8231BA0CDFFA761F54F1A574C99E1B24C53F33F3D1F34225D5CE2CEAC61930A07503FB59F7C2F936A3E075481DA3CA299A80F8C5DF9223A073E7B90E02EBF98CA2227EBA38C1AB2568209E46DBA961869C6F83983B17DCD49");
+
+        doTestKAT(QTESLASecurityCategory.HEURISTIC_I, publicKey, secretKey, seed, msg, sm);
+    }
+
+    /**
+     * # qTesla-III-size
+     */
+    public void testCatIIISizeVector0()
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("2773A7AD9D114C05E70C654102216989125420019CD31541E8A191B142C2732FB3CEC5C9C070A830C50A960E4B07C8108AAD0C044AEA0CCCA544422AD17966A52704BEFF1F2BC92423D79C128B891392824FD4EACDBD5DEF0CC2733BDF9D0D0835CF08640BB61DF4006340013144DC913AA00D411FBBEC59D5A3D20E1A3656319718F82F683978FE820C2AE48349FF42B63F44E71DA1678C2ADDD8447E9B30AA7D083A9FAB6CDE4B44AA9668F1CC4F58432D55340041792C06F3ACE7515DC1BDAE4DAE329B49FB3C4F086A0CAA846AF3C52721C1DDBD2A88093563A6F9FA11695CAC38C838024008B10AE0C76DE6272E0198209E133FAE50410192782130B838E56EC611E707140595C9B9620FB0E547C10A757EDE0FA29606028E8200740307E9ED8870D9199E0976E30F7C4233646209684B2AAC28E4B343C28008B4A4FFB8E02AF2D01E61E20F9C5E02886DA498AAFAB60A702AA4C2E050A4691FA6DF8ECE316AB75A61B55E5381606877488C630D33CE27382BC5EE368C5579F2620673E79789B1C79C405B97A426E09E49090BCEA3FE40E01513EEDD9081129C0A0A3B5884FCE5CB3FE7E6B37BD08FF1324ED390E507CCB3271150B1D02F9F15ADC205ED0068E141D50DC078D37E4E7E283510115811AE1205CDD1A109BB114771C0D4FA9E8129556F1F505CDE6E0507B73BB32B0AB3EB84AD99586E3E3326A0B1BAD6C7A8E0053D18368AA8BAAD7D702618EAF33A3B5F17E5EA410803E552E4262256682CD27005C054103B41EC37EEE354CAF7AE0F19A75EA470616F94174C72F95ABB4B9523CC4F291FC787A990E8EF43D2B8C1A76AAEC10C095F1BB0111E1A4046F6D131E307B960523D1884ACF914CBE40FD3130272510A13133123AB70356430620A3EBB2728172BC56C0F55064C7031D6A461693C80495930C25A04F61E223DFECF29526CF8BF261FCD8AF44CD13AF9E8AB227A579F35B0955AD56EAF7C012265B0A839D951B93C0E6A2108942B82C2658FCF12E165ECB57B74DABC2D30CA246F9B4FBADF654C011D00FA144170C9E9EBE1A935D620C43EBDDD8D1E60C29EFDA04CC17407514887F8198CCF58092CC75C241CB34AC2F9477DC0675E8CDB2D6934EC7B3EC25EBB370CDFB79072300575597754E5609BBB94788FFC5E12E713222BA5D6A22BED445E13E170AAD9ECA0D26E22CD0B808E1090EF44D9BE210E426765B9CB2D75954316160A44C3D936EEB21DAAB7AB1E60A4F651F3F5003061D80E0E17C0530D4F15474C2B861B72B3FBA058CCBD14568236D40E6B3C359FAE089388639703057DD09806C9108EE6843F02F03492C6B70A421B01DB32C10DF88AEBAA3CB45D4CC0604479AC1CCF0C9CD76250AC3345BB60BA081462B1667A1A5C772B505B5D705223E8AC30FF1C423783E1977E62277D196517636CDA83A80A87AA54087CF099B1494DB8F63C166E183CD3E8528557038D9D826DE01963B471E3337093139CEC888E6C5B1249826D966355BDE17CF989254C8C35036714852B24D89BB666EA8AB5AA68506125ACD8A51848478A2C14C94F189578C0ADC8D04AC0E896F6357DF3AC17172B06B5B25BC4A41CE3AFB266E17F7990327CC174001F71ADAF8F5EC08404A3A74B80E4974D20B631C11477BCCC1164AB8F49F7D6F57AA4A77F724435C218108DDBA508285D3E412A8778035FED2E516DA7BF990AEA03FD7E91F05537FB5A962A9259A0750DEC465687304925CFE5BD8F044A7A1EE638D2C448235D2BC2C9958C21DBE152B8D9865E336B57911AF86F404723CC995AE595F888FB675C47C2BC311F60BB3E47B4125CCCE51AD3A231A9792DB6788A54FC2935E22313A744065387AE450F0168BC00F60BECE76FA41E5383B0B45F950B27D327379865A73C6239B481FFB4AE78757CF51F5ED4CE20A4C31E89E19FDA2959158578439EC56CF6D29B0B2F8C307D63A13E8073E5AA7CD2C0FBE55EC4348AB807E773D726754F72EDD526B788ED61D031B3C2F64803AD34CDDDD8DB86C0078AD3CDC728B0399401A0C28E3D322FBD576FD97CCA4EAED9146CB3F83D0139024C14E249ED6996FC6182E739C72B177149EA08FC991CAA73DC8D6729649F395658B33D49B21D221D65E0BC90A0AD85E0F714D06E2CFEBE84F163843B7854F9E83A0409C07CBD22646C1A841FAA6FDA61FECC495CB2C881CAEF7EF4D8CF54B52241DB36B68093D40880822E808D40C2DE6D0488F476FC79064A0C8E03A05C02C6DB20F3EBA8E0067C154F2CBC7C4849CF87F7F1C92B4EA8BA70177FD1A31F691168317A3D4CCF9B9EDE02DB044645622313807E49BEE1D481136EB26803CFA5756E177EDBE1AAE6B0C8541B4FC48CF01014E54B2C789241DE9C54ECEC921AE7AF8A427213689A4A2CAB6376719C25A896B45CC414CF762B91A9840210095A46F3281A6DA086B254C916663B7941E4CE5281E33D8E3712226B201DAEFF4ACAB7C504C727F16D2F11C14885A2ED080638B153A40B3B12457A6A3AEF950BEFF282A079185C64FB1CACF19B8182900F2C4EBE28B7E370A3235D45C6F484FE3F75449E499F07F2F0A4FB1AD6508ED8CEDA34EC98A4957EAEBF25FB8F43D16F6DF3E32705A82979E221CC11E63B0C883D1EDABD4CD5A0855812477BCC726D68F0B211420C068D60BEB2D2CE9DA32EA68657432CB2014904BE4248FD08DA1F9C5E455B32A1A1C9AC31C7E7A1ABD8BD363A38AD78734D1EBED954871C274A5D54C1B492F01149BC90848E04D026118E9DDD0B84C1FF22473C08D267F1581D566C5F62EAD72C79D75F565BEC6C3275ABE3638D2D003CBA666B5AE85AA6E33D807F72AC157F64390320F3D4203D0FE2B70EECA8BC174D98AE7461444CB33688FA8BB82CB81F84DE6085BA9B63A9098A07E78DBFE5A20161366ED941116C790C67D7BE28680D3372C19E7BB17433903ED4688D63D4CE54DBC0BABBEA4916FA49AC71377A7E2812BE6512D504C04F51A6B01E625E806DECB5D3F6E19C726EDE8428DCE04677B2D8A7083A8C5A9304B06302F01973A169CD4A0D04473F769BA8B08E069F96EB07AFA0A5A061723B5C537BB6B41AAEB03F736F1B00210CDB816109D148692D49854DDA515DF53E9F737E594EFAA8033D576401CAF82ACD6CC7E8E913BCFF0CECF25D6C8BE37CA3B0D05EFB405EC144E4BA8B43814814B3488C6C7844924F77D06ED8E9D4090F758C13226577265B20898D40EF82D8410AC9C807D6BD0DB7124D4F23468736555B584BAD6864130027C825F8F15AD4A6550A5BD9AA04C69D38D44BDE53B5DF9EB202347876643C250278D786F8D8C4A8C9EC33FA57D8803909FB0991D370AD2FE3B6BE7E0798D4282C05B27410DA4B1718F05C82269C3717392580BF0391F20BA083057D3AA392E29D5C81CADA50069F812E6B8EFF77C59CEF51D27098AAA219BC59D4E549767435766344B754AA70FD43877CCBBDCAE4D0D6FE8F30C94B87C82ADC3BF62DFC6683D910B13D4ABC4B6B68C47AE51317F1F51B16918E7D9E851C653330B01838ED10601494C8BD86328405C18FD20A62F384129DC5AC3EFC58A915A182369F1A1F088608F64B314FE1BA543325B72986612765D3A474CC9A6CE04E1ACFA49BBEF23DD0B0E4E40C320578C985E8C29A653AE7046016E067887E85C9C3CDCC456CF811A256FDD8F1E9E8660466372EB85BE369D3B496991A7D99198794DDCA7B64B4BA91BFD7A3C258B170213639302B4EB45A8C9CE7C2A79FE3E3FCB0C23B057025CC207A6D02FABA9E299FD95525B4DA20250A1B54FA196210681961A04B9F6862C5F87586BB90027C06E9B769D15CB06A7867F7329731AD18DB714980DB021C8A93D32A20858AB61A741FE8B6CEB89E6DCA0960B06286AE297A5FF8B99A42660B3B802C330B91F686E6F314B62B2C83E8512D93C5E29D689AA1AB43401D0A866586BC937F66A3D0711C20C9AC36426AE20041358AE8C3E1E6AF31E4DF0157EE582210B6C32F346481B7D888D34DA15238C62E1B43FA4D5C1C67F6EC4F25A62B810418D3512B81CB107D9E88711E64C4F224894CF80D09997744FD7AE52446A7DA4E10051D597833AE1B36C86A0CB69B51C27FEE1B0A126FD88AE54BC04291183C0919C4912DD5D4E9EEC9C40B65D58DC33CB0289B0FB60D11B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD");
+        byte[] sk = Hex.decode("F60AF7FEF8F7FE08FBF50300F8030CFEFFF9F5F60103FF0FF601FE00FBFA0E0003FC030007FE0201F401FE0404FFFEF6F9FC0209F705F7090E0002FDF1FA02FB08FA010602FA00020B06F804FDFFFD0601F60400070100FD02FBFE000B02090308F9FF080005FC08060103030801F90B03FA060704F9FA0FFB050CFAF412FB0114EC070BF4FBFC03F204F606FD0400FBF400020806FBFC0CFFEF010CFFF9FEFCFC01120909030CFDF303FE0200FC0600FCF4FE0504130D010CFC0208FFFC00090A01FDFBF111050911FCFE010BEE0302FCFE07050A04F616FDF8040D03F7FB03FD080503FAF90003F70A00FD0AFBFBFDFA040EFCFAFDFFF100F507080109FFF7FFFD01FF0506FE04060B01F7F9FCFBF305FF08060604FC0DFA01FCEDF5FA020707EF06F8F00D0800080EF0F6FA04F8FC01FE0102FCFCFEFE000306FD030601FF0308040104000302FFF5FBF60AF7F0010B00FF0009FE090002F402FD02040F0100FA0606030205F71200FBF70909FD07FFFBF107F703FFF5F70FFB0D01F90301FEF30802FAFDFE010608F80BF603F8FB0E04F905FF0CF9F40EFB0B04FF06F7FD08FF04020103FB03060208FEFA0CFC0A09FC070BF5FBF903F3FEFF0312FE09F9FC040A0EF5FF03FFFBF8FD010E03F708FC08FDFCFB0C0AF805F90EFE07F5FDFCFEFE0502010801010601EEFDF307FBFAFDF903060B0C07F800FDFFFF04FBFBFD06FEF8090A050A05FE0A0602F102FC03FDF711F70401FDFB00F4030DFF0808FC070405FC05FA0AFF0EF200F6FCFFF8F20E0AFFF8F7FCFF06F708090FFAF6FB04010207F30402FF05F8F2F0F8FFFF060206FAFD040107FC0AF305110E0502060B11FA0500FC0BFCF7FCFF09FFF510F802FD00F4F807F5F8FC0EF9FF0AFBFFFEF6F902010BFCFCFC04EBF30703F7F30204FFF10A0BF80D01F7FC0B0701FDFAF300F60204F60EF41103FAFA06F7F7F80304FEEDF205F9F9FF08EF05F90DF6FF0307FDF206FD07FEF7010307FF0600FAF7140300FDF70410F601F80C01FB06FF0BFAFB0209FC07F506FD040A040C0DFAFCFF08F5FDFAFF03000503FA07FF0601F8F30A020201F5F8F9F4F704F60203FE0505FC0A0C02EF020204F7F5F60405F7FDF702050202FA0B06FEFD0FF7FFF201FE08F806FB020100F904F302F503FFFB00FEFBF6FB08F9FCFE0407F60609FE01FE020105FEFFFBFAFA07EEFE0E02FA01FAFFF7FDFF0403000207FEFBF804020008FAEFF6FC0E0C0604FE04060DFE0D13F5EB06FE000A05FF0E02F7EFF700050107F5FF03FE01F209FD0301FCF00204040203060208070AFCFC00F2F40BF6FAFFFF02F90701FD030405F50606070600FCF6F609F5F504FD0D0A08FBFD0207F3F601F503FEF404F7090403F90101100E0000F303050104F4F20C04FC01FAFB01FF08FA02F304F3040A0BFE0CFB08F80B09FEFC0105FC0B03FCF1FB0904FAF30204FFF2F9FD0B0201FDF602EAF1F9F708010000FA06090301FEFCFBFEF4FCF9FFF8FCFB03020A06FB00F5060704FF08FEFDFBFE11FEFB010202FCF300F90C0105FC0604090402F8060A0A03FA07FF0C0F0C04FDFBF7F7FF0402F8FD050B02F801FBF9090C02FBFBF1F30B0901FA03FB070300FA010403FD01F303FDFF0F0209F7F506FFFD01EBFE06FD100F000EFDFDFC03FBFD03F8090104060A0900F508FD090D0105E9050305FBF500F707F70503F90706F10801FEF805030B02FBF701F60007FF03FD03FEFD000808FEF4FE04FF0CFAF500FB13F600F5050BF50CFF01ED09F6030305071B070C040C0206090205F103020508F207FB09070A00FB110502FF06000811FFFD0B0D060401FFFDF702FEFA001209FE09FFFE00FE0BFA01040308F6FAFFF6070309F5F408FFFC02FCFEF7F80B03FEF8FFFE01FB04030304FDFF02F90AFC0A070407FD09FEF5F9F3040002F2FE0D03EDFF0305FE010B08FFFA00FAF7020106010D0A061913FD00FA05FBF808F404F9FDFDF0FD0403FA020200FEFEFB0004EF03FD0B0301FF010A02F0FEFF06F6F400FEFFFC06050603021005FD0603FCFC050A04F9F804F40E060100FCF3F3030BFFFFFF04FA0705FC0E04F401F9FFECF9050C07FAFDF7040EFE04F9F6F900FD18F7F5FEF6F4040307FE02060302ECED02FFF902FFFB06FAFE0BF90100020BF8060008FFFB01F5F3FD0501F60D0B04090204FB0508000CFA02000AFC06F8060200020606FB03F80A0D0800F501FF01F90DFD0203F4FCFDF8FBFAF0050107FD0101FD07FBFE00FC080807F600FD0801F20200000CFEF5100802010206FEF006F9F8070B0712F6FF0600070309E8FCF7FC0BF8FC00FD030F0307010807FD04FCFA08F8F6FE06F8F6FCFCFF06FD09F5FCFFFC04010F01FDFE050E0D06FD0AFFFDFC03FD08FB00F7FFFD03F607F80707FBF8F90108FBE603040406F0F303FEFA01FEFEF303FFF202050203FB0407021104F5F7FEFB07FEFC070B05FBF9FE0CFEFB00010CF80C00FBFCF609FF06FFFCF9FC000201FF04FC03FB040004F8FB0B0301001001090003FD09F9070002FA06000D0D100508F2FCF4F10B090501F90509030104EFF2FAFDF903FEFCFE0906FD00070802FA0304050C050A05F90AF7FFF9F704FFEF100005070113050CFBFCF8F30212F8FDFE030505040C09020201F70BF3FFFF01020FF7FE0005F8FBFA0B04010801FD02FDF601F9FF09FD04FD02FBF7FCFAFDEF04F90801F4FE0FF7FD08FDFCFE05010306020EF70201FFFE02FE040705FB0905FFFB04F100FA0A13FBFBF60AF8050803FC07030AF9FE0D01F7090E0009F8F4FD020401FC060607FE0206FFFCF8FEFD0004FFFE0704FDF5FB00010403FA02FF0005FA02FE000AF207FA05FD05020504000BF70FFCF8FDFBFB00FFF5FAFD0309B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD394D1695059DFF40AE256C5D5EDABFB69F5F40F37A588F50532CA408A8168AB1");
+        int smlen = 2753;
+        byte[] sm = Hex.decode("FB07A3E1D8B107FCA922CAC88C668B69F9BD54330712E3788663FFB44310B4D5057F5F479039A87C3B0564A2474D1A97B944E852300044C68751C09A7A5B8394D4D388456DE8535EE57238F2364DAAB11BE3983AE492D5AD2C9508CFA2A138F4666871BFBD394BBFD69E577E812A13D973E20D8B3E63976021D946B0B3DD53D343C344306B85942927A75EF08D7DA4BC31DA2B0F727B997BA243BC1294B30CF5399F58D9A1EBF5F2D4E5D3D3FBC580FCF2F6EC2457EA4C713B5B0CC6FC565331E9DC92CA419CC73A4D618ECF6A5B2EE146B28A87458D332768D93C3E01A43DF628E1EA0B539040B6D5566189FE8260A22F701EF93907B061275637359DB9BB6361A680DF9FF38B4FBFB2BB31CE0D1891CBE07C6C2F8E0832038239E92FD4F70F5BCC28BCCA1E9F800266EDD0CE007FE86052F1506DC45A8A9405F13EE63C963019776D11311AD44A01E5B27CA4C51E051BEDFE0DBB5A3A7ABEE5B245689AAE0470D5803D1308D78D8A15167F31D87A89E186CA8B5F6566CFC7148209485AEF22B1735FD487138A261D35BDDA857BC0A966A8D407463D1BE9EEF2387F2F93B1A835AAB97A17F496AD92654F5F70DE317120365DFF755077168DD6C3E2320BC025E361A6EB98873DE8F9228A223BAE4138EE114B926C48494CBE6065C08E759109BD18C41FC740057ECA699369737E78C15FBECCD29D1C6406819511B4D115C238469E7D0722D0F17756F2F9AFDAC4343500AEE4C24D39F7C932270796E110FDC2739DE152A42B494C69E38A2909CDF0FEB6E4DC8EC028562EDA3109ADD188CE142B8189565802D648067BDF6497E61758F9C893100C2FCD678DD5CB17699ABBD670EFFDAEE6BDBC510727A48ACF0E4FAAE0E7D9A04A27BB375671048CCB3CB2C69AB232A9C31F617B7608ED88EE44696A2F75F8F35D1938430D482C67861E7E63FB71BC6925F66D793C9BC30D0F11DEB80BC23E1AC43E5D2F91F4DD216AC0FC0379A8F50F290789E72D322E9F75433857D99E5B25DA53F181B52C61E1ED59B99D7D86333C69D8E7989F37FF92D3B51125ACA0C477765D2710DCB52804C620E39E85EF9F01F9C49E5C123B898952CCFDBBCE985CE9D3E3D9D7A3F254FD10614899B694AD59520CDA87DAD998A7F0F4118E14EF50470F3B970C89702849EBD5FFE168E387AE970318CB5642D46D8582013779EA03C956B893BD2374DDB002A14BB0FC8E2098E1612E47703EFE0A75F35B751EBA9539E02915AF108821C3C908315F1C34F578D077A78F91EB6DA12CB4CBAE937CB7246A0C56A9F997D58A180653FE8B5DEFCEF1BC6BA41DEEDEBA6F3D6B695A91C270AAC086D9CEBE4D04D6D939EE0A0C1201F10C39AC3708264DDBD4B1A1B7D245304F8A6AA4229A0F7599837D9ED334246B719C102239C7BB93C0E9EE6EC9DDD57C59D7B88E43E1C19206F356BDC92873F8D7A687FEA9408507B32C2F4103936FDA2A694F486E05129D69F36563ACACDE2D60F9E114502751C2C5A5EB27D8DA8F497260A7BA003A858802F26A630B69A554CE006D78223D400E7E757AD8760D1EE7C8A6AC629E28C5CC0BA8242FE96203D970637578BBEE77466A6ADB19BBEE0B3C894EFC692D200106967F53AD26BE7ACFEF2D3ECE609B89F6A940FCB948D458E89798E5DBB2D5EA7C75957FD9F53F58050CDACA4CF5613C4460C4E3638663D4E2A4975B8271BFD26C48207ECAB05712E1651C352B0621C3376BD713CF3A81DB0D4AB2C3BDB1EF54775BC980BB04E3872C9119890037AD8BA2A984F7F090D8621D442383A2EBB4667F885772163978B346ABECA297D983919D80745B061154543E1C36271D9F5E82D929D2AA007025E8718324E260E992528D0213FD5A5659398FCBDA4080B34D6AE7C7C7DB595BC0A7E0887EA5FC7FCC76668214DB2BA457C2FECC69A37D39C7C2009BC52F7F4304C26A260994395BCADE9072351DAF81D484876ABCDD876FA87CE76E5F024CC8D098351B777D9B908FA5D903EDA4BD732173E974DD36BA039FC30BA6F5EC7C60C507E1EC4A7100CC3042EC30FEA3905998483CDB77D18118C547805C41A26DD0007F73BE9447590DC544EBF8D6880E7E61104DDCEEF39866DB0868CF9C8C0EA3BEC50B2E608540C3BA1AC7D4C9C945EC830A002C15AF68BD349E5E2704F6C244EBC927051B1F04072411AA7299FE88A38D1963FE778F5D86C34DAE4983BC2B6152E84A65E820AACE1FD7BDB5E22606AEB8590EFE2A7A40D4159EAE17D5F4239BA3D2ECC659DCDD7406D76493D4092F0D4FFFBE97553A26D94B4DB223A8B62A135AAA6C692BA1DAE42929B9D4988BB0D7B06B14A60607115AAB791A3AE54D5FC0437954B9917EF638F0EAFBDA8C4AD68C997E732C071A49E6AB0FC66238629C6E027C1EBAE1FEF45874CB28C12298734F0E0BBF838BE199BEC0CE52D206EBA2C4AC6230762B476A36042ACEDC9DECA3D36C92492D998230155D08522D8E11952A3BB06128D11EF142A15376EC172B66294A7006647DD1831ABC0034F0E0FEF507D988F0E60625AA76A535C2388F8E7F5DE8F59997883280BE1BA98B7CE54C797582933B688CD48460ADB2602A82912768A34F9E911F31BFD39094E7D8918F6DA0955F03CF44C0D4B37FB331727F1C36CD130A9CD0CFB88B1F274EB3D58E82E0852DD6C80E0F368C43BA2BEE67139ACA77ADD2633788AB994A29A565380118FE843A74178FDAD308BA5F6A570FA337A6B1E9BD5B6018AAD8BC8C993FFBFA7015D7337477E28EC09D6D9AB9AFA9CBB96DC46F9F9304B0AD829D54812B8FCBEDE58CECEB89E7A992AC0400E7BF0AE2F90768D6EE308C78DBC7F8D0BABB8AEE8BBAD5DF00B04C4C7D70F8B3F0B6A4BEAFAE07295C27EB2FDA2E15C1D0BB5B4318944117FEF36A0A94BEEE967D56A679116151FA916727B66D174824D13DC80024EF0FFDE2C4C24781F6D24ED091F4CFE61B2151E6BC6F5DC08AFB5DEE5E53C9897805D76363DCC57B42472C57C8DD5AD2D028AC0AC9640C14BFB18E8D6D4E6CC505F68AD65C49693C42043595098DA51B7AFFF60D7B26EEF522B7C5D1281B5962E3DE06EE6A09253897379DCE3285DA42DF236431EFD2E60E255DB71F380FC58DA34CA88E86A243B20486FE52B6774CC17C58BF1A2E3978A61FB046EB18014F9485752A9BBA5D9A434F51D0C0A2AAEFF67B801A996E1189BF68A0D3390B94595018FF6F925ABC9F4E12C90B8DEC1BC7D2720F658826CC53082397E19806DED90481E73F99578AD4B1D21FADF9E9D808BA8ED362B3F86E6EF491B13F48F064C22D278016DB5DDB264A4A0A18382913723E8E3FAA13057872D75C158A056176DBEBA30D7C62984E046E2188DFC3AF9C56149187BBA63CBD1D52798B4FC90B95081F5152E4686D3B9293F379F2ED37F365FDEA9E15024FB028C4C1F76A49EAB005FC92692A09BE757CEC084F4856D28A13C36637688FF369695A1FBE9991CF2CA3A10C6646B36B0ACC0CB00BE7BDE17682842690CA18C17E690D937E55098C82FEB02907B83BE2069B336425277E3BAB065614A44FCFA606CE390D504E72A8CB79745C82AE0A259713243BBE863ADAC3496E9254F2BA2AF5007EEA92413327FAC766600619DFBF7C82D4A8B53C50EFEDBC76A199A8CA003D5BBF56BAFC0DBCAA0BF43E4EE53A0DCBC4C10945CFFF2398377EB3DBA89710C0AC3C38E484645A629833B204E68E03D9C02322313E70F20B2E0655146FC8E4D94464DDAB904534FF4A53FB8D5FF9C5B6787AAF969275E817D66525C07ED7966FF96E906FF6F9A3F0B61788389C1F779DC4E6457DB0C2E1056F28F589021211450772BF97643DC8351809DED81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(QTESLASecurityCategory.HEURISTIC_III_SIZE, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-III-speed
+    */
+    public void testCatIIISpeedVector0()
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("624563CB6E4499327172AF7F15FE1893614871835EA6F3330DD94337204F88D911FFB91B06CF2F07240203F75353906FB7EA7BCD0A4362546D94E60D32833623CD73B1D055DD1C2533CE5B81DE05C02966275D289D25134EF8577C7A489B383D3FA769DD3C1C033325034B731F7F678E5E58426F372F0F24DCEE455A8C6B0E6A68796E7B8D36317B23641E7D4EDDFA1E4F4A5012F50E70222E36527A14A559F4132F025F2C20BD3C2C023AE2E249E94D23460C80B94006CB1E38769A510911672D2E48AA5E5DD54F38746C195CA954D6A909CE33759A222399F209FBCF26641029D08D4C0C3D51D2FD7DAB2A6A3779365B06720B686F052A1FC5A54C5B4A26962F224C194096FA7048372F037D5E9772315B956881615C72EB57A0B661E9851F5BB61A17755BBFB571CDD90FFE562C4F147846407266A43AD9D063CD7710256D69FF6F07164E7A41D13E66C60614EB7F69B621F83237633E36E7F16605DC0A89A774E425323BCA551F211D4AC6110C314F39B873E48062C00255EE6E7E5BCF75A94F6DAC0E182C44532468658A2A6A9A9B72BAD7715E0765A10B520CED236A4D3343FE2A347D726D6107046877FE583F09FC50F4804CAD603810A15C7BB655400533721E4D38E020EFA64E3DA8277DAA437465033C486FCAFB5235B44C76AB27ED5E5D7BFA6CB2EF6EC7AE008AB5320CF861DB605321406ECDB56837572048175831343A1A192E7844225B6A72E66B42C98C7A3C513D2B5A71BCCC329BCF20246C241C4B5EBD5F37ED144C1CFD173F6330F78D40C9A952192201126F79EB1E444BE27E3FF500FDFA0BC27706FF2A4381454634540D3BF44CF418613C087776C940F5EC1928E1190F710909EB43EA715FBAFA3E3C140396EB516C9664A87277AAFC139D343D512C65776056A1C31104645033E03EBEC24D99837D1A427CFB7B72DDF86054744ABBD62B57A8355C8D3994F84B2A4B2CC1B731529E495F811A73414C03FC1B66BB0C58105283F42AF9B2521A5B717D8A3E8DF411A85C68F706006E8B7D1114769E2103FB2B7601DB1BC28B0355D909AAAB3702904F96BA6FCA3152C4F042596A7854B13827222DE0925A47CE40F2AF3FAE9F19395D70DBAD28DA8E7AC34F5BD36E174CA352807F2C49645B05015B389B3666A679AF7A033C8C0659600F6761263ABB6B4BC2360F29267AC82D80905502B7557A295406D605D75828565046DAAE09B12080EEAE67179D53B05A54444B4CC77C1E2DAB2D298D785B841047B5665D3C36E3181CD7AB2DD49F78633661949F585E0A18530D7940046E140D684C033915EE5DBC3A07C34A4178516F43964F0E2E04647324D26107980F68FF990C7B8C0C47F12FC1AC384520485EF061DF373B78A12D80810834AE36E5BA37397D35AA5C4625B708C51066F5C54CB1C52E0A9D3CBB772819D44A875B78AC4A65D3EA7621BE271914770B8D363CA911B8874CA1C16FDE314725B409391933DBD96B5BB013C2723E307D7D9C1C41654049204837D4074FA12B46E25D39E1EB3F74194C08CA47453E2092AF3142D06950FE14087E1A80970ADE4E7C6F6D70294F3710743854AF2D0A1776DDE166895950DDF06B2ACD11E5A248D8CE52EFF276AF9414B0DE264B315D61413EB731154CEC10F0FC230801229311566E781A57F65EBDCA4EB0CA71C62F281AD139D105108FFD5345016734F37C039377F71B36B649661F425A2CF3132CD40FF6DD5668C25808441EB86415136A55F57A1DC5991463A00BB3C67CED334979BE64A192471EC95F9F591014D607BD23585E5F0E03B33AA6E11E70F3314E7305336F5BF5E25E25D33C985B2D86BA0C41980199C506CE420F12523269D62949904174B86B383E41255F64940029094C0E9BAF76636E6E37B6711DA70ED1510E6BE35DE7C34FF8613BF3AD2358161E0E7F58696A669FD50ABEA278872C58BC3B1387F93DA26245E1DF1A35D32BB51F372F1133975E5DA27B5E112B43508D5588EE21D7A2704A9F7B1E7C62454D6D4321171C3647841D4A7BBB3B25DF019F856B7DE1652F26132C55049BF85AAEF21FDDFA03BB873761A72B389B3217C238B1EF315D4778EF4643431E1327350F240040CCF90C324370843554E9C23F6075089FB94677E85377B74FDD6A174F3B628B3F30778A629F6A5D9D8274943110095C3B763E4BC9862DD3F1441DE30256F20FE5DF629DBA55AF182C373D03D1E707253360E69455388554FC8140E97377B38024DB1C1CFAD014949552BDE132F8BC1D066166B48566C4A672B9F07A3C32399CF351AC073665FE4D52D11077CC6E823516920843FDF62FAD5D7D081B17470F16122317C3C84A7BA54B988E77952D1ECD012F90BC3C1E4152FD577E927847A8F943F5B87170A933C7DD5C8DEB4D0D02615E7B66D5CD399D0613CD111784C27A27A71516174B0D9534CFB51B6EE67D77767B818D79444F4FF519397E4C7B87EF06B0826DAE7031CFA63B835D4E2FC5035F3118463977458C6F5EB97F9CC87D77F107C28402F9D616681C108FF14C432315A368071A6F19C18108833137A4DF25764754FCF509EB6B1B118029CD7425566F4569020F5EEA7C3F5A6B0F3B62E8B615333F5FA43E15484C4F71496420D331D7FD74445816273113BBDA6DD5434DA9537EAD8479F20C2B4AE7648B9B04EAFA3A36143F0AB364552A482B766ABCB33274C832F5B3687B4802E3E369804275A0F443C5B76113E7380ADF03BF7057BAA575521D75F1EB5F774A7353A94D9BEB1BE70D174E602435FE55B8863CF5F71782B83F1AB62C222D5DDC8E04B20D5EDE755CB89E61855A160CBA7F60D708238267FE727DBDF67B8F160280A363F04B5EA62F79ECA260A0DA31B0C016DCF509A32E80A2B91176534A36B345FB2018C17540D14D569CAD14644F1135E779D360078F1D3DD4790757E035A4466B1C097751C01FC0E32D6A0C24ECD16D027441A38D26A9125F0D13039F9B2650E64C31322DE2743FB5306D057C1AE9B2051D916C0E076439070328344C970E47651B2D9E826DF12C6724347FCB3E13C61E0B79AD1E67D747C1BD3A9FF274B5EF0143126EEE3F6A72E10F75A70A77EF46B40B1A2D5A39BC2A2933DC3222787C60AC73ABBD110E73698E6A00C8BD031B626583832490B67647A3499A9431F3446DB4710E24F32F3F321E6C87427ADE28DEE2287D6120C6D633E1036A1B72067B955C3F873026E01957491B5F8D60888C13A91136FAAC24DF284668B31602C24EA4EC4BB0AF4A444C3AD22620E0B046E0CE3B28186265BD34C2EF34A74A56CFF15F230F308C6547D7252D340412F37F279B033C068961CA220A6855624EB860BEE53E8D0C492EA668A81164A87F042D9749DF8B493E4F2564D437ED53183ECB06961A09A3E30C4CDF37013A7435316DD6877CF3E2556689320CBF6B504205CDCF0B5F600EEF300669F65123B60972CA14785E4B54514143193B889D17415769413927235A24BA7D3828A844798179A44914C9F94B47701E07233E5B4050B6044270930583E5381AAD35E1C906952C1CAB143777C26208F90AAFBB6586295F168758400655DFB852BD314E5F5935735D66090521395B56CC60029B752D5D0E2B69F70043DA3A620D6A089661060B04E7402D33B77E726A49AC7C4D8EF52819E704B8B41745811774611BA3F108CD3143BE976A4A310D13320FB3C92B41C353C3725DB54B091B1E2CD9BB474360159BB615AD614399D7635465761748078FFB19828B72A1B57001CE20289E0F02BD15BB5E109FDE3E92BA495C660332337B94EC05568E4AE9065D1B0664CD4D11CA9A0BECDC7AD4041E2C044456B77E4E3C2066A33AAC412D53E66E2AFB12B946079B330BF0852D033342F2914337AE04425A02F80E1CC55C60530C08692008AFC758749726B5340B29F74A3F8F4045436293870A91746FAC025EF07C7D1D065B9D120962F87255E21E6EC73277D66B353D741F2D3ED099670C560D929D5E352D7CB5BB2A529F35FE8E514E4D72779256CBAF0B382A2E082E08294E6CC83955B2FD0ED1C65AC2355674823854F807EE707F83F42120375D4AEA21D076503CEC7DD539645B527856973BC0F06597081B7DCC48D428130A9F3230065632201FFA404FEFBB7EAAD60FA6AE57CE312CF7A51703610B6B9D0BDAD64A09D402C8D670DBEC4146816D80723CDF8E4BC6DA4CD5AA540FF510626F12FCA9485DEF65045257B5B24E896C647FD61C5D12013B3277DAD75B21243AFDA11109826B7B7F5873503323EC665D8C11E5CB441C4065FE1763BF302B21DF7FFBD021763F6031AA36369B198D761FD1FF1925583733EB325F845D754426B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD");
+        byte[] sk = Hex.decode("1C002800F0A040010E0AE60380A0400001000406101850408100020FFA0F98AF9F7E000701F22F08C060BE81F9FFFFDB1730208002FFF60F0070903E018006F917F8B72F60417FF9F1E7BB3FC0FF8100060000CCCF8FFF7FFD001412B8EF6FE03EFF04090A0CB0EFDF0003FF063A10C82F7F8002040604C89FF03F8082FCE9EBFF0F50C0000304FC0104E81FC0BEFD07F8FB03C04FE0C002FCF611DCFF6F5F4081FBF90128F0FF3F00010609D41710F080C004030AFCF397105F400203FB03FC3F80BFFDFA06FC032CE0AF6041FD07F4FD3F40EF5F817EFBF8F733D00F1FFF7FFAF81F00E8EFE03E85F40A06F407C07FC17EFBF70BA0E71FA1810103FCF90FD05F6000FE03F8F707F02F60FE0403FFE3EB5700FF7E7DFE081C08282081C08008F92D2C2030A0FE8103FEE5236000C07FFEFC0EE60FC00FE080FDF712FC078090A03E7CFC0C1640A01F5F81FB0D00E01B809FBF7E7FFD0508F0B78F20417E00FE01C4A73FC1BF8100F901F0F74F1F800208000014803F1F427BFA101A2C10D06080FEFC111AC4DF7F40C07CFF01120C581020C1FDFC07FE03286060FFFDFBFE173038801F828202FA07F417307F3E7D05FC1B50E8AFDFFF8306F005DC172040808004ECE5E33FB080418304FFFF27889FDF0103FCF5FBF73F0021FF8300FD152020B0FF817D04FD0D3C90E0FFBF080008E03FE8CF60C07A00F9E30788D01F8280FB10F22B686FC0407A00FDFF4B40F09F817E000F16D8BF4F4041FF0602E0D70FD07FFF8301020204B03F20828203F721BC47801F7F01FFFEE7FBD7FF1FC0FCFA0BDAF75F30DF000406FDFBF3DFDF7F3C7FFCF31F886F5FBF8181F904043840F040817FFDF80DA4A76FDF00020A06F02BE0BF9F0202FB03040058D09F037E0100E0FFFFBF803F7E0006FAC3BF1FA182FDFA0E16C0EF0F014182F5071440B0EF9F4183FEF909D4EF5FE0FFFFFE06F81B1840E08002FB0E0814103000007FFB13F403F8DFDF3F00FFED13E89FAFDFC0FE010DE4378850600002FE12F6335810DF410203F5F9CBD78F7F0080FF00E023E0EFE080040206DAD34710BF3D010204ECE7DFCF9F80FD02FC170490DF20FE010107EC0BD86F9E417F03F2274038201F7E7EFF050CF4671FC0C08100FE0B14F8FFFFFF82F9001838881F213FFEFD05E4F3077060BF7E04F9FBEBEF0F1F827DFEFB0BD8C77FC0C07DF600F42F980F60FFFEFC00F257E02F21FFFDFD01E653F0CFBF80FFFBF703E86FF01FBF81FA04FCFFD79F2040FEF9000A0410406042FC0606F4DB67BF5F0284070A0218C8BFBF0201FCF90D3838E0DF41FFFFF2E5FBAFBF1F40800300FC0380D0FFC181011024F47F0F6000810102120CE86F8181810200E0C7D7BFFFBFFEFEF8F1F3CF6FE0FE00FD011634F87F40C17FFC0DEEFBC73FFF3F060A01000818C03F408302FCF90F60209F7F7F06FDE7FFE73F60C005030EF69B476F600082FF1202F4F7DF7F3F0402F22D0080007FC0FCFDE607280820018001FD051EB4B76F204000FEFC21E49F8F1F7F0105EE0FF82F70BF81040500FA3FB00F60FFFE010BE013F04FBF7F7D0210F26F287FC0817C071302E81F20603F030119CAFB4F1060C0FDF9FCF717F00FA081FFFDF601DC1FD0607E83FE12E6EB0FD0E13E82FE00041840E040FEFF00F0190410DF203FFAFBF6F903E00FE03F8403FCF52FC00FE0BF7F02FCFDF39F4F40C381FA020AE417409F810004F1EFFBCFFF7FC07EFB070010809FA0407CFC010624A01FE1FF00FD08EC0BE81F003F84030F02083040FFBF7DFFF81B0CD04F60817F06030C0490C01F408108FC0F10A8BF3F3F7A041200FC1700A081FA03FA21D43730E0BE7FFDFA0BF447B0BF7F7EFBF51138F83F21BFFA0706FE1FA83F40C1000001D433E80FE0C07C0502F2070020C00003FBFBDBDFB7DF60FE82FC010AE4DFCFC0FE800A02E4BB3FE0DF7D0400F4FB0F402000007A03FE21CC3F1FE0417F03E8ED2FC0706001030402D62B38F01F0100010D0C1828B0DFC08401FF073CF01F404102FBF40F1020A03FBE81FF0000FC87CF808002FDF8EB0340A05F407F08F11B10F83FBF3F010001F013084020FF800101FC4300705E807E01EAF51360FF3F3FFE090A0C28D03FA00000FD07F617F8BFDF7FFAF9FD051028B09EC07F01F7F11F2800BE01FA09F20334C0FF3F017E06F9F92FA84F1FBE84F600FE2BD87F5FC08208F72D2430A06040FD02F5F10B20A0FE7F7DF906F4F7B73FE07FFD00F00510F04F3F00FE040702BCA760BF41FCFF0004F00F6040BE8100FDE907686000C1FFFFF8D7FF176020C0FD0000F017981FA080FFFF0204FC4F805E0102FB03D6EB0710DF007FFCFD0704D8AF1F01FEFE0AEAF73FC0FF3F80FB0D100CE8BF1F0004FD0714C00F40A040010002F2EB1FE0BFBE8100F11F08C84FC03F7E02F50F1C50B0DF82FEFEF9F55718C040C07BF8FFE90BD04F7F417F0114FC0378F0DF80FA02EFE91FA07F9F3E7CFC0BF24F906FDF3D01FEF709ECB75001FF800601D2D7FF9F3F80FF09010AE8AF7F80C081010FEA0350F0C0007EF8FDF70BA86FDF0001FA0E162428D0A03EFFFE07FA1B28E0DF807C050A082848F03F407DFCFCE9F33760C0FEFF00FF0B18B81F000102FD05FE53C8DF5F007B00FD191CB03F01807FF5FB07B0D72F403D83FFF807A8D7DF807D8001FA0B10889FBFC1FCFA08F0FF2FC09FBEFFFF07F8D74FD0DF3D03F300E4D337B03F827F0AF41104D0D01F807F02F4E7F7D7EFFF7FFDFF12FA3BF0EF7F81FEFD0006ECAFAF207F06F907F82FE8AFDFC07EFE0612F83F301FBF7C010124F84F50A0018204F9EF0F0820007EFFFC07EE2BC07FA080000005FA3BE0CFBFBF7F0705FACF07A0E0C07CFDF4D93BC8BFDF3E0102EDE52758F0808082FF0C0CF4BFD0803E0204F7FFF7173000BFFDFA13163470CFFE81FAFB04DA1B70E01F7F8003140830D8AF1F417F03022CE4CF9F60BF01FCF9E5EB4F1000BF7A080BDC0FC8FF1FC0830BF7E7E3E7DF200084FEF301D0DF2F00828201F2072818C0803EFF0300F4D77FD0DF3E80050306E40F60A0C101FFFDDD0B809F603DFB04001EE0AFFFDFFD81FBF1110CE0CFA07E7F00F807E04F903F000104FC17E03F501F007F0BFDD7E73F60C0FEFDFF03E41F987FC0807FFB0414E41740C07EFFFE0516EC2F9000C1010703E6EB675F000202FE00ECC31FD01EC17EF60028C49700FE3F01FF001C0490901FBF7C00F40700D86FDFBF7DFAF80360D04F9FFF8003B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD394D1695059DFF40AE256C5D5EDABFB69F5F40F37A588F50532CA408A8168AB1");
+        int smlen = 2881;
+        byte[] sm = Hex.decode("820713D8B1371DF090139185CCD8653129882FDC98C3FFE2E84BC7F2D80EBAA1ED2EF0270809C60D2AFA9E4250A237AA3432E612FC95427E40740031C6B8A6AFAC01A4D4430A8BF6A54F262C57E0235FBC4981EAC6514DDD8A92C551597E2E3C700C85C26FF62DEE5B72CE035F2B7457CE02556A6ACFDA6E985733E6062CC0AC1175DB2EC9D3D37F8984B315954C79EDEAB5B0B1432F6F25EC553971CB4DF7E012F19B9FDC5451EFD313FF71E8E97AB9EDE423A0F7D700F212B7A703735592298ACC16CA62BE985301E3B9C12D076E3C168A14F6FD5901934BED20199687B5236772A36596EEC99EDAD3362538FAC2CB46E0D1D546D112C90082307D41EB91AFEE0012E389D09ADAFAB94BC6C270087E769E9F1EF51B6F37E26F0329C925C87CFC661C65CC0CDDCB8955438DEC61BF2F0A1E644F4E8072DBDA1D3E03CB45C79B14DFBF8DFC9C220482B88AE60C1161B6D5B55E8B1126AD84C05C021A69408EC230ED2E377671E3E8662F57ED83D6C295E45A35B19E494BD75D0C2BC0FDC517D40B066E78A5F1FF99D948644A590998B6DE51BDCECEA27E13A118B8A33AB276563B203366F8790FF8FF6C617957FEF2873C32FA658D8F5C7D25F4665B25C73F7D4EF3CE91077285EB031ED4668A0692C3926E16AC928C4A331D8C79C8E93C2B93A2571D171B383E339608B3213462B20E56B6DFB14A6642CE0E2244C770EFFB68AB4D039AB34C172CA497F57F272030C376950157A32708E343F1AC11207254FE6A8F7CB958AD8934A5195C8D07378ABA0FAE7242CE322848BFB9B70EF852943E92A4AA8D604909DD0C3FFE9688B323E9126BD1D9E1365AE7243A0458C90269B565C09692C1FF6E72A5E687ADF21555427B77296AD64872F9BE9AE6556BF8A6FD9EF87BBD4C1D9D211598ECC04915387E3613A5530A3866B0084C25F3093796B92AC377F817E09E9E84B65AEE54E7D4C2D4E166F18ADB33A4F3886198A140BD7129722CEF4AC8B5E506DBDC4FF140F71B7B87B02410A25E5DBF25E94F8CA8E6E5DF3687CA1622F144E23A1859E59E45897002697B672D11D614AFCD9691607804AF9C8D7D165078265D9F16FDFCA92FA9AC36E6102BB5E4FBD25D771BB877A2105B03591D5A0805345440B1742DC9AB8979C09847EF3A92E40ADC3C6EE87A003955D688F0B99906185B9F4D31C34094AE8DA9189E828A235A7768900FAB1B192EC3A127586FBB1FBE7BFC7CEAE5738CA5654244CC0AE90ADCA3525F336CD557B293065D9457C1D64B400D1EC567863872F917E7D35CA192FD56A2119D3C3970512AAA3F20DF198E9D10C3938312461E2DBC93A2917BDD427DD92EA49CA4CA4BD2BBC1766AFE446805010683151817B73B26E71D48D9D11E5C5358CF5B366D6BC2206081C1603BCC6DE2429D0DD55DBD8244237A465DAC87184C366553320CFD4E5D14DB498D16A9CDCCC7029AB3F93CEBD090958CA3FD2062DD780B21705F77F7A6DD2517B3C2E23A950BCFD568C0D7AADB35CAB9FBABBF876E48819E1FCC137849F02FC9355BC9DB13CDB6C2AA9CF9EECB82AAB2A36F4105205EE3E9F098706ED2B5A2D7EA1A9378849D1B0690DD8898455E51DB42BF52EAA1005DD84476033995AD13A86DA7A2E388A929E0318F37C53C2D502DC0A4C21C88313726056F33039EF835F5491A2B0118C4263ACE4FABC611B0BEF2C355ABCA5E7DA1EFBD6D9CB570261B4EC5CA9F9BAC1AF7FB25DBF12D77D5BAE9EAEE0266000002F3BF104799157918F6227082E39317C7B48F93A9DCD2D5F6DB8F0D958E9F5DD9EB46E195366AAA188429BD99D33DC15E27047583DBD6A131B2BF98EE022003F124D8D6C82266236DBB4DDC44113784C7F821D58C75511AD9BEB9DEEDECC3DA9521895CAEB33660D9B95F66FA8A2C4048E7C1202E0A959E0CDB4E1D6FC304F7AA48B605C099824E7890B841589B49E91B0223F5B51862E5315D96E50348A74949F9F11AEE6C49C077DD119990F2F46676102738497B79E5959981A625A3CD5D8EF1086C6B7FB95F44B071CD18FA64052CA5E29062813ABAFE51063B94C6DB6DC70DF9E9B9C82F41B2C8E03B6BAAD9F5E484BC219AAD819D58F65C13BAB464DDA67C07D20E2F23ADA7610786FB20D8B6520DFD2535427C53FCCB3B66924322897DE74A10CFF1112FADE0B16D0012FEE2F852B3C8AA734CC413DFDEA0838C2F4821DC7EDD31D16C23AD7CA6B4E8E09AB764810BD741E0A7BA4A86A9F72B17CA2194C6BF62E315312F8D94B0D3D0F6D293D64F9D146DF8E08640E23855429D3F23D3860F63CEEE1E3F1B0E726F0DFFC74B494C1EBE52EA725089CE4F9F7B4F939B936F0592E5507C45B30C75844C97FAD4E446361347B38ACB22334EB7F79A6827754395C6EBFFAE5FABF096647C69D251829A50657E9B2A0EB455DCDAB45B52964C6339837D28BB76ADD4C44684C9D7FC8B3A559EBEF40EE5A2CC4DCA67BFF35DA1B736DE23AB3C994EE458A25B246A60BD602CF6428CBFA300320C1D78FF6498E8D2A504AAC0DC07E5A0D077445A78875F20275242F1D5290EB3B98171C272D4D686948D33CA4F3626D35C3C9376670AD9ACA8C420E5C43156A2CAB9D870E1D06EDD85E652D962BE186D96B2F9CD84585F67424D74004407C291DC1F454644B96E003BD562658D9B9C07807FCDDBEBE761223500B43792EA77AF20525EA0C1897368FC97097DB6CA4554D1AB78126EF40D6AE4A6961F8E90C4D3B10741B6D2AA3C89F0BC24985EEBDF691779BF1CC69B27E076424AC71DAF713295B6D6A620DBC1D6B9C8BE196CAC12E97970FF14A139D5AE21F9D802C695EC4AD957CB340760612894FD76C1537B0E87EE28B56B52A3276063A7E96E48C3108ECDFB2FF37BBD3E1B8AAB2D7407C91DFD7BB6F8CDBDEFBAACCC8D74E1E75B0298F0820DBB02EB372EB42EA768BEEEE93C891F2B768133D90A32D60FCC59BB9E62848F7D3C145ADBADA260461D0FE0D080990CF5C1DE9EC5104CDA10D6F1410A4693B5EED91E4F8275E86E18C2604374D1675EE902A5F8790BB55656ED228640E9043CFB7BDBC6E38850680F69FE51C60F6DB689911505F398B6991C19E5D9A04C4853749E9B5D90F8F627B47B9A490EB657E0576C587EF1D7A1E3005738BCB5503BA34B55888AC6D0E037D26463F827F67705860FADC524A50411E22B544988B15884DEC6FC463526FEF1459A1A47DEDA883B36FEE3C0915C42609B8B719D1E7D0AB1137D101FCBF22EDDD6A148D9EDB29A47128E13AE50F11B8AF5946532E85FD1F6F24D3084AD1F672EE9EF4C33C71AC6C7C8144589984E31A56E48CDE12B51007F4559C2DBD0031448E9A648F1A31AE84B9C457C59000AFE43576AEEFD34A8900CA19121C5B18BB8475F88967DA7AC8F5CDDC8F46B9D1DF7EFF843E65845AA03D3EF3AF35F6223B973DCC2367BE4CDE98464C51FE4556412664E1C506C86D9F691A494E70263CB841F1F0736AAA306F0A06673B6526CDE65478D7DB0FD9E4F49E7436D910FBF3BBD72FFA34823B83B78A7AB8754E9C6E76D0CC5033EC64391906A1B023949384F72FCCD85B4BE279FE5F69D04A6108CBC1706A49EC180EB6B9A93049F517CFC0E9EE45749889CD86C76B3E371BA5D9AF10E9F04911F6A46D734980A9B1D63C05C2276C5756FBB39015526008C3196FAA5C698B41F953045D0E366CA96DA5D46072B5AC836A2F87A5BF577453103EA9DB0816C9A46874DA2699777D071B27315C972C99E6453ABA11DECA1866E4296E451AAD416F1139118E4F544DBAC1993834CD5BF4CE3A83DDBF2177C2FE66BE72A13C93140DEAD9F9EBAAC1178B128D0A2726A91D3C07C388C40D1F000D251370E5AB73D5F429565A5B1431E9B4C0714E61F58C2918E73D381A38AC42181D3DEE092948A761232386CE273DD7B1F8B7C002CB8D9AFDA95CFC116A3DBABCEC7FA991726BE4AE4FE5D547C0B7206E9D53EF329BCF40A84C339C8394C8998B69ED6B372A4672DD79E068FBD7782CB3873526D43D8CCF4A4D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(QTESLASecurityCategory.HEURISTIC_III_SPEED, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-p-I
+    */
+    public void testCatPIVector0()
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("EA7347183E405433EFF49CB63A9E736B39A86CB67125110ADF35536A44940BAAFAFAB19FCA5B8F11CF72F7199E051A9A607D75D093FD1DD7F7038F4EBB172F9549747FBEDB2EDCF24BD007610C111E032C1852E4A92A8EA33057606200B17C785B8BB0B2A08DBE93185F3B931371B256CEF871167BD21876B1D62FEF325BE2D0A716A6ACC3D3CCF516DCB267093035DA9359E8A698C3946D88200E9033AFA1E87A30A7A626056F944010A5CFD972399AB7C52FC0E87D255A589EE0D3E14C4BABCD5CA50A561E71428C98B72D413B117F827F0FB557559FB16FF4EBC73C539C43040ACD34FD676C1868F59F84AA1398A61E99C090A8E1A7C5A19CC23CD21BE2B9CA6E23F6A7E05E3E69AD6516A085D2C58FFA0883EEC30C04B648414C3D4DF1C87AE2C5F06CCEE989ABE455ADCABB6C84FD17D8A9D1158E91C4A2598A153595B4C2F0921ED24A49F758CCF7FC170B3959B2F26791572ED54AA80368D5800025F6138B595C7D8B6B8AB5F340399AAE0C821C4B0A3D677B530227B33525D9A48AEE067368A81305FFCB2E8615035CD56203EBBED33B5B1F5B575F920A627A770F0644610094EDA55FDB7C7BB7A723DFD4662780ADA0BA214D91AAA9C832FC29DB0B59EC14F4681FF05B6E5D087E91F891416CF8150604DF06BC7901B6ED33A5967E91799BAF9E903EB7967D393772F7C170D4ACFA36AA333BBE00AA2A40FB3A8D1EBE9D88926207677BC27BFEA91ACF49E009725D1CC9CA4566FDFEB762F4DADA0ED3323A5D48FE9D19B0B60809DE4182EBAAD5113019C9FB4F9216DAF2574EDE87B41E14590AF2B6E71950881DF07B5303C13EDD4D516FB059C7B7F1DC5CE6BEF6D2ABB65BC6B2D5A0BD555914BCF21FAB87D417F66C87C0F007DFC6DD6FDF22DB646F887037227A20AA1CDACE683FB7F9E4D4959FD9D361C073D22DBD89C4BA32CA1B9BA803BB8EAAE6495269030C24C08C8844427CA72F321303F112EAD94E3823D354730EF3CB2F561728227BDC2321FAAD7ED43B54603F94CDC42A06CFED0F44CF34F357CE3019973988AE101746513DC8054EE0464A8FA8654A724277C511D0EC41EA009F837116E9B51C9491E0D6E7D4796BBB84E854C6097156F8A77CC8913F3D05E8A478667071AC757557FA30958314499E377262C79EE8EF891201A7E13710F4F6B252EF970B9B71A050681CFB0B2147D6108F7415A55FF1E70CEEF738272B8EAFAE18907E6D64F6729F5EDA8FE2E779554B41846521DC530C3CFD36A60EC145D0FDB6AA61800A534D9853A6B364F48F54558DE0132A39B4E5BC1ABC5BF376A5BC02439498B4C21FA9C64A72A9A55E51078724BD235E3D75D8AA12D4186C2D8E65CFF70E93211D996556B67A0231CF46E80B1F258C8FADD5662BB8D23B84C8C5EEB2612B0BC2A7B432B321AF2253AB16EBA0D24CCBEBC6D9DD83DF22DEB57EDDE1677CA943FAEC92E560E58FA0009F7DC4673722DF8FD38299FBB34FB61239611DC97A6D24B1ECB4CD6DF5EC4658304D34EEC218A55876A338BFF8BB2470B7F8275DE34863B27D5DA1AFAC7A24039DC53ECB99079E0D7DA4E3A5632D97AB065D11EF17BED267CC88EAC42A9A7C119D1211724D0E3A68A54A6220CE471EC3CF49FA21B4541FBD0CB8257B6396E4F2CFC8BD0756D4C0703F5B5CD656268E98C043CE4ECF34A377B8846817DEB391CACC4050E797873A1F88E974106471944D8A2A25E80FD54024C95A2B5E496FFA076F7930EA29AED839AA58954E5228F2EF55420C048E3AD1EA894EBBD4851AAF0A8C99CACA3057C486A0E0C2F1763B4F3440C87BAA44705F563EFA435FC68D165D0544902A3FB3B84FA8D74001F4C314F106D4AF37BFC54191598D2A28869F46D76B68D72D5FF91AC1F0E31F556F2030C816EBADFC9F72A55B9CB5F9D4C881DDF35448140025651B9D0EAD4C59845119C8D3FDBFEB219142C3C6820032BFA7644DF7E7D2783E27E8786053B02EC68EB4CE6DF29BF9FA8B1BA2E8A6015419D0A3D491E884C4059184B9C6E7BC35908EA60BAF531EFEC1D2379389291BC5E17A31F1ADBB5AC09EB3EB845F3044D31706543F063644F6D5E7D6DC686750134C9811B4BC230EB1EF7E8B214D82E444B0D3EC367D5D5D0912B5D6EF40F3D36302D726D9A988C208567067B5C5D9521EAD36A23C8E9C956443141FF78569A8E0D9EA2BD815A3A9746CF4F1CE603E0894529948D873A67C8F244DC10D9B706E127D508C0A814E9132E1AAD7DEC1BFFFCBB98EC65F1BDD6A8A03DD08256EE50869EA3509C2797184BACAD88798492F8958327D5F1071A90F0698EEA7676B4A86DA0ECDF4F3EB5553599514CA78773CBDFC9F091124E62181C637793A98E02847143ADC41CB3987334566846E4D201CE926D23AD382CA2E5E153342E7AE8CDE9FDD76148251F09C7290ABCCA43037BEE98947BD673B1FE28C0951298BDC45317AF1896D0E4B91BDC2AC886D18DB6008D8565FDE10D5D87322A5777C66A910FA2710F83BF61B396E9F82C2D68FF4B524616261D444F7AE05B57B4A86E23C6B759504812C305906ADFB5C900360E79EF73F0BF0233F5181608ADD756B40EA15DBA12B4AF06B1952F3ACE3908F6AE4625202DB122B5087E7808963426B23CE9B85260C924A677812EFD1A55E08CE2ED673BA2D6DAC7222A336CA6F0A05996DA63269EC3F5BA62E24593472AD7818C1800DB5A727D0C8A492EEEB22AB981CB688853751DA34F34A6723591909E2197D1A4D065737AE476831C19F92953CB7F889EBAC46A31D247A9108B3450A8F3B4437D0208B0D21558AF4E31DA942CEA458DD88A605DBCF1136A2E47897CE17030984952ABC600B8F30292DA4274593CC71497E9BCEB9CF84986E4C2047BE8821F406F1E000E18F050E3DD8E7F3FBC923D0830D1A84DB2B08F8E0623E3480A20A48E20A2F0F37E144CA58283A97A20E12DBE95327D69CAE4234381457CBA8FA237F88C466E8DAA72BF89E9517C28BBF4744D3214EE8B3263DD64C06C78AD9D0CC42517048DAF4AAD330BE626F23338FB6819329D11C10D928C11FDB96BC2A00C269D7839DEF20580F82F7D9B48CE896C45F568A6B38F123BE6B0690722DE463690EAF8C8336F829D4D48E001A7DB63470A39430E6929BEA765B3E8127C4213658C6938CE5D566546528D60C8B938196BE5B3378755580816C8120803A46084BA1980D9F129D47462874022454DA5E040456B2F406218E315149D1B79F7115C81B7F98E1A1A3F61C96138152C0A1B3E124E630902E6033742F7C74946B5D2B4D08DFDFD29FF7479C313FE889B1CCDA14904E0CADD9963F21E564EC09202A1B263440E9CD8EF3C25F2F5D58D278DF530F3940B79D7AAD270497DB9EAB1536C2F59549CFED6E5F4A9AC9907E586357C7CA305170B622CC3A571E45E1891690EF11C1BE641A2E4169C2D037EF6130C7C70104D4BF844CDBBB6C805A9B210B853417F6E2E4C85CF4D6053DDCC94122D5F2CEBAB51E0232E0DE9B19FC5CE0B78D569884F4F2E33BC4B1BF6A370B7F2E317670A136FADB81873EA13178798FA48E9E1123DC5B31D88301054F0D964C3939C0CF1201DBE5B43C1CA9E95698AD80AF3EE0633F973665468039EB73C63FAC0E70DAFD81F17492B022E98C61950A5D50846FB707CB7CDC7C51BA2EB7B2648C22BDF81C17A8EBEAA351C396E2BAA27D014F16388F215C5DD5CF53D077A4BECD1F57D0BCE1CC415C52052934179366443F80EC1D2266A80EF5A4CC09BA0E2301219CB9DCD88F30639B0A5363CEC3847CDC0207FE65B260EC1A4B5D40E3870EBFC1C02F2F4591A4D666E2754A523D9F1A5E79B69FE553B8405E338E4587EE2D03336EC2675A95AE9A35D9FA2D91FF934D30D3FFE4C7635C2DDD2FBF852859AA91EF913263070E238AF16D140A470A8A4A2A38720BD667EEB08E3436EFC4B74A9CC9725A7F0ED64B55B084E8EA7EBD1ABCB8A21E03CEB0F9CE5E3445F226B5797613C030F4ED5C445DFDF886AA6D7230962548B53CB551F3E6CB2FFF4799AE47E2293AD0FB30AD70417BB9F12CE5E7CBF9835B593F8659D98840D5F93FD67E0C48268F770E13726687DDDF8FD41D5B729B1B800892BC46D46154449A230139E80B59BC827B7638E1DDB20C366583892B8FC40E5B038772E4055BB625996969CFBCD95374F5966B5CCCDE8EC98CA3170A43417A941FD1CD00348E12465568A3DBD075A08D282D84401553B658FC64E233C5D3E5BC383606C737A3581CD38CC7BC362A7E79D46CBAAC8A76C2093879E2EAD94A9E9CA4703ED884E56FCA8B4EFF34BA4A002C31C2A15258879610551BCDDC393C0E1E4CDB543BF3273C4AA541D107D3EAE2DC643993779E15D214B7FC318970FCC976C344ED4D147BC226D1F193A7026E949778E67B395B111F943D44CA98E5A4EED33272B13986D48902692325FF749F546A9E6674FD6B8F67901725B8A0543C81AAB10228ACF3AB5F7EB2CA870DB85A69E4E63153DA7F5F8D8CBCB254E2CF003E04362F4E94C6EAB08F53200158B8F3CC8E8333E2EC6D5A4DBB607D6B180D45CBF05A7A9A60FE9ABD902A2EF248B9CA9C516D5871C88AF24C5376A4B3DA19B96E454DC969BD4DFA81170C627A2F6A0F0D3D9C65BB03DC24C470944CE27E108F3402062614F878088945A201EAAE842C20AC99D743E650DAAC82970709EC2D1A6CD5A49A36C434496ED6F5E1558C0580B61312F63C195C4FE5CBF43BD8C3B189FD5F0766041E8DCF336C550EB66C4C0F2B9AA420F260D1458448D4AA5CCBE22340058D60BB0C7B21231ECB2AEB51CFAADD4D10B683248F8ED6D2B61D4D5C66EDA259B30101F71F1C19A60C987019F236C35259E7C39DE6709806D24BCACA2897CEC81BAF76205A488E6920A3792A0B906BA71B9B5095C85F6FC368ED0214F026A6342359CF05C768EF34F1CA5E1F30E76D57ABEF961EA7097B82F8B81B9219D9D5EAA4143F23FB4812CC8E8AA5C089F35E697F1FAE5F45B725560892EA7A99F9C8AFAB2ED9033262780AA97DFD88B0E810EF4ED113494B8B4556C46C287BED57E7F60E0FE2A93938C25F910ECCAAE27293243956F9D8E14F08FC0D6CF1E1F43E5FE5FEA8A1156C94DC94EAC01D61A47966CB7819AC17E8A6C9CB361F4B13DD43A61156D378EC627B0E7414A29BA7CB0300FF1EFA20CDC4A2D2CE25C628B5A6109F4542C23B6E2D3E27C808AD2B706399C23E392D350BF6D7C02F0926D9E40B2CDFB98971AA06DBE751854D8DA832824A68CDD67ADF855541F9B1004B48E921E69FFA23271AB2AC3F2F170035553B8A8A00C8C248E2D8A763A4C95A709A38C56622FB8F4E3694D5B923A2CD8E538F113E8A08DCF19655C3E5818586C334D4E09BC4703573D24DC77804B24EABCF3841F372CECC9954A56ECFCEE60431CB015C84FC6B61C11E15B5899A81D07EA453DE77551070EA5B484FD937CF5C3BA7E8380D38B474379AD0211E35281A4FF6A2291188853439D472E476B2E93FA44F707FB7BE136CA62AB3E9F02D2B864EE25B357621AC7C08AFFBE1B7D6C184428E7A59C0D5365E21DEA5328B183D540DA779A38CDF2B55B710A3B8181A853D04482829AA656C77B94DDEE372B77CAC11D8781F3C554D54C3A74583E630C3FF32063672E903DD4382A85E577043B0ED269D9A5523E1DD32974640BE7AE82E94A13906B19DC3521F1950E1A5FF054C593D413446B3646E738636878C5D2FC64BD602F7D731F7CFCBC297A979A4B2922972EFC28BFF3EA51CA87B504AB011FDECF3344216DC8C99921BBED2E697203B4E994E4F88CCD491329A4E15BA8D757E6804C0F13C0D37D9064B1648C435229CBA60E53A764D9CD4047FED34209F16005BA94D07E0C12E1C27C38406181C347096472AB7145FE3B24069CE1BAF274408FA690391B73CFA1CA56ECD497D5770E01BBB2B8BD424873CFDD8B3D1A28AEDD7DC4172A5489194529A880F1115AFEFF020224FFC7F1B199D7408D888F765CA6F75F4F000508A9707BEEC60E144572D8E13FEE95956968628003148332ECF06136A9504E4DFDAC8BB6BD4A1267809478E43D693A6E8ED6C104A05908A37C75087BDB5954CC6D0FABAA841B86F5DB6473134015F75D28CEC95E0CB87B0A687D37A704B9B7EA7BB9DEB76BA63DEEB2F0ACD7C1140AD02A7FF1422B3FB8CED19962738FFE453F561E908519451F884A015003E65D98D81A61CD8FD7DA2AD14BB4EB2E7FA80E28BFF59DCD12D9065B9549641FE599F41E582E1821C2CCAC0C4DBA6159E0BE7F0BB2847F2299EE23010B7114925C4AC8C2BBA87EDF43694A1824B08745686EA845C02A7BCE2D98F973D49500E79F563B8F0DC054C5F1D30E4826093BB5944D7095E662CF008628278D85C869A69B634AEA2735D4AD59CE0DAB044D6D2F94004A2B9C1591F2FB1F7F88ACD7F5AB252B7E72922287E4473014D26CC43AA3D631B6A0E98444AA8E31186F8A7922C3A6F911A3AD2F03F4372F143F89AF8B758DF20FF26255C31E5E8C3DA86816318AEE2DAAD6655A36CC36A375EDBDFDA241BE8DE13080163D59E6221A90299AF4D070D6658D8EEAA88553DDC03778EF9872DCAF19114F8D6D8B0C13ACB647C49FB0102D79A388148C52BAE905C9D787015463125B336193B495F16C8E35F2146F496EBF780C859EDFD95773B6A19E9E5E582220D2DADD167379F632D8AAD683CC9B1EAA77F141DA66B6F002339C6DAF929F67A57B14191A2829CC55EC5A940716F09FC5A9B08C23881D956A36E2BADFC84E5427A866E2E76B476B2A0DE1B9CD3FB6AE156C74A7689B08D25DCA0AEC4191E33CC091B181F8C67BFE2A5DD82EDFC226D49A1AC7BE8936C27DE05D243209276003A63875B43E20BDBF79D8CA81F7AC56AE94EF46CB3FC9BE5473DC0C9715BF8DAC744D093350B4C75560CB1C099681F0E440FD682A387E7AA70B3F335546F026C5351440A5D61E3C83BF96F07A46DE986A21F7E5A2EB0A28AAC2087C3896E933B708B4A44234EA8AE48946C6A10213521A553A020D7C9991CC31526A4E94D210F97E4216BD88504779F3EA21D52C2A205FA47CCD2405D26F411A9312D561D4CBCA0763311A0EBA45FA48CCE15C80419FE5CA04BF416395D25265280E0382BFDCC868C719874DDC795329E07CBF5C8FE83A3CCC4DDA4B31B15702AEBF561F53634315EC8B1A5423470DAB46963403E10EEC58E9E1C5C4DCB3935B518D4623E6C8C68A4FB13B492B155E06EE3D16BCE6C3E79AC891742AF3B8D01767542EB6B7BC8EC71579136ADE2BC39FD07AFA207C3D3012229267DCDF6CC3E9DB25AD378F836763BC517DDA13CD264C90983D73E2C9E2703740DB4C38773B49625967933F41FF0220F40C976385367675005FE20AECB7FBC1EF5C71255BEFE4A39CE59ADA83F148C5E4383BC65F0440A1F5826537EE40CD55862E7239ABFAA1D458F64F7423668EFE8E1E979B34EE18A71F82BD1975E09EE1613D9F45870B98B9855D28F3C00DA8B9740F50E0C51E506DF1C7C804FBD2A1DD912DEE604B2080069AFC5095D23D726D94552EB7986C14C86ADB5A1306C6B074F5D3605A6C2189C4D22E1C8795FDB77803C60E3CC04B82A021BB89BE2E3C45AC5B914966D4A3DDCBC32A760ED7BA6291B6C728F13AC684A6B6F0ECBEFC06B54249FDB3633A786E0822B1A2E43C3F59011263DA540FE2B4B5D31F5FE2A0954E45E106E1255EEFF02A717BB9061BDB528CBBB7854956824311A755B68EC1C179213C418AE21B7DCFFA9F9DFD8CAE58FA1D0B88670E5A25934E7F07916844AC026CD5E1F768978FC0F3AF91922014D75E50F4253A2C2E3420DB1856C206CA20651703D4AE01AE22B8C3DAC60B8961586986F17A93258ED44C83DA6276E04A9D0E03B04E93939FBBDBCAD6833960F02337AB1211E72BC180EED3DC605052CF86F31F1960DD8DD46E848584E909800DCF13146B6A71A79AA92F2BF8D254132423794FC2416A412B001C47A9875FA4B866868A9A88487FE410F81BE5236B13BCFE9302BA9443F3C4523CD3ACE7DE93CBEF6D9E4E6090214494027F065AA834CF24F6CBA9293A02934E52F20C1C2BC9609B559846B643BF49CAE7FB8332BB91B719E1F1838D7F63F5C3A7D5107431E3B854E03EBA795DE9C717B0C9D692B566138CB338C3E5C6F5931DC33214B74F3A116BED19B91E27572A5EB875F14EE81A8B5F2CA2C98739C93C04611335D221EE922C62F02E304BBE507E82E329B4359807F595D11BB5CF761619FF685034004A5ABC89BA4DE82FFAAD693221002F399A5F1D11302B68291F85079D10AA2D1C4536C70C7739CD16293EAD510CE3A04D91AB80D5BD2D02A93C6CF5BEF703A2273E628EA272987167F8A3E2F8E53C3FBC907C2A5AB051653051266470D74F14B632AFBEE256AB24CFC2C7F6E035A0580EC3D1DC11DB98C7FE605183884CF3D7F24C4B3A3D05A697F95601C367EDE314FD3D7CDCD618DB63BB7599E4577D6ED62E891CFD85C9B0E43F0CA588CF282A5E5070AF8E9CBEA9F1AA1FEC53CF411C3EC301967130267B769177AF5508DAFB62F5AD7D8AC9445C62E59612A54F77B1606BD9EEA24998726ACA7EBBCE63F73C143A56435B3EEFD63310A6DF908979DE38D04D7CC75B5FAD9548071D7D1647EC6AAD9663DB156622FBA093B1C470C535BA155FF88EB90BB7CAC9BF4CD367F8CF68CBC5129A577C31BAEFDA834CABB52E0D02FF1A44FF9E85BC88F4BE69DD5C1EB9D4A547395DCDF8DD82C1F98252266BA21395B4272A50420000A40A50CCB22546CB50617F2D7FCE96DA0DAA8FD668D3DF70F3639B45D25910B8970A6F26ADB6001DBEC9D78E94D020307793CAC8A4E69865CD8B7565765C4C4686B0941F7945095A4ECF808A46930AC66F0B569908E97E9557400D265AD10D2A4CDD023D421922BAF3DFD93D7120E4F2C4D20900BB32AE8242295B3EC9D61252BDCE14AC6EF5B2F73895DC27E5CDBD878F24369A533B33DD67ED239C61C333C9FD879D267446F1DEE3837B66C410E3AB8B8BC4C782CBD8449C7B2CC89E81C0CD0DBBC2DD6FE3AE7F2537DC3BE3E7EBCE49EB63A76FEE5ED0209650BF6E06344FE0A46693855AF3D87FC61DFA621D5CDCF0A39C80F2715392DF2C12685F37390982BAF96FF6475CB436DBC6DBCE4716781BE983393D197902C1C757B831BA1564149711D3D8231E908C8458AEFE45C3147A71F87E046193C6BF35FCCAABF592E48724C7E0582077FF94EF1DAE47D911BDBCC43876978BA38C7D6372B23DF650AD5F27C95BDBBA2352D492B5873C3835ECD142C5D3007A4D16FA35DEFD4C21F929A8B02ACB71CFCFB9540B489F9F0C2609CC2E26C5E85847D06E82B7E53B37C698659482300A18B9C42E962E9CDA8968ACAB266CF1340298693C87CC18AF9A52832449EAB89E84FE63BCBD45AC3F9BDAE65E62EE8F97F1165E40D874E9EF75763972A9413C81D18A1E551AE0208053B6276841F8470C23BF48F493BE5A0AA94AC10CB69F0E60267BDAE69EEE405312F8056A54C47A80CD01DC81EEABEBD8336FFEDBE19E8D66C566658D0F8EBE7999BFDD8FBC9BCCC59480F98B5701C8CA3156A94794ABFCF3132445832DC9A582AFC83BA2E41B6D64C8A1560880B7DA9976FB61F1C33ED52506570AC3BC65E345629A00A611A442928BD908A93CDDACC7D8D997ADC9AD9EDC836A803FB392593D03A79440D4F2F812B4C76CBD8F01D3B7119E42B98C64865EB2AA1626EF1B4C1877F6E484BC4D776C6C47712E88E9AF422D676103A71EB6CB31F360FC0EE898A6C28950A448D77CC07D513CB6A58D6364CB1480892DEEA7585CC9774A42F6AE49468A65A7215D4112C5B38AE6516798CA6A1281042F435AA4D6872A2AD8FD600638F0297B9C732E3D08AF5EAA9E24334E56C57C931B3D5AD37C5E5A5255B3038311A89C600944C0946008FA7F433EFDF3C60B8CB0083DCE9A46247E7B8C790E9ECCD03CAA10379C13D171B2C912927324A818D3EB762ED56B1B1C6EF068453A75B2C3757AED2D04333A11E21F12060EA5FE58B4649970A194E223CEC7B9D2BE63D57742317E93AA66F422E0AD7689878224E921CC95B53511069CA6C5492779864E3657CAE50AA46CD8BAE0B29D8DDEA69F05D41EEF627749BA606F1A614166D143923E0A43A1822D726613B24F4B7C691205F6C6F569EDC542228BB244A6A133D6A97BA66018F9AC0D79884B2623442536F17D9A3EDCBD00975A5890A34FE87C37B718A67A085A2B3579822D6673A3041BB61EE24072B5B67961B01DE05F348A63E15BFCB48D3EE991BE1699E11A06E02255F522BDBB1F74A5E90BC7F46FAA51EC84D1473BD47A9DDF9B0C226DB8602EF74D9D6A54E2685D6631EF6AC744F51BE6609B30BAA0DFADABAC90F6322C4D6D0CCE589F09A659FD2B4FCD7F70874FC1E32C7602D4A70EE1F1A0424445A3A2015185C80838391C80637B369F43896BB990A00D55D0C44CE10A5BF10C853E7702B9B03AEC1CFEB983ECEB22C22F199776CC7EC01DE1782D27359DE58A4B177B138D59934A2308E937F4A6EDDE167113E87001C6D485974DC7ACD95BD3A0C79436EF4A9305EAF894889CE7AA3FB758C2C98800EE51C37F4204C11716384A997FDD4F9CFDF71723B37B8D4F4B15E457126BB98ED49099F0881E0386F4CCBFB86F246AEA4A2790D1B62B6430E35271025CE8C710A1413D293959B8264F6C41FEA8EB007FFAF1FE0B57A0672F9ADB6734C2F8FBE051DE3B3E80670F8169DCFB30CB6EA4A7A9A7E56ECAFD79B4A8C7E35796F94E89A1795D9B7E19FC40253E27967338BC4883E7205E2D70765C38D8256BB2504BAB38274CD9645F3F9C65CDA32A50D09C1B7F98F0467D9FFE3A7385B6473EC2012F265D06BCD1522BC9B82019163632C63049D60C8F79DAD64BD92EA183B3FEDC67B450B88FFB825E0F3C36443394D6FD02D61D42881823D8E224FB0D93F68D39FE0C8AF40C08C7E5A438B9B686F3280B67CF7998532F53DFC01299C7732A69DE3076F28D14769A324620C5535EED84289AA55B06FFBF2BB4D73FA75AC8EB66609C5486559FCB920A2F4217F7FC25184A27D75B197F6C2A479D6900D1D56CFADAB446F002615736C9D541981928E08B4C4A75B6D9E0ADFA6795E374D71640EB096BD017BC16B9A6E71EFECABECC9E85040D9803C88A5F11F2DAFD49B506CDA6AE0EA5BBA9A627C0E89FA6B0D35FA493E4C853D728B1AB214EFE14E39A999E4872857F039C67024E106454328641577DBC7B74F441B01A613576BF0A21950B079937BC9427464CF4428CA5EC21AA34F55A0F42005845D62355FEA077B9E6111DD2402C715A972F8FA43C317713C71255D834588DBDB63B8F8C1DC5D672B346135E138D835B36334A38B0B1514FDD87EBB6D8FB8DC41D13DA74BE01A1823E417D3F932083FAD5F2FAA0B099DD03A002B5BDA0F1997B65B62E2097B948B406968CF7759AA75936CAA33B3279A1A92882A9C9A7109090E0FACF49790B1AD1BD606D551A806B1EF434D491AE50FCE2FD7B17BD605236E4E20616F7B7E27CA1C0592F25F3AA967EA41A73D72F4FE4F241610D4F7806E603A1A188EED49D4C7EB6F34CEC63F7E8B82F33B16D7CCE26CAC7151F4A7674667406941291FB0B6315CFEDCA1039B1B28C0E3F29FD3AD285B044C3C24D317A4D83D47985A2EB0F661AF5589F647D92554407AB5664187871F4D22869948A11E31E59ABCD8A14E314CC46D88FD8E2F4679C473B02C3FA0B5704BD2ABB65BD4917C3E6ED2CDB400CC4C82F9A7B9BC71C02E0AC7A029C159E894A88426E60599BE62F97223D12E318037A16DF3C2AB4F90A76F6594297CD5580DFC287D080AC03B445A9278F51C61E8D693123262014A2802201D92241C1269ABB4AD0ECC02FFBAE5AF52D2BA5AB08DF8CEF1D5116C809497279E7D4D9754AA5BD616BF0B5EFFD40195FD0BD17A180BCFE0585F8ADB1741F52DC34D403993C71519314F103FC207AED3F205DC546CD7996E2A2CD2712E26008A74B292A75A1011BA15E8D567BFC19A97C4359F5209A93600B81CA32A0CB1333BB535B699C0CE95496EA897292410D8C89B2FB9157002F82CCE582D62ADC159C08C85357FCAA0F9A10D137F09C5CB1F427725732BF1AE5D12C95045DC4B8F04BADF4A3BD6DA9D99E30D0C79C3FC49763883F77397D1908A74D84FC9BE14BE0AE638B4CB261FAC04EE77AA2BAA5745BCB4282ACBFEB4945EC820CF507B834C70DB6ADAD6A3E5D0796B375B6A29B42D7564EF51E43943C708FA28C4F565D5867FD7E6260D44FCDBB491B4F1394120002271EAD1C1DEC08CE3E942EB8D681D837EF9387E960B33762B59459CC1812E8A847F7EAD81E0220F4F0A4896DAE94C2C6967EC578AA4D56D0D7CF72C53D17D57DD00079D10CF2E98282C2060B483735BFEF6197619182D592E3D9D7540929DEA260D6BEE7F38A21C267AD0B7194A1FD37082117529F2DBC64A39E170343ED8B8717796D802A57B57975A1232ECD48C79D2713EC1EF8F6FC731118B78F081B680CCB79A0A4232DE73D1EACA3455AF0331921195A8E70DA28D8BD04C178198D55D6DF1C5A8C1B641C26FCE25270A6D07D2C1B52A824A2C089988C35191A98A6085EF3A000D22452AD2C55DB0B87CF00C6BF2AB7E66E341C03BAEA55899AC814DC988391250A4D666A9F98F4A1CB16C80B23C4A97309AD0D42EA85A2776D3F097587648CE0564826CE4127DD1E7314EA9A9CF4910E17EBDCC40B4CEC74647B7271C3B735DC5AE8E958CC30617F01C3BD61B5515C91C75B98EFD8DF09D8BA8DBD513C5CCDBF7C5152C340500B09EDFBA42EBCA260DA419FE8856B4B1F904782815261B3924A5F9347673C02CA252B666D6498518A79682B321A8BCF2514CFE07E387DE3CCA6F92AFD4C31D2E83381A1FA741F5760845202EDFBB55582D3CFF2FF2B04A7D469825F1976CD2A8C388FF958F88B4E52C5916B30E6FE03BB9FF6D721ABAC2E7EC46CF6DAC3362929F00321FBB0BA622E1E87A279D5B01D43326D4AD95574CCB2B77E5C8F5E7E9670F4F1EAC11DC3716E070A8BA20E1FFB4FDBB0B75FDACEEC6E4C426C31CDCFA95DF4A1E4E6FCA2D6D674E641CD707281B9D5EDB5DEF9AC42A212A1DCC342DE5C1057E49763A791EE7635F8194533BB2541A690688CAC2293FF19F26B12E2CAAD572B616DCD9058918866155347FC3B7DCFC5A9FD84A9E020126E57CB6DBB5C3788D8B20C9D00EA68DCB344988BC7D92AC98E3E6A278CB536F0AD1A0D0DD8A60D2EE9775347EA0654566734F3687FC669D270A4FD2574770740E1E85D8453E4C7A8A97BEB84F229A630632B1CC90857FE2666E08FB105AAEC690B22B9035766D5A2ECDD63326AF9B48A3247D7F4007947B9BEC525611223B0957532B818B5A686A027FC0C5DB24A849EB573CCC55C204A2EA6927CB1E811F17E4E23D11399C3F88874B79B455D9470FB47833B10258B6739C120533FCC0A64E97B87DFFAABC3982A62DAD557F01F926B2D01A44DA5845BAB6C784D1E226539663F9AB7C59FAC757CF8C930D0BA7CC74CD98B7A60C755BF63B2362F854BD0D4327C6829E53421122BE8E46C54687958B142857039E565DABBC660F289F40E3036E5FCB332644C9BFE40F88DD932FAAF338188D4B7DE60C3D9C2153BD024ECC1BF3E7B653A3704A14B7EA6437B03FF83DBFB25413528AE9D4E8BACE91B93323BF6006A982D34981AD395D3E52BD33337D02A8370C088C2616B9CFA71CEA8B1AC23D3053F85C4B4B195B7F1F7B2BC11D0CD5FB5FA4FA64D95D14F0FBC26873F31703E15A74EA25CDACE3D8AD595A2EEFF6385406A2CD9F182C24B05D842DA26AD7CB762C76E5B255803BC6A281243700ED74093B6974B88EF179FE09D70483CDE0282E26EE377D526B088CAE8B72AF4D82DC178D71FD05BA2DDF7A912602EDB6153F45E3250571D1CA84D49E543B3717DE7E876D19C543F0A0EB8AF9774A6E2E7A17C679DCF2345D4775F292B150FD5789DEAD85DD1536E5FBD9D3445FD5A78FD7A9761A8D588A8CC97720523513DED7D6E95CDF640C7F7D0667B3A09134571A2E741DF8A007EA56765AE6EC046BEE94439E675BB49B6E32EA14F56AA3FAB2AA45FEAF65938EFA7D83934B5487896152F7C0409BA2572C18C7EE4EF61A5484963586E10F709A1396AA2565DCD9C0B4736B56D205851D2452CEC5E324FA4CB6B8698EAC2C9CE9074A17558F23FB7BC14868B8E73B424C8CABFADCD4C5A3EBB6903CEB7543FFCC5585B85BFFA94DD2AA324C505D7F08EDBCC0024DA4E5C1671B6CE012E605AA6B1BFD58D92E8353F05DA5893E3D3822D76338A4D8013297C5F643D648A53F62E8920674B58675C64D0C662FA08FBBC1E9FBC088FCA457E14608CB0D0272A4C77735BA3A7A26D1DB6DD2D0845178F4FE627EBA84ABD7679B2F86E3D9AF2171927FC74104A9F198733FDECEE23D2D51431641F657A85D63244E96D2636096315F291C019197E73C9E11B5C5A015C12A43D176F6D33E34D9C423C1050545E4CB89DAB3E6A58A8E2FF3415B4831A91E72718E5D09832C4970D3C3D600CAE926FF8A24ED9D14A5E5A623C7A777639C145D306F20A9A5B4DB24C99F824F40B5BF387A0E1B6B49468A62755CC437E787DF96748EA3FA5E537922307E7C0694699D227350BA020A5DECF680599AD95F00C0D73F50B13E8385C04BA03C0F52BEED8F4C1C1D9132A3B9608AA1039169A8FA850DDB4CF01966CF881C64EBFD758CFB245B486FE5689C20FDA833F67DDDAD4CFE4638BFC7F6D967F039786A330D8393EAC7A52150DD7D5D30F79A09CFA383A5B7E96E3B2EB6BBD66CB3D6D844B0BEE333E06D027E4379259465A38516686255550A6074B1814100A60F84BBC3CE560AAD08B21821CE740D90EF12452BD53516DC90E60A4480CA758401660FE1A6EE8A88B433C14698CCC86C4A270338DDEA2DE2A5ABBE1C5CDA9F2C222590AC705A0C86F927D830EE91FD1A3B9ADF35C4EA05915FAB0F988D17C522971B7FD2431088020CABA03543C1E76255045A198EF900099FF2A982D626A4DD09F0E3B573E0660E2B09A8C0E7000DDCFAD466E890A4746D92AC1877A53215E35B9D3EE718B92A14706FBBC87B18A42D91CD4017AF5C6D2F549A052A09B57CBF186D1E9C004624D4A3A7A8A03A89C47353EACFB90647B2C6B29F73C506835E320DB751A1EAD0538B74DFD39B383FD7042B51CD82CF10DCE0C984AE75C12DE1A5BD614045DC91989095D32250EB1137827EC5E0FF91D5AE8807389D238AC331185DDC332C90CC9EE4394FD9EB81CDF04DE8D22F6856C5BEE1B4E5739FFB19CC8A948D24D764947FCBC99218B4DA2C439DC410BE7262C062486D55AEF977C09A36D9262E2BAA088383422BAAA20F4D8F86C3A4D8422851B6343AA15C4A1DD5EF0ACDE63937BE7D7F7444286BF00621CB279F49B47835925D71E07FCFED911E94D378A73548539CEAA7D8D84EEF4BE67EADF7C7B57452CDC106033D2B22DBB25D05856C4617E144F4E23094ED4D2BCB7A5DD1DEDD661432CD2AA369EC0B32169D52314A8646699B49DA27FC6C233C449884F540068127850D6E383DA6CA607A0806561363928FBB259AB5D98ECCF00B697E1810CB5422AC9B2F12AF205CB3721B5FE1680BE7C8E9B89A6EEE126E75866EB44B48B3DB48CA63818E6EB691002B2130B6C591F2605152E382251153A042708781773146D57C7B16D62D5FCC2252CCDCFF10EFFA1BCF2D4305C34E4F19F07595102B0A23929CC5470B9E16060DFB7D0F28956492A5DCC8825FAB6133D490BE3BCA7141F049A52548BB792E8A84B0C6A2F7B13196BB8C27DBCDAE620019EC8E480E6521393C98866B04863B8736039F42C23CA001E3B83178E1A65FAC153CC2F32EAD32440FF6A8A4AC29762903B32140B49BB59E591B6480BACEDEC7CDC77D89C82F60E23B5B127B72B9A271996E2C9FE310346D739045EDC020668D69D7EFA248FD6F786FDC4A5199685D68084A1B4ECB0719A8781EA366B630F0C18D023F57CB302D92F8153FB99ACA4EC2E16C943FCB944650EF9733C97F66F986945B8531B65CA531798CA139D8A31A1CC2657515B0FF4235F0EA527E0EFF2E83B9F160857956C894E60AD59345C95D2319CA82ED49306307B75800036D0CFE6689BF9672F9D065678861F7E174489B789FAC5D6942A792ABAC3D85359C36D35286A51DB42C022B305D182E65A9D11602B6D2E88AEC5A556EDF3979E1660F162409FA90C3FC1377370C9389CDCB305772A225671345D30055A528F27AAA77819A7819F64AD7185202827EBE0D92A1E30B05BC17F45E8422E2A0DFCD3BF1347841339842F908E30502CEDFFBDBF01E2A1CA4C8F8BFD8489FB6A60F1EA4A2BEA49A9F986A9BFE1792F97D0842B74AFAD6E97902FA043A087F2DF276C5D30DBB274A824208EEC77AF7B84B0D4D18DB495E976EE802AD61672EACD514F7CBD355FC7F11673D9198447BD142B6AC1A6225851BA2D7CD3565CBD3D8A7341915B40F713165D6B4B949140B91AF9F89E46FD45CE4ECCB2C259BAC0DE91ACC11442A8C146623A0A64ABA992816FBCE99C835554B6ECA8C53A04CF70E81067BA2854FD2332E9A3813F9A3F1E039D4CE9F349FC1BF9327492DA648740E5ADB7E470B513B9375D439FADDB93A6B928440A31FF6F1184C33E1342CD3B29AD04C22AA2D68F4F3613450B7CDB2A3590CF60C03401949AEA3AF2060047A25F37B6B4D81783A6E3BEB0E6F3EDF523667F25D6093FFC23C0B38C99DFEAA9A6EECA7A7322ACA8417F80F819CE53A3090811733E71EA07B5D3773553335EA7BDED1128D48087FCC05E5860546186B657233B4A370B26969D7E179C49BE552B0D17DB34537645DA06F6B1E40A897FA632D1623303671A20F38971357C139602A03BD170783F6F061FA433986AE9747F3D235B662D29D1DB7F077004E34133EEBA14B2C271F3C160AB1FEA528C85404EF08A33A23256BB47A0E55AFF01ADB49845AF72057F2B9FCC6C9927EC5EFC0B510D65D81A123D082B3AA20CBAAA1301C425CA97CD8EBB2FC9D0DFA17C8F0010343BE3E4B2764A269C598B8E541E02295707B6D30968C75B5EF76A1E27B6C5190B818AB5D543E3E8525D007AE95B948B53E81E96D349041DD97AC579ABC59FE1AC5302691FA88F8453D90BA87796491EDE09F70C67422AE3BA6A5914219321E3EE34DD59379E412009869E6A611D4E0CFC7A56D645AF62E130779438BB89CA54BD04ECF012C94B59A800987B449BA086C859C91F1FF5D4C2C5A558342B2B836DD427D45C07D8789EBAD66C2445F5931EFCDD4EEC651EE22081953C30F7DFFBE1B98A8295A545F829C256060A36849FD80D4BEA694A404B4252D86496521EA0EF4BA1415BE3C3A4FE687E67E715D15C7DA44CD7FA18A010F46E8F9221466CE53503D3A9840FB77D7765B84D5D4CA19223CCB22FE81BE7215C8DD0E49C84D787FA921456A6943C5892539062313FE024A0C2E24D15D75A8097D0D393C422099BED9259878E4EA02ECF174CA3B8F5C80D01E998487123C10EC2E04043A4D87154BEB2087C7194F74201BDC8FAE3D8F202EE83A21DF9E5B3B0908C9321F4431CF108444CF5A93175B4563B253F84897E3F720E8EE87E6B268DCABA7017BB07C5DD29133B5878B6CBEF6DDE103E3E34D8BF6DF151A3A24C077D8529BCD02681DC9E5701F93939B5AAC45FACE46A5855E4B4C3AF82C303999937C48CF3D88734EAF75E3EAF171768F6F48FE2CD5FA9BF40328E642D2510E331BE5C3AD6632875A9BD51D6904B95BD5C17B24C43971D45055C7AC0299017249A936BA40E3BD17B63B510F4B7A3B4F236BC073EEF4F61CE561D893AB5B0A720D70AC138D47CCD5426C827E1AB13B2C140FD6E6903EEAE7B30A06851759FDAA28810A258306160452486199EDD0F2B44F32361E8EDD4A458A93012DDB8B4C08A565DC49EC6BAFCC759237AE69DA0DA41251D9E7E8509E4A09A8917418C6B2705CF7C463CFE64A67F88749CCB80B8B1B20A33B5D9DC3F1E74BAE981AC670EE2D86D7B4C8E891F3D5C98B3F5455423412C641C0315F27A263DE975D8D6DE1F17D6FA51262D8E6B3F32AFD9EFB72958460B5D58F4BB5CBF5AE1CFF2D2835FA294760854FF53522E2D52981FC276793EE5EDA989EDC921CCDC50D4B63A908BF47587E4FAAE3A8A1BCE3A21E331D9230B6B1C4CEC7743AEBB83EE003690D2CCB27301D88518A43B266A2E2DA78742230B2258E5D1855A990827B3A8B900362791AE617A3DC0BA2D12766A70316229028E09F96EAD7C773158A549AD9E500751D812D3F759468E22B86FF85DFB0C7C79148CE152A813D0B2D3979A3DBE3765102204ECDA68D24FA6330D599481516603607FA04439B9A0498BCB359F1EE20D65A5A84816AD43750AD1838EF4597876291C98E82E1F46A6963C7726C89470E778BD0A65CAD07E2A167AF6067CD058152E9C14979152103A7235DCD0755BDD793F9B23AE031133756EE3448CD1F93985B40EBE72F7F54124838E3787182147B84C3B3BF8CA901253E04BF808E3F3AEB84C05AB5CCA8D0C755D7B0CB02BF9E06F3113FFC098557DA3F439D397251D48D2FADEBC40286A17C66E0A8533328DB4DFCE9BE32F115A293567E4EA004157045D6996C7B73F27ED2F25536C2A381996257FD23EE4D962B663940C2A57442F5A7CB41B4E6615B223E91B55A7F98B416B2A6003D3A27B496F5E1A446DA9F0DED6A5AA0D7C283F34DEF97065049150B136B2D0F7A4207C904D836EB1E35BCA84EF2780068ACEF1942C2B31BF1F89CF218044A30CED4AF8743EF034D7F526989B99390D79D7454961C4606F6AD659487A803F4CF5FDA4F0A039664F3EA84AC4D7E0E805759A79B959906655FFE28609963BD8BEF5C82A50922A9836ADB18C9416358C092D69292C40FB3BA288D4029FC6B200E59C7C2FC9423360B11F157E5286D0A796A74A088E257F00954CA0789E3507E572EF3DBA68E3CDC7AE328F2072BEABC0E901D5F5DB1A1F41D14732F471F089FEA9D022660949C80D350B37B7F2F551DC22F665DE00FDA868F131E30C110CCB4C746663513E9DA22E143796EE2E241CDC47E2BBC148A230157C0378193F75248FE64858899B3B357CDC21CE8A6C79D2438569CDBE5A44015038C0CB8C260EA75698C272C9D5B169A4D2FDA21AAAB502DE3C4609707BA7C1B2E118F3F5B8A17DEE019465C6EA6B098139BCA2E6873D9945791F7D5488EDC2FD08E09973A55415ECC21556C422F2E0057387363A3D762FE41B054D3C5ED107C4628C15A409C0ECF77AA5AB911A7D896C9D106DB44720A42E0F89447B9E8D277859C941A5E49F4A577C0B6B0049972DBEA98A1E57991F6C0D239BA052CF369D94CEA0D2AC4A0F178D0652F9E028E12423D8D14D7C8ACE33F7E9FA9C2F8B987F89B1BC22190146FBF99B124D028A5AC57D4B21A8F2A2C9BF1F67841BCF3F47051EBD205822DD7F13AEF2D9EEC54D465D89C987851630D0F7930B4451914393C31468126AE0C607ADF304433E581A71C5A19356231A9C7D9810D282F3DBE109F6789BB997BB73E97650844F62FA1954D9BA9C227A23A65F500AB459AEC13D0D25DAC9DC885A01912C70005F440B0EB1A2C0FAD96C7D597DA59AD0199F7978D8DC871BCD4B8143420F5F9900600FEEF0D8C908CD259AAF1D446841B2F76827CE888E1B8A2C2C2FA63AA0C63764E173FF170526FA3D7973931E6C1317F9E7175BEC6F2D6D9FB36730A604879CE7B2FF5350D1A689A57C5D366EA0C219A8E839838A0FCF45A2ED7FD08AEFD2BEC15C47391D9D18E42C18F03B068DE6FC160C176499D06ADBBAD50F1773D036CE5AAA26247B4ED0B68415D4CD101C8B97567290CE4068F4CAE9A778F996A3831FFEF44CA682100850BB5B6290AD8CAA7C10878352C707C0E7D0D0D90462DD46C6317755E4151FAF1755106983B75B6F0A3FCB923D56BE32573D59062008A48AD6A4A87D6242FB13DCC2A7354D470633D292F3EC598A24AF75362BF3A4096C7400A1E97E31FFD716D75BDE945F8FE5A2A40C8C557A5181E7C4CF155F0E1F168A0F25F3FBD0D24C743977C12F1B6D0B4CA5042017194AC81EDE63A324CFE149482AEB04CE5209F0BD768C7CE90508DFBF5EA9A66A5A6FE038746852359919DDC76795A68A4B522C86D5CFDBA46BBE140DCC5B377F70FFD7B776AD3A7885C43A9A7B5BCE599415FCAEB0CA7EBC1D51573F95822CB3135B602ADF40AEDE049501F6BA26EFC18D4A5E9814EA2C2C4CF301AF331672896A9B383F7E582F3A09BAE7BBCB9AB088A0D7FE43827C393E5EC27F08BC5E85C4705EDDA2862BEC9A4DD0ED9BC56D592F6062F75719ADB6A9CF1C8C2526D6B79E4F53932AEF6031D30498587788B0569F6C0FB226105FE5CA0B8B5239C3D11C795674DFE2C8B46FE16DFE5C389888775E56BD14DAE9985960D0817E5354FAE5F20147B30001A9E8D89E5BDEC5567561A27A08A84E2DEF8182060C003A8CF5DD667AC830497A9D4BD1D003019630FCA28C1E272C8C2EADB1DBECEF8E2CAB9B60BAFDFBD6D75562294E0E169506FACDFDC559581B86559720FB517AD8A010D64D03ADDD2CF9CFE8D2FD244A4378DE585F2DC009B2ED6DAC2FEC87D6097A25CC720E7DC989332C5A341503B7D03CC57A875A1A26DBA88D840F24EF8C86DBBEF37905CBA382A8A5C9980504D0CD6CEBCF00F7335705BFAFDE3C3F82EB3225E5EC63D6D69EFD015C9C51AF71B23215E4BBA667AD525852CA6C29AE3AB303AFDE0279C45668D02A2AA96625FA7B41B268E783F829A3A2464F22436917940E6EB3B1D313F794BAD2514B35954718CB2D92CD7865EBA847D388F046CC7D5D3E3B57D12714E9BFD4A09A18A8A626CDDBB21FB599EBE030CED022E16D6C65085EFC6911F7D154B8C9787D3C4107110809219414377EB07AF5441DFAF02EED0B6C7A6BAB1D9902D7A3211F22536AEC7A0A365BAC29BF69021B46EC80143CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E");
+        byte[] sk = Hex.decode("FFEB02F6010609070CF80CFCFD01F7FBFBFE02010004FFF002020900F2020602FC090000FEFC12F6F6F9F601F6F904F9030101FE04FF020203010B0706FEFFF7040A040900F8050805FDF3FE070D060C0A00FE0CFE0D0D02FBFE0B04EFF90002F7FDFF030307F303FFF8F5F4F60505F9150304FD0AFFFA1500FBF409F801FEF909F3F3010201FCF90B17FDFD0101FC00F002F000FB08050EFEFD06F504F5F906EDFEFEF3FB07FB01FC0106FB0304FBF6080005FDF605FA00FB07FAFFFA07F8FAFF050810EC0001FDF107F9F2EC03F408ED06FFF70009F80C06F60102F6FBF4FB010207FB0A0304F9F6F90C02F80007F30D0C04F5FA0702FDFDFBFE0607FFF0FFF803F30D07FBFB010300FEF9FEFB0A13F007030A06FCFE03FD00FEFCFFFF070B1004F8FAFFFC0D0605FE0306F803FCF30506FD0211FD060507F8FCEF0CFE00FBF80DF20A0F02F8080011FD01EEFDECFDFBFDF5F8F4011210FF0403F8F3EB0D050212F3F80609FFF903F5F309F8FEFF0AFCF6000EF80BF6FEFA0C01F4F3F2F508FA02F8FF0A060009F9F70EFD04FB060005F3FD02FE01FC0DF30AF706090CFC0006FDF80AFD03FFF80BFA0CF30B070B0B07FC1003FB1404F5EC0709030EF701F705FB00FAF2F500000A0406FF08F5F202F8150F070BFFFEFA0603F9040303FC0206F8FCFDF6030507FF03F4070409080C1106F107F20AFAF3FEFF070CFCFFF4F5F10710FDFC070EF7F803FE060103FFF4F6F50D0B0202EFF30CFAF5F80C05FEFE070802F8FCFCFD02F81B100404080213F8F90C04FFFA080AFCFBFAF903F600FB00F60503FB0106F2FEE80003FBF9FFFD14FE04F50C03F70103F2FE00F5FAF600FDEF03F70AF70604FAECFBF70EFF01FAFE080803F20F0D1704FC08ED00F304F904FA020403F207FE0DFDFA040B1006F7F9EF06FCF909F903F7FD06F602080501FDF9FAF40106FA07FB0CF50C07FF12F702080A0900FDFFF8040A0BF20D04FD0F00F8F8FBFC00FAF0F91705F901EAFCFE0304FBF7F6FF0E0802FF0FFC020B02FC0801FE03F2FEFCFBF7FFEFFBF503F8F707000612FD0605040404F7F3F9F6040B01000009F5F901050202FCFCFA03FD1501F6F104000DFBF7000DFF0EFFFE0000030804F1FDF313F403FCFEF7090E0708F702FE0C040BFE0812FBFE0EFB0311F7FBFF00FC01F8F20405F502F40008F8F70FF705FA06E8FD09F8FBF2FB0704FCF4F3F5F9FE0AFFFF05020D06F4020413F500F703E7FFE30401FB04F9EFF407F9FF04000EFDFF0707EF0509FB03F506F9FCFC0400FF0900F3050AF50CEAF7030905EEF50C06FDF8FBFDFBF40400F902FC0300F6FFFDF7FCF1F014FB03FB09FE15FCFD02FAFF07FA05FE01070E0402FDF31102FE010802F902F4F90AF60408F9FBFCFBF503140FFB0DF80B0DFAF9FBFC0102FC06FA03FDFC04F90DFEFB0301F3010C0FFE0FF90207F80703FEEDFF0A000407F1F8000807F7051306FA090800FE0EF4F6F6080AF503F112F106FCFCFEFD0BFE020CF40DFEFFFE0F01F6FF08FE03F40B02F50406F401020301ED01080508F70907FA02FDFEFC030108FE08F708F6FBFD02FFFF0000FBF9F7030505F6060809000BF8FB00FC02FFEAFAFB030309F806020109070DFC030104FDF701F3030518F9FD0800F9F60DFCFF04F8F41108FF0202FE03F6FC02FCF706FCFDFE08FAFB04FA0B01FAFA080209FB0CF1050C0EFB03FE02FEF80CFFFD150803F7FDEB09F8EEF1F2EFFDFBFAFD0A08FEF7ED06FDF4FC03F7FAFBEF07FF06050A0307F710FB04F5FD0100E901FE09F0EBF6F203FBFCFBFA0008FE02FF0A00FA16FBFFF70BF80FEDFB0000EF01FEF900F9140C0E0901010606FDF2F4FC0FFF0EFF0EF8FF06F9080403FC04080BF7FA01F4040705FEFEFC0203010C090AF1FC01FC06FB01ECF3000B010E08EDF0FEF9FBF4FB0DF506F4F007F6FCF3030D0A04FBF9FDFA00000D010C00FBF0010100EAF7030DFFF608FCFEFB0207F601FCF80109020010FF100702F9EFF800F40109FCFE08F8FDF508F2F6FBF6010EFC02FE0101020D030BFCFBF2ED01F8010210080EFCFA000B120017FF051711FF03FAFBFB17F805FAF9FB0102FD09000CF90B1A0AF500FCF60EF7FCFC09F1050DFAF9FEFAFEF7F50CEBF5F8F3FAFCF6F6FAF308F6F505F6F9060A00EC05020AFAFDF9F50200FA0306F707F9FC150A000D0702070B03FAF5FE0EF7160D06FF050004F9FCFB05010107F2F50103F60305030610FF0B070A03F704F602EEF411FFF9F5FBF2F5F00101FE080BF303FB090402F30509031107020102FDF502F5030301F2F8FCFC07EE0A04F4FE030406040802F406F4060FF2020DFDF803FE01FC030AFF06F50A00FDFB12FB00F4EC04FB000B05FFFFF60EEE02FFFFF7F301FC0CFC08FB06EE06F8F9FAFCF5F30BEF01F4FE090400FDFF01F8F20BFCF40D0001F708F20304F906F20201FA0BFBF4FDF309060BF6FCFE1007ECF30202F907FEFA0AFBFB05FDF60BFEF5070CFCF60C00F5FB0AF909F1F40302F90D000503010705EDFFFE0103FC1004090C0007F80012010DEBF3ECFE09000807EFEF09010EF202FD0A04FFFBFFFD02F3010906F808030409F9FC0D010C0200060703040606F407FF0006FE05FBFA00FB0FF6FEF9F8091105EF09010300051304070DF80702FF06F30A01000102F7FF0EFD010500EEFBF802F8F6F9FC0602FE100105FBFFF7F8F00AFCF906FF0401FE02050A0205FB03140B040806FAF900F5F3F906F90FF9F604F602F705FFF8FE0A020C09FF08FE0FFDF6F3F6F2070002F8FEF1F90709000301F307EEFCFB06F9F7FBFEF9F50EFDFFFFFFF508FF00FEF6FEEDF7FC02F3150106F60803FCFD030FF9FF0CFD02EF0510F6020801F70AF7F8080502FFFCFC04F7FB090BF1FBFE0AFEEA0407F8F4FAFFFD0FF5EFFBFAF7EFF7FF000E00F906FBFD01F9F91A00EF0C0502FF050BFAFB09FE04FA0703FCFCEE0507010707EBF3130103FD04EFFE0DFB0503020C0D0AF7FB010000FDF409FDFFFDFB010BFBF404F0FC00F8F007030FF8F5FAFAFB00F700071205FEFF02FCFCFFF2FE01F00C020BFB04FB020206FA04021008FA00F609FD0C06FAF009F60FFDFA07FC090E0C090CFDF9F7FAFA07FB04F20BFFFC0500F8050DFF08FB09F30104FDFCFE02FE0AFC0CF9F70903FA06F6FBFF01F305F8000807F1F9040E07010A03010301010D11F502FDFFFEF2F9F904FE02FEFF09F4FF05E9110309F3F504F3F807F2FCFAFFFA0BF501FD040107FDF816FDFCFF040404F9F7060AEBFEFD07F50000050B00FFFDFD0108FFF806F5F60D09F909F610FB05FD09FF0C06080EFFFDFEFAF7F9F70B05FA01F4FE03FFFEFD00FFFCF8030D07040B00FE03F3EF03FE00F5F8FF00FAF9F80CF9FC0102FFFEFE0B0108FAF7F8FFF805FBFE02FF06FD0311F8FF0CFBFEFE060CFE00060200FDFFFA0C00F30EFC060608EFFEFDF2FA0405030707000108FE05FC050403FEF800060408F301EE03100CFFF700F9F2FEFC040800F8F8070104FA07FCFE01EDFE020A09F00EFA0C04F70CFFF002070BF908F50201FDF8F80AF90CF205020BF41103100205FB0104FDF50502FB02F7F907EDF3FBFC0E0E09FF0C0505050E020700FE00090609F60C03040FFCF8FB090AEDFEF4070AF3050D02EDFF05FC0503040A0AED0803FF09F50201150F0CFFFE0604FAFF0EF801F90CFCFBF9FD04FFFC06FF06F6EF01F811F807FFF9060407FEFF00FF060B030100FF02F902FD08F905FCF50700FC0D09FA0302F901040AEE0602F401FDF6021703F50406F803FEEAF10AFB01070916F0FD11000E060204F707F60802040302F60F0B09F802030001040114FC09FCFC0A0B05F5FEF80F04FDF8FFFD01F8F3F6FBFB00070704FF0FF70A06F3050D06F3FEFB040001F103100F06FA03FD04F4FD02FE05FA06FBF40FFC10F11407F8FB0AF6FEFB0303FBF7FCF4FFFFFFFD00FD05050900F20600FC0601F40D070403FFFA0505FB06F806FF01F4FC030910F70A03F20809FEF0FF04FFEC06FA090404FE01EB010106FFFBFDF9FAF4F91703F7FCFC07FD0DFCFF07000AFD07FBFB050CFA0702FCF60E04F9010603FB0501070AF806F6000BF6F4F9100BF4F90904FA0EFE0203FEFE09FAF3FD03F908FCFFFB05FC06FA0DFC0001FEF803F60200FEF80D10F80E0B030503FEF103140A02FB12FD0301FFFAF8F905000408FD070C03FEECFC01FFFA000F0C050203F9090404FA0710FE0DFE0FF4F8080808EEF404030909FE07F805F800F7FFF9F4EFFA0307FE06070005FA0FF5F3050A0400F8FCFDF001F2F312FB0D030E000C00FDF4050904010602F705F20207F708FA050E0B0206EF04F8FC000410F0F30DF7FFF508F7EAFFF9FDFFFA0504FFFE09F5FEFE1607FCFF0AFEF40206FC03F401FBF7FAF7F50402F3F8F9FCF8000903FEFAF801090504060307FEFE070E0AF6F501030FF501F40407031007F90206FF14F7F9F300FCF008FC0C060AFE04F905FEFC080704F6FDFE01F8FBF0FE080A0509FC05F803FF0E02FE0E05FA02FF1000FE050814FFFB03FBFBFF060CECFD01F901060705FC030B0BFE08F5F70300FDEE08F1F5F70204FDFAF4F6FF0705F50AF5030610F90F0C040DFE050001FBF9FCF60A05FE05060107FE0304F11104E3FCFD03FCFAF7FF0805FF0FFDF8FFFF050101FEFCFAF30C08F2F30107FBFEFF10F0080403F40309FF030B00FB04F50DFC09ECFBF506F711FDFEF401FF06ECFA0600020108F70CFC000DF9FBFD0111FA0002FA01F90008FF0B09000AEEFDEF01F506FFFEF909F80CF806FF0503100206F0F700F602F901F202F9FA1402F705F8FEF80A07FF0CFF000DF808FA0FF7110701FDFE0CFE100500FCFE03F1080EF60B080603FA06FB0802F9F30D03FB03FB020903FD080700FAF8FAF8F9F2FC0707FAFF0208F4FFF7FC140001FD090108F80D0AF1EC0AF909FDEE0500F1060507FAFDF9F6F8EE0204F3ED0308FE0B0101FE0C02F303F5FD0BF9F5F00C040102FAFF05FE0C010408FC06FD03F5FEFA030700F9090109FF0A000108FA0901F5FF00EDFEFD070705FB0600FEF2000E09FDFDF1020500EE0503F2F8FFFEFAF8F803050B0602FDFCFA0C00F708F40602EFED04FF0B04FB0115FEFEF6FF0AF7FB190006EF01FA0BFF02FEFE05FB010107FE02F7FD00F10EF4FEFE01010302FEF4030700030BFDF5F903F503F10D0904000A0A09000106FD0DFF02FA03F701FA02FD0C080F0D04FDF603F6F7F900F8FD0609EE0BF702EEF5F4F4F70105FBFA14F708EEFDF1FBF901F7FDFCF3F40204F9030401FA0203F103F90EF4110704FD00FBF001F60905FA04FD11F305FB0A0C0406F7FB04F6030A03020AFB02FD02FEFF0DFDFB04FDF9FC060B0905FBFBFB0BFF01EDF50200FBFD110D070803FDED0D010C0B0006EE0302040108FFFFFE0304F6070102EA0EF90701FD0711FCFDFBFF08FB06FCFD09EFFB0DFDFF01FFFA04FEFB0DF90A060D0407F8060D02FEFF05F90AF60DFEFEFC0F08FA0102FC01F6F6020501F407F90DFBF500030800FDFC02FC020AFE08F909F0F8FEFFFB03FD10F5F3FD04FEF80BF4F5FFF6FCF5FE03F8030107F500FDFF0DF2F2010AF90007010B07F2FFFB05FA080EFB07E9080614F6FE0E020206FA01F4F5FEF4FFFB03F6F2FD0000F60007090103EB0211F1040203F5F90901050D0F05E5F307F909FAF8FEFD00FEFEF9030C0BF2070605FF0806FC0011040309FF02FDFCFF01F404F80AF9F807FB00F5FF04FC0DF3F504F90DFFF900FD05FA0908F90303F50AFE0CFC0DFB0FFD120906FAFA0813FB02F7030107F700FC05FDFBFBFB020D030BFAFCFE1000F9FEFE050303FFFB10FB0104040B0B09FE0102FCF7FCFC02F6F9FFF90BFFF90F01FFFEFEF9F6F2EFFC0309FBFDF701FA12F605F7EE06FCFE04FFFB03EF0814FA05EFFCFBF0FF0BFB18FEF7090BF3F50011020C060604FDF015F706F30EF30206FE0A0403010507F5FB00F902FC0C03F901F718FD02000703FAFCF70B00FCF3040602FDF8090106F800FBFAFF0BF9F902FB0615F9FF00F401F9030709FF0305FCFB08FFFFF505FB00FBFA00F611F9FC0A0A04FE11FF021207F7FBF702FA010305FE01ED07FD030E11F900FE000101FBF608FD080F0D0DFC0BFDF900080F020FFCFBFC030513FDFFFC0EF7FF07FE0C09F4140602F70201030501FB07F30C040D0603FFFE010603F2F90CF70203FBFCFFF2FE0801F507F8FB0405F8FD07000C0102F4010009FDF5FFF2FE08FE01FF07070908F7FD0E0601120205FB010606F8EEF9FDFBFD04F80304150AFFF9FC09FA0BFFFB03F80B07FDFE02080DFC020CFA01FDFA0B0702FDFBFAFF05EB04FC03F1F90C03F9EE0F030B1202FA12060505FFFEFDFBFC01FE02FEFE1400070AF8FD0204F7F80DF10714020805FE02040106050C0315F60704FC1201080A0307FE0004100A05F407FDF60AFEFFFCF40400F9FFFCF9F810F00203FBFFFF07030A00010E04F5F106FDFA0EF80D12FAFAF20EFB05F20307FEFFF2F809FE05070800020009F0F6FEF7EC0A0601F2FEFA0AF508FE060004F606FBFBFC05FDFBFE02F9FCF7011100F6FAECFAF7EF030EF9FD0DFC000202FA0B0103FAFD03FCFAF8000F0703FBFDF4FD08FDED09F70B07FE0006F5F508F202FDFD0BF2020807FB0FFAFFFD04010408FA0803FFFDF7F00001FEF4FD01FDFB0E05F4F8F9F6F3F40406F709FA0EF805F7FE010B0309FBFBFE0706FE0B03000D0701FCF30610F3FEFAFFF6F80311FF09F60804EFFD0F080706F805F104FFFF0605FCFD05F9FB040BF614F5FC0502FA0705F4F6FCF407F4FAFE03F6130600FF06FE05FD0208FEF90AF7FCFFFEFDFC0EF6011101DC12FDF70407F609080D0CFD02030AF4000305FAFAFBF10C05F6F0F9FCFE0009F7000100FC08010DFDF805FDFDF5F7F7EC11011103F9060505F4FE07FE03FC0308040E04FEFB0305F9F5040AEFF9F50601FF010AFE0C00FEF6F9FE0BF80303FE0303FEF40A070D080308FE05FFFCFC11FFF501F9FAFAFBFAF9F701F9F5070703FAF7FCFFFA0C02FE0D07FEF20FFDF8F80705FAFF02FE0308FEFC01010C00F70813061103FFFC01F90CF3050C010F05F50101FB1202F805080D0701F7F302ED0AF40B0CFCFD0005F707F813F9FE02FC0101100401F90CF70B08FE0108010000170004FD06FEFCFB010106F9FFF80702150D02F902F8F1FA10FE03F9FB09F1F6FEFF0906FEEB10FB06FE04FBF5FBF806CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E4F5C254B6292FB5C3DB9561B8793D8AE3E1611423AC0A9F8CFC13E1C85FEC6B5");
+        int smlen = 2881;
+        byte[] sm = Hex.decode("498A996E2B6C14E9C84617F4D63149C4676855D15CDC54A85F1BA00CD5FE550C6120227A77605DE227878113DF3137E13ACEC54F42856C0AECCEB53E1E96760ED6DE0E3F353ED4E4BCDB6B1AAA5CAD3CE1DD882C6BB12D3CF9D1C9888624A54D3E024783DD2C39DD54EA4FFEB280B0E546008CA0C779C8917E8FBBC7A79BEEA32D5E6652FE93D8B4737D47D8DD0C1600D5DB9135B74C0ED6E94D9FF02D06F951FB680902009EB68C34028EEE5176E1D64EF869E14C0AB577197444149CC6EFC60871899B1AC9DA86F8373A1273A97BCE318F40BC6A2AF35CEB52A5EA853BA0588531B87B0E1170EA6F464948A55A84920C97AA9D3960B9A099E947608A1E393C634DBE5DCBC6F0BD55E853470F6AB286CDD126B52A29BE7465542D477E3386821901595597E4D6583B6E16326D4AE71A2967FE20BB755A12CB7037614D3600A2B02D4C8DBEE17670CE7B4B144C3D2BEEE9FF6049B94DE4011DC401687E83211AA37E7A9D043A0582A9A7DFDB0B5A3A4AC8CC58F9634DAD9730311F2CDDEE205FDFBF160DA631FB11D0D0DA9FB42223194B18A5850E80C3537F8148A965EEF7B9A2A81C69DDA4797B28A0FBF4FDB31559F940AB7F47021CCD5881934487A6CB751845BBABCF3D213BAB2C61D21F06927DFAD28581E2F8377E2A36319240DD8380BD85DD1A5BF4828AA4F8F5E11534F4ADFCD1B881DD8A094890B20C4F67F5C1545B4065B84F0B28A64630E332ED2426CE04754BD23E52A90299FF399B240D04CD6677E782409D7AE46F5CF718BA1227CE673C42B1CC0E759429F6486AEEF5CF9BE96087ADAC32606B3D2FA53254C62B9617FE0C8D0490C7C7A77C9D54DFF076D44DE2864E3AF3037511C05D2603354C2AC19D94B6CCB6C6362D57B85C6D3E40E5960F7D3DD00ACE24B07AC41014CDF58B106A6D61B0909DDABE19AB78FA29C5987E7CDE6D5D084C278417645995976C74CB7D9A7D6597A83CE1654209325B788F056220F672CCB16051EC94B34F89B6DD33859F6961432294DAA66805488E894BBCE31BD4BF29BBEFA3B987857A4EA2F87AE9F6B5EECB8BC3DF90547833F3240071AD5A5464CBC9F42DC0E9E96DBBFE1AB2681CE2DC100F07CFF7480DE919AB27A2F51204BBF779380B337A550371960757D3BEFEE45AC13792F9F885AC22AAB4A5263364078AD1474B206BC54795B69380B0056F9CE8325B7B780B6AF7DB44F3AFEC558CD93FA268A6AB9AB6714ADD72C93B5BB011D974F8D2D778BC476C5F41ECF7C7D48E632716EA393973BD67F7496078F937AA45FB33E8ADA4CE5192DFB244A1D8290BBD29F239CBCADAC9E1D260D22781B7B41E8A42ABE21FD0D3E3517AB941AD09013EDE52EB9081F731A7935B14E1A9132FF2EEB2C53ADDBCDD3A9656F5E22B0F6470DC961BC0C1B38BACE2F16649536D28271D7BB9C76BE5B63ED2663C5842335A783C524FCEE69D6964874D4BB56418EDB945EFB097001506561A1ACFCD03B15B42A009C9B150CD1FF57104737E7978185F980B333523FCD89F522A59055515A4895E68C07161C47320CF1E542F03EEA4CEBC76266E2C85916638CA4C054F0F085BDCF2A84B5666932A23FF8A40EA90F6CE2888731E88FB7D90CC24D66934B2FF378E662CC57D516072698CAF09EE3759AD305A30C0C222992D3A22DA664A25B74043D338DCFC6A70C786853AABF31B6B5C49D2BE0BFB0CF214467B511643106E729F4756B147467530F9477503BFB9393D36D4A9F964956DCC0C6ADA0A4CDA17D794F7C6A32C7D8E146C6769B810F97F9FF47BC187C57158684280AAED7807D2FF6B9AC3F4E11CEB29F112C30903983FBF38F5D118F084A5ED22E020F066BD458716B491DB9A9A26AFA5C187FEC28BB67C878F82EFAC22675C0D2B6D62DC1804D7933D68441A9F66419314D66540F30BC7B1A8D1F10F5677BC3C6E236F6D5827F70E3F471686F640A8253582F0E5FF146C3BCA53CAEF22BE026BE891BDB96697549DB993B9A4A1FB4B8034FA9EBE42F7A953155A050CE106EAA94C885E225F2FE61E254BECD43E34DE41385DEBB456CFB70FF8C0E46E19224D5FDB7A38F4D2BFA314DA933D8A93CC8ADB99344641DED33E8AAFE639FB38AA9026DBFA1F0C754192A57B8933C99181DF62C9F8101B86A743BF7D07924EBD6C14EF54CC8ED397D73AE2EAE7E2FB4D6456EE7C68FE6DC0912DF23414FC15575E7B84C161A5BF8CE69738211BA13614B1A40E7507701DF29D3B50C26C547C8E49537D10AACE95424520340A57F1B5B35CE62DC693694A2A68A95409D52DD0A0F167F4212826811993D4F245E56CC36A87C404C3218388947BA63814D3CD5DB8666EC2E2551E8E130E1A1C998F301005A645BFE2EF1D208412F3937246A5A41AA4F3D3A5FA2833B4A30637EA569EE3B2E6B72D90BCB4D79A7022824BD22FCE98905D2DBED895C069031D08B69E0B710B4472BE7BE2DB52898BFFAD6F65D73FD28C8BE578A152E53C9D2F04E206F9BD0714CDFC5FE8E17AE4CDFF12B9CB2EA5CD8CE7FE81B0033C9E1E91A88D3F475012BF6882F3D5A25C1E5BC92B89229D38CAF7314DCBA05636232E8409CEE11E7EA607FEA2FB2E2FDF07EA3994A3129DA426D64DE74186A1F8B432E106B786BBD302FE40889DF80A5B1CCFD129EE0B0DBA5C1D506A8CC69106D6BD5006D2BAF26D51DC9BE072E4BF5EE4D1DF9D7B4235CBC7DEA9813244122DFA753479CB66128753E5C4352D0CAACFC8CC577E2D689028025D72EB6A6BBEFD22F2CB01D5013D86A5F92A1AD6AF48F5B7C9F6AA8BDABC760CA25C0ED93986183F00CF3B9F37385DE183EB614D267DFD6E48C7AE1CFFBF6F977512C756103FBC5AA332F8647CE46D12CCD597A0D982C0CB30D7EC954515793CDB3CEB143DFEE2552ED21EA089A486CA6137CBE87DA4F27008B3758668DE776C46794303E316A3480ECDDC2CD5A97421EF9369CAF27E6AC5A56B7C9D42C64096EE13A1F8795042F24B92566021CAB8A988EA5E5BE324A19CF96BCBE0E5B1E32E932C7F45470D99D785BBEC8F9DA12EF268B330D43238637A16D699EC842CEA24E9BA3F2C58D642C9DC7C482FA73644B5E48B127573237D1F1821206081B7C34809F9839569EE2D2C650347B0EB48B76F1387CB79AC0C6306B9206A65E9C7B1DA55C153FC9D509F5B56AF7DFF6D3909F94E1F7FB57BBAF68555D2203B33F459B5C449113B559EB0272EA9C0D73469B72256C3D730E2752CF64D1AB0B47196FD8C07EC32D2E337242874397A0D5A37ECD4BFBC88C96B1655460FDD63B265D822A13DD20B8837F727BC1ACD58AC33603C73A214E90A59133A72B126240FDBB4BC450FCC19ED6E9FB9DBDA4CA3860A2EAB2CEE7D8A4534D2786695D6DF754F5EF52ADBF642C5F3A46C9A3CCF949A6E6B025856C07F801DEB544BD70C46FE86D4824246E56F171A81CDFD44730D55F89821A0BBFA449B9D0FBA039731AE81CF0612CD4316B0BAA743CB7E9DB42708D40C60A95973DC9CED8C5FEBCA232B093D7A5D6342325233187B9B2593288F6ECC0F163315598AE526861DEB3FE0A1DB1BD649D9550BFFFF90AE242DA3E2D67DB09C248C74C60EA83BBD56490E539C5D486EDD707CCF6C310C3E3A713A09183AD749D6FDCA2959AAF0B9FE73C2FF4373EEF3D4085951EB92ED015733D35DA818F48946648140A01951FA411FDE51792036B04A4A4CD3E5166DCAF152346FEEEE3F585F24532582CFE064DBADC33FF102A39BB4BC05247A3C78820B2639CE455866A344B557ED08172184BE286B0F8611236B68A9A97969795A6DF73F55D9D740B21C7D4832972EB79E033BB48F39B4560B0CA338D85577F76F38BD69626CFCF6969D40102291DA41BD67E7BCECA8BEE6D3AD58681630DDF7EEAE5A50F19AA0D1C353F36377D0628E0EB299F0464063AB8B1CB89CF512001949E1E66C68A750871AE0A3A5FA2108AB4E2920A08892D68719E092B48C3043325E5944F62D4098452B4A8CC63CC0792E256448F42C8DF5AB191A13DD00033600D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(QTESLASecurityCategory.PROVABLY_SECURE_I, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-p-III
+    */
+    public void testCatPIIIVector0()
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Arrays.concatenate(Hex.decode("14BE5C398A69D45B0E7A63672036E007981E846337FEE6A01789E7CAFDCD43B59471207B844CCA4240A56CF1DC37941B8669BB2E93B7A84687411425B04ADFDD81011B8DF309F9BC8069B5A0F53292F6F6D1A6B9191CFA8FE8DE932A1403296B3D6492B457849EF361481655174882C2A899482AC9236901E094BE6BE9113E9EB7B18E0018C46CAB21F14502C63553D383BDC53CA1064CE6BC6704EA05161F0C9071428340DD0342F66F0422AAB29950ED8A95363404759A8E08677223A08806814EBC824D40D89F7560175631533BCA8DF81DCED3828EA539BE87328C14183C91A6C24E496227C6F6A98197A8ECE46D5912D99F72D17651F5850901CF4C060B4D1E5C860C12FA479342652B54D5E5F58AC2449C6248483B900E8F7A2654DF3ECD76453450BC4162FD1AA9A36B6468C0E37A18F54A572E9E35C2047513466BE99BCBCAA678E0FBE657488CC110A27393BEC4841A4E7B784DAC3F840DA102438041AEC6BEC4AC261FE118994D38CC1461927D3C08AA4B3A22E2393FC49B5A5C408182FF943C1F59B143BF0F11420C78FC29162F9E2EB81277FD7DD8779F3FCC9FDB0E6065B1C41272709A716DC204C51B47533CE1B3BAE15EB050073E878F70696B92E6C20870E8F3C290CE4812E00C1341D4E36A9ED8BB974EF13A8CECD32F4D36A1E912CBC1A65458454BF58A874245EEEBB41F7FE6D30CAF412010D91F1D455F904459FC9E259C812D584EAAAA200395D23868E838DC7F78C1CFB23BAC26788C10A0DEC2AE358DF79980543B274C096D194D1750CCB74C0DCBE3C30B50B74C8FF33EC1C0C93B65747A01A85D56B17FC2F0CF091E7D98979028FC0151E3E3A3BAE05D0FA5BB9FB9B62F031E8DD9D597E5758DC1632B6040D72B0D65B1C566AB9F846C547C8809E678D86EAE55280F2E365C0B6539D3108763D7D6A3821992B9F0052152D8A4469CFA02B332DA1A41A340452D481660DDD8B2107FF8968113DAE0C4BDAEDB087B9868AC0E3DA8E8B7F54D5FA4EC26C94B18F1919E4E5181079A8C4DD2C2F6FB416D302FC298D943EB43A6B588EF9C9498036A0D59C85FB37FC4EA9C8084DA4C25193330359483EF8D9603B38E9A7107478762557A590D1DC1D07FCEFC9EC969F6811A92B69B184F8E605A489D0872232702464AC8616BF9EBD7ED82657541C3D96C3922B7E2C31A7722CA36B4DDA4CB775071AED78E2829241C9E20096BD64AA69FBB47571BAF11EF42918BC0606486AD5838EA8794B2519A5289C48517AABA1E51217C49718DE8040875D1E1049B0372A2C307E2EF6E43503314540D3C0D054ABFDC85FC8BB4E5B48FF071F0FCFE3BAD96E02108302EFECD21380FE59E1038D7E7D79EBFBC4DEB14CFD1114367EA1E64DD950A84B11E9C1815CA4AD3971EB20752EEF63E2F2A188277A93CA022C1AC3E0CDBE94110240F3D7FA1264385CA9B997BF24C086897D8B23E585D3BFA4F8B611C578F789BE382502A2A14D2CFD899842FC88B300D5E929DCF9388E9F60782DE92F344598901225B94B48C7CDDA04C5141A7296C3046173161A8B902FA9C830FAFD04CA53F62B11403E1D96906ECDDD190C77DDA4350F2FC8A33AF7C5C9BE9DB0C73F726F2730D27A32B85845032A20BFC5454E8C8E3A4501C7A04B5AE82140A65511CF68CBBA3A82F18448B84C0E576BDCC94C2482C08B95E03A66ED9D0206DAB74C32E160A16BCCE9F699AC3E9872BE87774168891C148314C1500B506B6272C87AFA7034899BA202E66DC224A60F824E9FF51C846D651C5FF5215F828093458D60BA1488FE84494A2D26F5BE5A484AE6B57CA2E5502C101A1C4C828A70AC1E7AD3D71CF8E5A4847A56F3D110D18FD501A0A60AF0950518ADD4008540799CC1D1D66DF821D92877B80A6C58780EC34B1C97CBE9ADDF79F923ACD142FBBDF39293A0ECB17F2424C2AEB5714710D2749DBC02351656F54B5268E114B549D1111CE4BA441974AC989AD24B5E1B07C2A74802F1C09014C752A898E0EE9D51485E370644988FC3DC0A531EB6609962AE8C583179833700E944BC002394A5A93F11B69D9F90E5EA56C139873241A8EA824C8ADA018A5A4247D8C46BCE6768FC310C2C4C3E6D3A91158A2F21B37AAE306860C60B890B4EA1F2A3ACBA3BA2D256031E0BC8683A41F454DD5E4929C6201C639A9C6BC26628ABF45B1540BB2F94D0D5D7515B8A553A0CBE3504F76EB5B5A3EA8FC6E115807303199A5ED93E1170F021CD9F48D5E7C260348754E4079926C69D6B911FCB5BA642A68D81477C4631A089037CF95C623C52BB59D697300E0919307E21CF9B0116C009FB38ED08380E2E3C26320A29BC2A01379E202BA115CEB0D61C299F71DD8E153AF3EBDEC1D0187836F6771FB71923CE6E1ED3265492E4728CCD62267B1D49F84200BC094769860C3409F5B07C55CA5A046BA1089CF0685B6BA6804335E8F196FBDCD01174EED8B35338C44CA3B666A5E7620C822E1D14449763FEBBA08840D446987A9308C02F307BF645F00A63888101535C3E256147BFE3A1B93992FA132B95CF3DA5CA76EBC1B1118D4469274628C047C647E76E69A21DA235B837F0EC5DB7A684E28FA38229704E38C012B82F5FC2329B7B8C32643D606C923D4D1CF945361A4E86F637107DD98DDE0767BE55991E42F422D7136CB12989434C2E2D73F9D6089AEAD7B002DA0C823877F102DAF2D827488BC5047F932828EC1598277A68C0A043EE333C25E8BA09B513B71053136C43AC637DE604A801B5D031053957F2AAFABE3B322466393C38B3E79FD3883D4B26952579C7FBF27BA3161BE47EC05C36274C486B28069B85F3A1CCC0729B67D4065CFCAF822314FE481A97410EECB6E629196CA2B4936FDABE460F9323C541220F6B81097DE9F2243598577EE8A931E02A7A4C99019C95A8D981B0DFD84E14FFD36651748B9B39358809587AA66ACE6F700727B16649545E3EF19A88C5840BD1993ABF60DAA5E75F3A8479EEEE400DACE2B9B56A010F5B704A45A9CF67123F3B72E00E3218B08D01EC3E62CA297B95777993D30E7ECEBB84E2C89F3008AABC787E2F463A0E8AD93708B441791389B447999044F2C297F300A09955F7FDE9312696728C4D57C41CD0D6DD171E1DC8ED13C71FEBDCC6E11FAF5661A4762AF1E481D290A99B714A5A9943210F686DDC15F52135DA1C324FE13AB500EF8990A7DA916D36704C0A2770172706F76559377B6B698235A2CA0950603E3664C2B18139E964F8A65EEDF920766960D7AF2C454EA459C8C8A8A0BAC144026EF3E07655E0468FDB0A070D02766ECE413F0CC6B0E8AB6141DCEC4B7176F747CA888BDE34072025FC5903954033E8C2E238C863DF874AE2352E69D6481D3EBA028CC82DAEED636307E25019613B69BFB3C3E789889F12120D6F82E6F147199D426448FB133DA20E2EA35916831652017E7D549BDCF3B2847457021F10F9EECAFE9D8F6B603AAB7C6D66204555BBA86C30F6411720BA1387C7420E39FAE827F454B851B1B6D7FF850338B9F6621F1C00D0160A61B1A5B2C1F416C6B3BF455A4C3E675E5C2CDFCE355C08513A06691497DD1A317DA84C14CC5FCBC8FF1F49DCD820117CD4E11FB545D5503BA4BD4B479A76FC62A30D3202E22643C4151874AD5A4394277246F72BD4F5D09C98112BAE11B037B00125E1BD1E20BA234DD5400A72A63342CDB726EC5AF987CA91330D77F3BD4CEAC2FFAD7579E8112C81758D9140FFD0AFAE0B6BB60CB78F40FDAD784F9085E2134F778FFA2FF71121D9D07E48DBF28864052D851831DF4EB89BD661882C4207E3F5685068D89D0DFD3F73C8072C946125A64B6EBF957C94D4A04409EF9B17FB5A1A7916C4610CBFED3474E563387B69793B3528650FD68C741E260A2CC5D2A229FB00CF4DADC7ABF168EF983F613F2B508A8C465171035D8FF0616BC319BD2B98C88C5728EF92086E4AD316247A199F6A123AFBAC048ACB1E0DAF62A0E17730674F61A801EFC2E6505E9E6029C862C29E79FBEDDEE69F8FCAB716723D6A99B8B091216259185A65C5A9461413EF8616B9102F0BB6FE68E014C62290345C2C89E77511D9746052A188A41AB3E1E141684B38020051EF0E4EE77C979A7B7EE08B91338ED742E30E91250741BE8D1EA6B4B105F428528506B646A027E82711C56D7503049021809C050E2FF3FF1001AEA49FAD40AF255713D431DD7BF6BB62B15B3F22EE53B4FECC7E865B3881A15488C18AC052C1DD90F21637D1E06034649DC8CED834F4A95C511AC19815C86BCE452CE0A9C15BA0B7E98E92280C9F8C7F2260CCE46687B46702F2EC81926B3A4BC9BF7C6E28247B5E248C231B84CCD95131D63DFCD33652D2C1B89025EC954F6C9987412F707846201BA2A07D80AEF7612E2CEA5B473980380736076583D140C71BAFF5C46023D03AFBEDF45132E16D02FA0152A0505DD9E20A4624CFCDB09737E08DD5FD1CF20C1896B7660163B564FCE0CC7B1339D74138550CCED7C7B950CAE55706EDAD6BFCF3C9447AD3D4D2CDE6B952702C0C1E1C3E3272959EC3F82233562959608B8DD7501D1BE022326331AC674D71C01883A216D8801880D14D2934A2BFF3087D3B4E28851C86180F474A4937DCCD901F3BADB1148946EB6FD8E50CAFA588749AA6987B2DB6EC838EA3712D09DF128C6D6810079C2EF26CF4F2082CC539A7B569455AD124E42347195C641B4291C6AE909119DA25E8C99B966B2EB3B009208971C159E4B03DEFD15052E53C398952988548F8D2470371D4142E80D074877E115735135478DA878947939BFB19D4B8410B7AE39B6EC7A7AB040BE4113848B806834DA4DFE8FBCA104D061D884C491DDBE44EF645DF53D89E468884C8692C8F5AFE3CAC08ABE231E721D93F76234604207D1B243FE4C9E69739D49E69ED4E68BB249EA4AE384A811B8005823BB893E8504DB7F0257A13F6DA14DFED71AC59FDB02603D11968F8631F4BC4F0548F51807F22CEB49A932AA154A9CB31318CB2C86413589177E97FB308092EFA08AAF8C4CF0316ABCC8BBB55DA9410721B5B90DFDE4682496CB040240B900A00BDF7D3E19796FF6378333D95D609EADF839E7E9BB05636C5B68DAB394E059844FDBB26DB9013F34FFAA96C88799A3B3CC8F192442A03FEA9F69E5E000D0B2E1EFD05B4D3953C63C0B71E7EED50A97F725542DB6B0D451C64971A9A600CDD5AA8EC6252E2AE8F73C16CADC0786CD970262780642FA8C320896980D596887B76476307A41820A008FBFDA15EE8126027B188092C4AEA08A734558DC1F680D8CE12C25F315421B7CB353C7D5363F06CCCB2806045E2663716188F439A9F654F8736D06BD0F98E1B53F5A5B1444F0CF43F1672DDCCA7AEB76ED2061124B5870C62C4AA66C03EE94DE1615627747D1D930A8DF250E9351FC8CB88E451FEAB0550DC351111F3DFBCA5B58F71A635F68F101F9FC5208084E75C4FD5E7C5664452A9733E481FEC874D17E85DF09FEC044B0ED303930F20600BCE3A9715654A13BC9194822E1008256B8482C6FDDA2E5A342D8CD120638152D48D7F3E25AA7589599B82E4AD175C5C4B09261337EA014AFF273F026F27019E2BC917CFDE737179BF8B8D9F563D4275F9BAA21D17AB16A5B8B93505F6C95BE36CD7277401EBAD6EFA0303678432227E125E6759270593C4903A7C8ABF8F7246E9A070A01C10BAE9168E261C1FCF8D8AE558A3050358F1D389445181A6F1BAE1685B66D1EA41B294CEDED9458325FEC7C0291762DD7CE6219B1D1615082C472CAEB7D616BFE1756622F212AFDAB6A0A597F03A7779ACAA95A4E385E36D4288A72123ED083BD8D9B939F6CA155E29451C81698B8236BAB70127029A258A6B0D6434F02CCB26A846457440990A72EC9EF332851197B6325E2D8499B8C70057F0C8BA6763404EACB43489E2F301B233FD66FE7D47B199A93CC355E1CFC350754D09104938A17F287C88E916AB3E45CE0A44987385E0E56B923BAC0D80EFE37779E570C20470B9B019A60C471D425437F8665B15D3287D02D4E93964778225A1428B579F4C68DA0B3BDE33C2A5014B34D93DA567BF52D11F530E032B0388F8964BBF09AA58FA6B64F4B463E2C3735DA10519312E98D5A55A01A326E6DCBA3F036AEC91206B089E3A1C19785C582263EF808860C79311A10E6E3F77C70FE96A0FE25C85C08A1CD43FF0D9A5447CC7601EBBB429D030AD2E28110258DAF68B9F90C92D4ADEBD0065CC9D1B090F57C93F8C773B913F8C150C6337FB6B32E4216105C91F13188A65E870DC3CCF8EBE18C19865D98645CC4CA866B04E55D8CCB25F6C3AB52DB299BF304D34EBD68C184D552C899DC57A03C783A46E01A8F82AAA35296930A687E6A99250641DC62E9A02D7CA5C96174198753BBE09C1A2A36F09B1A7CA42A787C16D2FB9BF739CCDEFED80CE40BE9EADB69DDEF02B6860A240E4A216A4D50A917D523C9CE43C26B56E4F87B7EAA8960283117D5B396989719476036E7C9DCCB3277192A502177F2FD5A24403C747D70E26B7D824988369517631223EC3B6D7233001020A54C2414C0C4FE3CF37057C59A4B2268197E17D097E24945D95508F23B10794DC904684E33203A6B074A466AFFB3AFA48A830EE6F1AE1DF35C703D5910AC3E71EC4805E402C2743D3BC9D824B02D0760525B5F738196A7E575D921331A61D2F5BD838A3A8A287E2718E538A22D57525AF21854A1B1F0C8044C756618ACAE9B294D5E36D84F720303600C8241CA2C117726CF6E5550DE534892A14115F7920491B9A54A84BE3C2A0E168D3E9ECF1BE3E7E7CFEC206CB22D97273A830514FAE42FF5B2D7FC1589E097217C16D658FFC1C08FA3FB37445B75A3AAEB0CFE91D234037DF9C824EFCB05034C1C2C89949032E90C760D038DDD894205EA12B1E6033F185AE335E0FFA129C19C435490DF53EC30C1B6B7261D1FB89E5427B8D0103BC8D8370ABC42A9B0CDC301D4B9827D27D2FFD361C93976234BC699108DB4B540EF89329CAC0933C1274487C80E4E8F128F54E5EDEC60A102585F408E890B52E590393318359EBCB710586261EDC1E66646B514660D006A2C126CBEFC21EC431D1E99D706BEAC7EC79D06A909D02AD7A694C672F13A16FEB9D51C3F11465A51512F14D23413CD10D7F9D47FAC45B751086E084CB1CBC39E64994C9696B0A3337B9A6E07151E85D91B46AB81E716798604FAEA1830C8EA55082C7F6206FC50ED18FA013934665F21233485597FB9767663E548E60A07ED62AB3A704D7950612032ACF4F811157B292F6FEA408F56CD1EFE1A6D3B8E616EBFE756F5055817F857073E69709B538CD7E863020FD0139FBA6AB3343B88E8A2C25EF716091E41F7B6022F3C06C148F2E819106CA03FA29A6B50E18BD16C347F7C3BBB4A331B98A4DDE7FFD3E6B7A70933844EF37A4F576B14FED215FA4747145CD5008CF90A996CCB7CCABE698566B97A4EF00B605792EF99E260CB9A5D86A0B153E6991A8720457A003003DB44D39EBD567FE9CCF84CCD6C59A765C272A15C683082282D3B103D556FC6160F4126FBC57FED0C183515D5C423229F1805BD2868E3AD8B901455CF8C7A1D2DD6225277C613ABCB6B6F3105C423D502669F729FFED2B857C7979C21CB74403024F3141534294AAF0C5B9C9E4ECD542BF31860009C24E4746D0A3ADB396B91F362F4C3FB5DEB7AEDE2B470487F2EACB3649264AF0C8154A3DC085497EB95DD60D4E6D8F82321BC84945FEE29CC232005E4506127A26FF094F11AB5E63FE9A9F328E817490907FB60DEC2CA22CB435F5562BE22A376A75C0965FF209C7CFEF14F3483D890FE0B659CAD716497C5377EFEE0BC2018581FE10A580A76BE0430A0015F4172DD42E1DB4684ECD7E43C6AB7F18F999591050F46FBC6E92E11A59167423B52D0F820267D604AD671732ABB111CC0AE8C4ED2899B070C7D3DC7D68DFA2154D6906964901414788D29C7F3501B61133E0DA145FD88B0487DA2B9466C988E30BFA9634D35646A81071D3FC599CFB0DFDAAC0D1EC9605BBFE4158E0F63D542728CE577AE346DED77D6C3202808E653E1D4F11461E764DE863E4D2FD87F715630B39C8DD867580572D8C4970AC640022B0CB82897D3779156ED9A10734CCA402EF088DAD77A12242F003453512399794C688E9347CE657424738B335C49D59E7AA2B422B327849484418CE24C45621E6C07D1D5E816D3A6D6A9A65DDB4D1BB04EAA72F37C7B4DB39EA71B99192D7587A5BBF51328412797E1902999C4579903A302F9831EDBEBB3795DD1D09B288CC1A257C4349EAF7E334E9B966C34699B6685BC80DFC1AA1A5DE83306195562AC896840516973777AF437EAADF751714892D84B63517E187731C34A11402204438CF587B8CC19DCF05C826049C0E3304C5A0E3029FC16D2F8B3DA03C2B169A6F6D6A44ED0F35826EBCE85BE7F4DBEBF1302CB042E0AFC3A349FFF98B50C8993FC24131B3DA02B5DC1155DF5FA3CF0B884D9F0892E26B1FD140BA03C4F877BB9C58A99FEB3322F26D5272CA6D000A83BADFE9585385078CAC90E7301F2AFB393147327793BD0A4DC2578846A4E7D69C0F5419D272F1F022DAAE4CF662E9CF59B972AE8A20FA21845A3A5009C23402EA65C2E3DE16209260909A6CAC11FC5A05F61EB20D2C888E3091E0044B80FEB0E3B96292E6AD661FA9B6F7ED9915538D42D44F182CF103956C59C3C6140606DC18F93BE449DC1E2F20986BE14D67EE1BD0003BB18181CB441D070D74EB4E26F9BAC32E64C1EEA97A26C88BD264FDF1A12EC31FD0553E66478425E453B82E0C13DA9DEDA7328C28C278765D1D3B022B2B5B3FB7CEE42C254811E85105D39ACE8E2B64F0BCCEF03571039B7FD9CC2886CDD057EEB9C8C5A47122FBC9B192843B861433113DCAED72885B1F8644A68D21444E59A4ED3E474B1753B4CE034E9E6DBDB4F41CA00BF45A8D1F0B229642082F87D266EA02A972C0D314276236CA5F2013E15092DE373C6F144BE76002843C2C0EE081EBA4A4C59A20FE826F4BD0FCA63C9C06B3DFAB222221D1BD4CBB603AEA2DBFBD6BC722CF479709788D3F518A6D0B542772208E0478F19D00276057D4D7A3C7C391D10C715333011096D1CB7A3646AE5168E3085FC987A0729FCE0D35808667C4F76C7FCB2410BDD80126E56A3C5C72522489D2593F95A862BD5803A0CB0A4843AFFF2400825D41F421B5E7E29D68737273A5645C2142CDA30AD09F0ACB3B720B9C28732C9291442F9502861197CABE46753797A57ED183983F4D940C80043702EB0068F74C514A3A9DEE904EC3854E91E6C33C5BE6770F57ED0540AED19DE64D3304B53EF5F0120E96A4DB66CB4FFC9B3FA79B8D29966AA2CD4522E84DB0DA115EEB79C1457341729C4E91768E92F0FDB0B7E2DC0725E6A98409A73C6400BA4180B4F00C8701090D34017B3C8CB3A6308BB44E843416FD6ECE1773FFDFCA953835945B67BFCCD563A47044F238F00E1CC82095B649BE6176F7A67F5FE8032EB8F155428C7C6635EEDE69176847AB40D2E60BAEB3018395A60BC3AE9AE32B06153A8AAB22FC2C944D8887E0B8A6BE6E3DD39CCE2E0C85C38B8EFD0A08B06F941A86E17800A88CC036021F9D023014C80DC9CA772079A2CB23EAC1F8D78B284D15CEDE7E5367546B1D71CC0332F09ED4BE7C9F0E62F5A80FF462D477AC30D282269A433E36508C6495BEBE3115ED517A24270F048216508D8C20887CF524AC40980E3766E028D553EB34AB7DEC4CB96A940DD15218AA17B8444B4934DE09F0C87D89923B7227F5AEBAE3AA6146B3BFA1A790B3C98CB8F8F711D349E5A524EA9DC5128862AB31F7AF61BE71CE9893044B0770877258685260D5E0AF6B69FF034212B72134B17F4BF2948BD12BB087E82416595A4E92AEFDC505956A449239927BA368220336CE8CE8928A1B997BEB69A42817C44C13CA0DE4FF65FCD100197338877DCA198D7C4DF1484AEECB4107833407751904A83BF8F39269A8EDC405E285E2854B84DA2B200371E126CEDA464B0600EBC9A89BA0A36EAA063F5F442440E669A48A76CC5F9A63105D06FB801B3968CB3BCF5C4C7263088A1712E1777870C5A3240A85500E3ED0B6BA588096C082D07049053DBC16DAAE9828F1B0576E5B003927AF8FD04CA0E38E7BD50FF1CFDB59E794F46520AE42270189176750DE56BC996AF0D47581A18CDFB469717E8861BE49807F38AE1ACCE1036B695D8F67AD2C7963951646EA6D792723894B70ED65368141A4C09CEC5A43812359E0ED2AF1FCB2198FB0C129D2E60EB7F3384460BF79A0F94FA20C97D602008D1408100D37B1563437DB470037C57E2E21D0C011A618E350C203CF459A3F8FE514236183A200BC90658AFEE37F0B94AB329A00361827B7C08D2F2A3B7153C9568426DC1D44D1E07EBF2BC07BCEFC31B5519690C4FD6758DC0AC5FE0E5CFBAF3641267503E0DFB689C4463567996A285FADB4D58F437A66455D5D38A648083FE6E5E0568A03274306D5BC413F1AF7DAAB5C283A22F0AE3498211EE4AD2680A3DE270F89F9F40A53329F91A6E0947244C6181C155A4E2E74C770A684B3B3F8C6E456F2E43F4EB2D0D3C5B07390D9E011E8D2D20E0842A84912185298022B627E87D7D04154960486150C9A721A750A035F601C701C7984296B71ED3680FEE28E5A47D4A78AF3142D4E034D26C431377C81D2201B87D52E57075A1D908A2E0200F9D16B602300A2316A19A1F9996A441D46BF4D1701335F742A8785CE099A948E1A8A12B867F8358BDB7FE440DCCC234A1A81C23D6CAD1D8129D815CD4B4E6073BAC8A78F6CCBE8CD0ABE28D65F309103DEB98F3FBAE877AFBB549D44E2399B8A07C4544985D9C88EA99C679F6177091ED64C374CBB54940771A59E4FB36D241F0764756F29428600961A0FB52E1E6B86012E74654B04337616067BA23B8BBB76C922C7B224614179F3199C007A7FCF534051B40A25DB8B20921C8DF46993D1D0BCD2D175DDB5F04905E9EA93C175754821DD2E07A38F06072A43ADA69A441EFE77AAC95327B869570970E8260F36401C4C7401077ACF0C2F7D3BC23B846BCE0D61F50911CDCFE1A11707565E4D52BCA7B78C61D96296D29B30E483E6C1EEA2534A783439E2D23740730092B170215427A78E079453565EFFA6EBD79EF6D03A39ABA3DC4C6165FA01DCC81E354FB986A09E543488C61F746F6BD7449666DA0D2A267C70C22B52E79E8A5270045EBDBC36A06E6F7AEB85635E91998F2DEC2D8F9FCDD5C955EE34CF4D4E039D59B9171AF084A7E1537C42C8A82B6FEBF3A528165DC465259E1CF7668F33A8D3E03927983AC29B1D052D4E12918A9FFB56C803A10ACDA79F00EE70967776D285CB7A1CC4D99F96B54B2B33429EAE0BF84FC914C182FFE66796DE6F06320B68B398DD92D757B80B4292141D6ABAD1A62394E0E5B71DE09588778741559E37A4FFC2780FCA13284C4BA15E0CA6CBBE2925C448D9D0C8C07699F30CDECC811707B4314C94CC6C2EB1A80346CB4338906CC013B532249260119E518227EE8149A7EB04C349CF4BADA1FE48D38B33B52648B59DB2A85F88C46B61BC37A3245C2B87566231A5AE26D645124993E8CB3FE1604C8CFE35737A654A248DAA21A20760DA210FFCC343B316FEA913658ADD411AE53EA837DA8B2E79DC92CAC6B4986543F5255206B807B9FBE07B56B3E211E337D71EBC1EF53A5C5E51EF044384995C87E1DEEB1DB610EAAF1A1EA866BC8B2E9560C2CCF522333E699E91C92CB4C9A615B967D2D4E992B1A8170428151AD7847849D713B41AF5D44802BECFA50F7DBCA626C5F60C5E9BAAAC67109570175944384E65AA6B624A73034F1C4459624A3D826E97848A0339417AC185B4DF9144E9EF9B691080E20B9BE8452D4633D106E65146EA35F7B12A9105A986FB8B68E31B301F8DB3ABB804244DC01C76E52C4C6972DFACDA999D48AF3561E849B10F00D06916172F27A490BFEF1E8D0FF3CB21F013409A7C1F2CE475B62CC5A86E527BBF369FBCF605BA881401DF460E0EDE1347FB49BBCCD01A0E12D7D97C771F30768E8AEDA387E3AE6E01615EB00F34734AC6DD320C8D97DDBC8F71D64438213D17147B9A0E188BF1C09D1686809F383384BB7101DCA45137C07632D5BF488013EA2D7FBEC4B2538B491081F90C47DA522D919EBCB87D83D941D06832A51C3D51F3439ABD64DB94EF540B86F627AFAB8779E0A52DA8ADC5285CD427965E223F2ECFBEEF8E41540381DA43D8A754B68F78D644A020BBFD295836E854B785CBC0B781CEA596D12ED21B0423368DF51A4252B0E2C4E73BA50841E36B8712A31166C935A4E41F6BBDDC2FD215496F1778C57C233C45B721069A5316D152F98689AE2A9383C9E7453B463F22E21BC78940944D9DF3301136FD829EA11E33A17B22505A2E0A34833E88B28650F67332D244E99587D8A528F891B1AA55A0A54FBE434E8815BB466D0CD0E2663892A0571775D1E513785CDC9938913309B15088C4D90A94C670CA65264791909EA3ACD5EEB929012DF6961400A53E0893EDBE432B0C595FB9CDC288523E70271AF36696A1D2E78DB6D7D3887A17F7C02D277185E7DA1521B7A45BA29AFB235048A403D86DAA6F9289F1612C440263A239254C219E695C402C56011D8AA75C163493B9BD37264A7F452208422988220A1D4A075AEF58E86A778149CB17D094BBDD10A61F94362295EB0D543411DCC6E5FDEED7F400CDD5EBC1BF8F7058B9FDFA71200157A10051D7E1753F92C14C0AC15198D82D7CAAC166B684F719E5F99730CC881A62AAE7351D0DA0C0052EE2D4BCD6D1AE038D0EF496F9F453DE414DBD5207EC64B5591291373D401E13A6AA04C4FF71D46E6657A6E888B9F480B340B160477CE7637816174BB060AF9039409DCC68950C74143080CA9B3BCCECE0DF5FD9A2F42FFD515C82606B324C997F0DB44653A83084A7C4EDB0A2E9D7C8630F5ECEE75313CC5FFB722A9F4FC45FCBEF0CA06384C22C1BD468A5CE5469369A50882254F79A63600B6D17360A318BD243050255A1B05921192D6A69211C26C68AFBEB94FB8C9CF9BE2068A5B0C9507BD2EFF6285473FC6A81AB96002E75792270E39EBC1C0258A63863E7CC9E58D8BF24D1E45B76E2C311F0F7420AB3156C4E7D5326045D861C3C2A25E344E41E11C61254462436A2C1D352990A3DA220E41B23017ABD92FD0C414C08C7C0F9754476D771EF6D542D09A61D025C34540BB18F143E80FCD41EA4A2B21E626E11AC84D1DFFCFA69DF901711FE916EEC4F8F0D131B7762F037BCCCB5DD818CF3292A17EBB0A83B02E8BE29827CB97C68025B6E36767762A854A334D07D46684A530332240BE0BA2B44F48FBDE026D1FD33D22650A2DA2331CAF89E4604A6ED8E262E31E23955D6660DDD5C028E841599E56210008F93D18EA746A8B0964037958B3F4CAB95CAE5201F664A68D2FBBFF221000AAFA138D8758FADD641ADD4931C261E218C7947BD7E79E7365E056BEF562A66299DFD2217C68F22568E8E64BBC228601E8D9915EEE3272E31DDB00F156539CC2C8964600608EC3D87CA3248EBB2CB5AE59C05C1294C22E5E2A22282BD7BC71AAEC0DE44D9FBF542A5B15429A95B6A1FA6EC76E171449EB682795F89D326227B05201652B89162C3035A368BF32DBEC955CD854DEED3E74B313ACA18508EBF16C8562A9E6CA624E1134589C3849B2E35AE1C569E736D7B6F11FF136AA6E61534D6EB96866139E7967102A095D4D9FA0B89817A156DAE43671818759A159BA1D3D894A7C9640091BF18B15B3419B0410A00ECBF11E70015AF7A3E0284547091BCFD92275FB2878FF78ABA5952741FE5EF76DCB5986B403945E9BBE3D681415F6FB106B684A88BBBE0E8311C1DBE95236846427F20583CE4F50C1E40750E074036EA7883184A4B1BF1C480141A082F62C112771B3C33517F2CD20B5E869F1D996BD7F62013F29BF8FD8C6A0324E1ED064D9B207D6A8434B104F4335806092B80EDF84D4A39B4173183A86317B77B0D1D13927251185A91896CC313775D466CCDF52250A6D36EDFB97419225F544BF0F0F179FD0E0EAACBF5DD87B76A31603BE31627EB12EF466EACAD5B3BDE7385F6AE1C4390D6248EB03A689DF78D4D63B53BCEE6117139ADF8AE3AD2249FD181F6554003999BC98F571B0B39930BAD526C1C0F1AF3F358B4F1228FF03FA69A8262C4AEA024B3E4984FC3DF5CA639410A70947216B0A82476E8BF4ADF0A52F18EBC0F79000B0854479349D5ADF2276C3FFDDB86B907D9A3E6F202CAE8178E20B656EA1B5FD4B686D6D4F9A5F106341A0CFB85FBFF11C1F285CE0C9C7C33322B28103406469689CA2AF8CF68D1F6226C08EA72F6813BD8D8A8B3FEED6D1CEE51CE8807781A4CA7125B8857A520FD126B663081642EA95E8CA9D76D1D2052CB60AB54C6CB9DA1C0AC094A46E1ECABD8AC28B4C93E76DFDA534975F36529BCB1B1827AEEC5F55C82CA9645AD172298FC2C1BFC38AA1066B2EE6AB32132058F34C5F17F66D0CE4863FE636EA2EF032C9B77696E37F1261C80E3AC7C7633393C68B29E809000A2C266A51FCF085B24DC4F55ED477A8878904D080E55471AB3CFA53E5730EE3AE85E52EEC2235BDA6267A18684FC1309F11C5E60FFCCD739107A649F2D760A4496E289CE4BE12A5AF1C16F53B4F88388447E13EE2B789E31F1DE44572A01A187F0D8A1D9FB3F4EB2A0938CFEEED42618AB5FC161AF9A89E54C476D24E30D6046864A3BE2829BDE851DDC11CCD255D5D961F3DDD1B8887D7D5642D2C787E9343B494A691E9474E86BA7D14A1B77DEBD1F6871960F48C42691789E718D69A58C94503ADE097B7C35AD8A44B1ECC51F60D4BC5FB08D5955C83B34FB8D90A2E2306791F7EE9FE820F204005746A79F6B34F33BB0D01AF35EDAB77BC3CAB56B3612F34574CCD94A9AFAFC6F12EFFD2B4BB5C3A540A052ABAFB5A58EB4A061BB575345A8546E1EB2E94AEA8569883A674206C6E3F370400D40E6A2582785FC2A62865FB96CC9F5588452D6210B0286F3C5B80DD613D8EBB934A53910F90AC21091B340243CBA168E42C481BC0E8A72414C9DE279581AFF1CE44BA05E7C04A8F0EBC9B1AC4539D2FBE519186173B0B8CAA9CFB39E75A9C3E172961CBD68D2ACAA37FD9FB238947CF34E220C06BCA10EABA0718B4EA77AC24E94985E7941A787108134F2AEB8596482671540E3C03A51F68F0CA1757E64F2F8159B285061C9BA19CF22B6D0C6E455D146824F3100B69E1914E4448200C330C05930D1F5457A34CE4EB08D0BCD5F1445455E259337E2E30920342009AF94E63B6ED85D399524EF3149C10F5C5A086F9FC26F2A9B273FC5915E73CF4A628BFD4513C7983024410EFF8A30C3380F2E50AAC7821F11DE55F9A3B2C81BD0F2CBBAD36E2B9C45FB28260CB8663A953C4E766918F1AFF791DBFA682E2786BF33F4CB170922E43200274A45D57E4F1FE8A2FC2ACD5591C903D4E0C8874700984F0B8D196568133E60BA5A3C4B1A4C688CF7184ED4E9C078E2B14A576842933F12B5779D606ECC64FCE28262AF818E70DD813CB32D9CFECAE0E4E62696E5C300459D1D71B3F39500097EA25A81FD840CB6BE80EB5AA72C8F04164221E77A12DDFCB980489E73D82D6F1C2024FD39AA14FF870E4CD3C8F2CEF2D1DB745AAC14E80B1DF21D7640872706511A39F300BDE1C215C3C4C040E00F579E852E3AD6D79A7CEDD8DF5C9F11FC0C459A3CDD0206EF21459767D98B558783F9880932FAB544B89BC844EABEE7C94308DE1334DCA8A2964812B76EFF74C44172B0665AD790CB8EF74A8A080E9C728BE030115CB39ACE40E44FEC1A95025C46937C7BB67013E4FD84B569EDEB29BF4028C7685C181229E5ADC18E72C2AF605043B42425CBB0A546E92B4F347115DDBF28BEDA859D46550B4F8531C873F960DA53B590AC354FAC61B124EA0880E4D728EA1DC61A7360DDB38EA0D08AC7A0063FF988065A66A709E03110812F8040B713AF9656D4C5B34460C2181772B1D7A57E188DD40ED6598E741B14B70B9EB14B6160DDF3C7B00A57B5B092405A4BC98C99EF51D22D06CAED110C7E8F09329793CCCC913CECA91E73EB605C08066D5E05FC5692B291BCB3B700C6310F2374DC515125E6B884D5167CD87F1A244C37CCF7BFB8F4BA23A516284B396EAB7E533E19F77FFF8C62684906637BB770B2001E786B2001CE07241A3A079319AAFB635120A7C029D3C70109B6DB7F9AB61E00DA79EE8EF06EF056F3628317E571C254F99078E09305854D6721E406926DA57994BB0FA93EB23D00D0EB1860FD33550DD43FC7F967850621691B313C799B1C196CDF53D5A65EF497133F589C3C96322843EE1404EFDF86609B764B9DD34064F6D2EECAE7697E75B004BD11079577D614A89B16E1AD58E010672BF8C2BC227AD9F1FC3F2B05243F0DB236F08804A42F63F663404772F85FC695591E69AA6019CD6B31024CC1C4264391E0C9D08EC4E896B59F6FA181FF6F477911E4C10EC6353C7131744E09842B0B9401DD2C5B89655339303A79C425C8C13547B9A29441119F682801DD11FA26AFDA02858A6F6421472C3200894060C9D671C4B7034A149E8C873E79B0800CE347B83E93EB5A42CF433488B16416819CBEBA38919B80A91968FCD32A00859D50D96F8F63651444BF0600E368C6B1870BAFFC845D8E4E59729A60995FF74805CE47D7A1EA847C174C1C130F20B18290C9C4CE8764D66DABF4FFA83D2F979ED313474B5934D22125E847147F65A76D106CE89225AD2975582CBC4C1DB1108EC59964CE839560FB924D7722D5F177AD6C37A3F116520127C88905F2994F70DB17A5EFF068F45CC3362F467191FFE4A42129A6404FDF61C73278DF985388FA20265B01EC5890405CFE6AED7748FB68226FB6247B3E989028DA71690B1F7211EAD7C1A5B5EE2915121D145CF2B3F2132D49547FAF752A157434C3DB8F69386B55F4354743C093EB7B996C4EC3B4E046158D7C937321178864499FFF8C890C286239E51F480DA10F0DD0F38CA84D4DFBCE2394F981AF92C93ECF3AAEE468E0E4D3A0EA1A33DF4EB95112A9482FF323204A9C230D9DAE43E18A6D0F290FC174E93EBCA3D3DE2CDC14DE43001106CC04783BEF86A45BD596C7138686E4C09233B78E19F7193F3135F49BC89E43386F2C1DB34215FB6918C9DDA989A760101BBD2E010DFBD4AC20FE04F676F46CF4255236F52108812D97D3CC64AC48B4DEB7BE327F61AAA4D52C6AC2BCCFB2ED8A894F7523C7F2F84887D0A310C9A57824831B6E09A31037F093081E97A4401FA23221B415F7B2853832A861AC5692B2921BD4A829D8C46176CB0D9AFA4E2A613B6ABEF0C421562D74AB6B7EC530C4CAF36F4AC70D4A6BC48C998D830E6AA167AA9E92A8975968FADEE4A8431556FEFA5CD8216005A225605B24DC095568166BB728401AD5418C062F81F83B6EEA6AD9DA335DC07B84CAA1C75525108640A1CF2A27DE5DCACC1741E7A627AD640A18B8783CE7E371C23968A8205AC7EFC4A6844DFED2A8FE126266A41E67494887EABAF60C3FC744C2FCFE09239FCFE91A6C034CC1FA5240858BB0174736387BE896EDFC6606FA39ADA03E886E5F660F9BA3688C07ECEB02D0944910DEB1B5ED6C65699E8DF4C973F2E0131D28AC2B9DCA39D10A7DB6050FC4C2111C41EF1967382EEFAE507E88553E2DB6AB50358600AD0FE8A0A4B00C81117CEFA33F93AC918DB4F758BD8B59F05265CE533B9FC67316C739922DB96783745F531A663DF80C703F5CE7C34AD57165583E219693291058AFD0C1EBC180E89F7A47E5B15CE41E1A70D6D43D2F6B60DD7605EB50C7E3AFF67CA9BD348D9790291F5C4D30D0DA8DE07FE303C576A5B39A39E55C615F83348DACA47264CDF4136037569D2CF0484E52C461AC8D22E7F91250B9B306ACE71DED5637263955538D04AC316123807C9286ABBD14538223CF2A446ED8110615280F015EB0330C3E9AA77DD7C11938F434CE346943E06913AE4CE6CE112B4F6DD95124F4233BD27E897C05C441A6F36DB3B565212FC1A8E9771C968E826CC1B9E6AA4AF5F3ACCBF6518B38FF4026FD247158505CC109F60CC4D4DFEDB0E9B885309B5DB3F48CA00128EEB4CCBB8AFA45758CAE49122DC109DFB171F4A85D40460BCFCBA5A9487196420DC858EDA59E30C52F2883A5DA570099CF4C225DA6500D869AE955BE49FA22C62AE7C710974F1236F59AC43222114F9AD6E6E9945309FEC819D23E146D8F643C61BA50291C6701920DE883970502641CED2F342D13E059A5061F1DD117961AA5ADBB190AD4BD5781D2AF9992CF3A09E52FD2E867FB180665C0A059637328B9FE2F4560216034104E1BB00E5C82C46B274291C0EA50320214BC228398C27AD8F8C82D746083A04F934E4DC2C0B7070710803C1F9B9084CA91ACDF2DC0A34B53EF39143E88835A104487CEAF380A7C00DACE48A3728D2048438FD3EC255D68A566DA4180490A3CCD5788603CDD559606EC5EFB8861CC1CDC5BA49301BD5163498B37DA0CFD8A8E585380F7237A319AD7F1EA1C32BD49C561451A17EDFA47485BE0BE077725C9560A0C262A8B534A9716483123BA790CDD9094BEFCEAEB7C093E571198796C4A21517523B2D091E2FCF42AD241ACA64D980CA44FDA91098E0F688E3A081706D43D460CE9403236AED42BB55DF1D5672DE8871CCE42464920E98D316F7F4FDBF930A1F0D828127B1931965B499257A8E8533C6CA30CCF312EC468E0A70004A45B073A163A3B943159552D6DEC090C822BC8093A467C7831818D6FB5CACA20347AF117E79F555348EAA278F1493AD684C973AE1B5B1DD1BEB92A771890E43E078F08300BE9B3394B18B1CAA976A034830C43679C3D831C416DF4C3334A9EA6253630131451AC729609BB45AF9AD6FF27E570EA438E33787F29040AFD12A7B1C770FC39A9274B3605419B4A641FB61AD1442B223C8A7C58980BAF33864C87B1C41E25200D880B4D26CAB5BCB3B5460A6940A222986672CF947B473CAF496386F510EBC49532C8993F2856DF67FF8205E3C0F9104D997906D24CEC47FF9339760C4AD1191D192F145188E4683F8B41C7E7A43BB118B44384622F2281C6C0CF74B849A673357A768D524C0D5C57681230B8B681FEB2C027DCCDA83ED450E7B6726436BE85BB645B16FD8DE7D67FE20ECC380AC5459F878AA0E8D418527882EA23F6431941367CF8BA6F9E763CFFDD811BFC4F7C5D4FE2CC8F7E31A172B1503F0B1DFC4271CEB480983E6B1E42F36539D1E6AFCF646890AC4924063565EAA55ACFFD9ED231A68BC0CC1E198C05C4B3A98E8612DA8740E09F92D488EC9201D4EE6DB698437A8AD8D393645AC72B20270FCE1C139984E4D790387BC7AB8F8960ACD6A6A79D07102DEE1488124CB28B382AC997FCACE34785E95768BA9273D7B8F4A1BFD64B76049464656D7C3467CA9441F99AF5E8DAA0B0DD66F2C8EB2BE46FDA405EC84C9B013F7063C38920083B1B54A183A313B3DD1AFB62DA777F180EDED072209104C0238C12FCB0A0FCB45CC7179FCC7B9516FC787BD0A8B235BA225C7EB8DF92BD14D2E1880AFD08C68FA9E43462F2E8465E8299C25F0ED4C9457E0C42BEA4F13465A0922A8C3BF445CA8DEBC442F6FF8142AC2D2F192152F97B0E79C44A4A42B57C23152693A31EF631286FAA44E51729A28D63784CC425DD30B5FA97781D575FA125C16DB0CAAC298C6E9DC57F38DF5BE47C26102D326A64CAB4C0932F9B909A3C3EF473CBEA1FAEA6BF97DC7EC0F1AAB05F855E8DC4CADD0B9473416BA30D0D52CBA5D45778DEC65172CC0782FA328FE58007529EAA28AD270F69117C062CE48636DD7F8B56012E626033FC0928BD4E016E5905D22E34ED7B0FB5622B1B0889F1033F3939A18B15F79FFF981A8AC831AB36C70685A641F759B6663B1F1E7A25119B6BFA2696B5109D0F82E2C685F51148CF94D6C993BE07872BC40E0538C74F7C0DC9C32CF6F9D887DBFF48168DF7EEC618484894850734BE482D7AE8AC92E5989943489523E1AAF9A8055D1509CDB3409EC497A472E51DCC8417818768A8CB2C2AB08334FB2BD14A8AB091BA153AFE1EDD671C14ADC068A31980D1D66097C2AFF335C68097EAF985892CBB52B9CE86C9A09A0D839F2904BD9FECCB2C7AF44041634B014CE0013520E84E63EEDA9BACD8D562031B9696C285CB5E9B644263F1A0F5553B0BEBAF708577ED5ADCA02C235B1E1D1168249D14AB0C9744DEAF03A4E15880837C13BB9D5344EC101434835A21AB034529FC496A4838FCCDE9D48501DBD9A3BC1868B80207EDEE6B67A883324A243B9314480C1F5F9096A5B147B87C4672914C2FF585DE510A194F730B33308BDB063DCD924FD4207083FF23C19739332632F899E0818F68960712EC0B41DDD220706F9A16B0757E6B6C759BCF54A9F42A3C212AEB60FA3881E6D26CCD92912F2F877C5FACE9640A6EF1DF1BB453CA111965B75E479CEA035D963EC59062B871834F8448593AA5B0951BFC927B495CFA796F11079C0047F3DE6823DA6E2C01414E0B55C3E949734678E6116A91F0C9B181251D18B02F515D07AAF37709A6308AEC77829D0411020C3BC10278499E947F8EC8EEA659CF1A5618208B01C2DE8ACF88C6AE4F45669FF6A541CE73132D8F87DA60A4C99AC33189CB9532E77A0B0027D11F2C3D709F972FB82E552F1C70B1CDD683BE63746DBDE3E82E27A91940FA80528CFE358741896361D783D92B110936397085CE25198E4C0EDDAEF80DD80FBC2B4D45F77C763BEFADB2FDD37E6258C2ADDB563D716F8CAE3D7352C863AC96AF178642E3C0115FC515D8397212BC4ACA838A01ED3448AB7524A611937437A73863B8407DE0C925D291F227C3ECD98169FE7A7A00A89A8477B083E0BA548B5D81AF84EF7A0F58E571C305D26A0D1A9B175AA9A0BC00C6DEA795A42E3046BDF4B26C89B3BA4402E983B6FACE5B0067460EA8E12494EAE0A34C5642902695815AC8DA4A5DD7D4C023CE3F32231B6B743CA9DFFB34E5814B99350A5028C0E012754FFA7F8EC955201091C41268E88860A26075138CFB8398F540746ECB74FB8F45233A7BA9398B36B372C7B5E4C60D5006B21A0BEF8D443C66CE4398F31A2150FA53ACD8B91E2A5B1A71C3108B849F4EBA1B2018DCCB7A041CAD0492A5B2B7D31509C411FA6D424CC248B80412423EDB3BE697C016D42C2500CAC819A4003B2E588020EF0EF8E8F7B0A432060881C81055FA2F573F1DD0A3030327869F704FC1795C6B9B4F9038AAF1771A0F83962D6BF12340B8DB0E49B8086A6051BB1EE112C0534229043ABCA1262877BC858AD98936D71D1004EB193244201DB0D014947141D0C42A1696283B02E5D23722696B1D092220874543405356BAC809B34E50EF1614202F48A3F78A82D8A885792CAA4B77F9CF09BF3715D0A25C1C604CC5266BBB1405EFDBEB4F21D80E9F2716B652763A2E87E854E174BEBC2D52C610B8B707D8419E01211440B86A9F2625E6C0FEB16D2ECF45843867132BDA4C3433F8BD6269FAAEFD21E5B101472700FA6DBB5D28E9B0F7936BDD220C036F04159DD3888C4BC8F017FF020183B8DB45822F3303E3DE3188DC6351D6BAF425AD15D47CC3095ACC6B747015A821AAF5DD10407DEB3337CCB403C0E94133994E989F785A642CB90794E91B8F36BC520FD74BC727582649AA698BE7F640982F17FE1C64CF07E51D1E307B16C8765D9596DBEFE0A1E3EC39234BADFA6D58214A91FA01900DE41BA8E22BB49456E61700D0A38D97C4E7C4A23A574866D481A9C606FC83A2D117932B73B2B541A1527DB1DDCD52B6ABEB81A8BC52C4D9CAC3081804264CCF66F775E0258510DC4045CB802CC8A111FD18EBDC79F661B929E33E347AD48562FA5579BB35D694C759EE500645842718DC91A2B9FEB4D72F4624BDB4D5EB5E02529F3E03F51917503FE008B585D03DB4BC1BF29F6590DC37DA1F445A282E44A3C3836708E3014D5516852E1276A2815895639D31DC3F95C22969FCCD643182F83EA2607BDA84B2E84252B7004839C1C2B5AF1803319A2CF87458726E748AB78B35D12C15FBC0BC84ECF20EB85098508307210AF120B4E1B4ECA176FC605FC13FDD73486D00A9FC6996DCDE6139C10DDF4DC868E79215D588B35100D03044AF5838538B093A6E78B5070EFEA4038397708AEB21DE1B81872947196E24B9280B4387D1F03DE22E21309C58C3E108A4E5E4D408DE170494304325388721724E8C5251C233869F80F6E7A9692720DF0C3C5C8B8C21D5CD177E9F47F722D96819C98785B1CAFB87EA6B0269F6B3641209523E643CD045F31A980DD2323BADA84E14FE9289F48617B95D0ACBAE4C6083EC9F5C092013D32658DD482039ACE8CBE64557C9BC742D29E0531F6FAC091738772836DB264852FDF89788513F2F5D6378D5CFAA62BA68F7A119981BE1C014886C0644DD2F28E469E3395DCAE1D4563122A4D2B2B0ED14A35C66873970A1DEEEFECA97BE750C08C514D05520DD7C89F5C588068FBF21229A6F86ADBC0AC79272E102C4055EEF9590CA0FC907D44C2466F3E075DED383772A1E894C244E2AEAED44766B9C910EBB858B169C8C8B70621156D0EC12833566E0C8E7441FA68918D6C91A0471A0C8D6E99299DDC6CE636ED2A0698898AC65DE14C04D505EE658FCCB3AD4346B1CEEED208940EF0EE2F3A1719BD6F0C8DF1F91756C47FA5843CA3351DD12509670605CE2394EDC2A19071C89CE827991DDEC83631422487B3AFA34876F1C0424B34753CEC2E82B76D02CF73828014DA48885CD6098707728C75B1C3FE23EB47633CB724C098D937176C07FBAF6C4909945BDDD62D068D70D54B2DE5C2474923042C7B4C32C8FC63A2BA2D41453154"), Hex.decode("CA09DF7F89B9B339C8D506B4E0E73FF145F5F87FE26354532E7EED21C6FE3355A737BFA0264CF661B9CE14128695D598254BAF3EC5C44273B375B70FE7E6457358C2459C1C19D589DFCD9895498E28A94D7BCEB9EB4C1CD113012AA3719165B7CD0507DCEB55F284CE537E82F60C0F83834833D02B6D825DA2AF1BCC6007B13F4F3666F02248A83843DE6ABAC5CC4A68608C261AE7CF5D8809956C9EA249AB005D0571D7399F7E51E5C730A15AB556E87C08067EC122998E0B4EA0C7D51B681FC82A8640333401C37179007B3DF4648D221CF2601F0B4223619F7474682A2607AF01FC5E6D40BD78E10982EF070C16706093A2B408583277D42D76DCE0D2607695A48254E8533E48DDBEFA39AA25060FF3BDB9B234964DC82B662DEA346505375A42A1405A78F294D4402ECA92C725F243E2B9D0C2974C05D45BCA30B97072CED73B30837F13EC7FC0F80000D26DD73D5E90F707C60157A7CBEC0749DA50CA136D517D1445FCFE770711F172854881BA302EC27A960F2CAB2BDE5B0631D2CA040CCB2850E85A868E0217384A46F266141EB354413F08D1EECD054632846123B83C9C15A03D83D9446A6127273F943538D60B8D7E98C9A9703CA87605F3BAA03D8BE3049F05394059DE0141541F3F08C298D05B0C3030CC6984077D5CDF9AE9D42DB1FB342CBEBC411A53A51438984B3B4CE07B272FB6D2FF5680E0BB6325CB137A3F26A6C86E5715B3C3E41A84DE438A94754DB0A19A1D18CF587680FFA2C435454C148040DE321E13E31929B348810F95852E0B6855F8E31E227C71C1062C8C61EC404A1F4F2CF73EBF86E92050368794E2388F5C58D171251BE631CCE534FF0437675B630A2473AA239F1ECA4E641369E9C67382D41A5111BDE590202A2336CA8FDB8132767C0D59E3081586C665802899275448463FF284AC49205500ADE4E93F7040725399509BB692C8EA04A092311030594C51730517D3C96D79EF3014AC404595D70E764F83D5B871C8678C0819239D3D8D63B1E29515E4AC731CCD93421EE6FC3AE78F520F40DBEB6B74ABFBD4355923B9F66E2ECDC39832420D7C575B89C82F3679EED6460D6249AB4CE037E8C835F18406ACF8CAB6DA80333B1B8201E0AAD1B3C8DFD828D1CF50B60FA5672F7091ABA26A21996B27A0ADB819F1B03785C83D004F8E000D8FA81956A378DBBC03154F766D536B1EFE75BB1C3BE660209E5D1E4ED205DE0B856138B57451DFEB1592ABC9625D8352738D50A1D1BA0ECE4CB4854CC187C7F35467879294D66B95F022C1ACCE296CF76D780770EC9CEEC099111BEC60E6BCB031D45318156BD59AD2F05A0264282C167326E07ABBD40D924D8A3C314D586B5F4423C081EA3EB8F9D4F85240106C55074BF1B38D335FDAC2BBEDFC6F61F02DD6F5472DDAB4CB5F84C09E804C35E35AC9637428D7BBFC889D32E78E2E0174F792D66C2940FFF090108D26E2FAAE166A92538B761C1C8FBDF4882102C2F607ADF275C20943D16527BD748A83B04BB8FD5018C5C5DF0A985644CEEBE3C200741EE8A39BD6247C662D7C1A24B500F08BF11E8E460203BCA47406D48D82502A41DEF1DC7FC9295E193FAE94057244B0F786F7A24E454699432CFE2F3F7472C63FC21131C7B9F6DF8BCE2D930CE65FCA9014A732D7BDED658D1A6CC154D7D995A9B1CBCA84DF26A2B65B080E462D1799E85575413D61A38C260E25B69E9C3A039BEE3395FC1DAE10B2C158CDDF4319C347074D89B27848AA6B05A13ACD6C51187313FDA50882E6B83B30CFB2319B4F3B3F5AC3A95B0E8A5C9B85E60E79AB7483DAA8B4769C14B75E28923839BC9A50AC8B2D424624A9F3A1B531F0921330B7B9D97C839AD64B1D2F9DA93B3A8E1A2C9A72F0D647C8D2E5004164B18130C39F54678E73E22F716CFA467918E440A159A8514A40ED2F0DA6873B9952F5D25414D2B2F4C21FFF321EEC20AB81E24B106AC33C648E96AFF7F293B7A8D676E21CFFF481EEF83F61674398AA01669004B5F7F86E54A7AFA6301392BABACECB2C18E0D14039514F982FFA26E024B5927E6DDCCCED9FEDB07A3FABA8C6445D0D63099832B133089E79388D62EF391D6CC089A742662A5898D7314C901D71004ED6C6D85B4E4561AFB8AC3CA25B1179474CA69D0E7AD185D9F1A3D8D066F8399BECBA4D430906446C3622F920826437E642CEFD375783C7D5A2A31326C0634F2A88422C456065D8BB515D818FF1ECD30FFD6A377103E933C086BAA9A12C068302E281739709008E2CFE66186A0471501082D1417EED5D233301683D2939B3D4C8A92FD5C4C3B28182FED67CFDA6A0D61269E2E123457BF8E436F57224C7F8BF7B63718DAA975C0681DB31508AC84634FFD75E55ECF9EBF27A59BD3D06BB909077D16C1A8C82F6340301FBECA553183CD09D72C320B97F1EF43C96F7DCFE0076DDAFC532C6057EE06C16120AC0145CD02DFD64106AC7AFF4F555E0ECC0CB244A21919E8EC955C18D320BE0B336FCA69D3FCFEBE59CD11D23D048102CB13879DF6C1A325B97534C5C4720411D490458ED1C5C04342E25CA0C94C2ADD754F672F80FD2E3EE424F7891AB751F27259EA65889B92FFA7C04B24D4076166ABA495F969944E6C41A3815AFA860B1465BAAF4A0FB2E139D366980B47E9F72D723A00D4D26AF88248DA3494B939D70352131D3D994E2E988E25CFA3DA83A53CA9B4215CF894AE103770BD04A4C75CBB423829D82E959D29BF84E83C5280DA95E27B5EBB45767C5CBE425A6B431FBD9EE12D694AC45268E81C26716255162288A20622ECFAE30724329A136D714DEF2B8AF842831F3472BD071414EAF694A8524369E97FAE312ACA6498DF1834CE9D8F96D1265AA59EA68124C32F75CB8C1259DED4D7FD0F5212DA0B2828B74FFF17D4FE475D81A9BF87C5F539A934025C6E07D853C2DC58A4AF75461D94BB5C32FD4A98D37F1DC90418F3F1EA9220CF6807B505A503F628A8969751161254226A93F20E1D042748E873296C1492D1ED5FC9AB7A8EECAFF71F114A49C8C192971AAFDE226C93EA168065FB0F03703D3B24247DCC7293AD97C1710CCAEA9FBA6FAC896A8ED9449AA16A76FB53B20C0C80EBC53F109F6E37582ACB14C916068B288052CA8389F456401D84FB3117552969A693669526E62D48ADCF24B2BC60381BDC3B138B25E2832409800FF7AD3009DECA801F408C8B251EC39BB4DCE6179119563F6FB451E43F93A83DA917C81E5C77809243F212865AE369996D62660F3AB34BCEE7F6FD8D1A23B0A8318211D16F4AEBD258B70F3C7E2D8DC69F2B52CCA0B7EE70B10B6C2D910A62BDB02160582C0641043A84F9AE710DB30E718245111CE2DCB00B82B1DA1D29F5D059BCF062DBFCB5DA4674E14947E8138B127D2DD201C4859035E4E92FE7B7717E971E3801827B7436E7FBC790791A2010A8232BA0BF3129B9C54BE3DE5DE4DA1D4CF6347D8287671C65E91E5BD86C5E69AC636C25511F6226DB92A5B6E92272E3342D3B605E2BAEA289121B14B19A4DF74D4941B24DBA840330BFFEF87F906BEC30A48AE50C9721CABDD5F12ADAA1D33A0391D7FE225BD111FC4A85447248C6033D9B531CB387FA3B29C70A4314F0C88DFD8070529BA8A52033B4379ADA6E37263D6301CADD83A9900ED804A628D98772577DCD8D8BFCFA64814983F96A90F9536E68BC05869EF1A1F99FC90EAE48E55066AEEEF9CB156142007037BADEF631DF535340188F5F2E7A415EB79889E6283256F4223FCC58D484D75F987EE3BF546C6070053C66D779C0F210925E30A152E80B19ED8FBBFDDE55801CA44E435481E27C5E04F27C57DC147E026290510BC09331B0EDFFFDBCD2A66AD13898E223818F4806196DA24F9B91AFC4E9B571581E0002DD6BF91331F3A640952ECE5DD244FE1474838595D8308B22DB80D560F4E37D3954859C074456446851D91E9E99E53FE2C3D9D0BF95A36C882092C40060AD0884D17295F7D454397B515FEBAB538C3BDA2BC4C1F93A0CCAF2C77E400347A6A31A1DBCEB24F008318957CE9D399E4ADD2A5844D2FCCDEA92D5CD3789F624AB34F2A387D600BACD5443AA18DEBF5096228F6F249C6632676ABD554875E7C3E8DCCC89F00B16343E164BF643825FC40DCBFD909A188B93985951D3D281441202D3D75C27CBAB4D20A5CCA82403FD5A4AD92ECE876177CD2C7B140A479BD55E9066BE4BF1AC453AD389C3311DC40311C590241B6B612BCEDF7987B8CFD11D65D3F249C9D7C638445C6092243099681F502303AA7766C7D1314F575BDDB3F388AEBDE83A6C1514F4C881C3698AC89BDA978BB9B04637BE90F1867F7126CF5EB458F681016556BC551514BC8B90ECF78DCB2914BBC40A925D2401A07AA6D1AE6085D710106B6433A1CD1A34C4CB1A752CBA1536E5C30279E7775DCA27ABC86EF7A26067C801631AD6BFD286AECCFBAB22532F51EF33ACA53FF8F80AB55CE2C0502F798FE4E727BCCD8B99364DA5417E24836F97B98EF05878C9CAD1CCB42A1443023173FD11EA32C5570F031686AC51C0FB9259336892E863EFBD7AE5691C69046CA1A6B11D7D16D528A89864258131F85F0C0A9F51F07DC3A8FC3ADE3B817D2DB1769745E4E59FD5AA1DC7F1C6422C7A51C23846DD21429AC07DB6769923B4650F9DCD06D25DC524912DA42567BFCFB1A9A0EA98BA0526C6BE7FCBC86CED7CBCAC9AB6CFC261675187DE67B3950861F58E47E0742D8F5CF35CCEE41622D0584F89F0C829F8191A2D33D68A64F518E2F50D44BD616D721DEC74E6597AAB4002FC87DBD9679014F9A887715CB2515559FD80946B41A4D27490AB25560E4A1FADA6834CF2D543EB9DE5378D07842BB8450104D39342E21B522E3A0FAAFC9D0771AC27B1706AF93B13C8AEBCC8D46EF5392760967A3B31E4900D5A4CB080B793558A56C1F1C4CDC41FEB54E85FA8444C7DCD7C364D8411EA0ABBCAEC8FC06EA482B59987226B025B6B455C31A1FDA8F6BCC1F00E5D2E9B605B330EA0A407B58AC110DCF1E7476A9DF332752EC63D50DAC7B4380D1ED52F080B060F79F2F145D318E9A09BA567B9863C23D943D58B3EE7B2A07D80402D11F1FB0B4CFEA9170AB3430AF016E08DA6A1AE4DE84E0BEF97C43FA2FDAA6EF9336E8F7D18D7DD0B953D832BE2C4FC05498BD8EFF0BDA01B065A0D0CE54E6C580031A79F19C1536BB261E43028AD8A0A4D4AADA0D5E7C498960289F462672AE9AE4C2A11973D0DC5A66F628BCCBC580D0D507874D01A68BC2720F82847FAD98E5BC13444663B246F5C269146F64A0F4FD6FC1F4AB9105C19A92566F8F341410C7788164A97D8ABF531C2860DB9004950BE0A92EE7E5D7B88D20183479419B5B2520AEE1EDDC951A4E6D4290BA2387A003609E5C826C470EE476F9D0109826F16415B0B746D87D093A006022A6F38E4EFC21548DFE5030C7BB45248067F5F9904A8FB874725D5BB4137D2E427C170845CC2C8DB4F20FB0A6BBF368AA9C4F206FEC9A4A567E65060DC133DCEE08DFC67656EB917BBBF1CFDBD1F315D71A75E8754A4AA9AB6D35BC618CFFB2DF03E796CC00D687CA807BB974CB1B298F1D31ED2FFE0D273460BF10BC99FDFF602C619E6A42EC55817C0CE483598EBFD917DD06451E96466DFA089D67EE1157F2C9D7C409EE5B50C048DE8BD31F0F297774B226661645719A8B8045EB2912820780A49A7C5EC24CAB39A218FF6C74576C5810B1788417CA180552BDDD82F2ADC38CF91192E2F268EF78827DA8A5754A6AC6D2F860EAA20A863E54694DB6C27D2DB37CAB532319F64804A5CA5C311A26BE6699830AE8B4B2DD67980CE9B51D828F6B1887B81681014C9874C50E043F2E0A8D7D36B914A6EAC8E0DD581384CD66E87A43760C838896AF808C33122356AB7C0680249EAD3A291C90883EDD4BA914DEE43DF13E1A70844C08326882A818CA5BD068C58AC34BA919D25EDCE6211866772FCEC1B985D651BD472DDAE6B03A6A9E0BA00DDB3FA2D62B07B82E05A3A7A7933CF0FB8EC22399B3EC77849B8D53BD7781971352EC1359951E8D4FD06244BBC2C6EA645BC37C491DE1C2EFCB1A88D903960EFA4C461CA21B4059164012537260B060652BD9ACC36D0223D575866530A9FD7DE11DCF18FE44BB84C0C6205D77196F349DC914736AB83CB87506989F83A627F3DE150523C1BEEFD9528489017C023B6738516692CE73CB32E13AA3B902DD2D0A7908760D12D6B170E3D04048ADCD8B8DF55FD18082F911F87E93113E1A66611F176AA53DC6EA272D6B76115AA6E210B9F1C23EB8E06EE521A3806917D584ED58CAA992C340660725EABA1DEA69C5AB8F05395B7B0BA8F414BC63E6FD42AF6AAF2A5CBC0AB15D57E7211E02A8344056E53A665F20EB760237A5CFF3547F84F4098936C4C05BAAAA6B6D7D64F07F9A047CE77E5B2E6234ACD3279386C6FDD5233141F8A799D62DA14D03869C6DCEF12883DA4A846FA286C8D5B09522619FA037D88EC559101C42A3B4379142EA7520513A51ACC3EB818C00450CB7F485E4FF855A9224FA37A89177C09DE1246FE245C366F4A96513C2E8A1CD77C4DE2F6422FE7204C4E501EF7273A1192AD5B2E6D974C472562BE666BF1261CB15CE9F9E24709AEB55A96FD838FD041F1274554046222BAE29F29666D3FEB06145BD3481A1B82B65D3FEF880491F39C21EBB2C03C70648ABFA376749E9FB3BC3997CF4A7F94932F37D35BEE18597C85A223A45D1A3D3A426D0D7060CC412483495EC11A0535DF8D8A6B92DAE50FB18E37848364E6D97E2D4B7CC63BBF3CFA62AB2E32321016A48B8193E82A2D3984ED573CADFFB90CB8357D85F6220E99EB60063A84B3E3841A34FF102075F90672A3AB1055D016DB70557AD7FD0D02DAC7D6794625010E685FA19F2334AF6F660473D066E280359DD5D2AC4080CE28D228066BA663A8040FDF8E72AD16ACB7661C993813E5AA016131BFAAA80437E04167E6759F93C453D64A0EF4DDA10057FB60D522F059A994E679027037BEC7F60DB81281007CFA16176569A663413265B7C308E04192E9D87E48D8C15259BC288672969A94AA2954E2D210AAE043E02C030A11E96C9D0983B2ECF3BEDECE727F834615731C26D5F187AE0423F5B34408288876254129112778DC998B713490346C955AC0E0CC86D52423A8C552E815902E686E28FA466190B2FE042A23835A45B3045CF300AB66DF4A0615C93164EBA72FF460EBC6A728A7B304E6719C719FB18410D3DEDAA751C02FF06FE18E8C26485580ABE81784D3C73CE72F734ED688EDAE3A06D8529FCF7900C029FB0AE926A601FF3739FE6EC7C5891D32F91DBF47985004A766B3C93611DCCC47D3E6F435EE0D21BBC0281D4D273172B3952617E24AC292037D6047982A6BDD0BCED4653CF5E07F7662A250107AD9EDFF0396913048030F87ECAF23310B9A83E01A46EC9CDD028CAEBB3DA078553C3FB3C61F7D41ADB0AA0B890447A0C07BF9FEB04DBC5C7162A036007D5C8E3199B21AC6498DDCB5760292783B0C8FF3D88431ADF9ADB23209197B5A1460F7BE09826EF91D1B6A6F011BF52CAAEF9AD34488C0594A3A4EB34CF41527EF56DCA2EAB729518984A1E30FA5296759F08EC6DEBDBF5D1ADC02C6DAF13B2D69020ABE3419DFB38FA3A9A8F7782A30F980B09BB1242DF82413B2FFF6640126088E213C9F8C3C2A6EC4DB005907AB48D1DF60382C61A14683A5030D2F81A89CBA572EECCCF26976EF2D61BED698F127392E71C5CA93C0C61217E92ED22FB1870E425100E46EB34B06819DFA694AECBD922A0187853C8BF33BC07EC68CCE254D4F6B5918A0297F6DF27C62313B4C67878C91A0410AA1B6142C0BB263AF02FF80BF4154BF14CC29C5ADF9BEC2A205031148B634B8D65E688D3C4431529C03B91784C7EAD5ECF020842A47F327142AACBB901586BC571BA17F2BDEE218C53A9E58333033182A667273AEC10D1CF900858DCC504694459D0840528C2F52FF2D0C080DE481B0B169696E3B36866E50CDDEB398B63989382DC2734E032179F2084FEDDE0CDAF2837E8C11444C1470DF1E054CDA96DE7971AFFB6A1E8B34D84F4408BDC7E932F5A2C63214891E0C16CBF8C20C145D18371B1DC6A339FD19116EB884AEDA668D8EE2CB85F68CD0B9488E24C06C75F53A6D9BFDBC71C8BB17E1202EEB10B7D3AA7F9AAF31457DD4981426D21E91DD76CEB24EA982E68A12F77850297B0592C9287996834958C2560BAEA0F3BB8E76BBB1224FDED7AF1D4A6ED880BD95F128F32BD392B3FD343D58E8C5BCC9B1424BBDBEAE26A2120AD759136DA7F28381B9F84CC902C4BA639294255F6344483CE34B37FDEEC50544A1D26184E76D05A6B0FCE9606870FD32E0E2619C7D6E280DED92194A60D5E7CCD7487E0602BABB62FE3A4EF15846B7683E7F0D262AC233C735ED78DE58194D288435700A78661B753ED26054DF4FEEE0923EEE786E5F6FA1A6666697D33436A4E9A3054CC82ACF0BD2FCF903464A2389C87A67D669538696E658D1CE6DDCA9AEE1131AFCA2DB21376D43E382A4B9810C454E1B613ECB972B9BD60FF35AE698B653864DFBE219E9205FFABA6700B514F41936440D926B3D1DD2AD696E8E86107AF6663A43FB4F199E5F271ED3F6759C54FAF4843B9EA3F0814C7301C77995A43DE6793E15027DF52FD7EAF2AF3811667430E52451F56DDF85BF290F8811D586A3184836217DFFBD78CE485E72D209AE267BB4952DF8D301A55A6BB1368FC522A398D35B59FECCA5F89C0635D83E6730990983D47AC6AB7E7DA85F57599C3C69F85DFF280BCCB38C2F515AA60957ECBB2F2010958C234ABE4CC7D755A82B017BDC2BAC5FCAFA7C9D392CD32769641DBC5D8C8CC3E376C66991A0CCB4B440015B3084665163C4876F488224AB6E8E0B2C215C541084A4A15CD774FD71F110121570E8C05A06064F8FDD1442BC841A78D6FD42B3D398EF2470231BBC717619E01144E51952F8F1EC605DF00F6D98D5F18C1D26E0271BD9D51CB27C09F99CB749286A46DA3DA77F3645D181DD9E24D3DCD01FC0BE2347E588A4042A49526BF89ECDB3B6A1DB48760C03B6362F42852FD49AA903840F557EA0FF6920C678F1AD06BD5C8E7BBC1343673FBA164218E09720F453E54273DD42BE2B20DE1870C7BB6312F833F76F2C5F2250414A5F3554AD28548F29C4273AE67830B361ED8ACAD7A4E13C7C832DA0958A9178BAFB4A40017CEB4E9697F8B0C055904D1DAFB539D48DE75A3D2BDA6B7E00510968CD9C5F4B290E25C79E46BB9562C5999B4762B053660FCE627FC6E92CB6288C529A72938EF591413371E263D8D71949F64C3001BC323F8F03F2834E34FE4CDD96E61305ED478CF278D3598E07960B6B749C34C22F5875FC0F5BB93EBB3A157EE54200C2360F08A03196FEFCD8471360384F32314E2F90CBBF887BD63C444A097A04F9679E373E9A6A0FE0FC730E471483485891261A1579A54B76BC8165E243E4CB7377DF346237CB311D6E1758F4BFA3FE6A0FF05D3B82BA44BDCD5E8C9E418DF41F81DC989967D329C3D53454D6A4D58D41B8AFA036DE038ED2D8E9132E9357740B33B918C8CF243DB3460092FB6E5E264A1924AC4F249B9A774493C4C3D2AC7B672B1E998540A2609D4049D8F5F63CDB1ACA366B7278CF90579926B3C55230C64246A65FDCBDEE57C8BDB34E36508999A9856B1CF748DFC999827D58558B77B322E0091E1A630C1C486997FF7B56E9CAA0621E569D11E0C3325CE6076F1A30F812A0692BACB338BA1F55B82FF39D7498BEAABA06AFEEC76FEDD62F97BA9027666DE9F49FAB64B54107B51988801325D9B51119E4AC32EFFBE13D1FE28ABFABA759A04781FE98E6881F29C994AA1BC5E9D0DD9EFEB18484D717176903A74D3EE1B27D54CB0E33CCD93887828523E41D115F7B51E7E0D5D43DC484F1BAE8111100C3F30E72968DFF0462780AAE01168088FB3EFCC30F329667489018AB35A9691FB76A6AACE8B02B872437127962AE12BFD8C82DBEF2DD89EFBA3DB2B63C2801C0EE6BD331D21105C1431FB867FCDC51C02DB248861348C543012A8878A6056C8D047270461D51EA4D0A85FEDF1AAA416029B523FABD44E3E25D9F555A1DDBD2C030F3527F758A0EAC9F69428D3F74BE6D8427AF8B0C407E0E7818764AB42004829C8F102AC9822F7766AE94A40A8F8F74F0C0FC1D601702319530BA0AA86326A6C555845D1C85DC723A07FDFD4C6F85C2F5C4D912CDEA186D12EE418839CE1F9456A42C603605D42BDD9C41DC40DD0720BF99AA3FB0E2A807EA3A406894C81570A323DD8DC67F445A53FE0DCB0C0835293EDBD4F850116D2B3896D1ED631455D9C9C5255BFB20453A2DF76E9EE3460A09C24D08CAAC998A080677D755283BCF4E4EBED728A81A6713B1A988164586DA4A96E666F5ED8AB824D7931C4E5EC3B6AF1D623A3ADDA1EAB6E7ACE419CD63E81CD93E81EA4D2A64D25A59DA51999707157A512B4033F52C89ABA4DEAC79217F725F8D881E3ECA42FD0A7C08520E273C9E2EE15D3E704AE06312116D3668E85E41E94DAD2FB0F4A506FD40431251AB643E4A866B361CC42963DFA57E9249904EFCFB430F30346A02EC19997F96A3FE2AEB5574C9D065B68804109F32ED27CC6DDFA24D86583C58494960A78AA820D143542694708F53973150FFA3C3EB73B04592CBE8EC365EFF2EBB491298E16E11125D37B6072D67C21E87EBC8B410943AEB7D8E23C17E2C45C05F46AA6F852759A78B07CD5463CB30395A5B407765D9CD99B00D40176D050D0AB736D663BFDA7110520639D1536022A0F99C9A4995DE1D6AAFC7F8C33423ADE5376E95744DBC797742F7F24258FB3AC93249B5BB7112A23E2D1F8316803D2FA940C5B5D3D66D13819370DE2FDC4A90494EB5CCC69A1421D0DF68424A0CCB0E53A7BD18682556A540436A8B43B0213F27958CCB5D5A474CBA693567663521140F3A4BBF5848CB4D77932A1050299404ADFA629566CDB21F91358CD918AA65109C971335E041A0661BEB972AC835A451A66B6AF503148DB5587540D4137A770B16412C9709AA6500364A38A24CB9C826EA367A62D330403E90A0BE83BB4E3980487F0B67A046B2876032524B5F185A6E4A9410CA7D9231DB109AB0EE09B25024F570C2B57192AE98AB0466A20D634F266413FE8F3E702DA112B6DA139191CD5D398ED9D1445A83E237954E9921C55308DCB34EE24B7068B69DC4A7E096A18FD968C2C3E23FE75249341C79366F4806436EA5EA1E7CBAEE87EE61E50A08EA13666C241A2F35B04FA20EA1D52C809F0C30D9D80A8BE6903C93CC448608FA51ED25A02A9D3DF93024BF9D262EB032A16DAA0F16D60BBD333E7E211059947CA4B794D581C56B8DB4E46921D2928C6A9AB5F58BDD0C8D9660CB9304A5FA488F46B54D65161AB28B2354B341A694D0D0BE2C6847BD19A532D5E34129D917CC1534B1EF2A3B69317DA0A5740262DA7626252A11B4F49D6C95E215762B642384373C4931EB8B879EF1FF2355702DDB6A367C9D82DF33539F9C15B38C764A1AD42C627A691423702064EF10C105B54BC05EB2CEF02BBC15F05A6E0DD6208E61AAD7D95422BA752DF35B71EAF8CCF4710B7908C7BDD42E52DE294BA2844CD5F801242BA10BE8A9839C5F4E51F9994D0C147C392FFEED44D66FE8AA8E5DF1FFF1FD88D7A2ECD4780D6586B128F6406E5299C7B5C903654B0D098D6D0C605009FF29249D6FA05015A3D8FCA3476F09097832ABBF8C5709DC14F167F4B256DB7B29199F481060BBDD60C3BA82C1051A5D4D8B6D6C81CCF9149720FA74C54DD17C3729E63D1AF55CF4F29A854D1339746C20B6BFA5C9A6408DDEA5C76EF52051BB31B2EC2145C9CE79F7E8377BA78043CB3E2B64463950FE6CF5865844520276378A55C34D2E861072D6A405FB28AAB1C32E6702FC93A3AB9A45FB51A0287E5286B94525DA50F0E95CDE3D82F6D8B65B13ABDF66B018D760F7C51C78EC19EF0BE026D237170AE0FB000EDC54790357C4015DC70BF1478627A3D021483D6ED3907FEC1094F10E03E30C3EC0A1AFF76120EE48D042E12BC6F27BDB7910CAD39D562B74B056E6E87132B46105AC0739CF50176FC6232AE0E673AF496C6201C95CC46254164498735CC12198C50C925E9259F041445ED012D31E10631782C8D69143A0FA5B0DFF7324D33D0C419FAD821F41392823D7E00BC923554002D0F88179BEE302EBFF417120DA81CA9797D29A92AD4DA741B0E12E81E09CC209C90095BC99EB7C172E1221A208E771B293CFD706660260635DA6AAFAFCEAF97CD119D6279C0EDB5841E7C9135C563D9A6152D6E548A408BC8F82AC2D9F1827653864872440C86B35DD2F3621F23E59D5B005641735AEB8F51296D5D5083DDD16846AF1F871752A3CD23B8F132BC97769B7684AB075F4E2F216249F68B0C9961A63A836684A439AC01EF6A93D49B09184EB0695D38908F25A734C782982803EB09042B15BC7E7CE2FB717A10771C76207E066B945A03A97A9857C44F1312C5C6BB57C8BE69609B1EA9C241F4A2E6063A92746B5E6FAC9AEA6941E78468A9D464473206CAEECAE33BFCB807CC8C548CD557AEE0E63CBF87971005E435A0E0EF7CB7ED136E8850DFDE05C5A1E5909EBE4DBA52E38038785458FB6E2E6582988D56EF366FA168586F2C3A24B4EE89631D7EF231A46CB78F50FBA33F05AE10F484CBA51DACF02FDA944191C074097A527249D485498B3356CFA2436ACAA4E0E31DEFDA70E2A4936040BFA3443A8A09771DE9180AC6AA017E952B8D2D0023C3AA2C70C89C78B7691C36853013FF77155D6427BFBB4FDF338B4F245607B5B2A1D0A6A1E1774C640D760948DE243AE11CFD829BDA12D117898686162EBAD1B41D6E7B2743FD6D3134313445E409B4732C1FD7FF5E03E797E7C758ED22A1A660A56BB8C53D11FB1F47944C281BA6805642E1FD4D46057C1A86E4D300F64FE88FF0939F154DAD8F31224A790F72A83C83E2F23FC93D3855C24A3027C531DC4C92797A33554468CAE2B88073ACE64A8B506260606540F5024BFE03627AF72FBA4ABF713CC7DD6F90FDAB5490544F936BF2F44E1D501DD010415783C04E735A620FF4195B5D911CCB470161E2B88D42A3D84392A814C668FE08754A97009366DCA99038B81E30E5FC53F4A09F2A10E4B8C40E687687FD93B931C2329EB26742EC6907ABA5600C9415D7B9341896DF4508B9C6FAC0D41E92B72604B0307AFA7EB9BB7B32DED98C62713971BAE10BD258B435AA24CFD91956BC7F96128981EDB49ED70E6A20FD233282C23DBD99AB4787E12FAC6EB42A289A5DF4737A9DAC91079D85F4E14F28387C7C2D0237625E5AE3642A8071AA15740A2B11E49C152687A4E346B243529DE6EB9C9780E9DA5093CEC7A0D74BF5581C1B934827023AC4A618BCFF934B65CEFF21DFAEB2151C7E0C4D64F91AC082801078E97BF131838A3D585B661E691F31AE0B7AB306C91AA6269DA4940352BD7E70CB55AE2483B37FFA8D4F6282935834BD2A171D798A924A78424E56F9881F0C98C2D154B32412B0927A00DEC8F42FC3AB694D6B3503EBD5908385BFF6AAF8FB92DC01D9C67A204A4403F89255A9B06AEA4F8F781383DF3E6BA5ABA36DB31DFAC3F8AAD0F1089DBC8148BB1886D27F4AD398A8C3C4D3BB6077C63ECEC218AA3F8D9CD6E0264B6465BF67869A9CC723500EB937028FC93EE67071DCD10FD50AC8F8D706239BAB3987499A2F0AA916C5C98DF02EF68149F28F2C01111A0AF50996D4026AE1A93CE35C32B23FADBF9010609261BD25C8661BBE64F1CB2F1926027E80F4A3466F04F94B34CB6C3E08771210E306CA8090A32B9861B4B4BB9859548C0A9C3D3D39C4BCB5E55854006037C026904829F30752EC29644F089A0CBEAC2A2E8873A081EA9E5B00FD97E77AF722A15809918033B48080F2C8B9FEC4EDE29C636E013FB4B17738CECAD690E2E8160FE8B299E912CA5E109FB53BD663CED0406D28F1D39AD70201906FA36DB09CD5EA20A9D6411811B169A0C81FDE92EA8F436507DEC21086320CF297070932DBE117427FE13BB4BECC713310609CF0D0077743D618AD60761580D40E8169698DE22934613B13CF86243589709AE6D717F78197AD045EEA1978AA11B0DDE84D347670E4F8CEBD5CF44719D1C2E7D2271446852DEBD3BC14B96F9C82F81B1D460C2637C9D435A544C556785A000A00F8A7033174746581AA4925C923BCE4C0CED41402AA1D107A5D7AAA667B19582DDD31864146D75DD3844042C1E994D6A2E2690A53DA29659B09854674863837B013803961074A6CEABBE1A4954B606FFE63C10F5B716409B876B8F4525A338802B34CA2668FDF2E978EBF82984205E2019BBFD865249A560817DB872B03D000174B7EE0452BA00E20840A1338DAF31679233BFED0C4CC538462062079BC8005DE87199B0DD680E9ECC5037287EB18C08BF1691987637AF6D08736D6E0D8905E2E1ED8CED21CC15FF3F61465CE2C808BB7DAF1F21B08C614762E4D3F42191767D41CB62E32F049DDC6D2FC51F680E4D89CDD6C48A9D2492D1696A05C1470EB680C8F90884B604E25C169F53C8A39FBC6D5BA02C5A474712BA56741236BADC59497F3A7A251CC52A82AFCCBE8B5E729115AF56324EC1F12A59F143ED014BA5F252E2A8CE75B449462002A49977F7C888FD4291E24EA1D0ABDFD8BE24494045EDF43264DADBDE2310A51923CF474C1FD9AF84E877F7C745A2887501EC70C394490ADF817B9134F84DF5B1C0FA17942E374D854B73841E464C2AB9F258252EDA66921804375862E6578EAAEB419417BA21488397E9BB68C99F4127491F5F894089F16A4303D3BB862C17BF225DEBB6258810B317632DEB21F9A9ECB11C5FBEA95AAEAB286377E5B12A71C11CC44B2D656873E6903C729402D2912F37387028655C975D7139248E06726DD12667C8074391789B11E0C68EC2011A7BBAB648FB754C20CA7F3C13D710C4611587D9A1B9D404802F8186963400D6A163D306697C072E638AF78B0E852E917170869E30C81F09C5DBB216367C009F6824D596AEA12F54F1074128ABC68E2F02A5ADBA25377BF1F2D3363619D55999C6C00DC8EC2A3053E28E3FA31BD2C9FD52E5A8A34BEE7F6BFB3A6AADECCE36DA5B77B77D7F5B12449CFB45C3971ED679ACD1427C5BFACAE6A03C3C8EB83CB6A73BB6DE3C5028110EEB4754CCEF4644C1E43809F22EA8EA5B2FD49E3783762494FA7D6FF65496115B993581A0283E686E66A5EC6F6B30CE8CC4258CE23CDA81B19839A5DD7A957515960F2875802388F58B7E5439CD06E56445A708E4F31899DA1DDA68FF0EE90100C9516364A1CE8045D7862B094BA4859DE7378B03A30495405BA7D2316EBCDB5C5292AFECA096A273EE3BE5EDAC093B3675E6AB19EC3C0BC7B1D88DE28CEE9377F9AAAF31769AD4432131A73218B3A11FCE061C9F297581A84E1B277A9FC4EC25D9627574F749D3F843ED73A46C8F0390F6599D335B3A7F591E0F9AC9DAAFBD43E9D056E568C3A2DD3C169052A33D94692CA33E784219C7240A537559AF9FBCC0089453129069F62F27D1F70CB3EB0BE9C555731C47EE166EF9500891513BE587683DA88A9AE9C34C1DF208417625B792997E1AED962BD2719541378C78205DE1F0B38813D2208121F46D36C000047E35098410132DCD171FD2168ADA258EA16641272A19A044D9142C7A861161818F8AC23CD3B07DBFA21AF9663C465340272A7E6932F1EF702DED1ADA76E01BC8054A906B8A11E7128950C05E777773E172831B4143B1C55D34679A6F2167DB63012B480E94956E46BFA5A664AD836ADEDEA8E02FCC1D01129854B3107025726862511339BF37D8BFA97D014B43A5F5AE93621CC7E21B362478725C092A534320B4A0C7902F686578CFAC97845964728AAE31EFD4875B2E0EE4738950693A4221AB31FCFFDCDB6EBE4EE727EF0D60E968DA6CB8B503E7CC293D59FA38170329B67F3D4C059BD0C09321F40719DFC839C00584E86C3B7C52FC88332B3AAE13132A6556566F56FFA0CF65F4311B98A29114A89EE48166DA8A7DEA2AAB7586A0338B3E5ECAE3F8A284B4E1F5B4F13BA7B1C5043FB4AA22F226F3242C7AF2AEF552885081FFE62278268C1FF5A3F24A1BE8F5863A867F136F2646FFB231A8C0CC65CBBA7FD0437BA1A80BD29A1FF18F08FC74BA1875BABE3361BAE394AE78851E4161CC354D16579C1F3DA19B5E8C4DBFBC6F625D75527A595969CE1F59764080B0D7882C99884F6F3A8BCB3AB63616804CB6F7F728D0110D827D8995DDF29731D4AC6F2E91BBE034A85F6434E4A6E93AA30FFC349C3ED2F7021A4421842348C90D606096D10E4392F8210BD5A78EC80C9CAC88005F381152322B40F3031AD300A1F7B0A118F83BF17F05E568F468FE0CE9ABDA646125CA920A10FF9020B384A2CF8767601C2A6CEC71553CA54ADA7954784D74B43E6237C8A3BE4CDAD7342F6603E159F4882F13261D43D41F8ED6B392603FEF00498ACAD5DEC2DA528D6460D2281628F8A76946895A18D318184A40A81CD50A90060DA062C8BBF15E4907821E986191DDB63AAC26E8CCD870C0CF1B7BE5C33ABAE6BAEF55A9FD89E59E3417DA25020A08AF5604179A2232F45A712691C3740F2817A71C5B69E3642BF57E35CDBA612BA5B5A0BF05F2D7C65F51785F90ED2C17898D4D2F62E76999C653768A03763E6D85C643FBACB833DBB1716C9F9E9E86E7A9986B375A2D40727146627A3B5B93CDA240E592942556A42834F76388485C1ED23B0752ED21836EEB731909783A19AE2CB4E487A157CA2C1AF6ED99B75885065FE273005435E85EC66962922C4C8C246160A6AE31CE13D04E48A27DA835948B3288E762C687FE16B160F27C54E1DDAF2E2901E28C1690A90118B7A04B4FA87ECBA45494B3E77E183646F8087F3821266E66127C5A0729BCA1D874C8521526966F3CE1094437287A88234D956B85000C54F654756304C23C4E1E979F985E134C4E4057D89710D3CA2DBE0CCC8B96DD989B0B5DE5ED2E85D9BD9AC48CA0826D1B00024C4D788361DB80F995DDBC23DDF0350F04A67F35AD819397B01210C6AB63D98AC308CF8EF0790E5DEE9883D11D9E0AB8C097ADFA2B2ED761C52B86FBE81DCC1C89A73B76D98E3C694099540C2A2941AD89B713F5EF4CCA5477ACBB646F8AB74B2384EA7DCD7A5D20D5B29DBC49B2646732AE6EF051C879C8AB11E16EF80E2241E7852DA941B8042D08EEE22DF4ABB708B2DEEECE28A9AEA56BB7A1654DE8A77D2AE487099EDE575A5316558E3D099A0E6649D3BF37DB4077D807E7D1C82D9D3BBDF162CD486EFCEB1975696B9B21FDA1A60FFF76304A07841C05ECBADDFFA6443902AD94163464E51DC489A1295FA0F29E5323C6CDC7F85930EA3B12B89CA5A808150430B1E6933975FF37993E65658B212167C8F0A359A33470986C8985B82F15FA9E0839837146605D392FD8ADCBEA9BBB8500B792BDC5B0633E98DDCB5A7B50C798B09C2EA5045694E6E1A1ECE77681C278C68A014EE030CDA031377D2F7EEE3B4558151A03CF2C9AFF390589F0CB3F772454395F63E1FC6B151BB3AE6218FB7F3B6A5AAED22A0944CD916023EAC65FB7A47C919B53AFCC0D1EA295B09777A4191D647B95153E70596D8D3671EEAF863FC6A0D956D0EB38DC91A72C6F68960731A17BC99EA47FA484D041A2AD6A861E0CD9C0EE4CF618989A3B146158088907584F0C2B1350E9163465CA8168B45E5288E8FEFCAD919D1CC54AEF5C82973A7EC74C23E3A64D0D2C94D845C360E43A9C38D8E26BCC3BE901DC89B98CA50C71453110E0D587D1BFF8B72D3066C8079C08FC8C179E09CB9F02FC3677A07689F2212B0793FB9303F9414152E80C24EFF0C5D7AEC991BC42BA622D309D2EA4716E31A50739DCC97A6269D3E66B7D3959AB40B8ED047B4FB20D58C7E067D28520BB2F2DA582D9EEAB21BBC66B6B3BBB6EA1F6C5BF1E4B3707C3D00A53F636A40AE882F101916397A32B8502DF50494A304688A189A4BE736F56227A73E4A5BCC68FBD548CCB46EF5E52C65CC5B3CC52295C77777B323BE13C5F0FF5B9EB1DDEA64B4A5DD7E84BB1870AB5DB087056AA2C8570BD444656545C4E41BE2D0AD22D1658372AC4E100524392CABBB49305B21FC643C1090A065224A74118C9D2F90E8CA3B40A21E631FBDC87B8C0A585DCB7D841F4996B15146C709C728BA7682E1CCE7F6D29E387DC918F51E3DC2FFD19186538B7D7D14F5BB021322A88B9BD145C2152424076C513561C1B3EAD8CE7BFD850D1957C5A2546542F332E320A525BED6BC94DC6FAC1511B3810200DCDAD2A22800819350E93FE7A967E0DE29BDFBDFF40E851774B73D93B0F62D07FFA18827E0565A043EA9CA41EF5C5D8578303AC0F3EF83A19F592A216F1E8ECDE9B8E2FD466DE77A1D2951B4FEB21C121EB8F032A2196383499DA9838C802322B72D8D64A837B831FFA32305CECDE8860BEE3AB03EE2159C2D8C083A889D7670E14643FC9620101281382359546F190E0EE29D546D546A0B45F42E4590265AAE23B9A877AC0A38321AE6FC787BC7868804E431D4290805475699860B827981F75386E7CA6A90A1A18C6D9533A72320649368E6577B1D9BAD80DA0F673E0669A4D3545AFB7F0810A03B218BB02F6E69B63DE570FE001F3FEF952FBBF30F85A0F0A72479ADB8BD4ED5D840335DA8D8656D1D39CA0C216DDBD0D276BA8D80DE10975C9EF520E8E1BF01796111FCDC33533AE0A338CAEAC146E218AE2B49F9CB021BFCA75B619D74A5BBAF305C213F4D7D2306B24D00E18A57BF95C8079AF0C31A6B979AEE3690E43CA70AB4E8798020FE173A49E3C855FBA4048E860E6647F5B9F10A05457459B4C07B58817E6C2CF4A230564F2020CF6D2B0DBAED99805E5BFEA9434DD7B45C892472D12E2E43D6BD13DD3A81C045BD6D8927295CD5D32F06180ECB43CC76CA0A8CEB5B0296B5E80E35FCFA4599B2DA64A3A2DD63ED64F680076111908444B94E124457654C1EA646638B8018344B4B82D1A6F59022C0E8817FF891195663F8A2AD043F6F59199CB17C5CAA9EBCA040BE65E617B7FD40BD0E97B0B66CA53E8F7D4943822313623F3748F61D4EC04396B9C01D2033CB097FFA4C076D81E82264336B1AA73F55433114D52B534A11203F97CAA7028383EA61807B4E88F85B491562423094C517044786A09EA4B808CF67748E34A13BDCF805BCB3A2A0F4326A8ED902ACA98A3456B3CEAC037DB88A0488E9973016B28060CE70378A86C51523071E3F4B91B08E8D97C0C9DCCAF6D3A143ADC3220DFD5C75301032D9A10D01E4A2AAF88E8D9B39ED25A5B2E9E336271E7C3949DA8F7D70C7501ED7DE7A400C2902D242BE5A78DE6140D7B4D1B625462D5254E9D83130CB7B5CEE943926B3F33B84F0FBD65A8D03C309B4F6F136DE8DB9E6653909EA5D65D6A5065514AD5B2F84509D456925B2C9F938D9D217F2962855D09AE1C836E54AA3C125FDFA020CA7DCD50CE2BD712EAD879A24533F8AFBB1E0AF0C54D60935E7F87BD9DD2CA3F49554CED5BE81897AA398301F7C0FF2894680A0042B52C88D8324E4DE5816C81407920E2A2E70196C0C00028C02B4315C00FD38BDAE9A72852923A77BC03FE28C07CA3CCF8087A22924B0FC18C9BBF774F55B8A5E30FF5478DAC9B28D390C7F82EB327F8EAFEF4AC0E9E25C9316F72E0D2D985C84381F3EAA924500A86CA4995E68A361A2D7CCE7DD05891007FD44A9E43776963DE127679D07C1E6D3BA9D4F2AD943244C18B4E869D273240366958A6E588A6CBA3E6F8D4F8E1C021D0BDAAE3B40E92A7B63AC343094957F2F4517AAAC843EA56159AB85948FA98DCD6BE81664C2DD2C882DBB91C9BCD29C1157C2C47883197D64179F12E3CB4BD0B778EC63DE092DA4FEC408045780D7F15C2712ACE9701802D3B50449550915701FF00F32CEA32565299F5137E6BFFDCF2B1540467712CDAA4C87CEBB42D7088AF4EA726B583449223E01BB125AB9C821B4F824F24791BBD01BF252BBCEC50060A69EE1F977BA62CCE1BF92C2181963DA86F7790A34C367A293F459C2EC4D488C9E574D1704D8FB112196F160C046E4C4AC0F6EF271AA947D88F7DC0EC7D7819780F3243A74BFAA6B66C2A9322D959219F465D8294EAC9A12285B4E2896A87F2E0BC51295C553E323F6D4E133FEA0E5E545FFDB971D1FF36CA1B45B6E44E1213DE0461C5CB01272039605361F620D431CEB205AA6A1F07F9FF9A4B2F0963522B476600A619C4F40319CEE9DEEF8E6635BF4F84056E97102329ECC6DFB80DA921D3B57122B8E5D9CA835BF4CE7431DD5F659AE22D3130F1C6290AEBE862EA4AB896C5BCA7935345D605A12E9EB45C21CEC26C2BBFA5E961F6CA943724F384319D252DBC66EB9210E8CA0032F5A54EAAF856AB7FA10F417310D09EFE3B44D9E55918141388427E5B4883B1F06035EE69E2A04D48D258C730FF178CC73B40415C9324723B59DBF07FE376A1255405CACD341EE9754024403E9215A73C33D9199DC35D3F9788C40E8CB6443F2040D216A522C1EE1612127D04454154255B501FCE244C5FFCD92FC7FF846E34CEDA2BC1F4A2132158C06A0999DB4B97A8E7A91751B750391F5AE003859A13AEA355CED13C8DA4E7AE3755AE942DB818F8AA1FD0B485131A53E823B81C316FD0D1185A452EE57FA09514B6464E64BCB92642F0FA43B71FA30C6918111053F957A91B41A478C7663D7A389D7E206843A83CE3E05218564ACA2BB6E1A283F3CE0533483A4D14AC54740528394F6158B12B0EE1D28CB26AE56231BA4EF9F3890AC41CE4D86792660E67AF491B46A6324954149AF79E12E452F5353F75E0A8DADF3C0779C3E63435E1438C320986236626000CCFC182896DC07CB36478B2A8453536277EEABA63E2E0515816D03C6DB95A05A32B984D50B0633535B2BD64AEF002B7F60C9370F844074715FBA08D34FD800F570F80FD269895ECF007BC37988AF09D14DF303C367160B6D09AC1E5640FBD1990272D363BD8EAAC07C03AEC98C49A80697B2D09338692734DE4B928107545AFC9CB62E081EF19ACD2688D6F29A3A8E6179C835A5118E129604BF94A75D08F96EF9591FF82DB016F1B8562F4DF888B170D175E50683AA19F4E94D8B730EBC75F50F3345DE17F98F73D17CED558BDEA0C4C689C3B4E3A46F3001A28E62B2CF337B5C28332EA5C046213643EE8F45D416447E84CE62AE4F6F853F068FBC70B821578B8BA41C3C5E0BD6B110A3B6CB0EA698D0010A45B8A7594F8077BE609138EDA8903AB3E7BD5AD6DE2D237AC92C7300076C3180A89BF8526EB6047B91114456FC813C20169E4E1BDEEB08A3FD15C51FD010890EA56856C42494C6F725031E0189F06ED80967707C22C9762C98A43BC07BA4CD13F911D6F342D3D663D5F60F9FAE435942E0DD7B64D197A082F080500291D79EF923A9CFACDDE43F059312A0DE76E7978ED69C70B4962ED8555DDC11452D33B163A508DC1DFC7940BCE7974B549A906F8B6AAF455131200E29EDC113CEA11D9F82CE643DD4511BCA5FA39DEE315E483601CDEBFC9196D92208FBAB0155FCD2A09C903F0018AA9B3913E6CFDCC307DC2B6A25FC7F21154821BADFD3A0252ADA968824AE2B88498A0948AE26D421AA6347C28901B5A437C1988FD8F6558A2A2176DCC41F6D42C2BCDA8374A0F3428B4E2B04E630E2E53E791028479C1132C37AA04E35A83F74C0BE0B71DC67B7E846E4B654972863C9D011737670C54A94E6B87E0196961B831D8BC89BE14CED0D69172CD5897AB87B2C1443B8FC46A7D8D91686BBBB1C1768B4D2439E9455271A4205C6F5EE26C45C627CD835B4CE0ADA396DA301641C7880E28A3145C841939B59B08F02B8C22C9D8EB02F4AED5E1A575DAADC17040B93B9069C10E985AC22DDF080A44458904D87A1AA106DE4B93F1648D36ABC89C80353C5C261A96C976AB63607FA22095C088C62C46550D7D15571076BB1F55A45D70D55A146EC430A31763255D290A0A5E8E7B5E366FBC616D528D022A39F6611723D43AC3E212B880F970E2196E692F8DDC9A09C122C5A57E6ED8A5EF4FB1F3ABE1FEE93D8FFF5140E0E096F38F3B94A53ED8071F544C3EE5B91CBB4EE9DBE7757C6736D83B6857D8ED8FC4220912E0495384C99542FB2D4A397EEAAE1D7161DEFFB35ACA0FA6FACFBC3F0A49018AE3A0A3D2907783D3186F0FF415E853E6C2887E01B033183210B8B0D910069CDA654AA6473F83C8CB217C6858369F73FAF6FBAC0052F5905839EAB7F55A9AF05E4CFD311DD2A212AC971791E6F89E621CF9FB193341207A20901597CD39EF0EE43D182B9AA11BFD18FA894D08522C038762027C49F37930A6D4B442D8450101A265FDDDAFA9138352E15C376068270769413110DE83A559EA8FCD0749B760818174207E713A80F54860CC4248A33721F57E531B579829D97408EB3666E47BC3407608EB076586801C686576440DF0FD0814B1704FAED76F01E8A3DA8664A1B3B070A3BB214C006AD0ED54B7C1724B28CA86008D961A094037EEEC0CF3330995AFF8FD9E3CACD820256F0D5E21B788ABCAA5282D74B3442581D18F4FB843F238AAD72B25A5E64376C2394ACE2308710B1799B0586040A135F5248DC0C9F0A6914C51C9FE1C9A4AC6C4C4D2A71E7535BB9643D87B5184B2774E1224269CDB787C403A9CB9C71B764965AD754D58601F29E1D403181EDC29A57F172EAD40723DF6DBEE0975337A0ED3CA20740359A892758062E97AAC98A6B9422BE031F119D910083FE7D780F50D55DE53B502C7BB623A8EDAD819AFA8FE82BFFA63289697795479C88396AC59157D6A5604350ADAF6975C45BB564486C46EA20E61F94CF3E0168D3CBCF3588B406AA1AADE22CF3129E91C998E62EAD3211A851ACCD13FD8232AD344C4147CDD1C338308130ECFEFDA8746ED0CD790596F142887F3A1B4F09C34B4F0327DCE6E46BEE1265D6C749622A1A0619D363FEF1390B5BADEC4D204B278820386BC20F8459730512D658EB11F150C58F2E01F21F262E3609577C80C580D0A896F309DB48EB998EA9CDD84535483DE96A46CA9738D91E00899436C24A32B6373616530EC33BDC39404ECA1682DE771BD6CAEB399CA3AB0851C4A62E6325DF72AC6EF548FA6E30584F52FA82123A72558C0E2FC632934924421E808FC306556BAF84E1B0218620FA3CAC343982615A5CB180B61D659EF1D26F6DE302EB71A3B4F562C22024018F2BEBA680C07957FDEF2EA4CBD813D786D1665A41F4723D2BAD5220178355361F0C2459390F2A8DF121AEAB228FEACE5702BE8A24A2824F5F710DD6E5E52301B152EA673778F0BE3EFA55314F43C577002ACF31E0FA904EA130712268270D867EC9A40E5671510FF23D0F81F70FED27B8EFFE44EFCCAC0B37BE7BDC146FD4A46D8B6F50ACF08CC21FBF17C44FBC0E2382D3551FB7B91105315BFC032004F968F31400B7D418778F52903149E143A0452DD731614DBBA337444417357DB8E6372BB5447715841952D7F72BE2E8C7FF56B5B1C017A4E83084519676B1F8345670B5E7773CDF61905CF1173CE0DC3703045214E6BBC32FB7397C6E0C6669001ADDAC54B7E29D06486FBB650F06ECA51385BFF5EA4CF84E3CA90D1F2646182F6977A93D4BB41BDCB4AAC7EAEB82EB80343910EBAD848C6DFBB70CB021296F47EA773F31C03ECB6920DF8C904D00640F8AF6A20E3194D5F42CAD2EB5DA0436478F09398FEB609F07C930CB756005A30EBBE192025614C409D84CBEC1821D7477814C687F4A8A7670C54161A222EFB9C09385720AD26F23EA7A1BE11842AD6CB7AB0C931475AE33618CB7F695AAF3E9DBF5C0689D13BF78DC4F7D0DD50AF00BB2328A160B26C5C8171C9AE61271321A6A54A5A4AC45A9682D4E699E9876BF7CF9A289EA0F6CE572FB2DF712C54C78735DAEE5AA413F808B933E82240B2BD937298A6196B88A70EE8CF4FD8C5710F898F61C186476D4BB77457FA71FC3278D7CC93100E124F597322570192D14EE1FCB65B56879A2CB8F7BF784AE8D1A66330BD9257443BED893F38044024A4D264251FB9214DE3B4378D4EC413644D751E1C96B3202DC79643164DF41E2591110F85C1F825E47185F43919F3391730E120187E4AE521CC75320970CB99CDC60E0C6FD9B04D33978683AAD36ED35B8873420D2C0297F91C4174F0D6B448EFF31286FB6ACD30E71C6B40C33084B5B9BC1EDFA241294E7841A7C03116922A2DC332D41284C0A0C2D85D89358061E560D4F634B0A9145358BBC9059D1B51E91F959E25815AB851F676A2C868D8E1A4C52CB6250348965DCE3DD13ECA9BCE89ACFC748559879AD070ABE3489024FA8EC9009232C7C858A22D559FA421F19FBDED910DCB6209E45062D1454B51DDBFF1F078A4532539C43019C7866ABBD5DFBBFF8DE61140F9E59A46ED3B91F83E1F2A20AFEF525E9BBA573A2D8B8944E9F350635232426A902329F805E590A49A44B011FBD34364378A123D086989F69639E77B968EFCADEBAAA00E59790681F624F8E32D7D31070430C5B0775BD9CC1D2CD4C0FEB8961B0D3E6465F3AF9EC89802B56C748BD4971CAC0AB14889B952AB25047215AC93DDABAE2DDA4276CC9EC74F732381608A302CD1AD95BC47384DFC13592DD5795544B8FA092A176C70D1148F176C8B1780159D4B1E2F9B264731C74BE745B3499DFB3D8980588E66C548136513CDDC14731FF59256DCFB696772A1480063BFB31B158B17862D0C913F0949C0848A84AAD08E456C71B4A9912242991168BEFC1B788E1CFAC0D29402750CE40895B536ACE81BA85AE7490FE43D2EDBD1D0F3261463612096992D5D8304F5F50BACB94447CDB617E62588916365C1123323F52C53D9DB7A8A1DA4A21F90A9BA345838285A43BBD1AB43472DF326437B8F619CAA6CAA6698FA223B1777F64FAC391FA387B4807A23AF218309F952E93C6F60108781003EEBA89F522FE0ADB82C1128EC57DA4E652A462FA7745A483A3195B8EEF2ACDB16B123418FEB3587490104861F0AF6E1C8A66A9FE3FB002EA26C3BF4D4A6577F651A50D15F53870305FDC40C610282DB9741A5E062D91F7E70034B4D5285D9EABD5426FF6DA4F4CE4F50772A0A61D98309A6F2CADCB76985FC00A570DAA0770207AA76D0B9988816C1FF5E1AC4B8C4169042916EEB683DE186DD390C49946C5A02CF5810888A369FF41FB1EAB5D28D1DC1A2AC0314D81EB5363C97D2FB6267D26D34A3658E66517CDB1F0BA512338D0C6374473C004FFF464A882810D97FCEE3669B6407F8CFEC4947237AF003CADB48E1981D3884E2C5907054F2D32B9E797F2C1BD271DDD030DF634E45D777510399D91EA105523B30C917407220F67B210C4D8EAF1CAFCE19AAAD17B8C803D9FA03A0241CD392083C0B517F3B5C17A1ACF7FC570985C303A562722AA0AF0800C633559D5801A0A45AFD4C4AFB1D93237A3870916FEA1BC05C34678A42F00BEB1BA03727C9BE887E7AAD67DBD192A31E6FD204E86922CFB3A27926955B96AF2AD03F781CB29A483C143E36A9ACA2C9F8354CAA89E96E9F03958E5BBB905E037A1513B0C90F7B9EE55E0CEDAC0D3A078AD5BE57EDC90FD10C2164D2EB5F0A8D92E7EA3217930E6648B0C48C23BDE188E584F48024DE64BF7A3498528EF0F105C6601A9810D4887D98FB940B27458A0A3D10C7808C778680401BF9B3CA4D40FB00582E6701311DC3B3FE34C5D28C5E27FA0581D8372246D253618B37602ED05B360DDDB03B1F29E30AC7D35BA507B75B1CE915D6C3A8567B01885C394CE083C213E977D06F8C0E34B5EBDB3CCD0D30849F3FC3EF00316AFF02DE794366FD1E51BE8C68142C8E673F8EF7E7C3357C042CA017E97EFCE31759AF3431CFBB9E5618383B259FF449A577B18C1475D22184BE92171A3D3B479B2279269856D6308E11341FFAC0294875F4A25A6616002AEA328BD9E17970EC589C7EB5BBEC2E57F94962B6455407727B59187126D2C84A17706429693885EFB98302680E2C4B391E6C5FBF14B0E1626E1914E3DAA24F365179A8B41AAE02C44A03883EE6F2F615286B47F0282B82764EEAC83CE825B704079B4A4F608DF52D5BC5BE0EDA7CD030402E4485569B8B4C6AF9EE25C7A6FB78A28985F0AA2964828FE648C60A1C44EBFEC99BF514A75CF170CF5CBA2153199E124B8CAA5091616CA3084E01AF0FF18C3309FBF9C0C52E76B5C368B5767FBEB09E872ABC9420D10ACCCD679C02DCF86726EF0D348FEB9B4BCACA463E61DE6B20D7AC1C3ED649704D87A56018ABDCE091A714B88F88C3F4E8F18020AC56F1357C0535615C3C94B8D912E81ED5D77F2CE121366B96F5290BDE36B7FD8FF01A4BC0A0005EE4163828B9CE06542DC11DC0108C5EAF4598CD07D6F05157A04FA7A79488EB6D429CF1BF6A45218C93A1DFDFA016900C2EAC81840906C512EDFD1828C7146D14E9993EB14A4EB3F6350D27B502D4B0D65547A28DA8C0BB46D947954CF8DEAC0AC65FC2175EEF882A4344559E4EB3A22FF081BFC52CD8110B8A2C0C9AEF20A7D89386888F6C061CA6B5F546B8F2D84FC42335BD6B0BDE86E6517FE1EBFCD5E84E6226770C41A903BF094D1F32A1C48973B0E257C281B07C2850B85B20491EEA77483EE11A357A182D9581888BC358834D349D1B77BC33C9D8F5993CDED3F3A44195B6CF18E3244E18C6AA9284EA677FB25B1363848D2545A7DE701A7DF13E59FC857B973B24469643855A0B4CF1104F9CA9A51DBD1596069E2CDD50E1536E0086441958BF43C3CDB51C236D36C9A1C64AD34DA5CC4AF4DC562CDDDB34100398F4BFD5E46E1A163ECB2B3905D28B4F2A32367AF138BE4C2AADDD341A4D1313958DD26E0FDF136EF84A60314C1530D079B453750CEA10E26D0696457CD5E7B601160C55DC0598CDA15EC094CD07A986E04D69C4464AFC7154049F620428F63088B0ED525242CDE9F6D116A7D150E6A04A51BAA36AC8EB33124849B255FB3AEFF612494AD0652208C2490F0A42FA55DD3CDEA6566A8074DD783226E7308A15350B80288104250A46F8351F3BF5ED1649DBC1F6CEAA9548BC0CAA19AA09B43562DBFCD4258F72B37643CCD8F9766C994D6A15966DF8BC281C942BDD8F94DC038C30F639A77FC2CA67D723A82ED02C38A3ADBC13CFBC403465698C90952CFCD8D0911FE18807FEABE9FC07D21D8C4C0A15B2A13061A282C64EB91945132D0DB70966273E20A0110768717BEC389E8921864FED429326935B960262880C0759478FCE67B3A6B3AA09839863102EF31D72A4840141632B75401C152FEC0C9B7C87088D67F70B339384970618F584325A238AA52034A11D4E2185182B5E4F778E02BC16279CC56BFBA0B93DC30ECAC64C5F121D4EFDDA6F36491F4E8BBFDE0AE82007BEC87AA960F0F4CC51C645CCF35DE6640F8B18A9BEC0497948BE1B3AAFC8B5F9DC6138297CC9B47DAD38DB0379AD9B5F4E289C20F1D34010CBEB012BFBF4CCA7322A20D340FC9B96C1C50F381D5565FD680A2F91169ACCC136B243E9F100BA3B2AD484D3CDEB999B3E686194FA19EC6A4BB71883BDC965E6C94ABCB2AD4EE1F3302C2968D7794424DF8ED0862AD4F59C99B2F9678A623FF2023FE2424250E9519C3C095FC83B6206CF44AA8207407AB5A2364ED9BB0904AD53A3B12F467C0C61319C8B85A7BC10D6E2C30C10F271A7C254FDC0729018B16B734039B7C9353692416DB1FDEAE1F6BD6116021BC884B10C1E8F4B1783451D8218AB372BEB89762D127372F8E276344119B24F35B04648A755B0AB4DCB2B920F6CA7E2C4D4C9D985F79ACA6D7A09A154FCB4393D4ACC949E86D21DC8A5B60A1E7F2AF3B5984D3CF8238EBB4C099321B0AE61BFF564CA411C2324A64491B88442916D6BC5E5F92AA5E445BBC9F379DFAAD01CABB1CD16D3FA9A0DAE3360817B3306FE07399985C0184E2EB6985177433BCFBDC42E08C90E8EC11C5033A1D9294EBB0B8D34E6668E1422046E6A3413EEA78344FA0DE3E929C739B5EA75DE9ADC43E5AC5C75DEC03679FDC85434A38B981C8BCF1CD53006D69416CADFB1C0E89AE314291E50740A289A0D035850FB0CFE2AD082FF6C45A572F549CEA96142C2D06855DC18CA64FA80FE0BFB9C323CEA8EEB8887652E2926732BCC85D1874A58886C6955322966F1B5655648309E3B2A26105CC819A33ED2477CAFF9B83271E50221073828070A4F3E33CF919B64469E0B8A893D24DAB1762EEEF8CA43C64027FC5E32D701C9736C9CC6CAB1269493D17B2875E4AEC2CAA5ADB052D341F5820E31E6940C0983560A5CDD816C2907004D2FFFEAEAA5B1E5F08A615FE1FCEEAFC136051E40E360D15C10B01D3781CD684D5EE9BC642D07CE4361C7F588DAC40CC6B80485632E9C1A46BCCCC4D2CFD39AA00B34E293429831BBD56C2430EE5614B96A5C036329451001396757D59204DC2EAFB5DE7BBFA09B4574E09D96F8E4CC4387E97075B436B7B1A125AEFB483ECD818596FC393AE6C8E9319D8CA5A6B55C64BB4351A8462E3F7050A2068AB58BA7D101D25822E70D3FA4FC4EBB5EE373874E400313531DF74A84C8239C464C53C334C359B34025CFDC4743FF229AF5391A6CE1D928BBBFB7FA1245902A2D4024B04736D838310254786D33BC97D42317743DE29E15971DA694059280C8EF92D80AF930A67B529DF55799CC0CAFAFF440DB8EE20B05B62E92FF447786F393416ADE380F2F7448C26CB5245DC51D3D2301AA12BBA23DDFCA473030F0EFA1CEA7764BBB5DB7212C4BAF1ED33501D6260CFEDE17A65291C1A87950E0279690E26472321AF5A9EFC5420AB4B860475C9DA1DE8F981CDFC79AFA684908D00F83D3E3CD1F07C9DB029C842796085D0663F4CDB373FF6A08002588EB1F77FFF4B9CC1A010289C9FDAF2A4DFE46F873562D26377F8B38104496698C6BF788F5A209A991B1ED84655462E3C8D29B83A99081C59FB3FADE0E9C75089D64E0DC93E8A58E95E4338544A89EC42AE145A7D95F9F590C8250F5D3F96ECC242B1870DB664A26BDA04E1DEEEC8C1BC44802F1D5C7434BC24759991C272836CFD0552B422AACEDFFC38C1A0F605E900903950136F97EAB802E392BE90451F25D04806B835549B113F9D6CCC88CF63E9F0872AA74FFE918D79EE2A2FAC79214E59023CEAA90E5EE5A34EDB93B9829DD672D1E3CA3BB159265BE00282183F28231309AAD2066FD6B32F0FC759980C905C50E2CB70546CFD876810490A40C29301C29A2C4DB2494D2DD5E3FC23E6AA8DFBDD820C41B1EF24C815A140A0B5950E22DB2BD7D74018A7C62C72150C95BD1AF7A5E88D244D5292102D6BF9719F9A6ED97B007DAAAC0B957110A4857B0FEAB94A0AC86A1A1978D4FA78E093238CD2C1C703FF256741F6F88151AC4B0DFCE627FD95D296019254147E5171BB3E59E6820D03F1B00B8116A295E7791B418D459DD57A21CC86500252DC8AB5B54DEB4CC7EB095FEF08AF51164708C54246BB912DA8EC424A6F853D0219D41C281C75C80A5258809DFEEBA702392F79E22100345046B86A17877B25E6DC2F8ABC1C53518247C0DA07B9D62C6C8308D61102C07E9D8B6CF3500A2830E186768D5385BD412BD0E3813026673FAA80DF47DEA18A8428FC1C266F30E93BBC2EAA32984189D249EBFB1312BC033530156DFF0C245B224E8F9A23A16AB089F8CAC35740665A2CE53847C559721D2903E9985D4093C6BE582054551422099DDC250581ECCBCDF9A89A062A1763214ABDA2A15E61B822E6BD7EB38998AC120E158233C34D2123A304111BB015A45C33D8F0C5DA622E35F955C0CECCBF9518421932870B2DDA10E6D2B3521D43260D8A19478C7DD0FFF96A51466B2E2701AD14E6D93667E61E78F1B89B3CC06515DADC48D46786742A71F9EB99B32A0FD8191EFAB70BEDBA9A07A8BD95FA35E162F5E95BB52666455C7214DF84BBE5EBD6285EC904D7F1F8B5B43EB7137FFFFA484CE26EF63DAA836137EB886B873514B84CA4708C7AE3F2973D6F2AF0246B99C930110CA9D456E99CDF275981575BBC2CCEE86956D971FF8DE8099BECE101DDF2772C266C2C5F2FB010528B21566B6CF0E8160DD495D6104CFA36448FD077124724C9C084A737093ED6ADEDD846F2B640F426C5D10E30598BF08D70CD114B8C8CCDFF36EED37599E6DD630C6AB7A28B23C55613AC4484A1C53D5B911A570F6E2F45122840929CA2E1BF0654ED678009B835F3425110E3EF2B5D746DB38024A0422BF2468F8F995C02DB9464E7E7D3CF71D677ACC109C5CF90DDBB58D2A096D30487B61E8659675E97C959CA822C92D872102CC6B90608CEE167D9B132D23429BEBCCA3A46301DD7415474A90F274406BA79343AA36A1DE8340C9402D4E519441D861478F0C5C3AE06C2A0CFB50436C473E2C2E2DB86A5828CC32884022D13E8CD42C09FCE8AE291228224FB5052F28F5A70EE2419E4A596E15236F10C36FFC09ACAA7E497BDB1C1293F2C7C3225D07969761B60198CB4C662A8E50EF8F5D8B9A03E7F93DF84C727A3A005D82ABB842124C87B9DD25800525B5E6748D5CC3932E8EDD092466201C6121A55F3090FCC3F81DFA48341BFF0A3AF4A3B38E73413E979C14AE201640E7E5EF7191037900B2331710A9AF82C5FD70CF9AA49168004531033593684FABD0190A64CFD2B458A9FD4B07E54BA1CE053F093841C84736BC28C5C4E050AFD58521FF9EA090C040B3ABE2DD4AAAD0C197921D03C3543889B5D1C7AA9850236A3B4DA07EC2FBDC309E058035CF5241D239BF090491028C1983A3560A93B028017D1E9538B688B487D0ADA40D0B71B6BED780A9EEFAB9402DB6B21644FEEE7000664F1AC6BE81900873C92345F50651C15901B41CD0A5BF5C742ED5D96F651582A2898A350198525AA32197A3C232A9B383DA9DD8C81567AAAADE83C66487DD6F24819178CD280252CC1AB5CBE515D2FC757AA9BABAB3A4F45AB0CB8682E86D3DBFA6084A5B77E78D17810ACBB55ACCD7EE74FBE624EA81E8B1804BEF83E426B76E004B228CA84DF4106DC33629D5CD45D4E345ED466FD3EB9E35E0F20D96FD6652DC6BEB8AE21BF46A1873BB6512FE102CB825FA4DA430A641045AD787B770006256DC8E845B33EB29FB82AB6D96D988FB428A25C61639272C261F2EC32AB60DE093C66E71D73CA586BA027DAE901839AF404EF7E01E4D2A30148DA9802C2C91DA78A02B41423BBBB011C29790ADC46FD4A488D23B8D473A21876B204ECEE0A50AC8EB5403973888265090F4A6F6F6ACB489CFB2173154928B19C849596F815B06EA270318CE585BF748BD8E1218090107B9887F83AF018B6A4E791247042C208D71A49EEE71620BACC8831398C302C752F95E4C79BAB3709323C04036068797532393509B6ACF47ACFD5F2DE0684C13C7824D6FC8984F25400A0E437AAA3F3556909B093709F5618C3301DAA5F52F2F3202F34083C248031CB88E54E138427A863959135C7F61E95A8BB0937B646F27B471772B78200E2BDB77CA6F5D373C2A7BA08EFAD330AC8A08C4810A1AD52E6033C738B15E3B471A9C550E26061A6947B2B1B36945F85A205C20B25CF4730064A1B05C5CF1D4453498271D560E7CA4F3868BD279CE2743464B58F38480F1B9728220630D4F09E09C32BFD7AACF46F0C730DCDDACE8FF2C781A929A59C0AF4E237050F418637353666B90B574F901686C18088990E2BCC1424327800C630641C315D89E2DD2FB6ABC7385077E1BBDCAB92C20FB27B8D3D1A34E25284C8EBAC0C00CA0791A9F443C53BC13FCC8C3C1E1F40E12BAFA69E1661E17F58B4F05362511955DD23ACD62A3ACCDBF800CB1CCD8785EE66CA60514DB48D6EC5CB9E3E64ADBBA43FEE5AB077DCDE6D295796330BCF3FA9A0D58A437561CC9C00D6B3230418CA4D5E82231C07624495EA8AA899DC8F42E5E99CA542ECCFAFBBF2E8149C824E466E40B35141F07332600C98DE345C3DED7EC0BB3437ABB9E14428CDD5E3DA0CB3FFA21007921CBF1F32B04E7372D4AE4F83D557887D2678FD6229150F64E1DC21B34EBA49A2BA7C3F02D586E53645828E1AE7E42FC9931D1FC61D391ECDCEB3EA0A48ACD2A6BD6CC69481D76D024BD139041E16AF196BF3095A59C79ECA55DA7EDEB74C99AE836BC4CCD81F632C6258006CDE3BE0AFAC92386BC841C2291C7915BFE630E1D3124F4EB90CA4FE87CE8507BE49394C7AFF19E33E0CCEA628477B461641224D3B05931128C4D34C57E65F2B8D78E050DE20CF506B1EA44E345250A5267A0A61576484A8E92E43B4306AB2F3E85646BB386019516AAE9927EDADEF34E29C0390AC4203EBA0488B3624A34FD9927B31B5C606170EB94E83A91BB284E1C8CDCB0F4B360200AF4B659943D388653F85C9352AE19488DB79A2FE1305E2BB8FD331A397E1985743E789E4C63912E06B14B66C82285AE4357FACEA39E7F82083EA60194CDB07A7C7700804527712F4708BFB430C38C102BA6BF437C608A61AECFEC2C5AFE516E0C9941884DBBD1BCC3165121209025A39429A312A03C1DCD1A69B0DAFFA9246FA3F869B62975FC8601162B240F677CF56C2158BD5A9D835EB8E07D1A8DA24A7EA24723223B56543DDB4B42BAC5CCD94AE87333058CE939E590599DF0F85A3BF02B02B67EB303C59FB86195034D560E225E70F5E55B4F43386004D9CE6AE430C3AD91D76C1CB4315680CF71C7F2B4B86068E225CE232342E9FC1AD173721A3CB94FB6C34F5E9E00A68DB4D3B597F092020E51BF4F5BA6C434450D896274AC2A021C675DF5E43868169573B0BCABC9B7C79C781F0AC0C108B18773B1EDA20E8583E59048E5D4A603310C94687BAB4FFED461E045A4BC3A3B15744A5188337A98555C70E11F018DC6CC6DD4A3EBB5BF00CA901B7A207E6C021C66DB8926D09F9151E800878979D15A2ED571750222E095A4A1546BF319561639E988981F5513410CCC60EF95C5C6768D83F980423433E0A4322458E6B007F43AB7342969B99790F094BEFDC2DE15FA1871E494B503802BAC8CB842563182321621C210975CEC766A0FFC308D808E5BD8D56666192144621A208AA516A71D3F9C2CEF6F02511900229B46DF8BC708C44E3D8A298F708196BC66F75F91732E5AF062775A9ACA36CE2DA64BC"));
+        byte[] sk = Hex.decode("07FEFF0702FB0106EFFCF40401F9FA0B03FF04FDF2FA0F0201F40810FC02FB0D140204050CF617FF00FD0805FDFB060007F2F2F6FA08FB05080504FF0801F306EEF5F8F607070202FDF9F3F6FAF5FF0703FCF1FBFD05FBFE03FBFD0509EFFF090BFFF903F20002F309FE0302F7F3050906FE110207F5F1060DEDFAF909FBFC1502FD010707FBFEF3FD03000A0301FCFAFAF50500FE06F50D030008F1FA130006F804030AFEFF08F5050800F603F60700FB08F80D0AFC06FF0FFF01FB00F7FE05FEF9F60301FBFE0A04F9FD05F402FC00FF09F70EFDFEF7F10808FB01FD030203F3FE0EF8F5080207FFF30D0201080A03EEF60104FC02FEF9FA0A02021000040705FEFF03FD06020009FD05FC06050204020A0D08FAF9F1050DFB07080A0405FC09FD1003090E0C000703060204FBF806000AFDF7F4FE0A0104FF04FBF6FA02F7EDFDEF050710EC0B0209F90600FF0703070509FA0CFCF9FC0304F6FFFA03F3FE0202FBFF0812FD1409FE02FBF5F80B08FF07FDFF06050209F609F90BF6FC0013EF020E0F0B04F4FA050DFF0BF8F5EF0AFCFEFBF70512EF10F505FAF7FFF9FCF7FAFDFC0808FE0A030CF8F50C02FD03F108040612F80214F6FC030C00F30B0EFA00FDF80C000603F809FF0304040AFEF807F60B05F5FAF6FBFA0A0A12FE0007FE0AFC06FE040606FC08F9FFF6FE0AF6FB04F6FB060C00FAFCFDFE0800FBFB05F7F20004FDF6FD02FC0210FB040DF604FC06EFFCFAFBF7F6F9F003FCF9FB050801F900FE0A02FF09FA0203F1F900FA06F501FA10FAF806FCFA03000908FC020AFCF70102F9FE05F202F2FF0900F702F4F1FE06FAFBFD08040705FFE9FCF90907FDFAF00F010B010B02F4FFF90FFB0401FF05FE08FF02F3FDF3FFFF10FB000FFF07030B0205020D0B01040BFBFFF9F71AF00306FEF704F3020AFE03F0FE1801000AFBFAF7FEF7EB03FB08060A020109FC0E0107FB020BF80506FA0CFD0116F603070E0808040AF60808FB04F903FEF1020204FB060FEBFD04FF07F8040708F807FEFCF9020A0A00F6F80A0D01FB02F5FEF40908F401FF02F8F50B060205FC11FA0404FBFF0305FF01FD0303FC0DFF030A0AFD0009FD06FB03010BFE0B0A0D0003F3FA07EEFF070008F7090206EDFAF9040709F50D07FEF50EF9F7FFF8040208FB03F6FDFEEB06F5EF03FCF506110200F6000D030603F3FCFB0E0EFFFA02F504030B090B02F90806FE0D04FC06F6FE04F90607170106FEFCF6FA05FBF0F6030600F4EF03FAF802F30004F30703F5F8FD05FEF0ECF9FC0209F906F5FA05070CF60FF709FEFAFB0D0305FF0102F807F703F0F40D0204FD02FDF108F80D0CF7FAFA070503FB0307FDFAFEFE08FA02F80D0B0AF7F710FC03FBFDEF07F9FD010104F1FEF1F4090507FA1101ED0B050212F80E02FE000708F5FFF202F500F7040DF303F3FEF4FC0D050105FFFF0A01F4F6FFFCF6FA0001F908FFF3F3F8060AFD150107FBF3FFFB05FA08F6000DFD11EEF9FEFF000CF600FA020204F70F0909FFF3FBFD0906F7F5060512020803FC0001FF0C05F8FEFBFBFEFE05F90307FEF7EA081105070102FAF706FFF41407FEF901F806FCF105030F0100FDFA01010807FFFB07F50DF4FBF70D01F805FAFCFF000AE6FE0D00F80AFB0007000009F8F8EEF3FA13FBFCFD05F9F503040EF608050CFC0E060304FBF80E00080901F7050C0007F9F009FCFD0001F9FB180EFFF9EFF400FA0206FE05FE06F6FEFE00F207F0F9030A0AFF00F6FE01FE0C090404F7F0FF04060F0B0806FDFDF6FBFBF2FFF4FAF80CF9FFFAFFFE000703060BF900F80A01FEFFFF0201FA00FE0BEC03F7FA00F8F60C040300F6FC03FC000014FD09F80203F7FC0A0D0007F806EFFF0409FF0007FF06ED0A0A02F3F1EF0403F8070405EF0102EA06FA0206000308FA0205010FFFFCF6EF0C16FAF7FA010DFD0503150EF903060803040004FD01FE010607FC15020808020406FDF5F304F7F8F6060AF7F7030CFC01050404FB01F8F8080905F70607060AF3F303F50501031200F502001205FE01030503FF0400F2F2080AF7F9F8020DFFF5FF03F8070806FB06020704EF02FD0C0108F90808FF0109F70BFC0BFFF30801130A09080202040EFFF504F5010704050A01EDFB0DFB1109FA0605FB030CFD01FC1601FAF9FD09FF0CFFFDFCFCF5F8F50606F9F8060102FBFFFBFB080CF913FB0409F9F90C0EFCF8F9FDFEF602F4FCFDFC040902050AF8FCF9FEF40C0A04100701F60E0901FC0200FF08FAFFFA05F70803FC0E0707F9010A0600FB010CFA06FD010EFEF7F7030011F202FFF5FFF20706F7FD03F50205F70500FAFFF705EEF7080C0A03020C0001000702FE02000404FC0105F0F7FFF0F7F6FB020DFEFBFDF0FF07FD090202FE080201000B0409070AF807020EF402FE1003FE02061202F8FF02F200EBF90BF8FDFBFF04000704FF0306F7F3FA04FD04FE0CFDF80BF804020501F3000403FAF8F704FEF80FFEFF06FCFA0109FE0AF200FC05F3FEF90701F40E00F1F8FD020BFA0B09ECF2FC090800FD05F109FAF708EB0C0D01F30AFD0B06F70004F9FF0902F604FAFC02FFFB02F5F50900FFF811FCFFFF09041402F5F604FC07FE01F4FD02F0FAFC0005FCF8FD06F80003FDF9FD0EF7FDF907F5FF07030B03FDFE00FE000F000A0D0303031508FAFA07FD0314FF0CFA0B0206FAFCFA02FE01F7FB03F70004F7F409FB04FFFE0601FF0AFA05F90401F51005020A08FF0A0CF805F5FF02FF0501F503F8FCFD000403F5FDF1F805EDFEF9FD0B04000008FF07FB0C14F8F9FB08F8FFFA05EFFBFF0002FDFAF2F802F8FB1A0202FC0010FBFBFEF9F705FDFCFA07FE14FA010D070B0105000008FCEF04F6FD0B04F9FFF00DFC0A050608F9FAFAFE00F7030BF50BFD10F5FA0F0100080C10FD0600FBF30A0A0CF4FF01F20A0E0D0CF4150000FC00FC00FF0E07FCFAF90CFDFDFCFDFCFEFCFEEE0A03FC07F80205F902F60A0208F3F7FEF90202FD00030004F2FAF903FBF80103060FFFF9EFFF01F412EC0A0105F901FF0E0A03080202F6FF09FBF209FA0DE805F4FBF506060110FC00050FF5EFF50A0BFEFF04FE14F3FF020EF802020508F5F9FEF90806FCFD050E0711070D01FA060205FF0204EFFEFDFB07FEFD00F90B030508F6EF02FE16F400FDFB060304F701F3F4F90501FB03F8F404E5FF02FB0A00F8FE0C0B000AFBFCFBFF00FAF2F5FFEB05F0FA05F5FFFA0408FB00F90204FF01FEF00C12FF01F8F6070402000502FCFC0B04010E021103FF0BEF09EAFDFB06FDF9FA080CFF11F90AFCFAFFFE0301030001EE0EF3FF0000F40FFEFB08EA00000F08FBFD10F307F1F5F200050105F9FB0AFD031302F4FFEEFE00F709080BF7FD06FE0EF5EB010DFD0607F707FA08EE08FB0007FF08010102FA00FB00000803FD04F806020500F8060CF4F9060EF9FE020A05FB0CFC0006070202F6F20003FD0DF106FA0206F10304FDF80F00FDF6EEFFFDFE0FEBFC0D1BFB05FDFF02FA0806F7FCF1FAFD0403FF06F80E01FBFBFC0C02F203F90B0B02FA0CF815FFFE080201FB16FA02040802F50503FDFFF8F7FBFD020202F8060D0601FC04F1030202F30FFEFCF50BF60901F306FE0605F9F8F502020A0406FAF80208FDF8F9FCFFFD0EF30B04FFF5FD040001F8040105010100FE0E00FDFB0606FA04F505F9F811040607FD080500FA07F300FFFBFEF0F5FD0209F802FF04FFF9FCF9FF0AFBF800F80AF9FC09F9F6FB0EF80805130B06FFFF0104F40603F10001FAFD06EDE5F8F90C05FF1202FEF000EA0006F801FFFAF1130CFDF9FCF806F913FFFE120609FAFB06000CFBF804F40BF4FCEC00FEFD01FB0307111206F9F1FF06FE020CFDF802FC0003F9FEFE06FEFBFBFB0BF8FB0C04030BFB00FC0405F8FD16FBFF0207EA08FEF5FA0F07F50506FE00F3F20BFC0A07F702080FF509060203FD0505FD0BF6F905021206FAFDFF03FFFC01F20204FEF6010009FF0BFDFC0C120402FF0602FC0106F40601FDF6010B0611FFFFFFFFF8F5F7FEFCF5FB02F805F50105FEFF0202FF09EC0408FC03EDFA00F306FCF8FD0301FBFA08F8FD0007FCFFF6061011F80E090D07050506FAF00AF8FFF40304FE020AFCFF0CFFFB0BFD0E0C05F50C02F60EFEFDEDFF090802FE09000BFF06F208FAF9FF07F500050901F508F1EC0BF9F6F30FF8F006F6F50007F107EBFFF80406FA090401FBF812F608F4FB09FB0FF8F0F1EEFBFE050011FF0AF7FDF201070C050EFEF5EFFD01FF08F500F3090BFCF201FEF90BFA0DEE01FDF8F5040109FA03FE0601F30D02F904FEF804FE070700FB12FAFDF9FA13030A02FAFFF60407FA02FCFEF3010E04FC04FAFCF4F7F802090307070F03F808F911F4FBF004FCF704FB0B13F8030B01F307FBF405FFF30105FAFC07040605FB0006F7031001FE120600FB02FD08FFF60C0709050108FC0CEEF41AF40DFD02FAF50AF6FEF10000F2FEFDF0F6FBF9F30BEE0D0E0105F6FF01F105070E06FEFF0713FBEBF8FF06F50100F504FDFBFD080AF9F6EDF9EBF60B09FE0C00FDFDFE00F51EF9050E0A030101F70A01FF02071BF2FEFCFF040309FA03FBFEFC14FCFDF6FC01FB0AFF020206F6FA0D11F7090BF9FCFD07FC080709FE06F4FDF5FDFBFF01FA0F0607FC02080601FF0506F7010808030006FCF8080F0200F2EF0AFF03EDFB03EEFA02F00AFFF80DFA11FDFA0FF801010BEE0103FA02F5FE03010704FCF6FEFF07FC0901FEFFFDFAFDFCF002EF02080506091208FA0B0AFB0116FD00FE0416FE01F70B0DF60110FF0608F60507F701F6FF06F3FFFE0BFCFCFBFBF80204FDFF11F307010301F6FAF003FE050704FD02FFFAF503000103F0FDFF1006060EF1FAFE00F8FE0A08FC02F8FA051406F508F8080306FEF8FE0F0108001305030DEE07060608FF07FE1107FDF80105F20A10FA03010C02FAFEEDF3FBFCFE060104FCFF01FF06F6F5FDF4FD0904F702FB0EF6FEFE01F8FAFB0003FBFB01F915F407FDFDFA01F9FC0A000807F5F8F4FDEFFE09050B0BFCF9FA030103F2FDF907FEFAF70502000FF50605FCFDFE0A05FDF5000201FE0EFAF613F9FB000404EF00FE0D04F901FBF60CF7FFFD0203F8FDF70301F2EB020405FFFD03FB09020405F5FD06FD10FD06F5F5F808090404FFFDFE08F9EFFF05040CF5F412FF03F4EE0308001007EF0BEE05FAF80FFA02080807F90001F8020906F4F2FAF901F501060800FC0306081601FAFD07010EF606FD05F9F305FDF504FDFF060F0606110A0002FA030007F80E040BFFFF070100F6050008FB05F8EEF70005FC0409FE0007FFFA1000FE08FD030B0104030D0106F8F9F4FA0901F8F7FEFE000D0FEB08FF020B0605010B010EFDF500F6FB02F6F8070000030AF6FC0702FAF90DFDF80009F901060B07FEFDF002F206F109000DFDF1FCFCFBEF07F7F307F608FD00F803F809F9010408FCFFF807FD00FC0DFDEDF90706FFF7FF0DFA02FA00F60000100205050B00F602FFFBF70BF60001000A09060A0A0A05F7FD01EE0EF8020BF5F006070AFD040B01F2040EF0F60AF90BFC03F9F102FDF50000F3FB090FFC0705F209090304FFFA07FDFA02060507F705EFFF0BFA01F2FBFDF503FEF5050E01F908F406030005FDFC1603F601FE00FE03F407F50308FEFF0403F90204F509F1FDFD0503FB05090802070D07F9F8EE050E0107FB00FB03FA07FBEE0012F310E4FF04FE000C01FDFF0206FBFDFEFD0802F6FC030702FB0203F7FE00FAF6F8ECF5010DFE00040BF3FF05FD0807F2FFF9FEF9FB08F7F7F6FBFE010708EEFD04FEFFED0209F703EF07FB0AEC020BFF060AFC040308FE030D05F109120CF80001F4F708FDFDFCF80B00F203F8F6040203020010030BF9FAF9FFF404F6F9FBFDF6FEF8F7FC01F2FCF8F3FFFFFCF7F20B0F0A00FBFCFD0408F50DFEFF08F504ECFD13FD10FD08FD05FF06F70407000C1005FAF7FBFBF304050DEC0101FF0CF1060010FDF9FF0705F404EF0307F9000106F801FB06F802F8FF080C0CECFE07F5F4FD06FCFC0401F7FE050EF3F9FE03FCF10302FF08040907000104F90303FE11F80602F5F70201FDFA060FFD03F40A1205F205070AFDFC0A0002FE01F902FFECF9000D00060EF0FB0D06EBF50C02F614F4080402FB000704FBF706020814FF0A01FC0A0E00F0F8F607F8F608F1050C04060213030BF60606F9040C03FBFFF3120403FDE70003F4010EEB090001F40903F8F9F5EF06110D08FD03FF0408FDF50D0407F9070201F9FEF4140F04000A07FA08FEE907030A05F6F8F4F7FC09F502FAF808F6F6FF06FA0100FF0B0D03ECF50AF7F0FAFD0A040901FD0FEFF70BFDF104F600FDF5F1F80DED0D070009F907EDEDEFF201F90B03F90D0301F5F706F402F5F7FFFF0AFBFFFAFF08F9FEF5120405FEF808FF05010A0AF9FD0DFDFAFAFBFD01EAF6FE0BF3F7080306F60D000403FEF90AFB03F70D06F907FDF811FDF806F707ED03FE0900F508FF0C07F704130805F6FA0300FCF900F804FCFB010309FAF9FE04F505F501FEFB00F4FA0A0500FF020A03F90EFFF80305F8FCFC03F20408F5FE00FA0009FAF80EFD0A0311FBFF0D00F9060505F60504FFFEFEFE0AEE0A010EFCF701FFF80302F0F9FCFDF5011209F8F80500F4EC090CFD0601F612FEFB0F0201FCF31007F7FCFFFD0F09F01013010707FB020307020402F9FF00FB00000E050600F7FAFB02FAFE0CFFEE050E020D07010304FFFCF9FB1101060907F606FCFAF9FFF90408FAFE080DF8F7040C07EFFCF7FBFE0CFCF40DF908F0FFF80803FD13FE000208FC0EF6040AF1FD01F2FDFF0800F80FFD0FF807FFF9FB01FAF8F9FCF8F7030CF7FC0305FE04010003080AFC0300F5FE04060AFAF114FFFDF5FFFFF70AF7170416FE0AF70207FB0AF5FDFA06F3FC10FD01FB040A03F9030707F708070507F308FB0AFA04020404F8EBFA06FCF403FDF7F7FC0D070306F5FA000AF0FDF300010EF8F9FEFEF904F801FBFDFEFE04F2F0FAF8F2F005020FFEF7F9F4040AFF000BFB03FD02F9FDFCF6FD11F815F701F2FF000CF7FF000AFB0F06FF011400FD04FE050209FC03FAF807FBF8FD060201FD07F607FF010E110AF2FBFBFD17F11508FC0202FA06F90B0A0602EF02FCF0F203F4F8FFFDEEFBFC0512FF01FB0103FFFCF5FCFB00F703FC0A09FD02F8FDF4FA07FFF404FBFB0CFDFFF1030610F3F6F5010017F501F505F4F603F1FF0A04060209FC010608EEFF05F60604FAF90202FB030502F4FD05F70312FAFF0007FCFB0B020C07090AF9F2F6F8F203F1FBFF000207030411FA01FFFE0303EFFE0606FEF6FD000901F900FD07FD0B00FC050004070DFD050CF50B0D03000104FC0002FCF6FCFFF008050D0002F00CFC0506FA030DF604EEFDF90609E902F5060AF7FA0A0309F90BFE02010E0B0405FFF6FAF80307FD09FFFDFFEE06E8FF090805F5FDF6F90C07F40C04FB0BFA04FBE604040DFFFEEF05ED020108FA00F804FEF00304FBFDF702FB02FAFA06E208040402F2F4080501FDFA00070DFAEF11030F10FDFB08FE0B08090101F1FEF7F5FF02FDF8FA02F8FEF8FAFDF4F5FF0809010108F80106FC0701FA0C0B0DFD050301EFEFF7FE04070A050C0BF6F308FC06090207FF0AF4FF09000A020304F5FE0204FC01000101E9FBFFFF090303F5EB1802011204090EF80004F9FEFFECF2FCFCF303F8F505FF07F80700040D08FE08F00006040006F9FAF7000C09F804FFFC00F910EAF8F80700FCF5F6FF0213F8FEFAF8150208FBF80803EE06FAF504EEF50F0800FAFCFFFA02030211F50CF6F8F004FBFC01FD0BF20CF7F60100F7FA05FFEE0507030807F8FCF6FB01FA010600FC05010308F61402F605FCF9FFF40601FCF9F605FF05FE0011F80AF9FC03FD00010B11F8F90802F9FC0600F50A02120501FA11FD03010AFC030C040405FB02FB04F0FAFCFBF700FC10FCF6FC01F301FCF803090DFA18FB0701030DFD02F204080806F106FEE9FDF507FF1505F8F8F8010C030909FD050306F40F08FE120A0C050102F7FBFB000604F8FDF8FEF8FE080AFDFD08FF00F007FC0003000401FDFFFAFC030401F2F6020CFDF80401F7F807FB07020C000C0101FEF6FEFDF60811140E08011407F905FA04F504F60AEDFB0210EFE405FBFFF9F0FB04F9F311F7FAF104F0F6000009F8F5F8F7F303FD0201041A04FC0509050301FE03F109F1F7F9F10311FFF3FB01F8F9F6F6F8F80AFBFDF20AFCFF0CFF01FBF8F4080DF502F201F0F7FC0AEC0713F6F90206F7FEF502FD05F9EB110001F8020706FA02F80105F404FD13F8F5FAF104FB0E0301FCFD04FBFF02F5FDEF03FEFBFD010D0708FFF4F6FEFFFEFD0CFEFD01F8FA1207090001050AF20909F50513FA06F3FBF4F7F40301010307FBF60103FB0B0AEDFC0D08FE09060A03120CFCFC1800F5FA030AF901F2E8F903F201F7050DF8F9FDED06FEF8F0F5060507F5031001FD05F3F008FE0BF9FE03F5000600FA0103FD06F8FDF9090CFDF60CFE050A05FFF8F206FD0904FC0200000106EF07F301FD08F701EDFD0300070F1404F30BF506F4FC09FAFFECF8F7FCFDFAFB07F60AFD0AFD03EE090900F80207F0FE04FE060A0B0603FDF9FC0000FBF3080B07F90904F0F808FC00F9FEFA04FFFE05FDF3F206F8FDFB03FB07FA010CF60DF7010FFEFFF40407F302FEF0F80D011306FE06F905F312FCF10107F3060310FFFDF507FF02F3FEF1FBF700FB060305050803F90703FA0405020BFC02FE03F315FF12F80806F80201FA020E00F9F9F90309FF080D0501FBF512050201FD08010AFBF606F00908FD09FBF30810FBF7FD130109FCEDFBFEF901FC01FC0FFC0403F51A1603F20812EB0404F9FDF700FB08F5EDFF01F108FE030CFCFDE9FF040703FC061100020C04FDEDF9F8FC0A0603FC02FE0AF806FA04050AFB0AF2F4F6FBF4090E060504FF0703FB010401040606F9010C00FC0B080400FBFD0B0C0505FBF3FCFCFEF610040D0808FEF4F001FB000EFF070806FFFCFEF406F9F4F8EFFFFC0605F80805EB0403F91313FF0303FC010206FE06F4F7FF000AFDFBF707FAFE04EE090A08EE0AF8F9FCF7F50C0308020C02F3FB030701FF0D08050BFBFCF9FE1200030704FFFCF9F3F00CF7FDF805F501050302F7F9F3F8F608FEF105FE05EFF6F7FBFF05FE0713FCF80D0B0303FC05020B13FEFEF9F1FD15FFFE01FAF508FAF703FDF81208FFF9F90700F4FDFFFEF502FDF90B0C05070701FE0AFAF9FD0706100007FEFA0DFEFC0BF8FDF6FD0104FB0309F706FC0606F101F9FEFCFB1302FCF4FFFCFA03FCFF07FFFD1509EF13030001EE040105F901F908EF09F7FE05FEF1F60CFEF7F50704EE05F801F70208FD01FEF812FEFAF50102F4EE0102F10906F902FA1316F3F60406EDFAFBF7F5F808FD04F807060701FF09FAF4F604F60002EEFCF2FC13FEFA02FDFA0814030207F6150004F6F501F10708FA0FF7F9FA0108F2F705020101F30E0906F50F0BFAF7EF0907FA0003F4F8040A08FC03FFFAF4F8060605FCF807FB0DFFF6FDFBF7FEF8060F05FFF700F1090907F403EC03F705FB10030E0AFAF7F7FD030AF6F205F603F3FB0AFAFF07F40BFF03FC08FE04FAFA0001F60903F602060DF6FBFF0801EF02F7FE000E0BFEFAFDF506F503FAEAFDF5FEF50C09FFFD00F9F6F809F9FA090102F5FF0000050A0400FD0DFD0705ECEFFFF7030200F2EBFAFA02040E0CFCFBFD00F90600FBEF0609F0FE03FCF9FDFC05FB08F3FB130DFF0F0208FB1006FDFA02F902FB1108FDFFF8FEFD0311FFFA0502FBF905FD0AFEF50C08FC06F1FBF7F40003FD07FF060809FBFEF30A0500F6080BF60D0CFC030707F5F413F903FE040006040A0C0403F612090002050E0AF610FB0401FDECFBF7020DF9030405F609F0F0ED0103050FFD0B070506F808FBF6FB0519020703F808000609F402FF0907F3F9FC0704F7FCF7FBFB0D070AFA0BFCFC130AF804F2FF040105F0F1020408FDFCF9F407F7FBFDF300FBFC050A0E12F4031504FAFD0214FDF103F2FEFC0801FEFEFEF91502F10700FF08FCFB01FD060C01F6FC01FD070107FFEEF010FD0B0208FA00F7040103FF0BF904FEFF02FC07FEFE0007F80305030204FA03F9E8000D02FFF4F4F60408040105F2FC0DFD0504110102FE0F00E706F301F7FF0108F90305FA0202F3FCF612F101FEFBF80E0C09FDFD000013FEF6FEF6FE0BFF03170001EC0501FBF4FDF0F80BFEFD111C02FFFC06F801F8FDF9010BFE09F6050602020BFB0C05F90401ED0FF6060E02E9FFFFF700FE0E0304F2F5F6FBFAFCFD03FCF5040417FDF506FB05000BF8FFF1FE0405F102FEFCFE08040C0F06ECF6F6F9F4FFF9F9FBF8F1FE00FFF40CF4FD07FB0503010B15F3FEF90CEEF80AFF0BFE08FFFAF3FB0AF301FEFDFD01020706FEF5F714F601FD040A0A0D0A0BFF07FA0700EFFDFDFBFC06FDFE00F804F3FF00FF0AFAF9F801050F07FC0310F7FA08FAFF0203FEF6EEEBFF000009F906FA0A0CEEFFF1F7F4FEF3F5F4FFFC10FB080608FFF6F906FCF6FE0D00FF040B02FC0BFF00060D01060A0AF600F50CFFFCFBFFFBF9F8FE070EF5FEF8FBF606FB000DF706EE04FBF0030BF000F3F8FDF002FA070A0603FB02FC01FEFA0407FA00F2FFFDF2F80BF607F70402FC07F211FDF8FF0101FDEDF601F4FBEB04F0FB04FCFD050101080A0AF50302FC0803FE1200FEFDFDF3EE090E050DF9F803EEF1F4F802FCFD11FB02F4FE02040A0C03FF1506F806FEF9FCFE01FCF4FD0104FA02FCF9EC0911FAF2FD06FD03030A05F50A04F4FEF7FF0B050001FB07F80601FEFE0CF9F7EF0C1209FE06FF07FEFA0600FF02080207FFFAF5F407F5FAF500F90303F81106F90BFC050FF900000D07FD02F9090306F90107F407F3F7000007FDFC090003FEFA00F307FDEEF0F9FFF70CFF0BFAF9EB0B040605F70504FA00020CFEFF09FD0CFD03FB00FF0CFE0205FC02FB08FE06FCFF06FD070108FC0DF60E070EF7040D00FF00F1FE0108FBFD090008F7F812F006F7F2F60916F0FD0F02030404010502FEF6F0FBF10BFDFCF3FD0F0FFE08FF00FD02FD0602FE00040CF100010AFCFFF90818F800FC010F0AF60A0403FAFBFF030803080DFDF9FEFC070E0AF30200FBFCFBF913FBF3F9F9F1F7EB0C06FCFF040D01F0FF080009FA0C0AFF0505040008FDF6FE01FDF8FAFD01F9F90708010908FEFAFEFC010C000312040804F6F8FD0406020D0004000405F8F8070EF8F4FC070706F80A05F4F90803FEF3FC00F6F206FA0CFAEB00FC00000AF2FBF705FF0AFFFF0604FF040709040401FE1505F1000EFA030F0BFFFE0602FD03FEF5FF03FBF0000F07FB030000EF01FDFA09F80A0606F804F5EB10F502080606FE0A0703FFFC0308F40806FEFFF9F6FDFBFFF1FD090501FAFB050E0BF9FEF8FFFE00F701FFFAFE04FF0204FE0CFCFF00FCEB0F0D0006070606FB0802FB07FB060306F907EFFFFFF8FA0AFE02FEF70F06F8010100041211020608F505030407F400FEEE09F9F3040D0B00F703FDFD01FA0004050E01FCF801F6010806060308FC0AFE070BF813F9FE0F0EF8F700F708FBFCF2FDFC0DFEF4F7F70BF9F4FEFCFCFC0BFFFE0AFFFFF6FDFAFEF7FD0C1005040AFBFC10F9F907FE0AFB090808050100FCF901FB190FF20C00F8090106000C050AFC0A0201FA0902FE0503F2FAFF06F0FAF6FB050204F905F604F6FCF90005FDFB00FC0B05F1FDFEFB00FEF703F705F8080A12FCFEFA08120001ECFEF7F40FFC05060008FBFFF607FAFDF403FAF616F8F402F9ED1204090A05EB00EEFC10FD03F602FA0FFD010807060504FCFFFFFE0C0BFEF7F7F4010C0AFDFA040EFE0415F702F3F3F905FC0A0102010B05F4FFF7FBFBF701F80EF400FA03F8FEFC08F9F91BFC05FEECF90B06F907F9FBF409FA02F70605F5F6FC09F5FC0BFFFEF007FC0B080001FAEFFA0DFBFEFD0504FD0CFA0205F6F908ECEEF8EFFFFA02FC03020BFFFAFA0E03FE08F400EF0102F8F1FD0002FDEB060AF9060808060712030401F603F5EB01F6F604FBF8F0FDF201F50AFD000606FB03FF010E01F9F707FD000C04FD020F0600020AF403FE0707F902FA0CF8000503FFFA09041019FF0CFDF8F0F5FCEC0BF7FEF80610FF04000504F5F907F204ED0009F70002FF03010208FE08070404F606FE11F4020C0505FD05FDFF00FAFDFD03FDF10A0103FCF801FEF4FD0604FAFDF2020004FB0A021DF90308FB03030109F008FF01FF05090007F107FFF70607070600FBF602FC09FEEE00F7ECF201FCFB00FE0000FA0803FAFCF8FB03FAF8FAFC0B0903FF0906041003FBF901F900FE05FCFEF5FB020203FB00FFFB050B0503050003FEF8FB050105F9FE00F9070802FC0BF6FC0FEF08F406FE0313070404FFF8FBF9FA090AFBF9FD01F4020504FE020FF8F3FA00080308000C0FF6FD010BE603F305FBFFFB0111FCF5FC09F5FC0D111200FD0306FF1101030608FF04FF07FFF6F40301F303FEFD0903FE13F201FF03FFF5F703FD0A10FAFB0105FDFD0D040FFEFEF8F60CFA10F2FE00F404F7000DF304FCFCF40F0209FB020300F7F1FDFEF5FCF8F9F7FE0DFA04F20D02FF0B0EF80AFB0AF90A01FBF303FDF2FAF706F802F806F60209FB010702F5F502F9FAFCFCFEF9FCFDFAF80108FDF609FB00F6F1F7F607FB0C0101FB0BF00BFFF71A0104F004FEF3FD01F90DFB04F507010B09FDFBFC18FFFAFC040EFE09040004060708F8FDFFFD01050703FDEDFA08F5F4F0F2090811030701140AF9FC03050306FF0A0D0109F5F1FF0107FC0200030DF801F5ED0613FFFEEE0C0A030705FB0A0E00F8F5FC0A0C02FCFEF9070F0208030E01EFFB08F5090A05F8FEF7FDFD1001FEFB0209F70F0202FA02F900FCFE0208FCFFF4FAEBF7F6FE010801F606EB04FC0BFC04FC00F8F805F10FFA050DF30407FD04F0FBFA01F602F8FD04FD060EFC080F070F0014FB050F0106FEF00503F4F5FEFAF600F0FDFA0FF604FA0F09020010EFF5FEF904FC090904FE050900FFFA05FE0BFB00F9FAF4FDF4FEFA0CFC0D040701060204080E06040402FAFB00000006FE04F0FB1200FCFDFBFE020C0A0BFA010C0CF2FB030404F90D0208FD0900F7F9FC030CF3EE0416FE08FF0DFEF0F3F4070DF904F907EF07FFFA04060406F0000D00F3070406F6F5F808F4F8FB0B05F90EFD0705FEF0F9FB0803090C02FBFEF6FCF1F3F9020405FD07F30EDE000108FB06FCFEFEFDF3FDFBF4FC0302010204F2FBF50309FFFCFFF500FE0C0A0A0E05E9FEF90609F80405EEF8FD020303FE0AFE16FEFFFDFEF6FB07ECF308FFFF07FB0A03F6FD0004080FF902FEFEF70A09030A0910F80E000E11060DF505FDFF0402090C0FF708F6F2FDF505F5010706F90606040704FDFAFF00FBF7FAFF0D0A0110010201FAFAFB1706FE0203FD03F6F5FCF8F20D021205FD050BF6F1F10205040BFEF3FCFBFEF9FEF1FD0500F9F9000106EF08000908F4F8F0F4F8F90602FF0601F3ECFD04F6FE0C010202F4030001F900080704FB030108F9FDFEF8030602FE04070BF8F50713F8FAF102FB0DE6FC07FB04F707FC0E01EFF80B00FC05FAFEFDFCFDF703FF0706F8EE0308FAFDF501FF02FCFDFFF2F5020B0B10010D000A0A01FEFDFD02F60205FAFFF109FA0804FB040F02050012F603FEFC06000FFF01F8000A0613FD0BFAF6FDF806ECFD09F20000F2F8FFFEFD14FFFCFDFE16050C03FC030D0811FD16FE12FFFF140CFEF2FA0806F205FD08EBF2020905F9F2F9090601060300FFF9FA14FA0B05F4FEFD08FE0D0A0003FE07F7FBF8F7FCF502F70204110C1101F601FDF1080D02FF06000403FAF90701040AF0FB0103F5F903F4F0F510F5FEEF0A0107F505FF0401FCFD05FD030B060FF9F602FCF8EBFE06010DF70DFDFA06FDFDFDFA04FD00FB08FAFF090108FB05F00C0BF700FB04FD05F6FEF2F80608FC03FEF7F7020B050FF60EEF0600F7F50A0C0305F7F6FBF3FD06FB06FAFF0012FB0602FF0705F700FB06FF09FD040206F202F401FF07030105F8F305000E04FFF7F8F804FA04F70006FA0BFEFBF8FDFFEF08FBFE02ED03020BF506F7FC03070400020E0608FE04E9FB0106FCFEECF902FE041700F6F605F6FF05FE00030404000D0500F8050200FBEF0AFF01FEFCF40604FEFD0A080008EFFAF7FC0607F9FE0106060108F4F7F3F004FAEDF7FD06F8EC03FD030E031108090A090301FEF60603FDF501060A06F9F9FBFDEE0D0805040902F403FBF901F8FA0602F602FAFEFC02050C0902030A030D0D08F30609F80CF70507FBF6FD08FEF7FEF909F6F90808F8FEF904000BF809FC01FC0C0DFB0AF20404F912FFF501F60A05F2030B0614FF030000FC0008F60C05F200FA070104FE0FFB090C0E0806F0FEFEF8010000FA0305090507F6FFFAFD0D020BF80511FB01160A03FA04F309F9FB0D0D0302FFFAFE05FEF901FD09F51201FF0F05130503F8F7FD02FAFAF903070D0BFD0D0C0605EB07060909FE0D0CF500FCFEFD1306010502FBF4FCFA100DF0FEF9030009020009040901F607F8FBFFF70CFD04020708FEF506FCFEFEFAF702FDF705030902F9FA01FF0110FCFB0702F9FA0BF70C0A02020B0705FB0000FB11FFF2F80BFEFA14F7F6030108080001F111030715FDED0A0B05FAF5F7FBF605FCFD0401F3FEE7F20007060E020503090608060306FA0DF4F202F4F2FD05010B0005F7F7FE05FBF50BFFFAF80707F706EDF8FDFEFAF3FF010905FE0B000909FB0AFFF801FA0D0C05F7E1090200030B00090BFA06FAFBFA0A03051201FB0600F4010F03F80B0AF1FCFFFEF101F704F4F201030214FBE0FCFFFE0CFEF2F5FB0B02FBF8FD02F5F2FA0405FEF60BF705FD00F909FEFFF301000902F510F000010001FBFBFDF0FFFD0E05FFFAF1F91304FE04FF0201FD0A06090305FA03F0FE01F6F802FBFFFFFB01F4FB0AEB080308F9F201FFF403FC0C0AFDEF0F010203EDF3FF09030606FFFCFE040101FB02FEF3FD06F6EF02F6FDFFFD0100FB12FDFEFFF604020F07050B0F1AFD0406F7FAFEF2F70F0A090BF108FFFA0402F2FEF7FDF9F5F500F9F5FF0F06F50A0204FBF6F70A06F707FD06FA03080306FDFB0905FEFA08F00B0FFF11010403040600FFFA02070202F30A03FBF1040C0F0008FBF904F70E02060A08FDFEF5F3F6FBF80807090AF6FBF7F601FCF41700F806FA0201FBFC0B0BFBFC08F1F401FAFE0D0406000407FA090505090BFD0B0507F4FD0A09F90408090AF8FCF40CFA0B0218FBFD0411FEFE1004FDF201F80213000509FF110CFA01FA0201F602060811F30900FEFBFFFCF10106080A081107F1FF0C0303F506F6F703030AFCFBFA0EFD02F4020102E805F90501030FF4FA0409020109F2080BFE01F91302FCF61803000CF4050BFBF5FA11FE03FC031402F8F0F802F9F6FA0D1508060605070AF406FDFBFA000C0A04F90003FBFA0E0007FF0000060600FCFF01FD0009FDFFFDFFF50D00FCF5F5F3061308F1FEFD060903FF03FCF7F2FEFF06010904F214F703FE0104000B0405FA06FBFDFE03F50202110EFEEF041205EA070607FB00F9FE03F8F7FCF60F0512FCF70200FDF804FD0702FF030804160803FD0C0906FE03F7FF070E0219F5FAFF05F20401F904FDF2FA050C00FDF102F4F60BF90CF307F9F6FCF5010BFA0601FFEE07FBFEFE15FEFA0CFFF5010DFE06F60700F6FB05F708ED1201FCEB0AFD01ECF7FDF5FC010300FE02FDFEF7F50803F4FC05110400E90211020901F705F606F802FC09FF0B0AF9F9F7FEFFFB0CFFFDF309FCFD03FD0F0FFD09FB02010CF30001FD0103FEF1FF08F8FDF3100AF908EA07070201FD050C03FA0D1104FBF7FB08F20EF9FF01FCFF080313F70001F8FD0B05FB11F7FD0109FE000DFCF603F8F80A02F905FFF80CF9050607F80204FE100000FF020B04FDFB0109FB1001F7FFF8EBF4F408FD0F0BF9030EFCF4FCF600FE030807FD04F8F3F8FB0A08EE050304020606FF03F80AF006F1FFF806170305FCF507F704F80A10F7FC01F900F602FCF8FB0009F10704FA0404FE0FF70400F000FFF701FAFD09030203070901FCECF206090109000B0FFE0A040203F7FA0BFBFF02F800F9F8F9FCFC0C0504F9010FF9FF08030504FE050104F8F813FBFE0B00FFFA050603100505070003FF0D020FFE01FB05EC12FDFEF009F4F4050BF6FDF20802FAFC020C02ECFE010602FE01FBEFFFFE06FC100EFEF6F6070AF9FC05FB05F701040909FD03F1FAF400FB07F908000A03EF01031005F511F9FEFBFC06050812EBFCF90103060516F8FBFAFEF9FD020AFE0A0C01FEFEFF05F40303EB07FD0106F9F601051206FE0E00FD03FD0E050C05F3FCF400F110F804FEFC0902080807F80CFAEF0A0F01EFF40D0305FBFF02020B040000F50706FD04FBFCFDFAFEF604FEF905FB0B08F6000C0E0700FFFB040209FD04F5F202030309FF02030D0A060B0404F0140201F7FEFE06F3030F06FD06F2FC04EFF6FDFA060407FCF30508070703EB03F9F70A010C0C020EFDF8FEFEFD0B0C0BF0FFF90714FB01F90FF60AFEFAF407FC0BEF1205F001FF0508FBF0F5FCFFFD04FBF9FE00FA0A090E0CFAF7F3FF0200020905F8FF050CFFFCF8FBFE110200FB060AEEFE15FD0BFB09070FFEFC12EEF80D06FD0108FB0805FA02FA110A0607FFECFA00F904F3FF06FB0C08050A000AF212FFFEF5F50204120305F803F5010508FCF80202F90F02F81705000007FB07FB120300FF07F7FA01F9FD0A0212F8FAFCF7FBF40705FB0B070204FAFFFEFC04020D08F5F70C04100FF9FCFDFBF7FFF3000A0800ED02000A0304FFFB00F7F80A0706070115FFFA01FAFAFBFD01F8FBFD02020204010AFDF703FDF2F10801FBF8F9FCFDF7FEFB12FC06FD0AEBF6F600FA0BFBFE06FFFEF9F9F3FD0701F3FF080CED10F500FC01F4090E050407010903040807060404F8F5FB05FFF1090CF0080BFDF30903F9F804FBEC00F60201F50302F6FF07060807F4EC05F8FE060DF8E208F300FDFCFDFEEB08F4090104F50106040E0B0D031608FDFF0E09F80DFE05F603FD10F705F9FB0609FCFA02030C03F806F806060506030807FE0107060005FA0EF81108F6EF0C06FC04F707FC04F202F7F60B0307F3030306080EFF0B0BF8F9FF0106FF07060104FE0A00F00204F9FE100708FCFCFDF002F4FAF6FE02FF07F30B08FE0E0CFAFA09FEFE0508100CFB0C030F0306FDF9010FFA03F6FDFA08F605FA08FFFDF407FCFF0600F416FB07FA0206FAFA03FA0507F3070E03100BFA01F602000C090004FCF6F30FFAF2FAF804F8FD00FE06060601030307060002000105FD04F608FF0101F90AF6100900FA0500F601F70909FDF1070C09FC02F4FC0805090DF10BF9010F05FBFB130DF20106F104F202FF06FCF1130CF8BC708C44E3D8A298F708196BC66F75F91732E5AF062775A9ACA36CE2DA64BCF62CAA4F63293C7A8F894856E9F263EB9CA4A0648141B4B0EA3A2D3364C36A83");
+        int smlen = 6209;
+        byte[] sm = Hex.decode("3FB2759E1BCA07730566D4FBBF3FC19A39C68F190095789EC555884EE97E9ACDB09109C5A9CA96658603D63A5A23C9D8DB7D8C54D07738E958BDA1E631ADED4329AE16B183374E1C3976DF1EC94BC79B773E8569973EDA418AC903B534A83B8B2F7CF7856ECED63F2626530EBBE3D22A734B457F2B1F168225753A0F3B030251CE7F414740D707D10A65BA6F7464E0291749AB9CC694A81CF54AB8AA21F3687D197FD3AD1EB37EAB1A19A67B1C396BC480B2CA19D650FCD9335FF245863787275540D7D1F5FCB278C558AD6CF15C154073A846B51754D45A2439FC08382E93CB657E1CFD3B3DEE5394C5F81513041A356D2EB938CF196B1B4BF96186ED75697745A3304BFA7ED23F77DFEBEC60A2C2441EE6B695CD17E2166956CACA25B09DE505187CBA23F62D86D0871BD6B8FD2D116743110DA3386A7401DAEF39506C9091B5D32675660A7380C5E837FB7206604C1A4931B8D6D452C5F4095EC1B7EF0CD11710DCB6A18749B788079AB1902135B62280D928167559696A3A2EAEFADE345F91D8CE49EBB109CC926C28077AB5B1B9BA946D929C2BB969061829A6E58A0FA5B168F5A1A7722CDF9A23CD75946CFAD9D624F4DB6A47C78EDA05EDBFE164F6FCB4C32BED70FC7D04BEAD5392F26ACF330B99A9A4140B5ACD0F215A16CC463F6BC4B7BD5D81B51F98C586E58C68BE97ADA48CED599933F2CDD994B9B52B3D93C44D9ACA191FDC0FD894C8AFD58B05D73432D6A90991FA87089B68D7CD7D1B136F7E3BF0FD266F82D13B1E456FBB6E29DF3D330E2D9440AC0AB5805C524463EDCA6D27ACB13B1B4F0881FF3A5A19E2D512A84192FDEA959FB87A555610DD44497EF3AAE91D075EECF7945FC2B8FC531B53FCD236687D05457999E1B3510D92FA433205E4E42E5B474E2FE7EAFD88BCD3375DAEDA0FAE4D40A63E09F2C99CD9620279BD1C34A8AD5B014CE8898B17AB33C1BD311CBF3CA6EBE0C9217AE3D09D978C0A8A41FC558A8D31BF37FEF59D1F46575CCEEA07F500E1CD85CD00FC20B449892E9C463C60342F526CA6F25F6291DC72330B5DA6C24CBC0CC75D8146CCFBD1FA47D36250EE5113BD9A54846E40FCDE4CF33E38328B48BCA3953C99495AB14B5AE2ADAE893181B1AD49EFF63D3A432DAA67572006A4001179363EAAA1701FF939D69FAA27EBD7BF71DA1029C84B4980251ED9C19AE67591AC719D2038D39BBF95B29B217A95CE10DDADF65CCD90A6BE492CFE0345D5AB3BA26F44846E96E330E853D68132B6F5B1B8913C02997551E55EF4A2C48B0EED24060FD549087E85ABDFADFB35B13D9A44EC677818EC64CF1446EB9F012160E5998272993377D8B751F87F8F370D904642EE9AEAEECF498ABEFD14FDFF1DB90C41401FC4F6DF93429A944ED1F90D834D7C5FBE5651FE291488605872926ED853C0B23E958DF376C75AE1909BCE043EA6AC3B035ED0350C07DE7749F7181E2DB5C5A4CFF983634AC34A4D416CBECAB4BC40C96EE2AB20A513E865B792F2BFCCDE4F9107CB9887F3FA77F4FA9D625EB32FB856036768F6E89424345BEC0CE938D71C37DE7C0CC130957C15D06E764D819AEB5E05328C50BBF7B01EA9F2952D5861705FE66A8EF3F22295C408259901ADF5E675A9D72125467B18C75A0CA5C84BE77E8A678FCB1A98EFD90496527B509CEE1DF01B1C535273B109E5ED1F8C6012EB79C7F5F5B9412A4F7DE6FFDE45F10F79BF1135A370C953BEF71EECBA75718A56441049B8C306E371B02250B179ADC42B9DC0963C9D6F991F66AFAF05B7ADFE8FFA71E8FF3BF14B08550A8499F543A8FA45C4BEF6D4635EAF889652E4EC4E24B7343C48F32C61C661291747B6FF99057C797B7FE93A58FC219D44C3973E1C11973F8CC00A29EB43235E0B9DE326F134FA55CFEF1B7D1AA00FFFCD8A3F9AFA1127EDD97C5D20D642203F65D5C5151AE773B3683ECE96726CC61C13997CC8C8F291D77779686E33BC09EEA6B8739122D2B90B78022F368B01833D94ECB2091BB45A45AA7784603502B1C54657F8A7F6E122C7B46393A466933BD806FB4411288DCABEA5E2788AB0C088AC9A45C587C087CA9FD4C131969BC9F2025DB8FA4484CBEF0C6C7D887FC22599C28593DC8D586ED81D38D35249D7E01FF6DB898CEE78B6214EF2C215B76E55FB0A65CA3EBBB5586F4255AA2BD802C5E2B60A6051B5C4BFDB48C4520290F2FB1BD5A6D156CA375C1E700765F9923ACA126D41CDBA2F2DF339D143870CDC2964B20483154EB8E8D59651C17FA7898176073DD353B629D327BA130F2E56A9594D177A1EE82847BA8F8AFDC669F7BE160F1EA6C6EFB747D9BAB0AC58DCDC8D33B5F7BAABE5274DD7D4B5AA439C713615D4B255814A5D2AF44D5382264F3F9F48872A38D36D1F15C90FB768C9B690EC989FF28CC135D29330D84EA33EB173271DDF8F5DF885076251370F59AC0E4DEEF640D5C04ECE8778C1D8F839462C5504916CBF555167836076C88A848903B3779E007745F3CCC8282CC526306DD63492F0A004A99821D1BBC065A255F4FDCF426BF97CF9933DA6A79B577919F7605446A25CB549A3057785522EF5A56E3288D42D714163F16A36EBC6607779691C7A1E5C420E24A64299AA03930DE872575A18A0D004CE7CCBE85BE4227F2146E176F0B0689BF51988ACAC366444E2281B5A81FA505E01223818185BE05074AF0C94C4B99F1AE6CB4033487DB1EA46B3A59A1B840845570F468F6E8BE1CEC813148FC421A4A62BEC0D8CE9DBE73AC17D44FE36C93DDCAB402EDB129BAA93795AE54608169F58BD8D115EC6392DBC2BEF8BBFB820827E76B266F480D4E7229B4119C35AE0AE1F222DAD22DD9CB3DBC76D80722F74E6BA87C7918BF33811850F3C9300E0FC880B889CD3EF2C3E2D63B21AB7A05867B460D7B96050BA8F812B79B29851CE5EA92202BB553972EB1ED8CB82289987EF0B314D2E787DDE3CAD96238D7BD182CC404F8B19F3FD4AA55C75DFEAFBF6228A37B72CF57152B4CF7A65872ECAACA21FAC42849BD2D1A5812196AC6A52EB51709710AE1BE2345465DD98D42135BDF67C1A022A338F4E60686746B62B2624FCA52A46DBE515714A2C8E9CF76B14D2A7FA3A734D27657DE99C8A0A5883A1A9ACD6568A0F5926E93AF2017CCF8623C7363CB52390A93FD1143C575AD3B35EEC387321EC87B93C16FF30DC8848DAD229B0CC561F75F64D948A0CD82AC62DDE0AB9DD13973E9B6A19DEC429620A6BD52F0203820F3D2DB2369D121A093E64071E926919BB8B7BB0C3E58EDD9A69F5ED8A1C7210290BFADCE31284BB69C5F02B5E8D13F19E159FF334DFC5AC94798E1C87230424AD32FAC495299F357F74D914FBA9349F0D26491444C5A7B2F0388ED098A182122BA1C1A8FB751C52376E4385449B06F92C3005EF39BDB758AB542F7B3BA43A9D6D3B1B88977712E0A8F08C898AF5DEA1DD4E83BFB763DCDDFD34EF33E208F98AF8F90AC173ADDAC9E701B3F014560A1D029598D91C6A5715A4102F2162B8FA44A4B9F002E0E1C8D2C5906745708648A690B8CA89E6ECDF1BA155EF1D256305D94D790A856C79E39A22470824AD09A52F96D15CA3B0C09D19CE23C7880D774AC00D59C3009183163FC98FF74F99E03A550283A8D300E1C618678E2F8EC2D69B15E822571ED7A5F35D6FCBE5AEEDB56FFB6352D60434CCA2C5DBA90E82AD9298FB1369A06A1FE93098C86AAEDBF846F59A3390845B8EDD2FB8D08622AF52DFF43F1918EFCE00D5184BC19D4C782027248DCD875EC1B9BDBFDC7DE08A92E4838AE7871B897D9F5BD23256B9686CB001B5B2DBE7D79B2094E2B26C103C38E81F51A75DB8871A6AD28AB5AADF94BECF43D928B155FD4900A3F0F2332B992C0091B8B21D8609BFE9A1EA84313ACB83ADBC8478320BEBEDA9AEF0BC1A9B3FC4BCEEC226D13AE4A5163DEA4D019B148EF98285717AC45A935CB71B103FFFD9B6739CF1916AF5F3D61AC37CD9584DB4A2F9A24D38F61B83232DD33680DC30C32CFB9DBA27A6BA2BB8B2159F3EDA7F37402212755225CD99CC1A37A99F2413A4C57D1A73D9BA1DD02E310DAD355A14FD01A3EED8F73008607775AF0A5BE621EC16F2111A5148F977E9F79460450CC21AC5FCD8EB04BBA536385563D288BF592D3ED7BFA3A12CD9F914CD0C35770A4A928705D25E13AB4A05C437E009595B14381A5E215870EB468B06607F30FD88CACD1C11478BAB4372B9438AE4DB6C606941BCE98322E0FDCF59009DFC5E7A4C483B00DDF2386A236272DBA0D8064F27B7C1BE90DA8B592CEA17EB33ABD5A7E19DD69A805A6AEB6773D0E8CF6450C3C2FE55E0B7180CDD8863106D75431482BDF9C182977B91330B0F0ABF796D5348CD84FE0036E52860F7801ECD8EA9E69784994D425CD5ED09A63251835A233ECA4CA9B9AF6CF154AE4465EBF089F19D32024AA2C144DA004F84A7A6F22C3D71600245AFF5F1E5A1D9D5CF24FCFC0228A534E9E6447CE7DDEBD8DCCAB3BC0933DB333A39031EEA2EF6B81A9BB34A7F67554FDFCA8048FCD97E76CC082A93FC4F98D32910B895682D1FB3F5E97E463131F56364283ECF2AE029149B9CB73817AEECA497360C23ADCE8111D86E2C7005ADD2C8C0503A99DC91D30F9D20866C17E296649B161416C181F136608A05434074D567CDE728F7B31B60B97C04055698E108E6A2A65A77EDBFBB2F05AEABABF166B6DF25DC3A70062050C779CBBA550A62F021CD6222165082EC37858901D0A46C815579AA998EEBF4B3387B6BD71B0F16EF5B68CBC6A47E1A3DE015331953F3D3510385F1E048ECB84E7EF929142225DF16234EBD672B686D297E5A9168C6F97EF6C97501D4E086E9D73D1994D3C38C912691D8F0E00E0E2F0CD95B42F3F70194E3183BF9537188D1105D44839F7F3EDAB0E10232DAA9FC673D6071F3589095B6C392A766F0DC9D9B8DD563B1EB82076CBBB7C5636C516D3F200A3B43D384E79042E936E51C3D1A4D3F621CF738EE51D6D29F22D658E2370410E841A5CA7C3122AC26BF26730AEC0B23D8CDB46B6F3E0C4EF15410B15D83BCDF67BF2AD9EDBBC3890F70670FBFF86A4645A58F1EB5C49C8F6973CFEE2E1E38879F59D1740172A52744BCD839CACAC94BC3DBDA37A97A2FBDAD6AE0CCDF080B57CDE454092D763A1020E176515075DCC37148AEDAC1C7BA090B12DBA260F08F04C689661EB926716ADF5FB7159822E226BD3DE2EBEE1F98685CBD99CCFB09A6C9FBFBE377A122FDC01B9B7FD82F759129A6D0B0FCC9E8AD566A19FA04C12B95C4154E35215F5F11255493AFD5E66B73D4E0572F01454D1848A13BC0A9374BDC90994B0004D9D2EAC8FB506E7CD764A1B2A9016BEDDBFB987741FB68265E5B05CDFA46A4A7A1BC25ABFEF37118CE8118BCF92F32C433E5252D8DFC195D4CE5699A2CDC1612EAA171423E6299F86C2A976DB4C1E421E140BFEE8406DC649C57FE27A77CAF3481B8687DB07F9921790AB5D502CED457C3538E688F143A69175A535101E1DEE82C5E39A579DA178CE233105BA5275E27E79441548D9E0F70CBEC5C7E179D6FD8E167F90C3A79D964CC23CFE18D6BC300753961BAEF0EC6AEDDDCFB1760B6DB3D0B667B8E120CD3F867017507783F223106CA7753D94BC60E65538589FA87807F7AB8791C1957C9510A9FBB91D513DE0ED7166155E911A5622A6A49DCF1DE3DEA4072E5DA75583B6EFE781FBFE31F7AE50F4FB8897586B8078144CAE9842CEDA34EB9E0B996E3CA46F11BCCA46ECB81DFB191236046294BB0120ADFDA2EC0DBB7D4CC6353BE9E64FD668710B1CCD305349C0B7877566E3DDC4E21985FA6000229004DFEA9F39421476A5E33C9163CDAE4C7676B1097E48C70DCBD4B39CE4AAC3ACFE73CE9C0D826B8A61CCA0DD2340C860E57496A1915BF065AA6557E26A151603D31CA94410E6C3E6D5AC80FF6F71DAE371D020CEBFB58EC73017C8D2D6D6221598678D752DBBA95113CDF71848D2E05D22613451627456A96D15BD277EE04884F3F296B0DC5314EBA22356CE09D4C0BB7246419D8674E4D10EF64B3F057D95E70BF1086ADDB100453D81E19939BB18AAC1343FABE39E308284A8D6A38BE238B6E0503A504727AC20B48A2C26C130E7A9678BEF07A1828ADDAB90740526F99C11D3217E001172CDD33D6D6E949A8ECB963C0A5283189AE8C98B21933FF691931758E842977A4F3F337252105CF22AD836F91E4FE5CE97FE4CE59EBCC4DA2A56D9413BD356BF65EF6FB2CA2201EB31BA56D717D2F9C038BFA76A52610AEA57D45500A1408AA8876AFF585A2FF309C3EC852B9568AE1CF9D97635759932E61D61DD6E67402912EC538BD0DF9D0BF25C862C91E301AC499E64C061FC14E89EC361A10FA0C85132BCEE7AF028C663BE3B0E12EAC52F691FB66E58E34EF2AF4C5A9DB0A0AB01DA372E39BB824F9D6AF847DF6A08F680B4F99466D6E6F404F0B92210D641375ECBC0C10A38DD1CF449A75BF2420A294EECD649013E160D793A247D1649E03A535292ED3D88EF7901DC845CD9703BF0948DB61C08EA89C33A3DA436874AD22ABEC43A5F2896833E15B3102A66E2A7D69D272FFFD4CC8979B9733718B95B605166DBE16ED1FD1C5CB2571E7D9FEAB2BAF693448C1E9613E8FBB25E05E2838390F3D9D2CA88D4B1739AEF4039A168E908D245ED38CA96F98569FFEA949946B76BF6F1DCA0B866CF0AE157314A28B76654EFECBABBA1C58ACB48E61587B42DE0F5BF8D5A3DB6C2E1D1632894D940CF10F71390B1DE07A4DEC38EA93A67439361E71FD8149D26DD8A21E9B98DA1FCC014052D0FF54DA3B2A335D0122FAA1332B193043A9119BAB7BC8F3209812B5280625C44ADBCB92907B6C387E0BDB1B9EDC0377C03E305C7333897588C2EFDDDC82E95ACAA329839EAE514F8E8A46A9126847933E73820922CF62528DB950EAF940F72A7383A486F5E210120987173A75A7D1B12097287F9146735A7BA110E22BE8B2FE720B2DA6CD8D59C8A32E5B43BCB9B42B78103BA2C25C13693AD4F2CF49A93D4B543AD7E4BE7E7304074E906D114E77C9448A8318E71F67C3C5A3B3379B40D44D9D1740C8D5003B03AA614099CB77B97A1B121DE9F75A69CCE3B7F1A2AF7B2B7702919F45544F778D5DFB9B7DB1C5AD98B36ECF231300178EF1154F4B93CE197815F6C5D2C3A3EC2A3E6F9FCC360B47D5A362BF4A41982ADE9C1377EE1162A9ADA02B966F502931045537D5CFA700C5EE0B818C8B280CDC07B1C34046A908D31CAE7681AB7A59AC371326211C170E9240B4B00222F628ED734ED39D5D0A4D1D4937202F82A531B94084FFB7033595460B41C5DC021D955C8EBDD8787140F60FFA21BFE680234BF5EE2F3F998FD9662A657BABDF4FE4A3191D7456B928BA2C0F08596015B803CE938522A88B44292223DA54BB1A0318ED439C5E0B1513AF667D1AEB8A0228AACDADDB02F689CD43FD15B8B10B12359EB83936F9191BD4671A3EF7F5998B70EBE29E551CBC4EEB214DD776AB62B20D1DB533CB2621AC0D00D88BE1E21243DCEAA1494EBD581992F6F9171B9FEBE919DA5B61A99D2D5C3A23EB447EAD4857F217CE80CC5765E014B851CDB56518160B69C56F4A1FB9542792390FF4DF407E1BE94408A2C856DAF3F95EF4E1D3CDFCD5ADC8F9BFC95F5D7A9AF8835DF3205FD81BCBB06DC10A72E57855DF16794F3B0F0EC83B36922267755777517875390D7E5520D46610B5008ECE82C08E20888C4785A1CADA07E929E1324EF710D48212B739EB5276A4C895C46BE8A405332DF93E93BED6C2075261F90DB4ABA03B4F7884B663268AE18C11479098611FCB7B0F9645E315819995DF3F3A45221A6DB71874A0585A197A01E67CBC85DB02A4B3ED2BEEE43031E7065BEB838C601BF2B161633BC9C04B506564E5DF517F1445140029665EFB724F5F5D88FF654AB18A6D65AB04E3F36720A26E4D9676148020D634FF0B74DCC828394F9AFCE7C8443097A0091FE8255F03D5A9B5AA00FE9E6056B11BB772AA6361C51938447B57F3195081483AA1DF6B5A71D050AAD3C855A770A1636019DB9EE75875D542848A285140B1B7FE054B4FFC719CAAA3B21FC5BE4B92F45D7A99254733303DDDA18AB271CCB2DACA27B600CB5CA50E857070CDB657FE559B21CFB3C596B24727501707B64A657E3B46FF1DA18842E0227C02261F88EBA6F9502856752D5D5B6208FE660645860E42A32786C3E54E8AF6DCD3E9ADB76BC0200A4A17B1CD89815301AC45EA3070EA99FEFA310B7FDBCFBE817EF28A04F44A76B38015A27985E5D9D706997C1DF67CCAC869D4FC9972AF47FE2908C3B1349728796903DA78F67562D693418345E041FC6F9E3A991299EC387C53CA9DDC0428001216637F92AA19BED77CAEBBC2BD3972AF5B07FADF92F54E82210ADFD3623939BDEB5D5BDB019D89A1EBDDD98C1B1D6BDD854EBB594516183B32C24F4C8DF07943CE77857AA0F10D27D3FF0DEDE7026F924E34153F9741CB2D7394401B20BCC53965ABDD26881FD3CF2AC83825E1DC1D1402F679BA8AC57737EDF7FE7860580424559DFD9042C88DFDE010943F3C3BFF61283143C46BA1E85CDE3284AE526CD5DEA7A401F07AF0A88586C599FF640B4B9C4C601E650D7E9E4092A940F9E4771084112F8D8A7FA8E5DC43DF8214242AB81E8FB5A097F80E01C9B436C125DD616FF680DBB16A24F90F45A42CC1A05111E062348D14C1EFFAD81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(QTESLASecurityCategory.PROVABLY_SECURE_III, pk, sk, seed, msg, sm);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/RainbowSignerTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/RainbowSignerTest.java
index 0c573d8..e692e5a 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/RainbowSignerTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/RainbowSignerTest.java
@@ -13,19 +13,12 @@
 import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters;
 import org.bouncycastle.pqc.crypto.rainbow.RainbowSigner;
 import org.bouncycastle.util.BigIntegers;
-import org.bouncycastle.util.encoders.Hex;
-import org.bouncycastle.util.test.FixedSecureRandom;
 import org.bouncycastle.util.test.SimpleTest;
 
 
 public class RainbowSignerTest
 extends SimpleTest
 {
-    byte[] keyData = Hex.decode("b5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
-
-    SecureRandom keyRandom = new FixedSecureRandom(
-        new FixedSecureRandom.Source[] { new FixedSecureRandom.Data(keyData), new FixedSecureRandom.Data(keyData) });
-
     public String getName()
     {
         return "Rainbow";
@@ -36,15 +29,16 @@
         RainbowParameters params = new RainbowParameters();
 
         RainbowKeyPairGenerator rainbowKeyGen = new RainbowKeyPairGenerator();
-        RainbowKeyGenerationParameters genParam = new RainbowKeyGenerationParameters(keyRandom, params);
+        RainbowKeyGenerationParameters genParam = new RainbowKeyGenerationParameters(new SecureRandom(), params);
 
         rainbowKeyGen.init(genParam);
 
         AsymmetricCipherKeyPair pair = rainbowKeyGen.generateKeyPair();
 
-        ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), keyRandom);
+        ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), new SecureRandom());
 
         DigestingMessageSigner rainbowSigner = new DigestingMessageSigner(new RainbowSigner() , new SHA224Digest());
+
         rainbowSigner.init(true, param);
 
         byte[] message = BigIntegers.asUnsignedByteArray(new BigInteger("968236873715988614170569073515315707566766479517"));
@@ -53,6 +47,7 @@
 
         rainbowSigner.init(false, pair.getPublic());
         rainbowSigner.update(message, 0, message.length);
+
         if (!rainbowSigner.verifySignature(sig))
         {
             fail("verification fails");
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/Sphincs256Test.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/Sphincs256Test.java
index 88b5c6a..2c90160 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/Sphincs256Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/Sphincs256Test.java
@@ -1516,7 +1516,7 @@
     public void performTest()
         throws Exception
     {
-       // doBlakeKatTest();    TODO: digest classes still need some work...
+       // doBlakeKatTest();    TODO: we only support Blake2...
         doSHA2KatTest();
         doSHA2RandomTest();
         doSHA3KatTest();
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPrivateKeyTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPrivateKeyTest.java
index ec5cbe7..1ec0337 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPrivateKeyTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPrivateKeyTest.java
@@ -2,32 +2,73 @@
 
 import java.io.IOException;
 import java.security.SecureRandom;
-import java.text.ParseException;
-
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
-import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
 
 import junit.framework.TestCase;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.pqc.crypto.xmss.XMSS;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
 
 /**
  * Test cases for XMSSMTPrivateKey class.
- * 
  */
-public class XMSSMTPrivateKeyTest extends TestCase {
+public class XMSSMTPrivateKeyTest
+    extends TestCase
+{
+    public void testPrivateKeySerialisation()
+        throws Exception
+    {
+        String stream = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArO0ABXNyACJzdW4ucm1pLnNlcnZlci5BY3RpdmF0aW9uR3JvdXBJbXBsT+r9SAwuMqcCAARaAA1ncm91cEluYWN0aXZlTAAGYWN0aXZldAAVTGphdmEvdXRpbC9IYXNodGFibGU7TAAHZ3JvdXBJRHQAJ0xqYXZhL3JtaS9hY3RpdmF0aW9uL0FjdGl2YXRpb25Hcm91cElEO0wACWxvY2tlZElEc3QAEExqYXZhL3V0aWwvTGlzdDt4cgAjamF2YS5ybWkuYWN0aXZhdGlvbi5BY3RpdmF0aW9uR3JvdXCVLvKwBSnVVAIAA0oAC2luY2FybmF0aW9uTAAHZ3JvdXBJRHEAfgACTAAHbW9uaXRvcnQAJ0xqYXZhL3JtaS9hY3RpdmF0aW9uL0FjdGl2YXRpb25Nb25pdG9yO3hyACNqYXZhLnJtaS5zZXJ2ZXIuVW5pY2FzdFJlbW90ZU9iamVjdEUJEhX14n4xAgADSQAEcG9ydEwAA2NzZnQAKExqYXZhL3JtaS9zZXJ2ZXIvUk1JQ2xpZW50U29ja2V0RmFjdG9yeTtMAANzc2Z0AChMamF2YS9ybWkvc2VydmVyL1JNSVNlcnZlclNvY2tldEZhY3Rvcnk7eHIAHGphdmEucm1pLnNlcnZlci5SZW1vdGVTZXJ2ZXLHGQcSaPM5+wIAAHhyABxqYXZhLnJtaS5zZXJ2ZXIuUmVtb3RlT2JqZWN002G0kQxhMx4DAAB4cHcSABBVbmljYXN0U2VydmVyUmVmeAAAFbNwcAAAAAAAAAAAcHAAcHBw";
 
-	public void testPrivateKeyParsingSHA256() throws IOException, ClassNotFoundException {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new SecureRandom());
-		XMSSMT mt = new XMSSMT(params);
-		mt.generateKeys();
-		byte[] privateKey = mt.exportPrivateKey();
-		byte[] publicKey = mt.exportPublicKey();
-		try {
-			mt.importState(privateKey, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		assertTrue(XMSSUtil.compareByteArray(privateKey, mt.exportPrivateKey()));
-	}
+        XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
+
+        byte[] output = Base64.decode(new String(stream).getBytes("UTF-8"));
+
+
+        //Simple Exploit
+
+        try
+        {
+            new XMSSPrivateKeyParameters.Builder(params).withPrivateKey(output, params).build();
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertTrue(e.getCause() instanceof IOException);
+        }
+
+        //Same Exploit other method
+
+        XMSS xmss2 = new XMSS(params, new SecureRandom());
+
+        xmss2.generateKeys();
+
+        byte[] publicKey = xmss2.exportPublicKey();
+
+        try
+        {
+            xmss2.importState(output, publicKey);
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertTrue(e.getCause() instanceof IOException);
+        }
+    }
+
+    public void testPrivateKeyParsingSHA256()
+        throws Exception
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT mt = new XMSSMT(params, new SecureRandom());
+        mt.generateKeys();
+        byte[] privateKey = mt.exportPrivateKey();
+        byte[] publicKey = mt.exportPublicKey();
+
+        mt.importState(privateKey, publicKey);
+
+        assertTrue(Arrays.areEqual(privateKey, mt.exportPrivateKey()));
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPublicKeyTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPublicKeyTest.java
index 41b5160..fa96e05 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPublicKeyTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTPublicKeyTest.java
@@ -2,16 +2,13 @@
 
 import java.io.IOException;
 import java.security.SecureRandom;
-import java.text.ParseException;
 
+import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
-
-import junit.framework.TestCase;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSSMTPublicKey class.
@@ -20,28 +17,21 @@
 public class XMSSMTPublicKeyTest extends TestCase {
 
 	public void testPublicKeyParsingSHA256() throws IOException, ClassNotFoundException {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new SecureRandom());
-		XMSSMT mt = new XMSSMT(params);
+		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+		XMSSMT mt = new XMSSMT(params, new SecureRandom());
 		mt.generateKeys();
 		byte[] privateKey = mt.exportPrivateKey();
 		byte[] publicKey = mt.exportPublicKey();
 
-		try {
-			mt.importState(privateKey, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		assertTrue(XMSSUtil.compareByteArray(publicKey, mt.exportPublicKey()));
+		mt.importState(privateKey, publicKey);
+
+		assertTrue(Arrays.areEqual(publicKey, mt.exportPublicKey()));
 	}
 
 	public void testConstructor() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMTPublicKeyParameters pk = null;
-		try {
-			pk = new XMSSMTPublicKeyParameters.Builder(params).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-		}
+		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+		XMSSMTPublicKeyParameters pk = new XMSSMTPublicKeyParameters.Builder(params).build();
+
 		byte[] pkByte = pk.toByteArray();
 		/* check everything is 0 */
 		for (int i = 0; i < pkByte.length; i++) {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTSignatureTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTSignatureTest.java
index a38530f..89cf25f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTSignatureTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTSignatureTest.java
@@ -1,51 +1,50 @@
 package org.bouncycastle.pqc.crypto.test;
 
-import java.text.ParseException;
-
 import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTSignature;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSS^MT signature class.
  */
-public class XMSSMTSignatureTest extends TestCase {
+public class XMSSMTSignatureTest
+    extends TestCase
+{
 
-	public void testSignatureParsingSHA256() {
-		int totalHeight = 6;
-		int layers = 3;
-		byte[] message = new byte[1024];
-		XMSSMTParameters params = new XMSSMTParameters(totalHeight, layers, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] signature1 = xmssMT.sign(message);
-		XMSSMTSignature mtSignature = null;
-		try {
-			mtSignature = new XMSSMTSignature.Builder(params).withSignature(signature1).build();
-			byte[] signature2 = mtSignature.toByteArray();
-			assertTrue(XMSSUtil.compareByteArray(signature1, signature2));
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-	}
+    public void testSignatureParsingSHA256()
+    {
+        int totalHeight = 6;
+        int layers = 3;
+        byte[] message = new byte[1024];
+        XMSSMTParameters params = new XMSSMTParameters(totalHeight, layers, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] signature1 = xmssMT.sign(message);
+        XMSSMTSignature mtSignature = new XMSSMTSignature.Builder(params).withSignature(signature1).build();
+        byte[] signature2 = mtSignature.toByteArray();
+        assertTrue(Arrays.areEqual(signature1, signature2));
+    }
 
-	public void testConstructor() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMTSignature sig = null;
-		try {
-			sig = new XMSSMTSignature.Builder(params).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-		}
-		byte[] sigByte = sig.toByteArray();
-		/* check everything is 0 */
-		for (int i = 0; i < sigByte.length; i++) {
-			assertEquals(0x00, sigByte[i]);
-		}
-	}
+    public void testConstructor()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMTSignature sig = null;
+        try
+        {
+            sig = new XMSSMTSignature.Builder(params).build();
+        }
+        catch (IllegalArgumentException ex)
+        {
+            ex.printStackTrace();
+        }
+        byte[] sigByte = sig.toByteArray();
+        /* check everything is 0 */
+        for (int i = 0; i < sigByte.length; i++)
+        {
+            assertEquals(0x00, sigByte[i]);
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTTest.java
index b4041aa..92e220f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSMTTest.java
@@ -3,516 +3,17682 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.text.ParseException;
-import java.util.Arrays;
-
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
-import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
-import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
-import org.bouncycastle.util.encoders.Hex;
 
 import junit.framework.TestCase;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTKeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTSigner;
+import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
 
 /**
  * Test cases for XMSS^MT class.
- * 
  */
-public class XMSSMTTest extends TestCase {
+public class XMSSMTTest
+    extends TestCase
+{
 
-	public void testGenKeyPairSHA256() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] privateKey = xmssMT.exportPrivateKey();
-		byte[] publicKey = xmssMT.exportPublicKey();
-		String expectedPrivateKey = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f5bb70f454d7c7bda84d207c5a0d47211af7b489e839d2294cc8c9d5522a8ae";
-		String expectedPublicKey = "1f5bb70f454d7c7bda84d207c5a0d47211af7b489e839d2294cc8c9d5522a8ae0000000000000000000000000000000000000000000000000000000000000000";
-		byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPrivateKey), strippedPrivateKey));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPublicKey), publicKey));
-	}
-	
-	public void testGenKeyPairSHA512() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] privateKey = xmssMT.exportPrivateKey();
-		byte[] publicKey = xmssMT.exportPublicKey();
-		String expectedPrivateKey = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5a47fa691568971bdef45d4c9a7db69fe8a691df7f70a9341e33dba98a215fe9651933da16a3524124dc4c3f1baf35d6f03369ff3800346bbd8c2e179ae4abd";
-		String expectedPublicKey = "e5a47fa691568971bdef45d4c9a7db69fe8a691df7f70a9341e33dba98a215fe9651933da16a3524124dc4c3f1baf35d6f03369ff3800346bbd8c2e179ae4abd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
-		byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPrivateKey), strippedPrivateKey));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPublicKey), publicKey));
-	}
-	
-	public void testSignSHA256() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmssMT.sign(message);
-		byte[] sig2 = xmssMT.sign(message);
-		byte[] sig3 = xmssMT.sign(message);
-		String expectedSig1 = "0000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf807261bcd177ddc133bb0e43bfef03f25536a685cd509a5c8f2ee46c23e2d46149d0de58feaf910909d213823f98c5b8eda2522139675fd7a82deed6b795aa1d8eeb0fb5295e6d15c410c6b9dcacbac9879a55cf139c09dbc57456adee05a8075870b5ba92aecfc2f289d47e1deffbf10cd01a278bad1bd063813b63f79db79c38c357707a0c31c3a9d0d58ddeb2317440ae05edc55f2643b53c6b18160747fb27b5f24f2b0b2f8840b489d51a6adcd3d74d90a6e9e3c96630ff5afa8de978e03f117b1082a0d877aa0ef75b90b425b0e22acb8633b6404be938969ae16d926b8aa4e16eb13f8d8b9ae8f7eb16e0ca7fb69b428011d3bd9390836d46f971b83f093dd14b9df1165b8e256bd9d80354ce4c60c077f21bcad49ec4fc0ce33212c45460899ad83421a98fa5b6c153d1d3aeced17702e266353acd195d408da05eb38c550feacd404a38f6657360c9f7d373d25ecdd885daa76aa3798ad57d756f0aafcb6bd46d58c78322efe0c94ca8be6a35ccbb75fa14b913965ee02792b021c99dd60a81333feaab0757ee5ab0bcc7a5541e7871b1efbbbdf97e1f455c44327e846c856c3c2cadadd77b3fd4043a0ccb02f2527abc4be9a878ae61d78891f3b26b838c162ed24f90605f1ff34a25d8518b5ad57c897ca7ff01246712f04d1c85fcc89531d423ab4d1359b2e42037211516e096a90ea956a5ce83b423403945dfc613239d2defa006c04a9084f27487f81ef85b7cd75e4a0df87c57343caa9805ad886aba0877dd6b123775b51104c45097d15f2cfe2c8622f229d7e83440e3f48e1b507d47a07065f091af9bd30d24a5ea7aafeef7537d05d896192e18422792b7d57fe63489c05c36614378bda9cd9aba1c2f64a45ee0851d8f88dd9e531c39f1e2479b81631919fa75b12d864d49e03902a495965e72c99ea101ca2d02b08b0ccbdf8c38dfccbde27510ac37a40ff4737aa9304e67477c0215f496f5b5f83716f44106b2d1f609eb1731093757cb4f6d3f5d3062b38f55fb7e2a39ecfba7dc601329dcaf8181caf7412ee160aca1ed3c99932f6a673a08031a11bb64f641a2728e2fe973cabc7ba40783e422270de34919fabac4126a6dc035531efc2ca61271ee0fb33c95d4d4f7a00abf39f98557d5f7bf080848792996fbe0b6a0d783b647de6a3fcbfbc76481c79e2d94d660c81eb108875fa8ec3263f868b1da745064667d742b53af85731707aa8790fe40e1b06e1bf9efc9c6ca01ff879ff74729513d346007f9bb71800d128f6da23ecc6babed12bad95b3694f19921f7135a37ebea9d27a8cf1323b0e691739fa44350e4f6afe6d16acdfafadab222878899294946b6854294d15b68e9f7f93d4ed8c11d020e0581b9a464081ec7f5fed13a19cb33f32baa974e2558299b6f3a1546e25eea06a7cd780f58f2483459a4dadf9752dcd529d09e96028c6bb62c1e08607b3e27ac235d6ce3b267a27170f1f438be2ee415957b105979497a702b788f7908209fe6b6335e833e2f725b9b95a3a72babfc451b64f55aaba51bd160f857a64e428943e03e723873753da48b300a1987585af96cafd52ac09e13fc989c08516a252354b0a38120715629805a215564df944580205ebbdb56b5dcaf84875af5e943c2694914908681e0cb42334b6f433689dc94a9201f0c5b33e726d7bb2334011d728cdeaa34241b01e9cb9bcae0381451179a178803814c427a6f16ee1a885069ddc37679b5019993d90e1c11bdb007ad4300fcc577f00cf37115a02f5148d663715732219817c2b82071c1ddf65e696b58c455b41f576efb23be40652fa3750075aa88bfb63978347741d77efdee423563165f92083b2388212b2a97f4c075a4cb10fdfd50f20914fd9410fec1d180f2bdbd717388dd7b65a3edada491ab6e9dc572caf564e4c3295c98216a005cf0e018db54f85e1229f02c1475b25f3a01064c87f2cb82152c12e806173774ccff810f1c037dfe7fad1e7f9087d4f5d4c7986aea352e862a06f49842247c296683c0c13a837e5359681425913b4052daebde3963b4682bc8a4d977a50e02c44c3a4a7b90f25ac452733012dda1daa631af5d6336ddf743ec1fb5d54ea6fdc9f463c4577f4031602b8a0ccceda6f634b6d6024f7b07995cde12b118da114a1218d0f3fb86b5ef600fc06a244db3f93dcd4f7761d2d3fa3d9bd461fedb4353000e6b39cdd4b7c8431abdfa7945727ef046208525c54c128679606af8b290d52d9e224803449e5de2b3837623afc7539230840dbcd2f317dbb332abd082d7fd7a06f56073f08c49fd3b1706b3586ffbb5aa0964c3edafec744d32e41732ac4cd4cb9de2150f42f7d80dfa0b3a50b2c2b9aefb8d613572dbe808057f45d366aec13a30d385f93c830cd330ca3f13b43d6ebf7d98ba8547b9eb1b6b5247d67571e0c97c215c3f49dcca4a5ca83bc78795149352b04b4ed9ea0d3aac261571c0a70aa86cc3650ee06092304393a4cbc4abdb367b48024e2edf22885fd0a3154f9d33253426c200c5b58e93eeec4fd5cf80cad508d9005fb29ae4c50c54d6a5f9d8baad72ce566b3ff638fb62e3d166de11aad553dad08d3c7a2bbc4a1bcddf96b3f319f74377b61b734a67b58ce9780d5ed4d0e8a823bb0b5fb5ce325a1d5c69a6709fc76032180f0fe7f2e7c98ddc0ae9654f609e80acfc27a087e54fb9753a12f12e6ff064cbcc9195190a85b779e79b87bacf85abb62f41d4c95eef2a062abb0dc014290bf53bbed19a2eed94fdddbf289210669ec240face374bd200ed9769d320fd694dab4ef862ee24d76ac2c024a60a246cbf9ed2c82ced5b3ccd0567a333a2ccd3737539b0608a79c0228d579589dc48d5c1d762d90871c329b3226c8d08d420749b609c7a99189675c5e67460c3e20da7c2ccf55c8e447fcd46650ed010085025437cf1ab53993be638416c338245b39e4b754bef2df63a94fa02f99e1b0f0f6ae60d632c4b6e3dcb165a2c22b075909dfb80e26fdb08bd57ee5222ec65ff19b344e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711cfebc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672fb34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f8936c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b724da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662bf90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de061b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f127adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a63176d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a5734d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb3509394735f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d662417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c22882d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2eff62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612dc283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191bd96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf235daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933fc5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce020744615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e406a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e485c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96dae4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018ef4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e83914933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d909996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708aba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c18434653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c438976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37defd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b4989994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaecad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc672455405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb1731523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc85c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd78428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e882b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4ff1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d3061bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273edb85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d954ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b38106a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cfbf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303eceddae64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d990be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ecd17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbeaeb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd3233050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e353482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b4115bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a2705bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073ce17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b132265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fea737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d20264d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858fe38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e08f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e001997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b051e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4cbda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de1422bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb5710c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d39206f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336bc8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de93b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f5713f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790ffb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a99a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61ccc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca5374961f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c887dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d2301070b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea31191188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c91ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a51edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08fa6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b206090ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b9978a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60dfb55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea2022810636f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8dafb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fcb43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f12477a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e62077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de0222595eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b6087e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d2944b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34bbe78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a675070615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dabd73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e866ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c047dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20bfc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef080356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd9173eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e3160b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c60839ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c25aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdcaccd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc652232c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff5597eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b448113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb517179f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d9378ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac0088de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce230096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb572edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac4470701413f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b7555f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e795399fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f429ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d8496321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd54700de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf4614bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b03f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15eabed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb9793004140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb7752daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a5686071fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547bef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a8969e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e66a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad112922d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d16513e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b333112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914ba1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e1335ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be262473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940cb15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e067de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a7723d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa33e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce278669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f317f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ffe55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d64cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744acf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcdaf558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c98aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961cdd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3ef02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723bcd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88ddf2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807cbaed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e353e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b574357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a58d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135edf67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b96853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e475bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e41cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3dbffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4da4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dcd275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0bbbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d506271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369dc0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b893c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb05d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce036a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b283ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca68e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f18b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b41d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b6423648a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6eda56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f125bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392dfd430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa1578982f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a044042520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a619200a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bcef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114203232b492e0681a31dfb3";
-		String expectedSig2 = "000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c2ae1d54c9a9c7b7def19a3bf459fdbc5c47275e9e4c2aa37bfc97839d9996710cd50ceab45fa2ec26ae272a2a3797b580ac874bda3345cf56e069b6540ff8f6032eb07d62659944eb573c33d71058e500018bc8776f1c003171c8e962d41051f29ea36dd569ca57b3ffead498f1081ffb38cfae75b368721f67ec1d9b5d7f8e2913d63f309cb0aba8c572728b11dace05e5a00180073940b13ccbbbbbc1f8228c9bb0ffca4ed14934fbdf189888c9d6f9b53fe29727019cfd06da3ee308870f2e980e2279409587909ca6970a57267233d8856fc9d53e7be0958bdbd3f465429fc1c5d50f6ba64525ad06a238a3959a84c2a1e95e95a59582089114ac497f13b24d035e01580f0f861b78df39f21fb4448e61c9a30baa20ac8694ce84606c7e63ac0aa380cde7a0d30ffda28d9753088163966cbd11b6f55533801d953e787bd8544b97f6b93149ef0227e0ae15a900e70f71d1f16374474bd99023fab5eed63e2be6da822d975719e13e81bb4890503ba9c222bc8a448d51fd233fbb104f80f5c66c55962241bcf85221586ee2a390e6220367c9cefccbd66d4c3ce2cf38bbcacb77a359eec38c5b99dffb4896ae34fcf04db3f9a85cb8225317631d22dc0735addf0d84b8f2d3d9010a3be169f41296afe814f2aed28076479140d2cbe60c237cc7fcb1ecde0a7d6f11f11e748cee5dd5393a31435a45fbdea245aef1993391d18c79005f1b3074edbe08ffaa2c73262080702f28e6c0a9f408aff784814c37526cad7c5904c3af0b5e5da7e3135191dc4884ad167b5ae1c86a28e5a0c635b2c543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae463eda47b2f043afc2540d3afb0e4e0b5edffe0a8e906e557c3b84c154ce0ddbf44ee38d0c274e5bea1c6b6f36e83ce2374ab0aa09d51d881e768dd35fdd5dc0b208eca802e241626385ec46cd04f57110e12dad60b89eb0dcde05dccbf7c06718f5e211382af998107730aef24245b89ef6765456502e9af89e59b625ddfb50114c3db753eb15eace8e4871f2b4a49d7e67b0bec3c8f649c46183ae53b516454bc55ac1c79a6f97ed70d530ad84e371e3c8f3dc92407cb94f8ce14f76799c58f7f2fe8313cc33578764c849fb1ea20e58ad35e5471ba50b665390c76f3130ecc7811e84e394a35bf0859a119f97258de1833565d8ef142c842b3a27a17169841f2100740f8f0b55c90ec1573e660079653bfb0a45d17f5396aa2220e55c1d23dd9b79df765ac64fc2a9442ec286422e0af685e1ab72394934ee2dff1801a5859202c015e30f60ec155d0b0ec39cfbf66eb7ffcb3b46940c0aef409adc181c55c5328f671b6e0b5d18edff0bc335ebd2ca9afdc73dab4955f250874ff7325655ba9b3b83ebb460193e323bd86c7a5799f863816b6cf12006a2d114c252807eac1eea86a25e81d51728b6271aff7dce942044fa5f48e94493e250c6f62d0f0a39cd90b499c85f059b2e8cc6bf85e0f2fbfd5b306088c13384757a9f6d053e682dd57aa1690b4bbb878f0142cf1fb2b9934be9a6c3638f4e360de688083fbff64bec3b33762de91fb61571f754c2e6484234162b2f2d17368a44051b4d3c460077e227eb148c67f555bbbad500bef99dec034f6126cd16fcd6372e243f72dcf15bcb00e3e9c7600d6d31a48b97106f8a2cfc09e96254dd6474969304d3eaa4566c96fd7f0bcf20570a5a401a51d368c775f81b58b4c55f8c7375ed9c644454a09740a7b92faf985fa64783323895cd914cb6c88c9f1d58baee856151e1bb042fae42aeb3c8a1aa1e0b6fd26cfd8158c25af30fb98aba4b1caaf5dda81fc4c8327286572a8a230e5fd8755e6770bafb3a0bf1f85b700c985f53d9713e7a9e5ef04856c896a10a339402553033f36aef7f1d79e6c46ce0398e0504a2487feae2095ffa48139673a4cdc293333dd58ef482f38176de306f921e69bc234f599972636e8c16889ec9e1096767674f9724ced0597ed754123cf301c97ed763d537750f079351019448ea2897fb51df298c1e2eb4c2e27300a4924d7ba5eb33596589d34722068c6da4f6df3b9f98899812f47f799e8fcdba4988e38e933c3f751085890c164557228543f2862e786e579bc5be47ff69096cf9677468dc96b364e1e7bdf9492d254539aea7b9c4a646859fff5c56443087ec4c9bf6ba8a523d630cb6974697183a3009ad8bf5d74749e5707ca39bd818e42d47f93e68938e8194411a1498065f3ba1b2c755c2f702eea34bd6ecbe7d3e1dbb0108152bc8822714984cd750a0a7ec6bbbd70593e515a445176e989fff9f6afd7daa6d7e7df71612d196396603653b949e86642d13c953eea461507b91367a9a31cafa464d250de18f25719a601e7b3a730673103d7dbf20cea73421ffcf62339c352f69eb289b681d78f9f1a256c2941c40e2ce2b85c2474b7bc7aae399751ecf1b2ac8fdba9c4725fa8b6db283614cd13bb55966b7eb7ecd9a97dfcdbf1f669333258f65846d72c0b2245135d58e59435ea5a3855acbaae2f725cc6b4f35a82a38da8927c0410516173552d054d0fb69808401ae1df88fee5ccea1876aae0960860bc46b21504c4ddad287a4b51dbc0490d1c948f8c95d7f793f5f6491356138d54641476fcf4fd0cc70a2e854fca947ddc79a823af0b3ea0d73a5f5e9e61027720b9171c6830aca146e8cddbaa77a1501ab51a76d278fe2ccd9cdf2257468b7be7e64326c6083369bb86d9eec23dea1644cec11db72bbc842ab5ddee8eda08d0348267d48cecc9e37ef85d5ad0f93dd623a2db5537210b2762ad77a51ed8bb9019185c001f9f1e0de6fa0d8fb1d796f0e4e975468922e452b9cb4cb00eedff3416a03c80610d970b4ea5169ce43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307277b9a09c038aca00a99ff63060dad783e060e4cd9d59cbcb2076d6293d19fcea9b90c00398e7740910f425703a0da099490e3b0472db2a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711cfebc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672fb34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f8936c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b724da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662bf90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de061b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f127adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a63176d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a5734d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb3509394735f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d662417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c22882d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2eff62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612dc283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191bd96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf235daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933fc5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce020744615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e406a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e485c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96dae4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018ef4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e83914933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d909996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708aba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c18434653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c438976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37defd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b4989994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaecad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc672455405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb1731523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc85c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd78428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e882b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4ff1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d3061bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273edb85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d954ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b38106a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cfbf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303eceddae64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d990be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ecd17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbeaeb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd3233050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e353482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b4115bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a2705bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073ce17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b132265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fea737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d20264d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858fe38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e08f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e001997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b051e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4cbda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de1422bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb5710c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d39206f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336bc8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de93b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f5713f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790ffb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a99a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61ccc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca5374961f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c887dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d2301070b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea31191188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c91ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a51edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08fa6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b206090ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b9978a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60dfb55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea2022810636f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8dafb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fcb43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f12477a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e62077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de0222595eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b6087e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d2944b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34bbe78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a675070615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dabd73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e866ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c047dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20bfc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef080356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd9173eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e3160b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c60839ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c25aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdcaccd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc652232c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff5597eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b448113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb517179f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d9378ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac0088de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce230096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb572edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac4470701413f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b7555f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e795399fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f429ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d8496321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd54700de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf4614bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b03f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15eabed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb9793004140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb7752daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a5686071fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547bef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a8969e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e66a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad112922d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d16513e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b333112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914ba1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e1335ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be262473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940cb15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e067de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a7723d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa33e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce278669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f317f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ffe55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d64cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744acf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcdaf558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c98aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961cdd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3ef02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723bcd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88ddf2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807cbaed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e353e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b574357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a58d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135edf67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b96853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e475bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e41cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3dbffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4da4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dcd275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0bbbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d506271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369dc0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b893c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb05d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce036a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b283ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca68e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f18b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b41d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b6423648a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6eda56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f125bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392dfd430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa1578982f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a044042520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a619200a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bcef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114203232b492e0681a31dfb3";
-		String expectedSig3 = "0000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc1ce556901faa3fadf6e97db78040156ebdb2e7bf2ced2b6aecc9337747a7c11b90d77683be059022e8b4eae2196632e270235ccf92b9da827f31070fde1c7646feb15df59b17f97fe82bf9d574a85366c19f48cf32e9c04f7366d5ee7899f48f94b7d84d5890340f377e9aa295ec2b356b78ed6ce99ac8ad8ce5bbeb00280d28eef208dd63602130aadd66cca458a21e4347c7008e937d773c7ae46117706a1cf43d566cbbf92bd7c45cd3a56f52223439bfe06a8b136aa35c76381ed5576e8e1ffe6e9fa145cde1fb377560ad07a109552c08090a8f355192aabd1f3fdc112350c794af5d165e432d777d7b83b40663be13b990c85e602d9ee85e65f13a96d203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c2370aaaf4ea6dcc2b226beaab6b814e7021a6bfb9536aa4745a09a375468a450c7c29b7fcdf24f248ad7bf8ba0f0379f9486b33b1c656662f139aaaaa917648568943207b76902227961981a8b9269ccb70bf4c88a097639ff4fdd736a006a5416a8cfe2f7685e175c1ee30e2f8878e165a2693f0355cbbb3874d29fe6ef76ee992122323e957211920d1e06178a51897b7ad577215de589c24d23411aca8ae2b76adac59897ece299db3da7b4a250244c278e8c4ef0b31f1079980e2b9f5f2229f6352fe258df489ece62aa76f5557a0bdd295ac403da918eaf4d8e7a48d102e0ef71e06193ffc698a42f4da29427c74564bdd11dd3d32803d6007b1cdcc26dd1390f8ed4cea650e870886724a3db11309f48682ebb700832cde372d09620eed82c39c2eea41520419bfea541ba2815ba1f539275ce944fb66f94f72c47383350cbcfba29eb33eec09b53cfc896c3cb089e72d2734b08a605df3c09a6e7cc40f14007e5535b59a48e24ddc2921492fbf90136f3bc051a32bb02e043ec20b7c4da5ae92dba6089a76f1bffe50a22dbfab004eb38dd3b11df574a7344d2f418a2eb8bf1c172fa9bd04ca573ddf8f137200daad36ecbafbb79602d6ef2f996d02e692d10b4c2c5e6c7d0129d85e3ea79e6b13c2af471a2192e8ab8a6f4c96185468fa763a3ec418950bbede0f53ebf0182e5a5ee299130e7420abbda1ce2e4948121b6756f03816b07b5578293e4d14da9ab6f67eec0b0cc1a9b8285760d0ea3eadde48c32a1cf5159611845e3962990c998cf1928a7e4d1a44f079f25a1a3047c2f3495231a7612b9628e17fc7758f020fa42df2dcedb74b4844481aed37c108bbf9bfe44c7be12c34d661573a8f7fec1424ccc2607295d7dc3f21bd6bec75f098d7d1551f1e884534186c284a5633b3e3c5e942d5544339cbe5d03f1bf4701fa4b0533f25a45d768e74a6d8b3bbb1dd2b38e5fccca5c49ea6dcf9a08288cb53889f73a8ac7b049d480227570a73c44602c57238c4aef9184fa40ef719d8a0e2942faa96cbd12855923a0af0d3dcb17dff0f114f107b5cc2994acbddcb6938b789e2fef0dd5457bd12cf7ef8eaa450d27f554e447040602b1a0a02396f3c9a6c90c3b2b531f7ffb1be0354dfddd27388a771a912b6b8cddcce92bbee17553276aec5a66e0cf9af9a5e304d6880f4239858ed7afd93368125a822bc930e827b9ce31e043c1c729741248186bed44f1a791ce136d9713b537d7ad336f9516f0bfa22a09a2f21dfd24b86e16a30ec471bfef6675e72ed2d450309144da61655c6c37026cf108af5628c32fd0f36cf214275aa8961b22fb1e042959d81bff3c1bf2128d698c37c2cced73b6defa76f5649c2374da804550cf7a7d68f43c6c810ee467fa6224f0dc7dc8f94dd45eeb137157304a85031790dbb065c708887a0b8c1e1f038d42bcf909174373846976e27208e5561de43052a6d901aef041adb39b24448cdfe62d988836b7128679358afc3898b83093ff7891c92a60f5f5fe9266ceefb73421c43b2b1dfa08a180bb4dad83dcdacd97a63add143d640de6ba4a6b5d62e29cdb74dbab64162782da7b72c0f3d5dc4fbb351eaaa423026842a7fa340fcab11c61a647ce183ea5b647ccb3fef9eb104ac9faa0a4366160a7887e65e0fdf4f879b7feff2020646088d1601a69e97780456beaa20db6db621445387c736786214485b97a09fa79e2f818a21eb6c5de7661b67c91bde23b6b9e3365582db91c853b770f7e4b87dfe35b875129e1c7f81e7102e4a774a3f7dce92a3d6322724d27aba6e162e9a75212a0bfaeee94082a87f01690a974b60690c83f08a31cc6fd7329a6b03327eb07b3d93db67a7ac0f3bbafd8c9ffe0876cc7308817a8ddbdb26b91266e40ba7668d30a02085db64cc1dac1936c88b3035cfcb394e82f80bf4a629353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17ae460dc51df1044d01d3ae1806653b1dc235c63d516af4cf5cfe7a44ee19794df99564199a5f98d0a7fa6f534fe2bc6d930ed46b9f795d01f23d8d5ce859451f59fda1eb9787c6a4c59a68a3ba658a936e0dd7ac6adf311ff7b0824b62d079bd8197ea10c6099eed16b3b98aba3003d6e24b35116f9ebb1240c01ed6aecb164f705c390fc984aa900b9d41eda3b3ff33ae3ab737c99e059402822c2e8e40a93bd500f75c03e4fad42268f05d84ec8a6480751d779a70b45996ee27cae86e78c12b8e7d1cf123d6e861682557c95c4b0c2bc9368312b8409a4e52affbcdadc5af2c972243390d1074a64c90b2ef4e670c7acee106eecc06a1c51451ea8e7531d84c72cb593c1558cf5d36e9a59d275f88fef060f3aa60ac06e0c15f4ca46901553d27ebbaf3ee098628ce948b82e3b0a6f0ffd334a28b8b1d93557499b3778159e8a320e54e22ad4f063765739722ed89ac07c9738803ffc999afb8f2527436c690032206196b15d909a709df1ad319de092e30f3d373595c704192d1b9f6330dc1631bdef5fb01d9ba7c0790cd840f3de0d0cc48b5c6640b3b7729ff9c1dc23e3b44ed3814db7352eb7d2a5da6cee5f7c906743319dd75098fd2c5dadf4e55b4a461969be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711cfebc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672fb34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f8936c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b724da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662bf90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de061b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f127adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a63176d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a5734d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb3509394735f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d662417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c22882d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2eff62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612dc283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191bd96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf235daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933fc5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce020744615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e406a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e485c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96dae4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018ef4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e83914933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d909996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708aba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c18434653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c438976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37defd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b4989994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaecad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc672455405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb1731523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc85c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd78428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e882b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4ff1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d3061bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273edb85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d954ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b38106a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cfbf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303eceddae64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d990be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ecd17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbeaeb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd3233050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e353482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b4115bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a2705bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073ce17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b132265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fea737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d20264d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858fe38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e08f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e001997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b051e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4cbda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de1422bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb5710c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d39206f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336bc8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de93b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f5713f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790ffb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a99a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61ccc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca5374961f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c887dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d2301070b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea31191188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c91ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a51edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08fa6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b206090ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b9978a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60dfb55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea2022810636f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8dafb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fcb43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f12477a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e62077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de0222595eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b6087e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d2944b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34bbe78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a675070615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dabd73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e866ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c047dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20bfc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef080356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd9173eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e3160b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c60839ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c25aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdcaccd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc652232c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff5597eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b448113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb517179f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d9378ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac0088de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce230096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb572edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac4470701413f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b7555f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e795399fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f429ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d8496321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd54700de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf4614bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b03f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15eabed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb9793004140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb7752daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a5686071fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547bef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a8969e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e66a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad112922d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d16513e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b333112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914ba1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e1335ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be262473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940cb15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e067de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a7723d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa33e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce278669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f317f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ffe55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d64cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744acf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcdaf558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c98aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961cdd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3ef02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723bcd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88ddf2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807cbaed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e353e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b574357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a58d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135edf67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b96853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e475bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e41cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3dbffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4da4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dcd275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0bbbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d506271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369dc0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b893c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb05d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce036a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b283ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca68e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f18b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b41d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b6423648a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6eda56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f125bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392dfd430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa1578982f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a044042520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a619200a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bcef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114203232b492e0681a31dfb3";
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig1), sig1));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig2), sig2));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig3), sig3));
-	}
-	
-	public void testSignSHA256Complete1() {
-		final String[] signatures = {
-				"006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf804a4744898d77fafd84eeb618115067f23f5f9d5d882269b5987de5d5428bee7784e049da83c891856b5fb20f17e1ecb6873c3a87e1a630e327b36981a5622aad6d9f5c557d7132a99e3c5effc1abe17560b3152bbc98a0dd701b491e62e281824c43848a0d958ab132ea905b4d5a0bf4f65eded991b01b3839cd188a90d76bc2b7e96be674af7638436b3a535dd0e8e3501e1934a674671cedefc300e7add098ee28caff5d9606f92e22c7769720f54aa4849528cd483747edc348c81eb8f159b9e395166f34db8a19539dca8f00c1620a45920a1412a6ea88f734a4bef32d400b33bd98d97054a6c56dd93df2b3a316713f67aa59051405c5d5d19785fd47f6149e3993e2476c87dae826bed2f5319878cc8db6bf515022a5658b06e08e4900a9393527765d9fa852ae7621ce5aa0648413bba1c650ba3a82d1863c17785f4b65e3dee8c385db41331f6f1ffaa7fc0ca86400aa4bdae6fb713d4445ebba37b4423c30e601c567f90e3f1431497667f9102256a69876d47202dc6f734e27af4a69e44ca7959d50704b22451bb2bb8f8ccccdc46cee70c14b6afacd715aa25ad38801e0f556e2e5cc204c5a995eea1ddc65c385512b41294b5912ef3e4dc94ffac6294bfd7acf2415bf765859cd9fba85b8f451809f428342e5b56097122f1fc7a022b8032971814761062634671375b2052b06471391653c5c3214dc96ef984a100d28521f2a28bc6d7132929f8eea1481ac636bfc728c9c819c193e98ca733497c72fee51d5d822d75acf9944db04e5d1cbaf50f83550c6fd4a360cf30621ff2912cf72b9aecb43ee8ad5985c2241253d2a5b265e64af505d87fe317517e83a81fe07fe056fd8db3e41dbf48a161b55c5d47278c6f9c6f333fba22886632acb8c2f3ba8263052e7e9a0b102032cd9de760e38764f3fb6cd3652eb548be64fba09caf0c6ecc065430a3c7a65810213e42253966b9a8ab1dac361021d0c55513586596462c99a1f5b79e8dff022ecd0d23388356f8bd3d22b28307f96d84a3dca78deb2ef353a16b706df68aa468a8f9f69f97c9bbcd358fa980f6a356f1ff38d2386ee3060e63d104193a7eb3ce1aa69504f5fc565052e060f229b90c9a6ba81924aa582087c3c2719cbb6bac10aa57a5f487b9b3b433eb2bd57f90e15660423d88ed013033ec1fc50990df8f775721a40044dd89c0b5913d7e7711a980cb014d2a2a007e3d9968d5e3454be6138badc5d9efe53eda4ef225b1fec37a555a7e9346007f9bb71800d128f6da23ecc6babed12bad95b3694f19921f7135a37ebea09d83abc314c79cbf3cd1264029f713c79586c386dcd20e6b18c1d7bf196dc7bd2154baedec9d9ebf262ca81bf7739ad02cc6fd58924d0f526bc7da6cb7501dec26ccbb61248830ddabdd60df3a14b4c0a1d455b24c47a784a8b527a7c39ef79d08247b9621baa7d29ea8d2eacf77f787c9d028b95c7c8aae0ef9153fecb980779fb5bec3015caef9f5a6941a778faf418a0dd4da88c5b10e8c65108de5f2b00ce093c50bdab5dda9bb571dcc88c06196f1ac23feef71feb5940116a7c484259a214065afdcc4b56440a5d5779574882aef274dde27f411967ce80e511b1870d2765ab31ba5a096e2a1596a1b06cbe867be177213d920fe60bf08d22b4dbaf8f2b18409145ca5bb8f8e1bd87cc2cadfeef75743affdd3288ad8b0890c472d783394a85316f08e722f8b91ca88c8dbfb764e81c2d7f71521c339a4467bef9a786b5bdaf79e9042cef62b9e49023440fe37e4e4e50924275c2977f48a5fba67e901aafa9f9c2457030920fc5f6848c6f637f312eb11264c2f9928dcb697a308ed8fde7091720a53f1054cb824bdc8d6e44661c3d3fecfa0d4037f858a5363300d76b44febc85ce8966c014579eb6905522db9b679e29f995b36be3b9412e4d384db76c6764748f7c5361750d1f5dc2d8aedaa39f2d58aa6559fae1f63fe85cf867923bdb2643716e365fe04a507a3cd3518dfecda3e51b861090dfce7d7c1b3cc6f0fdfd9b64364c37f9f1293fa60c8f5111bc21118932b0a7223fdb680aeabfed18804594ffdae74d289c1ea58a33cf3ab8807deda2c9e1d39eb78c44e53afbdf0deff99b308cada91a327b6af600e51ad6214f4d16a4d5e0c2d84ade4bfa973dda97bfa8081b1cbd453e3d1fe1a7cc582ac459eb4ca25b81586d17b5dd50f7fa4e015073871aa935b76730399ceaa99b71ace71d729742d95ce95a93b699ce1a9aba27adc797438742ea8910059867c28f6e5725b66c8b4ae938ab5fde7e1b9b8799b5e5fd0014437ad01691deb6e970fe429d0978a797e6b973c90cbe55f57f28674e11c0118297f7fba8f400d40f82bfb61abffa766ef724547a9fd5d2a2f64d8bb33d53a7187b27f3310c0c772a93c920cf17b78726a0f2736001c2d7950baca885e2a212a3062d8ed9c4c4bf8060d4297133be148973bc27d6e3fd2d7c489a34cbe3a9b7c46de2a6db68aa2229f8985c832ea2b9b73177013a69a12f1f0f5e502537504675fa47fecd3b27c580f7697d47e02235629824b363c348943d28c50fb200c8fbaa0e9b801d692112a38cb125398b5847776b4cb0937f857f69ae3c82bd969c9e2a51954af0bc9529fc180f2155f7f94fe79799ff878956a51dc09edc775da42908b94b2ad3ce16cb2f994b5486b332caeab4409d47f6c39ed00c4c5d460f44708dde11c6fd26e1237581a2c6b81741446b0144fca8603ead4acc7f49a261e1ecfe797203f607eadb54fa5765542d39de25d0b2b3ab04411b034b31eb324c00f013927b24c33d70525f410498be2d7358ea01474861341c508622cab0f1663dcf17beaf051071d6545190452d78252d8968b0d8cab0257bcf0ca1609c7a99189675c5e67460c3e20da7c2ccf55c8e447fcd46650ed010085025437e7f64e396e89cebc106e7bf6c54b886c8197f19d9a7245be704ef119c787cda0d632c4b6e3dcb165a2c22b075909dfb80e26fdb08bd57ee5222ec65ff19b344e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"01994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c299747f908e53b302e4bc0117975de9992ad3fffbea32b86ecab009951f08d2d830f850796c3c67ffbf48f370cd19b6f8d6baeebd2b8b13661dda7220559e211bdf5c480847e3c00fa13252edf3da2441f05e9d4761138ddbca836cd39a2f444e87aa676c266eb2c251742b59129c164ae1776547db60fcd71eb605e798de79fd48b54736511b7fe1b561eb28f0c316ae7697d065384c5f98dea2dec130352181285786b091f2b166383c55a58a7aad54a1520a74a74515aef636deb77b9fd8fbc8cd982243d503ee360b84303623d9280f9b379fb8f0183e3a7f87698db93af07a79469b25d0c90c79f389ff1247a968056b8e9f6c123d0c3455f41c7867266f4d035e01580f0f861b78df39f21fb4448e61c9a30baa20ac8694ce84606c7e6322868b0dd3c3439f7c01d49603b5d56f27dfb5316ec1376583d578be061f88bda49568d71f3b6e1d72013fda003781c1e3f7a2d84f0a5dd3b1a06c56cfaf64e6f1bc1ce02403c5b0f7930033dd6f813ebac9335865d03893d99caae239c17ff9ede587d58ad0042357ec654e1d4992ca85fe67435e94f7202678f4a0bb5d98066a8c34a617dfeba3d60bec876f6f06f5c0bb6bb77142cdf44021d5e7d44e2fe34f4e72ee73e6cd68d4cfca61e8e7caf64230fc9b270dcad54de7cbd7c6deade777a292088ad1c76a79b0dcc26f4080aa39967db9dcd659c2ec9ecf8f5a7dd0abe0dd74b99cb5cc9a17517a053652a8afbf8bd56a76304071bcd9b0ad1d0df36f65f17338b83aac6fd9900b1ac613c3af74f327eeeb749a53f6777d0f5e71f529c543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae46fd0b9e1085d56d7ccc968bae0dc0d1f94700f788ce48449a90ab4c2727d14224b0d2703e5b220b62862da2af2cd7eecd7d4483246ab87612bbe1401ed07decb7337efee2b50bfb0690e1d76b81003c0db62b9be498aa11731c0186bd68c30566c4ff56957ae29cbf197dd7a218d753541c2f632772219c3d25c2e900ff7d685d4c3db753eb15eace8e4871f2b4a49d7e67b0bec3c8f649c46183ae53b516454b04e1a3f3a854de28469ffd213b783fe0128a5c12982d2993d5177197f102d4bd26d932ebb0d5c3fc6c93a57e185fb719e3449b878ef63ec3ecda13b385bc820568baba5a58524e2da63a0628f5ef530b5823312f0e3cd0270ba3928d95e89a13975979319554c204d4e893fd7dc3d59d14fe806a01896f7b0fd7d445dacc08a39b79df765ac64fc2a9442ec286422e0af685e1ab72394934ee2dff1801a58592ac207a9f15f32db4802e781beb41be0b97653f1c36daffde0345e6b74fadce3ae24d7ffab9efedb9ecb5cba9592825b605d379a3b32f0200145f1895de8322ed32fc26f338718d55a8e278945c877ba42c462c0cbdf7975df9583be83a2db540d8d9fa6ef2db037c63cdf6b6f42ecea0f2a1da1b4d69caec25420fc842a4c57fdb3394886f8d1cef3ef6762bdc6dea1f93446273d2f6926a920d641f58c742bbc04827384c6ae02e96ed314d515903936a3dfe118ab5b391325cbcf3c8b020df1db6fc5ba5d3d6888d64bbba1cb963466baf66070edf20f31af6d24da2244ae329c14760b1f36b137c75d9ae4bc5f26f7c747fc373c20ac4d4c038587e1a92dcf60708752da4405f242b9a024b9733709b4125cefa5709b210dd5a96178af6bb017ebf8761e91fdb27f189afff065ad0303ee1340b62283de4604f329643148fa7b92faf985fa64783323895cd914cb6c88c9f1d58baee856151e1bb042fae4281f10a9d8089304e7e05d8420197caa981907e609993ff7f4214327cab1d64d0bb97febde0283b3a7cbc788bd2a73e70f7f4495c380b4b8a2a49a53c0042ca04b92ebcac3aace002cea307286cf66357e5e097edf4a50b8d14f56fd26a2b22d061ea4a33d54472be18efdf9a587cc6e93ca31ca010d7389593585b77c6df62a97e7d0d9d09cd1c397a5a7299c13dbb449f8d54d1b2eef2eee0d659d0c94a8bf7b3c5da71fe6713b51a6fa17a6f0b2caed679a15ff38ef120df61704119d0cb21d6a17a15fe53935f317e5b77479c4c18eb2f9dfe757f87094cb430739e23ee4066644a2232dd8b0b140e225dbb8914622a4fbd1461f46dce6db746e716328b56e38e166e48d873ae6a1f7c08b4050aafb4b21f992922fbb30c0c9d9d59586036003d2f2d2cd78574f7f68e2f48833a61574a097ca3ffcbdf688d217619ebfc5b5f3ba1b2c755c2f702eea34bd6ecbe7d3e1dbb0108152bc8822714984cd750a09520ee27b090a28a7306fb1aa76f5235283013bc3fc8734dfe644d31514cd1517a05393b5342182ea2b2cc2a9fdbb502e2c2ec2dd4ff4e84fbcb28e3d8c07febc2301119fa36cc1124e08d4c5e3744c0a1f9421bde2b81a2906245c830a086909f61bd3c205219bb0f63cb42afe6517db96f3f5d939cf0ced8d8a7ec2656c13230e998b04aee35673d3b10acc64588e0d3e74ed61f8ca878fd3b2ae919659e0a01783f9c3c228a1c24af30ac4f5cce260de21bcf6879e8eedca12908e6440f3054d0fb69808401ae1df88fee5ccea1876aae0960860bc46b21504c4ddad287a4b51dbc0490d1c948f8c95d7f793f5f6491356138d54641476fcf4fd0cc70a2e8e1343c5ed6f0d49f2679a9c12836a33d11deca674bc60b01389316faf9fdb093f64937f134a3fb592d40b3f6af83df06e834ba00d9eb899506be4d9bf25871c00395c3e28cd168fbd6c6d4e146f5bff31c8876085bdc09663fb8387378293a8dad0f93dd623a2db5537210b2762ad77a51ed8bb9019185c001f9f1e0de6fa0d80fb23b2b7bf8e523f711e0ac88d3a98a64156219a59f5c2df88a61d37eb05bafce43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307277b9a09c038aca00a99ff63060dad783e060e4cd9d59c9f340e78488b0090ef1cf16991be0ff81271288c9232be17b7d604f875dacfa6a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc8e3038926c4b98780fd65b4a33c723d041c203a9e1c5c4bf2739b45d5840870c72ca7ccdcd6916d5e6b5fbcac59fc5b6c05b1559b7893eacb5b48d8dbd5120c2ae99988b49d7c580ca3ce4673f8700b55de80c0dc68b914f800efba17bd6baa3344858712ec0cdf54ba393c71d69af46d0f65640435bd0200e943a27ea01152bdff5aba3abab82b1a2dedab39d9f0bab23407da278fdf6ded66ff8285473a9fcf43d566cbbf92bd7c45cd3a56f52223439bfe06a8b136aa35c76381ed5576e8e9da70f0fa7919b3d1445abcae14473558434a927c009d1568da24e3c1d93256a50c794af5d165e432d777d7b83b40663be13b990c85e602d9ee85e65f13a96d203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c2370aaaf4ea6dcc2b226beaab6b814e7021a6bfb9536aa4745a09a375468a450c990d9fd04315a961de8bc1d2cd717a4848cfca8f8e331e97ea3ebc55cad91355ef793bef8ec9436397b959559560706d1b792d3be14de924cc04f71d99e0e3163cd41669467b781f2759ec517a58c4b7a11ca7d13142a7a5be04f87cebe9827a2457cf41db224cd508a5ffcde62a2a54ef967d12991ec9357a57333098df55239d2a2ba42e84fa80cad014d04b138014767c5be3877a6c130a10f9f5c8e83ec65628de86f6904f7dcfa79686e38922b40d656f54e7b3e8d8b22fa6975037cbc50ef71e06193ffc698a42f4da29427c74564bdd11dd3d32803d6007b1cdcc26dd3095df49e3dcf05fdf4905b206c00c4bf7a8661adbe20b04f8a31134e2f386ffde1cdec317fa5aec343d0c832a754c5348b1a1c89e79a57135ed80c4f3ac3eb0380c475b80e7d5e4f3b3af94bc599e34cba6cdfe390497f5b9e9eeffd54587c0ab05606b1c90ddd35d1e618db21d6044b214f8453faa84225a251aaafa4df78b27067c877cb428f840b3199fbe73fe1c120085cffaf86a00c317391f30312209e6d9cb8b4c813115dd1fa0517928be1aa4222b9f64f6ff8eb01dc2ddcbf758c95d7c077be001d182daa82b956c473e32d2ec75ba61e998b10ce5ad205b8ceee821f36b2959f083749d924e7779fe7d83bc6146812290822579bcca0e8b3b550b21214cf1e0e5c67b352075b9c5628756376feb6aeb779d2a59352c5a2a8521bc20fb2cbda4c3953983c56394a20b9004769ab280aa5effd5dd6c0380a077767c318e5bf07fae3392b1cd42a5c4f29b906d8ad611bf809f1d424eb5fa3f0e6f7a0c3c57439aa22d650cf4d9c4af79eac7b8193bbccb75a94fc00957db274e4512514351415448a7f2917fa0efb75cf03864b5e74c82d166d0682da6fea6c3001eeee520c85d6565456d97db5a2614668b2ab4d475f97ab1f5afe8ac20b46a2bc8d2707e836d0b67afcb25b7be36c90608b0f756cec5c3bedbfaa02387bb5e4dc0bf7ae4d42c0548641250fd19c7a256d5f23879e3144d7c339fc1964ab767c401769dc878df1b388110f46071fbcf1390cea1d0ab42f860700495cc8b00bedbe28e6afde5e8421a991159e1f57160affba05ee2c45efe882fed3653d1837f389755aa658384ab68eaacb36077516507f849a136ba5dafa4b65b80975ac9f8119418a99b3e2bd518e1e7a9eb8af7f5eba849afba85260e312839c8f3020802f5b0a09a2f21dfd24b86e16a30ec471bfef6675e72ed2d450309144da61655c6c3706bfd3ab7025d37daf17d0e852ad884a98394ff7a08bcb63f6c3a2b1c9a06c494f8bb926335a31bc1684185e4c5c7363d7240ddd7bd1700dace25f48e548ce21d65f7decfcf5baae126d7131248b67ce5cc471fe815abbb40798bd53b8e25b9559eb0c556d18f42dbfb7c3b42389d09fed688731e20befaa039b9fed20f6ffebbe31e01b543be905e9a998991fb44e82b8e6bcd88d09ba15ee58b380a44ebc18229828a55e37ccd7299afc57eec259cec93baa8703f4fccf1ac4f0b689d9ec7b2d31d09893aaa6e3ae084009cf8ed266227aec31d3eb0fcb2725f72aeba4e09a806143c0bd3b265310831dadd6929909d7f3f6d17fbf17ec3888e3c2789dfad83b43344577510f974a39fe48d922c2a22c6fafebfa549473d9432df34836163cf60a66b4c24a94a0917600f92ccde90b81d5e5fd70e567e8a89edb0560e164928ee96a35fad758f18f0571959626930948bcbe47cfb642792ccd83bc11d8f883b6143c8eb738480131502027ca456fca88d89c10dba6d346978d1b5f60b271dad4df49e780c1ccf71d8e6a6b3a1d0ee2e6d33b72b4d7112f7fd8883031a882189eff767a5d42b8ad26f8d386dfdf8005da8afbb4f8ab0d94c416c5cdd07ec8fa389e3d81394eef2198ea5876d413ff67606a551ac9afc117ff9089b50d725c71ba357f770948fcff0392c314dfc777f37d2317839a443314ef2b91f69276696de1724a1f207f94dea2910a2c3303ac0f89befba9d94b3d73940e6fcd7142135577aeec7b419c655c8e85830d403fce77a59e5fcb7701d2f24716b63d313a6f2e27ff492d450890d61d5f92bb9962502d1651467a795fab27c874b4ffbbe201ba4b02ad79a1fe2acb7002b6056bad4dc9433beaf8f2c6e6b0ce5b1ec4f1d3478aa2147ba2d65e355638bf465fff4c2f6340438675d8e7ff8ca30df1e400260bb09f11169db9cc1575dd1c55e8b41edc066f770f1a4c9ce43a4927242d0eae4342e2220ba0d5b02fc1431a720f189116d0994dc734189feea865e49c7f0f6c7a1bdf2934690c2e6b03b3d4c618cbca4cfed4e3a5bc0204aa307b023e24f17cf41cb8a0a9df14a4ee68cf62766a0d49a07e662ab75a4bac93db013d911878947f9e2c2399fdf7ae9f2c9e421f1751f5ed1abdb4a16b25878b67f4b56c23b904c58238c944f61f837d00baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d82473fa78bdf8387b0fbcb029c564db2906d29b8a1d40b1d36ece89f25c0c6a42bb045058b00511ed549f6cbd1b1e102ddbc84ce321c841a5f8dcbeb8a7bdf3b839be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647489bbf7a4c5b7b7904296d3be50258e80a4ca6b59a6a22a1c63576ee508e2407cfb66454d3e4fb310c896480822cc3800f0769622463743c17601947db673131d542d7800af123d2ac2d705844b093404f2072dd430f48bdb09438a21119e1b3730ad685a39c4e8c67750bf56795a43d93be6d5d6181d05922a13aa08d95fba85bca65919b91eff118c81038214df51fd883fb56e353334d0e3cdca46afefd63780c4a7919a28e3e827f67951feac797e39e96e7c3ba19196f0f0175b91d5c7c5976a1a1d928fad412c4e2a9cd8c841970fe3055797a5dbdaaf32f4de79602dbe35d0fe38b3f5ee7dbe1f6942ec427d8b15cdeac92278b2a105e647c3ad4916948d22a6df03aad05a9c5f476bc4e70169ab50710c2177cd78598fea88681d34a7e4f9c4cb8ff3fb1dcea06f624c0fc879cc5e307ed78eecc6f9aa44d382016ee472ebe19b30716e55e380065ba32927ca73bfbdad670f191876e671787e859cca6e4c895dda8730a40d0c73b0487ce327eaa9c85815d308ee08ed25cc5f1b48d302eea1568fee7d44f8a40b4496cbcc25b6dd7b199323db3873709c2e6bb8dfc7edc95724b541840a32d6ca4465e335c4bc8fdf5aa5ebdc3b3ad9874954771a734c9bd9ec525c081b95802534cc71cab2d9a3d3bca09403c9d6dfd7d7d35c0e4ba866c9ff837c3d538b270aae8d8152db295b20b7a19e2a3aa383163d9162fdbb7ef5341887a5eb94828253f6977a251607fa2e6853159f0b64d5c8fcde6d18cc6dd02e72f7ed723b74da9f3129230a75980bb9697bbed6475f974c20b12a02ed7313ef4ca5f473774e0e4b5f520cdeb4fed526ea10375656c18b349f984119fe43133794c2c7e2672503f150b5a740c04e3a763af2ee0c3b14de8e0516222ae91bf50b7e5cf92f5cc6df8ffec7584f88a9deea3b5ab4a8d8ba79c42b9bcb028ec911c4c93526315ad80b1efb97e11a692c4160d6f9f6821c0625889b524fe437112df5845df7fe8d9ebf47dd29ec45448dbce81dc4efab911477ed9408cb7a15978f2cdd7b779aacf3a4d4f44d0d87b60e4bd6dc145b0c40fcc6ddfad114281d87b68d4f51e9bcb317ae7436f2cce963a327bda95e9e460ab660696a528c393ed21f92e38402dd50f3d643563214546d0e4037501b3456b4dc5a02d1e4b9b59c18d0c341170d73841ff7ace5c7f1dae32da6bd6f97adf3a52dbdb30a46424abc17a4a89340d3345ebb08fc03a7ad4578a64d1488b879823a35afbda39dd9ee9d0ab8ce94bf864f3cb1105ce74bd99e85b6fdd97fc3c54393ce901e43344d05f31a8376265777b18d853553b95a11f2bb099be5d433fefa7c97b8d3b229e423bcd1832ce5bd731854fd5e31d0ccb4421e4f73f121f4b79404d06786e3318fb337dfed40359c63aa685457ad21b69d365da9ba2cf1e665d02995180df2f35312c4e45c8f3ce7215be2ef053807341c00acab30a0c7dffe8e75fdc71704f35f13de8ac21b6e39482271d036fe5b8fd6020eb0ef06f80490915de57255a3f9a31d2d6750f86ba57ee7320cde75c4a49bd4d5efe393775dafe2f209abfbdf7762c560106006f4905dc938f91bb88a5f3f6c0176e38b4bae238f34b2f1319c2d48a8be83e53959b26e6109f8ac292b9a88cbb51dd3369dca35e58774a9a02d7fa47d47557bdcec261a7fd6dd60437fa9af3324a36ba88c0ac652c3760f5a35a585245a0c5978591b1ff73ac058ed438e359b09feebc290d3d3a5ba3670d62887ee338099e94d14fc40257e36bfec7d5f3b192062a003965acd650f8285c2d9b5a1ba46ac46042b985d5f631188b2fe1a5624a92752d3317d177089b68a35966fa021dd0f8e4acbce56d1643650c64ecba2243431c06d4065cf738e5cb0980da72a35425f568e3e48e9c7b73ec5394e45842607a4fea3be2853ba6bb6b3d27f7af28f0e41cd6b42ae977f09bf4f2a1d594afcb83a2f0ca20c7e442bd7052a9ac85fe67cb537250ce91a255d3b7fe4b2d052312b839aa59d306afadcfd503b303bd2dba7da45ce8dc8eaa303a1f9495038a3e2fb77e4bc35c5399556b58004d9cad13db1c1fe7ad4c66c88882396aa36c267227441cdfdb5a5c89dd23616a7563b5249483ec60e25e36024b5251ed6e0557a7414180b084266c1d731d2e42544513fd2d53d4eb964ba1c67a4e13b192c5e41f5c5b4160e8336a2956c1396e55b2c417fb5855d4aa56dcfc54f8e19c6eebf9eef09ac5c38735090d692b837cd08e50738e106cdb3a5b95314744b8d67bcf40645ab09f1233559173aea688279d0c31cc885227e88bb3e02064762052fa3b0e69f70e44dcb1410ac1fc604f27e86796c6d3819c82d0971df6b5cd89f2403623c4bb252e60af6717d4c2cedcf5dc96015c5b700e7e915e60aab7a1f85bc19e2b6a5307a51988aa1abab57a201a488b7d8f18716f0bf8aafd9c7660dc46a29665836e0aeab04806bd0fc5989c4f63f7ba4a58834d8d5447f70ebf67d33c57ddbee6574d5b741af87217f3c0ffe4f87c48e2d49a274968b87a254eb41e3461dfb92f63bf91f42f43f33f7ce9af91ab8f28d2fba20dd005a1c04633f3c5478b93bf6583f3f7da072dce7ba305b717af5930ff609084e1f4c1e7b2725517188a734e7dfc69db20e4f33408611ebe4dcd039074bed4698035c36336daee42097c826a0352278dfd649d7ce54e5b18060fd25679d745d701d24aa7efffd56419df616dabad7380353034f639a981931fc2e6c2e54414fc3bdfb77c15f5b5181120e745215d126be23f8cc9bf2ecebe045fccd5d3d1e67dbfdccaf54004460e013c27cc86975f3edb844be4d037b07cff57cd14ef994df7541b1650f50d02c77b79d34204fd67c3f2b1649bafabe7bed873702c06d5d0e49d1b372fd3c38453e9bd59d04ff5409825280d38e100f29eccc045ecbb69cc2c9f37f898b2612c524f3809415d3a07297d5264bfd1c8c914b49f8e6088c80bf1700adbdce7bba60518c29485c0a9998d34d2065628eea51d4005b39785ca870e09710e1c93982cf716daa1c350b4dbbaa17a95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d730736706cbadf5497ad1a81f2bf7f3b557cb6bbbd5d978bf852b88a085e9cf4a5eb5ec10cffeb17a9674d3b049db297acd8bbc53127aa6b8c1d8a75f5cf30f63dcc540901e5160e2398d80b465926d043481b1378fbc29a6e10576b9dddea2cd568b1817517cea8b70f0060292bf0f5e018c9a6565b290093f97cf3b554ffe3c288eaac2da5c29ee03f0f3d9f9f22e3cab561226455b8d36436efabbdd3d17b2acca5cc37a571a6989f0068d0200ea3ea68ea58630606fca858b76ea64532d6f6414ad68f3797efd3072d3990abdc7c9af995dd021d4329e150041267f9e60db55309c5725a249a6cedade7493df0a9f5a689b502c672859b8a19e6e09ed9271d2575a3c40e58a2bd97e7cedbb5b62c58b68ff0299b56e3669bfc3c1762f0c3cf5090a82fd00a18fa308caba36c6071216fff96d9fa47f89a4c543d20f3091a770fe22961b72f265e9ae09a1f590b163968ffd8ac4888531b2d5b89c9b64756c07f34c1a9463b3ce2995e4d67cbb8061163f5ea86254aa3a9ef9c2df31d03f9f47fb0a48232c5d6eb223852d7312052f181aaf4a1f4c0c7f791bf51eee3941b020a28e2fddd367d7c42ed36eded94e11716744a06ae864b279a67ae6dd5cfa8b520f7e71ad9836dee9b6ea564b36c1a62b092524a3fd872430cc5e6a6a35a467c6cf4b9f9ca780adcdc0285ff33e976621ff78d26da6d2b66cebe392290fb0ddd76ec55b72120eb909adffa19cf4e33cc4b3128ecfbce1e3ffcd37deafc13f65384bf4c2a7e9b5749cc9e6155287ee53fe83251048bd8f926e6f21a34fd0f760186572e8c7a307cac394c3968c6e2b0f2a8bc822c9ec2c6ffcca5167b53a5923b6b5ea6f555cf6ff4570fe1716170c3bb1660b01cd19951c5ec0cc40a91c68a9382866a46eacc2eb88d05f2724f37845a62ea87c67056e1eb4128e88b9687b02b4dc4aa0156f649832cccf4f79dbc06fb31697c66c46281993ae4ab53de1b1fa57432cb2f0a8b46d920bbe5ba26153c2f52e2ffcbd9331776c162f7832ddc91734b944200459426697104296c18da11dab66ee3159f2de1fad4ced3c9f5b512ddfa526614e4b91f7c9d19c8c1bd7ee9f415834c0f6cd849327a057ebbe9b86fb0354feb682209df2b6ff81af9d5272d3785cc6a195e01db7c1c25cf50351927f8d560ebd6f120889e45bae8ecf90b82c22779b431d1aa6bd9d772a9c0af93e245dbe9ea2ca05be0a4c521167583e99215b2bea53e7393ab9f7195dac2395a782c6b36ad395d4cbc42e6d64b61a7f655ef39caf328d0a17c1aa4d8feb0291f8246fb6b74da7c7de45547dbaa64def8f08c193a5cf512324f075a648e951d50d02f9fa463f7d4b0c27e999c35751e011d88bb0c4b5fe8d2ef3b1c27b0a207260d58fc172e66590b4d71563395df3ca5e740039ae85e1d4a93c8e285f7beadde875dec895a9ca624cfed01f34c167cd6299e358b0b1b77df697104b14b6b0793305c9017acc469e7ca8c76405010b6d3b1edf561330b8149a3e5b5cacda07e5ac5bbdb0c3a71705e7e8ae5c2476d254fcd25f1c17cd27d90da52969b4da165b83394e6b845a24142ba057444bae362f78130b8fa9d2812a24a2c729d23563cada0257ca32f552561ec70203951d8d3f35f3e8d33cc3a53fb683cccbc7e82bc53a20f9cb21d07c243e4cab727d11d9652ef8af29e6083a5e5b6b2bc7106a022e4b59e93aa70153ed1db4593a4cea984a159b313867ca7493589d364707aac2c88028f472e6bc1eb56d40901719727d8725bf98e101c2f225010ce0a2234f1363949c612087630c54d1cf340ab9a6c565cb17c59d58e488274cd4fb5aa50371dc0f70cdb7ff71b9d6521ed30d68d41104300164b160cb36c4ca606cab9861b7b7017b837ae3e2922d5ba35b3acc543900e4e1c28ce98ed71155ec342285a6e6e00e6902a125f149063d36e8e4938b57f0742fb62ab729ba4902953dd09d72f4338043f7f2b14c2b4f84efd29329fb23c41a2816d3efe74a6153f84afb29b8e5a7c6b72ef7bad3eb23cc9f40ebdacd55454622ea1a4f7a76be76a4a19c5e30b28b8f90d50fe8c1aa9f1adb644fe49449e08ac8d112bc7cf42e0bfa9cfe40c04af153f1c07b63bb44550772e6507b6aa4ded6e01bcb704a1b71efbd27e63153d30fd91101242f1f19c73a50738461d68bda9e855a51b6a9f379714859cca45d68d93e6a5259ee9a794322e6f5f7588ddf5ff7f9a23c41913f44d8427480b9de69ae18e340a12ea1369cd3907a85e7f30469e0341cb75fe4af0cc0ba475159322ea2db6d4fbb48d3e8292de5e4b2d6fedf0af22e00ca81285348cf5e556fe743d0e83cff826ca1a032e26155f81431d5f40f8d95b066ac7ee17988b0254dcc35aaf2ec11408ddcf8ab534b1e980dc956906f760c818158cf383a903de3707687a3e6e247c01e3f82096fdccf12381b514b47555e0a25a52b8dedbcdcfdf196d9d108ac98b2cdf9c54ec6d94c3a0a2725a0c6a4caa101519d474714bb08f07846be64a263c869a8f85daf3203a0fe7e589cb488bbda15f8dce2e3bb94ce7164b44ff839f874d701bae37f5ead88186f8cd88586da5defa7b4bc6e2db81ec9dbc3d29b333b6d9f6b1a2b241c8f6275a5422a929ee18c6ce964a896d6a0d495de1eaf456cf6e7ea8369ef85903eb119d9e72150ef62baabfdab6b9e4b1bcaad34e1a6a6e2afd04fc8d0d256a4c3ce2f275990e8cd563ffb37cc2da9f7f778231f061d58bf5d150f52b4df852e887c9deafe3f1154627493658bcbb42f0121ed32f98fac0ff8abebc10cb1e52504fb85be939733e873c56117af98c0c071f27b51229db32c2379a900bfc681f09ea829f13989fdc033f4a9be89b3a48a6cf198f4c3c64fff5c459c4f4c4082adde4db31635bf7ea4d5b96721f268e2e87c7677a823f4e20edd4f4906e7b9b90ce9de6632ee02b01f1b6e291e4dab42a74de484820c9b4209cf13d17fc0e6e61b2e5fd921d1bbb3f442f5aa0b18eb54307494812d5d24189ab71f8ad57c99c84626730b1c2841fcc0a53b3b8fd5ac637c7ad4897c5b711488c6f93b0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"05b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b520aab69aba86a4676d6cb0bccda97e23a4feebd480f62f50c298b5f8b4dc8407b36cc0e762ca9c3adabf18bdd1e06a3f6f16ffc9b7a58b02d29f917ce5e98318151357b29fa50328c1c8aba336ecea624fb63cfa145c1490b4d7a503a0719df0e9220ac1aab57c77ce87c9e23ab080127302d6fde978bc12cd17f6f817a184ea6e71fff019329e5f26644fa36bb95def273bcd7153c359e00d8ac6005acb2e2d6d5042ad5482bb43aaf0b9beace816ada62b51023775ce26a367ccf29675ff2ee0e6f3b64227eca734775e5ea84566f43ef63c0e5d5af30626ed9ab65327325b4c3f9b4031f70571980c4e85cd90b8314e0199e9ed7fade4c0e2b9a67286a2939b4093109d5494c7608c58e0beedf22fb15c68ba8de2e0f2a687a5a70e29a59c237ca23c28dcaf6e0e5f6be5367465787bb067854da40642211930d28980d36b8012d1a7fd64f9bbfc4e4627c902cdbdadfe3a1e362e713dfef1472f9dbcffac6c05d6cd4f82ea17d1cc910e5fc113e8142c7527c2e14194b622df786f5fd16722e5904cd62cd565838e1667217f4628d259bd59a06cdaae9c90d77000b31e3b584ccc7f30962b0ee5deabfbd71f806a28d18ac45320e3876744809fc8926764f6de7d33f56b78a0049450d76c64253c5645e88b30167bf368d19c3b9555d39af09baa425e9f1f9fbedc1a23d052d50b41fc4110b8edbceb20c0c6499e76811dcd6180db65c9f9f4f24a6f2b4b96915645d9fffca2917bda8fe6a0674722181fd4687b2e52700bab560da72ced49c0b73c2cd0580fb6e2a19bcd2d81c75a06a56f207ca08e332ba06e64ad8c5c9acdb0e67dd35747adf74aa66d3543486852246eb17addc277e2665a96e43c99f671d70bf489fc9a44e6e7dfead15d35b7bcc1be658639fb247be6b4ed49b0addbac3e3be56149ffcbc694144c7a9c54ee5264d02c4f55906ecc748b2c0efb1372bb2df547cde72affa6d085846a3cf3036af5d74a7a75be309e9b83e76d927bef3b21dcafda7831e484ecaea0d969c96f7a69ca42f807534257c1cab421e29d63c52b3bf8c4542bd11e57c60827c1004d74a644d7539bb8d8563c1453912ef23317b4e6814c7a1fa2f16bceeaee8c15491b9bc4abdc27047eea25882428dcffbe72dea42f564b3e236561346b87b793f599e3fb4c733c9ac1661309bfc955ee74fb524ce903bb200700326348db4e3871a6d3fb2e362f6d91ee2a402b886be661b91a9b3841432cfcfea7c67ceeb412047f3d1423a7276e508bcfd73989cbd87e659d123554fad15d0f0b4db23fef82e57f36b0924315e1e175bd4c7865391d72124cfe1da4782597fd5307f3c071ca2b03f27bd682d20c88002356b4ef86926311f338f7fcaec94b2142612ea1f1a013f7b2775fa611d52e2388dce088139eff39061597fe2483bc5da820adba2d81dc3553d409863a73dd62b41743cac6c16bcc5fcff4b2a65331c0949999c7d781d2115f92e10240b5a62adf4c7f7161d8830ea92a7199c567e34e8b7912bf420791b10f861e5a6aadb299646cd77280a0600659f5b87c737ae1aba713c3cdcf7bcc31665761adeb5ea23131f6ad80f9571d2b22886e6185d7a11392950513a98dd34a0382cb5ce67870ff192503b4dcc6f1055bf80a609a034b05a7bb6b006775a92de29b125ee3e1749dfc4f0ad20a27e8ba5205e1ea52ce5b01dbe0a517f76c31bbb60aa359a23f2eac13bcefa2d8ab231a37a97c094fd20e0dafc784579afe8ebd4589048ce9901e1702da1111f5082d67345432fb3a062c3d723cc0c8768438ea6a53070670a96ca678b93f16f9347f6452686dca8f18a0772df206b32b3c0f8650a01374ad3ed27d4b348417c7f0c8af885e0e13cd68c37345a323611ec73cafc409f7b239afd52c58a78e7065dae7772facc096ce4e7d1eb923028504ca79802e4bd3e04bdd3b900d01eb598595723baf4827599014eff32d17c0df91aac123b01df9895c932ea4f623085d0fcb632d88993a1e7b5e1e1871c2c160819d8e8f8942e258bca25f89d8ab4c1153156aa7432ddf33d136be1851a51c4fb456da7ea95502d823ad9bb6290049954fed47f92de0a1cbe4d651813de5fea790f5b25eeae4f5004a3bbdc6d4b3675276962f86b041ee8391455ee394b3ef3d95bf4fc424f0ef3b476152041e881f29aa771dd7f1863341ff102b7606ec9393955427c53ae7dab1bfcae26b6c01de4fad27f870a0a55a083e5b672ff7182548af516eb8b116ce2e1e29212159d683d1e2fbc301c86123751a5991ce02fb0dbe2ea4d71241ea4617d55fc5a684bc083b6f55de23f6954529e1daa478a8a64a9b9cdd0af5131d3881903f9113245f28fdb139ac17509a825366247fe1d99937e13deee8a50bf239bd623ba6b3ee93cc39a44e4907c995c7ed3b3ccbc028aa2f7e2a9052f9ad3a81561d65bdac7b57372dcfa6e34ed2f2bdcf254767af5bd999b5ad48c6ea9793c0ca5192fcb503c9639da0ce98f8ba127f6e8f89159e28ab1bd10a5c3155958bf2b7cf4c9435b7254e969cadf2523acf1960d8e13febd2f219d9cc872df960dee1177bef9ea1585e150b753129502105c5b4ea532b0b8aa294721545d89d6d3d527b2e572e17d1d45d1e49ccca48bec5df70aa506c3cd7ea1f0345ba72f00f0d3e0b0f13cd3465376b85ed41ecae2f48a5e3590a5cfc39f3fefeee6a57ddf9c204f56ba885c61a21a1ca9b6072fc2923d9373e3a991f8ec9eaa548d79ee137b515be0744ce2a301a3af36aa09dc3aa8905ea03884ccbcce7386e816fc5f7fdfad404b067a5182f468bab857b295f1fed4fe3f8db5acd2e1f9721f6bb3f5b2c05e3690caa77a7f556073308a124f4a4caf0f878d8778c9333b7b48b381780544fcb6705df4a3fc01a33d69d29ca85e6111fad72abe215ddbb6d715be8a39d19812a8840a31e17f88d8c118de29f8e5b0b4360c3220a80e9b777a6f9aa5187657a484aea6a3f8c7209740a3262937779e353b7ecc5a7b73e2938ff7983c4effb41e5a9cc210f552cb304dd14ec5d92e8c1d7200b2e0a7063dfa3c7b36fd9b0acf0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"06cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e2e66542dc1c07bd65fef62f2a8b9c32de71638298dd0f69dcdc748a51e143d800f629943c49a56566cf13d72c0d19744c75af1f46ccac678385ce852b0aa9e6c00dd19360b3ccb3a110523f18cc02492e221ef624a312fa3234bf17b2df012748b690871cf1b0f68e8dc2df0627e6b95a2bbbca7a2fc851548b112439a36103dcbffcf658fc22344463a6286966a4b1ac8831075bfdaa3847a13eaafed8b4480077e7c9ba19f6d91220f2999842ee9d743b0413fb064105f0a0824358974d4bc1519e8f83fc3a7e83f3be5ea03e4475680dd31a7c1fd4e99e8b303570241469cc1cde6353ad7c97763baec7890ae4d7a2199a0744114545fe773765218fab36ffce6ef4e98f6d7b24ace8705aa81b534225a0f2bbbad16c79a07d0cdc9b4fb86f023f08b484af868f82ea9e3dd49816a4d54a507c5dede45570717d551a8453dce4d6570e5e45769656f471e486adb0178229bd0b1417c114e2737c76dfdbe3f7a96af33b6a0478e02d076927410078ca60b3bad250f01e39be83c3a45fdc26ae0ab1e4d80cd92a06aec6d8ef436aaa356d4b35ae297b98ff3602fb1cd7b295118c2607cdcb34eccdec9dccd3f2ac2688e7261b2d4130325851eec469b378ef1694510262ac4685afd6edb38438a7d62f189df75985e042fbc5c8f7035e05790ab7d95bb2866547b98785e029ad95a11014a8749406398875c5893c4acdbab24153abc3b33f85a5a2096d04b981017e80384e8a5366ce823ac9bdc357a9d018d12d0b8f4e6da201dd14ebdbce809b1c76af800c23fe6e4ea92aa80287c38d77cc2e9067d0dfec357f4ccf49f5b5305f8dfbd37e2383cbd11b8ef1c1556ccbb96d474bab8c353f62d87b6fa0786b655e25bd1ee72aebb4319e9fc65a6e7cd4c285d991b606bb6028c4c7eb2db7723457328fa1d5e2f6c157c4e869d6f605a2d244c98e669f1609e073b5a01e33c764c5e50228ffcb77ed7783706a99c9c760510d5e118036453dacadb96a2a11cc43a12078e2cc0bc7286963250ddec578180722f9dd53dc73f5811ab9675f3001ac2cda5080b0d5cd6b436df526fd9907621cc235a3caec728dd8755f02747d0e8055e202e509bbfbc92c5c50dce18c1e3fe7af4c6a83700b04fffd0475ef08b02b9c083a7bfbc835b2da937b7527ce6d183cd9895ab498577253ccc8b5b57905c4ad7be3746821de2d3421b49f789fd9354d1ebe7632fba9e71e79f7d760c8d71c80013743ccffd78664d7bbc32683f676194c2aa95b7ec953fa9eb9d81d49a061ed7cd4102e079912b890a3e28e55ecb4df30987ff6ec74a6446dc64ef41f329346de509a4ea95e61198ab527a7e37a46bac1c1ee6e3d8b91213a440f908ac1119f287b10f4ec0546a418deb1c065aea3c1fce7a326a7edbca9f0c71e43b8852e0ee2e2161fa22392be9ce42515e77161a0a719071fe07c3c311b9599df2149c136fdb4d1e748d134e49990503bf9c05f96121af7512bd4a56a6d4cb2ec949d622e1747686412364a8bb5485c332412fb6a24eed1bb22fb4401db7ab742515ceaaa0a604fe7d0d9a01c86fc2da90db9dd9ce265d492f0257ab1ad56e3f3777a1826a028df443943322abb11b4a179ef559a0809eb168d810af7ec52c45692d208474a4567a72d697e2d325de4c8f6177ad3cbf54f72c39de5ccad2a8f7d885641fe14dd7898f64c28d295977e6b2cd74707c6ef739106abe9378009d10389fee45c34b86040c83d3b8b066da957c0ad4a039d91f03da10444014c47f38e61aaa685382a540a6d903bfdb339eccf25591135e1be33249afd2bcf1dfa9add5ffe0cfaa4a866bcdcece58c029fad29233542f4d01bda72e2ae40f06c1268033ffa86d9f55361932c72af870797c663dd7295d54fb5d4888bb9a0c07f73940a7b77c2cbd59928c84c73bd55683673fda7993c2895530538945b24600f85227a2cea8a8e393716f5597175dd9028b34ad245f6b3981df04011dde743081703679bb523588e7e51ba9a02025e46fd35dbc2c1de6ede0c741e66f287b483c415d9c5cd46fc2ae6c7e888a433003ec4dd13c42a06b4d7e9dc0f735866afe825507b6440a282d6f0977316d3b35a0ba5e903820d058cde5f96232fbc64422b66ac58d6e35d8b9078ecd596d40d836447b3fe4e38cdf1cec5eec0e21a8e89a966952c68b78e0333660cfad6aaabe466343128ce43ea8a00e6ee0c21d22bf15194a68a97273cfce94ee399dca71a0995d319d928df962b7c21587004152257298646e093e4fe6301740f32eb0d0fac7a68e88fa8f7e6a03759ad379afca083239cced9f19b4e1e45dafa49874751e2a11bdcf43a5034d4262b254ed8d605e10b162c48f8268be77b563305d6e516105f147612a9243e2fdecc0db5eed59b7119009915a92396a3cc7d8daaf2f457c6c4f77119628abec045c33f029310c5e1010bd940777f89632520ecc96e2b926ae92bcbd4e4f7037ed56f196c9e495b9b1fd54859110c7179944b015fa4581fdf709be02a8fa58a1e07e749d5bf64edfb166a72284bcd4b548c0efe1ea0fef7e65deaa7e4a47871785d8725f7aff0a8b8f19552845a6c1ae25ad74628a93b00b23632698850f341c66985cd203448527a5cb608a661ca800ecba14d753a4f64578be106808966065a1e6501a539adba2124fd5a7d5ffe053806ecc6998e76e787f23a8111687461cb11d7c1ea39d828d3b29cf796c3ef65a9a2c4f455a0dd2a8adae4f1d164b3c5cd5ed5c5006547df0d7953d56518ece379c9258863c539f96846da074198fd3a0ab0b0792d56590c0ba57e11ad52dc9496f8b7ec2ef8dc5d810eef944f972e9c3d43c9ad66b9e5ebe3e1f9c2a985a1e1a09ce5863699ae7413237ed774901166abd7dffab0af4203762e5353445587cda22c6674d896cf813767dbcffb7c4e877274ae1922de5fc37ed04117448ec2a6c19b0c8cea3bd32603da50df8a25aa0246da7d1add751f79a7d08755599be76784ca22962058b1e50c481aee05a90b0db8a85c07d21a1dcd9db42f4e6ae0e503a5cf94b2a2983f55a7d3713cc9ed56ce326a229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e633bd325591368b31ba0624dd6ad1234a1fde1529ab6072963caa8d85b08c9b874d2e9e85634e67d7ec2faf99438f55406b5652e876b33cd216bfd508bfe86e1906eb873bfa619450507c418a361e791d47114d4d5425100c984ce80a8f4e647c9e3312e49a6a32bca660edb9edcaa29c7f06a85e42cd78e70687ca67f18b107868e94f4bd3b91fb4d7743f77456bb13eb459cf14d6ac88b473d5d1b05099150c1daf8a10de0041c72ba1789d88392276ae1d7f9c24a210d650c9b58dc58a437fd4e8f20a23f2c42056ba556c88f37e60d6eb710e6e6ff27aaa874ea14cf210f3bf0ea399d5c7c515080bddba5a141f89eb99984582a0c3378c05f2f58645ae4532dc3e4bf7b1f345f2be89f44b575e7d16abe9c68fe2c8885bc2de13bc66aa85ed2a6c6499264c52a99a90a6144db80d80a20675eb11ff8d237bebf08a571a70ec81248073f8459bde59242a529e6e81069776e0535441d18a3e839966219e3202b530cbce34a314c6f608240f8d90cf1ec07add9ee0910cd8e819f5baa53f0b082386f98d5e6c6c6a435d3f6d02a6689cec23d406fdbca9ebf392d1315caa1b3b5d5b8cb0c6bb7375c64b935708f0012dfb6f633ed2234af72a154c110fc41c2a8a5548db2d8ef102f607881aeda6ee24b0f5c4f598c7dd9656b57583f05dcffc57f99e126abd32c4ffc3e2a59ae6313a8a7455c9f2027dc23a124316d04c9ac9121b29079aaca516f35f02bd5cdc599276c8fb5e43ae5b54738054d6ae6b9d534e8839a6368a61dded9769841a896271411349ee66a8229111d10c88dc21f4dd84ab5aab5b6a42db9be5dd15bcc505c569e41bf5b15bbcbd43896d61e70fd758f389bc11aa9de2c0d0ab7e6f2e48f921ea5ae08352f2469939b0f95b50aeb7c29768ee2799a7050f6baa321b70267838f8d78dffaa93368493e8971f22e0811496e8bbbf247dbe9fde951d8a5b0253515d62673ed9bc06eebd5c0a779027cd124c5218298be9f8e3e47334bf5341976c188161bc4e95ef24e54dadc5c6ae911698f45c958c1a3c3f77a61774cd2307248e4a9d0e8d077f6686d172eca303980228823b1332f56b8eb54ffc0b179dcc578dd5d571adab2cc9b423a4b8b6d3f6c6d39673eb1e688659b003381f490bbf58a66986857ac5641db508f4a0d1e1135284778bfc2086ab1254a8d0307f3288bfeb835dede235c9ea30aae45962dd049063c871738dffe8aed893477a2fb309b3839f99176aea300a7c06a18c67b2076c244e79f3400aeadc1549ee8a3bf7bda1590c3d15fca713e509873cc90867915f3886ab14b575d047e2ed6178f952c41a59d12ac0e6ab97fbc9c4644a5e1a30bbd05d52c912a63298f3087de45b8dd4ee871da19f3a7298e1f62eb1872959d52f2517f1b4d2db257b699ebe1be75292c1da59ce870ca2876da22976c2c8f8cb5d85ff85e667c3c56e0eb78c76ec4d17f4f110c4eff86854aad12604b8de317bb913f2a96ade99f44037b2ce0c8c9193ebb6c4f492f0edccebedf6d3c465e45f1454a5b282e5367e6edec3d6d0e8e7e715c93919b576d22249a6ad1b9d9fd5b213137fcd070aa5ee73f16055b0486e2eb00ce0ace65ee2dd20f8e2ededa7b3768b8c4b9b0d4413a8ab796f44fb406f20b853f516ad05ed4536d8940dbcf405f946956f4a0ff13f502ad758e95bb0961f3895ca68da6f433cf49df9297b44477fc57c63a19092c39e23afa2205565debb9e5c5e7a7ece41f5ac17711ad03d3ce87179f2cf485bb8e554afbe0df808b9e5d4e6c02adfe0d744cce0674990d4830df5105ead5dd97e20af446829ccdc39b03e27bb8927e15f59c7f8a35e3bde8877f2a84fd07f17fa8939ed51ad5169852d9aa90598bbab4351be9eb4c4d39badead509d04b7b9499ba27167a85d68d10076eb2b57349b16858edcf93d43a061a0caa35c2697f5af586acbe3c8c305d80cc9e34b25ec19b1e66cd654e4dd826bb670725cd4aea7c085db37e308e302917a22877f5b5219328b1a656083bbd37d456115ac4a859452bedc7e39e79e2257b8a861d3572ff7c0a65f82fd2980d35972cc4ddff28379ccb3427f96113171b1087841d32f6f2a611c8d43cdfa36fb2c9b91dccdcfc8228aa26bd41b86a01a9caa3107c3f8ed9c79d7e5861afcdbc75e7e4cb53a9378f1826efcfb0b8730606f826c046a5e24f4e19fd0beac27555f334316bfe175138626b21810017dca03531ffa5959f3b9653b3b3f4617c91f4f1ad20b1074fc7adfbaabf59fb9d25784826b5748f359cc35ba122a7719493830ec31e6d9a8408c4bc516c4c3ed334b2cb4b98fd8eb42b59717e431965e7322b7fc67b1f150f845da131dccccf32f55975d6b7b898d5c749578b44e2301c2644c23d777cd8321ac0d9378ea6d1ba251e1811fdbd698712cb9763d2616a2f09b6d8829ea94b65832b77349dd527985cf208baaa75ad45e788579ce1d71f19ea79d4d721ec8d519d22888c286209001457bf61550fe5170587b99fdb0d2ea3165cb0df8090facddf036aea0fc1ce535c656c790430f5695a1c552579548e4b716ea84a2c84129b05b2582102edf979735c2b5cdf9a06247d668980229aa506bd32263400a1d9faf888cc270b1111a63892f88d244c57180d27e26c34063d49fbe776cfeb54979cdd7934e0a6609b40a9129a9df73ba6bc36a78eb3cacd808461fb41da93edd42dee2fb6211422b6ab9fc005ade80c5d846f1a1b83bf92f5884d82f6e8ebe0b3f9596c3e01edf24c89af2659a6aa4183ca8ff5075c9eb29da35bd70078728422a684ed76d095da1337b4110e4839665f56c95a35174ee946fc8f37cd585054d390794048a24f667cb74f9279f83aac70ebc364a7ce5e2a356c1bf73f7b8b598a99b6deed765f8867451d62723ddfc04797ff7deef3aa27782813c182c2946ee0106f95f2ad4d7f670ed12208f9d138754a0eba619a6f4fd45d299cd7f08152fd5b7a2f3c9fe3911de6eee1d59edbbb07e903d5fd6af134f159714dbd00686f75800c820b8cef25c2edb7e82dae38e1e5e643eb7045ac256e9e93e471c051aa229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1de7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb5942b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece588a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e61c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f68604830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c6590218da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b8901cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac6861bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df6007241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd3996342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368aecbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae22903676f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aeeb9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf73690cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa97ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f46778b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f125518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b5226c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea783492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df317ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743fe5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be641371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd05f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accfe3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b105952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c1233c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f8c81f2b04f4a9765554e8b8022eae8f9f655303d6491284e4ba7d5e8222f2822a7a7c1367853e15e647d3b64bb2979693f3d1b82f01e9eda03ee11d0c816a485cfeba334a38e8656b9a973f85d47a53b138a545585799db9b45289f014576663033daecaaa72010b62db7491c9b430fcb00e7aaa181bd962d357a0a7e20f858a2fcc7e7567e85c3abba47794609dfee73196abab814d619194d5185feb85c6476fe88a82243e0cc60f2b415dcd86a04090e03620f1eff3b8cf062aa967c603d29dac6213bbfc78b48a47c5a8831e987b2646d46447144d365e93f8d464e32bcf7667028f551bc6c3e3076a70a3b6c4ee82ff0be7741b4e5f2bf5373c77b9ec59ca5481e6288ea2f9d1b0e0fc88613595212b1a95706da32347e434046e0efc188e9914945cc988e6bb04f6ae63703da43ab4876f522bc5a464cf9181244e9c144f8a68e66db5d0169c49bddccb9698ecaecbf3ab7ca77a2962e2dcd22e43246a1d4353c67bab65219f17541ee33534f67f258bf358448f98511fa2a14c07f76f234666abb81f404342435d62c4560ec49757260aabff7abdc12bc3cbe643386eba0cdf7ae1828a437256ef19ff3d37ea0db347da8dc122b4675016583b073fd88aa31ee0aa6a051447b765b5a8a0148b3b4cc49d47c0c7588d9ad6cb74298cdfced510345da53e9565731a203fbddb454539bbf5525f715f1dd73cce1d1dcf63f592d930d0e43b0dfc1b0e3a0fe5de76c825bb6cd548a4e34701b70b0fbf3750cb0a195126e49ab69033ad12761b3eced4e2b7f447b3703ccb66addb585b5353af7544eb7fba6636bd12ab396c3dfacfb070d3b8999306a5e42b59226b55dbb78164f43259cac31ae4aa66b5427da4b6f39088988a305ec5f177e683d8a35e29672fd9d2495425fd477350f3af1b936900e322a3a679f239a127e5adaa345e6f80925242980a78aab55e13143b99a808b080cfe94c92fd17d5058c06ca45ba9d70f1afbee59ce586e33783b589254a9b23c923ff6f25ab78b0e925c0ab608fda05f99611f29d0b27a2353fc25eec2233eb2dcd6bb0051766668fbc15a2a2e279513f4e1fde1d6cea589cbb30a19be843f7b7a4e5a9d38508bd2346dcf7083582904c6440b13a1b39fa83051feb96940b266a92084c4ca7cdcd475615e3252bea92f2078ffc38e08803c9192839d0f938af61c7f63446bf935eba51f4cf0d731f11e54bc6c132c29b126a4392030b02951772d64bd1cf9391b5336f3c0d02351f5bc88cbf2cef7a7ceae0c22f14353dd72e5c639f37d84125963196187837d0a5c940abb61a1b4fbf3c68a26c2e86160fd21a9f870de378ff95765c8cc0ff405ba66aa86569c5765e0c902c3e354498fc212d60591164ac943688352fb14b559749d4252c338a943cf827b8ea16aa7f5fa1fa8bda51cd282221e3a03f2f7902c79de8f469cc5f46067abb0ce06bddf66780748318996ed607634c4e919c62ea3b2fb6425634c6b349b6160ea397abd78cfd49918df3b451165fe3b1baf23cef3fbc899186e66fa96824fa25e2171acdaf19abdea3b6626570dccdc427dad3f2585c3aaec29647247facf840f27183095b2ec7b7e3d394b354dcc3463585c17eea4229a9f7d3059f29978d482fe71755e0ab7ee3f0df7deeb1d4327737547403815021ebf7e0f382050c843d03602f513cd4780c8dd74bb830662a4ccf4dd8387d7ea1f4bbb4e843ebd6da5b8c3e7504b9b151b2573305d2ce18936408fd7fb4d5934f6149496e11c4993e771a52aa45b2ec387c796d941ee04cf5a0680b2a1b5897243c62c3e36422de8bb150530bdd5b3b717e31a58906dcd87b7ace50cfc3a12344bf9c6929afd7fedda234464831845550d517062dbc7635b717c05b7b9adc5d94ec3522895a4035718eb6cbd5ec2c117ad1d870b9250b06c776ce34ae4f554e612722ed1fdc4186d5dab9e955a488f02dfb88a3bbef28aac15dc238d53c185532861bdff752a6d73614724024325ea4f13b2d538d2a5a659176ea7b1dbd658ccf9623016411ddfa62a23c46f9aca242e83b8cf5dd27accc0683d5d8ec2925344c8e986e490821b58cdd50b14619a4c257d46a12bcbcce5aa21e16543c60f200996bce780d50287d8290c24dc1651ab131b44b2a2ebc7f13c40ba2edff496c385f62a512de14172244a7a4641d87a7d7ab65c5230553297472165bb3ac97e20a0d03a3fcd786c717d0cbaf058933403716bb181907a9bc0fd615a307f7d9fb9b9bac6a009ce184a2bcbf789b3467e40c2b2214b9d0f0e3d14348cf3c1fde628cfd9fbba66ec2f502ea95e49e38bcba72e4aabe21eaf50a7ac92ed8560288d1f24fc185de19316e6c2e300d1d05f67e497089fde120f701856ba8e4005ecd50f46a213e2e29ef742276b6228f80ff79568d226cda6e749143407c17d67f3c6309b3830f9599ce20926615088212ae770fa58b17286152ea1fad5c9755b33e9efcc43f62eceffbfe15c8130691e2c745332d195d9f975cf0a787b33c9f95f1b668e7c3abb5d1ccf30090b24017a62334a12916681d7544652d8df41c38ddd2c6afafa42bc84b0ca2353ed9023cc2a975ba2da748693a0b70dd782e9c706f1a065ffd3f056abe63167975c6b1b362cbe4250f7c5ccc6afdd98752a07161731985ace9060782754311a7a619538afb535645f109c35bc51e13f4ca259139729f8b18d4dfef0279736257f7ebc0efb38cc00ffee9c48506579cad0703f2255e63c0495708a4e0c20776794f39f76c3230341121bd3431637f4b0cd1de7dd1a6f6e092b627367de6908d5a87c8229352cc4f3633efd4139289d1c5344428a20ce7596ccb0ee2270d59e6baac1c5349cab5d3a3c90291f1c1359090e1b267396cdd01aebd8aee397c9509fdf6755091b4075164901de8d9c453d64f8189714aa0481ab52f9257eeb4e9bb4cf65179b15513c075efad49f8feb4db9f9aa209f0772e985a68aa4fd78b39aa696512dee444b3adc140415e4f8753ec2fb1f93c041e15ac7e266c042ca507cfb1155fa4acf95331ec45a0ace6c12f9105d877028f4df37f8585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a8ff881ef6fb928c0d4a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"09cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c1016ca454596562a3818c3f411c52092c52812e78516952fd9d0f04ca41b211d58ac01333d7b4b9a36f4a974d79ccac3194b8d5ea4b5273b3b8a4acd1cc9519c67ccf05a9707e4b06d954f82de481834ac12e1c4551d796f9c32afe9a0087b24038967c748b7183c76d59e53b2b903ff1b56bc56011528d66d4817226ae123d139d7a1c64857154cf762fb6da2791655744f9adcbc169b5a6eea13081f2b2c67d65c8162dc004a04d50ad491610a83fb71e0e38df13c2c5826333ac327299476c5805095da9ae395759e82b65290b7fc518efc10c1a67aa9e28b546f4fe88658fe55e51b2d204ef13c12ed71a135b62b5b14082f16e532caaa1f3bd3d3c2e96e1c09441feaa7eb90c5772d2bf29138cac502347e5d259cb6a1c86fe77d54ecb8d8d1f40712b3b29fc41c90889cb6f46b7c4584122e8dde577506cb73633433e3a08078abdef167ca338d62b6bfeef700ac1c932b0508f6d39103f691b9b29468d16258e6135df930a1edc1e89e2fbf425cfdcf6da1bda291e9ff187592cfe70ebb5c94380e7fab5048f19a25e211eba054636564b86545fd2068a750cfc0a51f45f7b9ff0c73dcaca2702290610303aa2b311007b299427dbfa9ae978a4f433eca6d99584f7687a1667580934d90ea391c5a35f6e22d9ea9dec2cd39006f3eb4e6a8d259f48d6d19d010cf2c1e232d92b56a05d7ad9f1464aca7f2eb51339d4e08463c1e4532f5a5f6553d4ff5224d74b263c4e7c18bd6697d6550ce2193c9e054e4cdd6ef902d8ca554a3189c00808e0a6b595d029f32ac45d9dc67dcf5bf223720aa95cf9ae45089eed114bc2077cafe7aea348f3686527a1c74f827a5cf77468c7fd131dc55da4a5fe9f54188b1f4d1e6cb3299fcf891697212142ef82823dacdb21a26dfbdf4262795f1cebb1171580ace52980c2f0b8ede17ded4e5f634a2cb6557da1d47ac4d7ef37e780c105831116ea283174d93682b44d1300d863aa3c4267f924ad88305f1edd68ce6bf9cc610dbb19235309faeadbbfc3853e1e2bb0de3f3fd4110db82c910365efdaac8c9c3ce826a2ce70b2c745478a24ffaa7598dc14d97981e0d87e74fd40ac74ddc4cfaeb56c7813ea5f51401e7abe2b6523a0c40e77ebc263236d1d4dfea7b922595c7666755ce2d7cf18e49f8bf4b10a28e129f01fdc0ce6b5898752845d22a5e3d6ba09c79f8e5dc8cd46cfba62d7594cd665eba5e5b04428e57b12afd524689c9bf0de9452ec17d9d54b22efaf8507ab964e66b60e8746a8aa19e9e71185534649f1c08fdc93f17d77cedb705eba9555d95bc105c9b658316cb0930fcb324070095c52862656ec4b9512b7f869ff428292d9a1bf13825021b65a3aa5c0f8ba722a53033f4d9d50afc17164cc34b7a0719d72797eb32a714dea3b33bd9365665d0bbdafdbc883a8912325a7ab24deb29d7a3e2ae8ec32c26dfd4399e1730c6c19eed5dcb6a9c8a52254d538ca3ea66853cf9d328f832b932319d2fc691cbaaf3fb13a6f47b10b5960e7de1d81c1d88df33069e710672b4efd281a2946de4efcd8f03e9b4f185f0bef25b9c51d96d985f52911e0c8f0a8634bed247dcff60bfdb783615b510f0f827341554e663ab76424085847882fac5183f35e9f53155df6ceb37d7cb09a16933a63ca01ed56b2839c8fa0c45f6b0a699d2d08dd7e432b9d6fbf30768c3097c45f466b7956f9bab245825a04aec288a08bc11471080ddfa397d5bf528b2af991e04e726da808c5683d42a73119c0e50fa92af50c62ef2d91088b71f9bd8032a4d7ab5a6ec67ef5de3bd0aa1a9f1b8b143487d477f9c0a38cc9a68d23ef51df6a6d45e5f77f918bbce6c4261e4c9c8e9de904a977a1a51cef2535ac4beb77e6e75bb468776d51f9c2deab3b08ef107c8f246611686774c9af9e10d783dd14258fa60118f337581210d26015993e11de60a72fd0e57dfa8cbbccdf3699b5d8208c98903906962ec23a4de0af1e9c627598fecaeab3d87a72dfaddaf222210452388a7bdd461f18eddb006b04dd5c699975b5616e5e6eba0fe579fd2ea1361ebe36fd3d3245a437dc68748cfe6f113e2e012be11b4aecbc9f8e02b7441640fcb806d47226de37d7791f2485121f2397b45f34935ce45e223fbabcf93b434281bb732772fe4430da125ef281c7570bac5b69a8b89cdaf8e1c1aa52d9d4c33d5af0715d6d809cbc93e977a78fd7f382480a09b4e2e48e9d2e0fa04eb17012090dc61d42ef5bd9ba3189784955645a588d296f38d3ce654c3893c070818cce03551bf810827bfaff75378cc8ea1a53c23504e96e88b31f3947214ad2b826bf62ba15543432201fb795cf63c5c8fbb0f04bf01cc4ca8e2d295ebb261aa40bbefc6f0896f8b9885945fb1d401a87a2464669b4c02e8ce2fa588c5dbd46acaeac200783eb39123354ed97d6e27019325425705ab8d70b8dd595eb2aa27e2a4ebf933fdaa9f2f17d76845b5b94c71d26a3e75b558bd03b7f95bacbc21bc6581ef4d38c61fae5e38a2bc94d652775b6f82b8818457237cdcc39b09442526d43531db8bc779722e13300de3491292ffa093141f9d50cd3a92c3991aabb9e30200c329f59391434bde8a52c7f54d917a3ddf4ff27b66b162ff2e90561957177fc80a32a5804d2ca9c938d01bfcebd721976c0fc64b335c6b845a5ad3d7d3aff748e4b781febdeb7b427ad97a8793cd9708c7cacaa75ca72f167e4a1f60ce6b6785fe1d636f414f3256422652558729411a99c30147d8636d09e3e580b8e65434573def99636590322b00904888892f9d8dec07ef77e9d5652a2b77484f634056902b0474d7b108a44499bc93a7e767ef0c6f3f43df40da99742a0fd06ed2e372502f4e8bb6e2742e6d037223b45f39eabf25a92759b1255933706c6c66bcb89e468c01f005225c62c22afd7573a069d297a2c7932dbd967d71e545f52537ed2dd359a6216363782593e8f698d1b171a160acee104cd3707b095782c3b9b4299482af5f7247e52dcb81268ada19a8eed5d17d512aeb134a08e0a84b79b62c2c1de14c982b71ffcc9965b7321dacfbb60368585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a8ff881ef6fb928c0d4a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebd35e62a3bf8be2a7bc32bc6bb3fcf82f9439de3243963a7f5e8859b009f59f68fa6dfd33cedf8e882ab560a26e99d6e9ff9b21545443d114aa106f144b73c221d7304128c88642f0bda49637743cced0694b69bc232d1657d7357a0ad1c4b95c761bfc02a33908582f8ada2444902b61be98c878d7dedfdeb2eb43ef0a618799572b5682eea5aeedf1610f3461f894265fa7b08bfdd96c32c6a6fe297edff73fcfc679ef59c4568bdfe9ed44c199640a70bb5952d16c8164c02940e7c2df9eaa9fac4fa8a2b7426349ec487ce3e3399ea70e7c562fc8433de6930210fdc1306ee6907a7e7d98ef0c0082ac78b4fa8cd824afb8d55e4a11dffe8f0a39904b7395007f370dcd3ddaeb9d51b38b55e8b3b50f63879fe3702be4d3197e9927cad54bc9cf97fd866189db5cc17c5cbeb98ab6c66d159e83422969fdbaf6746e297e608671d57c2a9b056d6e01883e54073556147d11aaac3c681713defc05a8dec10c6700f9956bf10f3789a56772536740f7b9956524fc2ed56028e2c3584e41d702a36a76adb749d66c65a0438cf2529b32da4460d24e6e24b0276b73bfaf0b644d8c0bcbdf422963eaf1091f96ed3738490e1f4f1fb610b9d9d84658c07da28c42cdf46a08a6e2197689a08ddbc3d71227725dca51083be9b9c60cded369a9c83d5d4e173e2340148054cb6eada9359680d7d3b82037a2d56e41831452f222a1ffecdb5f789fa71d23477c1502a22a9d1ac2a3d57645b09ba338618ea587879dbb0afdce7d9853995511a7ed342c76a40eb124b868f2761f393eff0843221ca470481a240f4d7cd5441d5c0eea4b113df0fc103c6b2078029f778e6edaa4d7147e6a5838eaa7b5e8ec0448b09fc610ac07e1315d7a933e6b5c0a9e46b424fc582d98554063f70981e73de0a749f69853aaa896cd8999ed8079f5476f224e429c8a01158a36400955f10db68ea44855a6fb0cd9c41f4d6adea377b75187ae085e15b893be55e72100f630ce7a7ffee3ff2ee6c35a1cb493c37b1138f503930270adab0d43be8a378f0e9b510b185c72995a06ac69d43a1183b97770cdffa935e26cf9459043ec2ee308bb569d4fc24b04cb84d604a4c2ff28d2acd64bdf18dde078d414ba922cf58a49d3f34ebec076ffe9e2ede4974b8ed3d32a7ef1321b7593f1cfd8d0657eafd77d7718f91788f2339545a69a08883dd33987ca1632e856b2a437427cbfd5d52bd15f408aedc23f4990c2a8d66a8c811a78602745a5982f4750c41163527f6325da328aa5e4b3c203938f88605efc7200ae74d310e30f37d3680026f1f40aec7e226612de18486278c551bc76a58319bdd02bf631265d02c849f87b419e1440f45b5555b2d8aadc1c4a6e55584f642ada8bedfa0d4546cc8c0a1848587e034c320a7db1fafc6a1515854764326a3ac4885d948f7a65ac8ada9cbcda5d8129dad40fc46917af2da711d79cac915c990a660ae36249e4ef920538528a5555d0317052d13f6c48bf14761dc9ac8171d2867a81acce2f59bb740f8f7d63bb3faefdad93e695172e858f94a9b7c324ebff72ea0837dce9dbf0b88169769e188b8cabcd9a8c004ec3fbfcd45592a58f13b20274ce6ec9c424a14671ed7fb27035791e159c7f89a95af51142618e34392faafb09271f324648428c34eda8e0a67c8a47eb65781f18dde0d3f3e1663c2681fef6dddf4a0865c15cf20044146155a4fc61cccf95f7c66d9ecc45b886eff4c2a9aa268bfd4fe83dafe9f77db70a64908eb32c864f58bfea9ef7f42f90a002a87066343494221f648c6bdd471bfe0460b00c9c163bfbc5cbd08808951892a292f0bb8ec07c4a9d1e7cad52c085069fb4cea253c25f397e898977f3960cdda5d7e276dcec8150aa1e90fe0b7c4d4fb88767be2cc643da803da16e625b930e9cac781fd5df7265b8c30cfc06d4f9c3ea10df4f3fc2b09de24717b6ee4f37f6ae0461776ab425e5eebdbbbe4beace99d57b537483acd1268429fa18a17368937109508f783e6ceb302b973a2ce41937b4478c97d7acf46fca2736b75067836a0f5c90d7247359c2e790966613db3ce44f76cbe8d2dc5f4ba6bd48eaa05479215cfd7315daf830b249e89821c73a93d3a1942466f1118fa55c91f682c0ab498bbeb0161be5cf5912bdade63a3ca1b1add9b6d34f79a6902b3e43dcfdaf94c8abc470b79abc0011d83db29ea975d5f7227f8ff5dbe9ff4ffdace89dadf7002f8f12bbf62446367f380c58200e2800f6a87a81699a7121f30c22b06315f0251f21afaf0044c17d93d074a5d9bd4ed829ba9e69bbf3d5462040487bb9a0f865df3edd370a8dea32fa9a4cf9c82427fc5e2fe1832a000c767b24dc0e920654950a3eebc57e96cdcf4c1355a12c84b6a58646ee809ecce5cbfd6700866b865b28fbf87c9820eadc183a097bc81cd176df7fada7ed7754844038b3a77371e1d7a47989e662dc76231aad1f93b33ac933b90129c5ed4c8c85054cc3c4299e3d80bf994d0b5ba667cd3f0cf19b01fd3c5774e08cde192fa010c3567c0c58089d499a02f91ecc1d0d44ede178714b42b5b086860a584f3b54f93d2368fa6bf6e6d371d674abb336692c207132def03f01444094826c30fa009159e80a84717babd7d2d1d2adaad57e845f8ff0bab8b1244bd8434c0df243dd120a26a3582705ed6f296396dc82e0ea14917df201ef0f0c17b4fc0e9e093029a669e64b6356f5ab810b5088b20b42a82d3c1cd7892c59177274e2572ab9e6d1f6e76965af1bd53e4c115d4d9fde6d475e99186bde72fb21b3045f9712530976c4fb260f3b98a4753b37eb1c667818428caedf8d2212bb79d0669e661cce2f2d1adcd08f19bb68e39ae4b3f9c0ce772f46406b354fc94f2e7f5f29845f92d64e6adcd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d8efcf94f2e1546a637fe78716e9a40c03664cfcb26188303d0812f9d708a99c84f45a29e7e7483fc6a23613dd2ee32559863d45eb5954905ae83f57094eaff4a349896f6272430eead83bac308803c2b75eb8352e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0f554bf393bc6565486a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0a4571df5426261d040767bf18d804c9f1eb4d258d7eb0cd45df70d9feec00ecf4fd422f24aba950e45ad45b0a3768c46f5427a55c4eb4d2bbd0ae03ed6eb06a6551b2aec848ed9b6179b2ecfc62b472b80dd4c2b514eb8a4fa67bd9486f6e00bc838c49336f0e6b6bc09c1abeef1a263116f2b49f6074a40c52d786c64e5f3885ec4a8045eac951f0405b4a1d14ac6a72d392aeb153bf3b1a58d3ca0b2a5528fdc87aa01f5167504173dc677d0ef20af95be736f34456f8f8ab9af4167d7c9f393340d187726ec8f99d444b7bb6f5d79fc6ea4ec3c45a6b6b723a3e0bd6c159354aa69bdab614903dde06a9c95b02b22c5249cd50a4db5d03f45347a38d8dbb80a9525eb7cd823e0c8cba1a009ec4eea9e2bae0dee4e18052cfaf7c1e8bbc5e52a0765ad3692105fe101ddd2190cf4ba3c0c0ba94b665cb0f68a3ff6509fffe1e9db94b96819997f4cc88d008582734d0e4dd050d56ce2de68596fc53a1a052a8d2af32d997812ca09b03ab55252afc4f7fe8a5753306f41aba25a8921d6306d6df18ee1ceee1f83e887cbf6dceb2834352803527dcd0e67b98e4fa6815e291c39455da08811686a1861ff10cd14e4f11d2a185829a6944ab4d231dfd439b731167c168603c0fa9548948244b32e9cabea3f2e4de6eb0001644a1bba6a7b04be72cd2b136125fab2f397ffd2c2ff5a27b5fd8eaa694d043a10d49504f3a4e259f317e76c80599e4953d8dd0bbad7cf33a8864258d7f967a0c48bb3885e29f8ec97ead44cfd4812c4316982c1a5fb6bc6f625a1dc9ae568ff191fed64e2f39904d49a8fae9618cf83566b8ca2847ef6d0cb8234282eef3152a8d516e4a91a58bd998602765a5902d6cda1e601dfb45d1775ba17597ecf657e0a1f83bd4895aa2842e86addb93c876b6b5903ddd9ecfd38df321c6882d8220d58d979428fc2e3acf880b7d29af181a5de7a6370db1d67c6b2d6ec29e556938604e17dcf36e017914efc8c9b06c24d65274512446ec2eca74ddf738607fbfcf5e4cc3e5810fc0348249d1b0fc4fb36b5465089dde9583813df57726620d79f0283b785d9dcbd4f041c46ca18985f8c6c01954c0ecd1283f985ec40ff9818de7783ddd8eae9c19c6ada78feca20f56aa0440d96e9d5cbb3e621ccc4e79e6ce6df27bfdf9567663364de5ee75c5bb76cc3ecc09a1732af0d119d45e076e48c75c4aad82d420aa3dc52622f5e6d2be9ae089a91da6b467784148d09b0cffbc582cb4ede2a7320e03bf8cd2b237b341bec9f54e6d49f7797eb53836b1cec068e126a074b510e61620f3352da9b98ad1380de751f52403d5c6c8f3d2391e8b2cc5b6f71edb2ac94b01f113a2759d3c55abdabd3fe819a54c517f37b0a6fb307244fd5d4ea1f17d442bdcc49cabe55afb5f128a85f3f9741d19b1a81e0f89911a6b74abd9b4c592be6b4b18d21346316445e2c7c284189f908cbadc3e103515ebcc045142d2f7e0566b663509a2736a4c765f248c85b780f42bdbc213630516a250faa5797f5c3adbdccffb0c60441c80582abdfbfcc6849dcb0e18ec46635276a9514fc39f7bc5232dfebcc2ecd66b9690ea5d335a0e6adb7add31b1f4a151df084f76691b7ad48840c8397bec54c660190948aa949306f31b4d468086c7a47113b7c024e618199470c86673b51a7f9c3f1f83d5979f3769c4246cbe2db66bf4a410dfc9ccc043dcb6d66fc6d1d20f5567f4c028c22330b05f55aac3ce5048a1309293ebd8de60d2d95297bbfcf921b4492313742185336b5ef206c7b5e2c4b2efb1c25a56d7fc6d3e6e697b81e894d0e8beac6e4ae48338dc4b82174c057ce3d96c84a0ce7dc639f8ae796b578feff427485efcfcccbf3b6a052fd9bfece616a08e4253305cdab740ab18b16a2b059e68f02a763ecebd684924f3c2b51a029d036f575f525be694e50f6d392c46bb06d40d254f4e565e7ae7c6f6fbe2ae93a96a61e5ab229b3e1c5a4fb3f3739461c53e372e583ed6aea784b49c65dde2e317f02b04a1fedc1a406e5ed0af1de7620ed75a2582123989e87b4685b6178c38658d85f0da6f0df9bd217f087deb4c6d4def38baf0b20c98326fe43cfd062e37c109101211add82c5e5ad0b4e8a80bbb567516c158549c893d889c82e6b6727b3d3532d2da5d50b86a4f5b4502d990f107303a8141caf12aea7499c2b6af8b4951cd99c88e6e77da8e0399a4781fb5ffeeadc74196aa11efdf9cbafb9cb71d040112e61cea55e37cf89f15f4b7916254b6124ca5f0f46f6cf5fcd5d76ab35ed7a9df5e527d3c87de55e108964147deeab03f906309137ee4acbf14bf240a69d43a7cd9dc8518efad0b2db71064172691efd2c4b7f18d81bdd8db3df39eed97aa5a404023b972ad5ba45509c60e78a7842f062631bce472cecb104188a444a9435670acb1a0fc355dfead888304877a39e216b619f0e922d8f4e33b3d17122695241d7e1433c78d9f6c7b9e9d7c5d12984ca02b3c87e90b6f2af4f4974a30297bc5ae2bfc812b45a72d43568181795729513e3c9d9c6b0981262678ba5eac9bc16ec23efab4ea696f28a195086b51037dc2fd433e283d0ccee1bb6c07d72b0a49c141a1b5c5e195c5efa02da9d5638d0f23996f73c19d6359e4b1ff761d042e9b9737a4f8e6d3b126b19633837a9ccbd7de5d3f288e5aa9fa71493e3e8ca2f0d1f5e3a31c029a1dd5d00c1cb2100c2ab14ffcb1f7162216806db0e4ac52bbd4372130a1c888d02447063cb0bd869a483f4a8f07a61f9d9eb0207478f23194d1f230d237ad50e23bd184ac5a5213310aa6704cd8e6da622588a4c16fd44a54c1dc1ba6f7bfc9bcdcc1c661523fd2f6c577713a8f7498981619ecab95ec42f1e1759932a3cdc5ba049fbb3856a2e18cbda268fc599146eba584f45a2b7acd9057c00417bda24cfbc798ff87dcca42e0ed4c56c5827eca22d09b9feedec97addad5ffc75e2a38c78ef9a7f7d47dd4e2113a5035c87994efb5dc2e30142ddfd1e1fe0a3443ed7308df2dce03f3526c0b9c06a40338ec77d33ffee72441c334e3b662e6663476b89ef26abb232542cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0f554bf393bc6565486a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce7b7df956797bca627ab03e01bbbda8c37cd5b7591226b7c5af8405cf76a0f97a7963fd35f115daa9474c2de662c9406b505b3dbaa877edc9750c1ac1e145dcbf7a9a065561c1adea5c6ce70b26ed74fdda3f156f3bf5ab6befb0d79f1c79e3df8c036ade5c916115378e7d9ee2b5d2cebd94e53fdae54e0a22c37f1581e323bfcaec2d30baec9938ea398721a3f05c4c23eae043a3ee5851ca98bb83e9a96d270e767f4123f81f7771c0d2e426f053fb7e18c69102aa567dcb1732109329508ad7b1ff3f0ce4875e37a47128929cfd7decc9a93b29ac0679c6177cd7cc62584b6795dbeea8e406f4ca4388389f68d6d35ef511986696170a51143342d2445a508279b98986e43dcca106ccd2f1b69127b8c22ce090937e79d650dcf93eb47e0e4c74127183bb38f7a855797a6bdf873454aa208032d06ad982500f7f917aa44c5b294d792a65c5ed4c0140d0ef0292d504255df0fa49feb1b75ac08d1a842400420df6f116b74cd1c8cece081c295ad7bccc722992b88d8f43e4022049a18080fb5103241f5fb770b91adefb80da8051bcbd7a54a6c8a450bfcb7e4d55897f81692d52c1706c51257cffc7c17af02f7d18423efd85c5c9cb9041f0487ee8aebee1fedee12c6041209dcbb9528dd5d927789d291bde3edb177743c2ac0df6f973d238a0b5aac65685de044f0d44c78c080b19f734f699012af17fc0ed95e425a2cc45522d06f1a02c746b717a980e61f90568b3e246fbc2f7ad43097eaee464d747078ce8caaba482b12b1f5323b8612abb31948f744f9cc615dae60c4ea39b95f6ac863f68ff68e3e637219ad2adfcfe1d5917c83d3ac10f71964e71c40a4a12a541ffcff34eb45d541c4881671ad7d48b55d62eb260844aaa32b6307fe59fe8e68e0bcbaec5e74268a969a9710b2f5cdf5451bbab88064bb23a0d716174177a24f8e617da9dfd81b43bd13611e807737fd00e4cdd4f697a4a4f3cff8f3402b1f06a2e48cd54481fe0c775a09a9274a312834d525312908e493e4ff58e81e3fc1305cf0f7b65f830bb433873ff0c991509959f83546c29eb126b64121997fd58b39a35a0995c7376e334336fff12b6407f934ab702e4e1ccb48fe0b98a5f8fd64e49e0a6319b6837ce65b5197230c5788e4b87af6e3a80396bd299f4752e2067fad434dee3d593f9f536ff1a2cc1a03e791fe69c788cd3d31356d8c5eb0a96256f4fdfc1ea632c2a3f4cc9245619738bd3f80107e1acbca5fe67d760fa047e86ea68017f3c0adecb7b7cb8a602c405f36d58100b79c8729f71b1d7b357ef8689f7979f3963c1649896c5da6f2b83a10cf09d42c47cb4edbd432a99011548c7d5e257a2c1d42ba19fdc9c1d6e7275d91870368c62173e4be75f939e7e7933be2434e6aa7cb335de9344dccf1d4b47f3be14099267910b2a7e8ee8616054f1b431a5e8d007fe6caf468e4235efe6730d99c900ac6b80de38d4154aa9749d75bdd6521fa1235c2ac17cee5128ae707c2a01e789adc2641057c897be134dd6c38342bf7b4bf64a763a4119ecdb4fb5decb588ff0ef7a20ad6319729db446e27beb98ee39b20ea5ac6ffbbadaafece100c6ddaa63b0e45bcca30adb8428bae82073b2c494c68c737091b01d731d61883a57f4262aad733defe7f13323df029463430674ff40c4b35583e1c47fe18044893631d5aca6083cced498f285990d852d925a93e2ea8c9027b2fb7d886153cf0781cdfe498502b13d1ff546449d721a5fb4909ab2389f9c33fc1276b238a5f927129c82360e430a1d539bd54fafa2da6b5f71cc1341ffcb7b700bfd6a6dbfc8b2ebee5dece3dc1d4a5c9b58ef40bfd9f7edbe8d17d934b82345d354500177dd77b4b3c7ade83bf1017c07f939083c2248dbd0080845403ca0959e2fabc2592e834d60e17dad433c30b677cf71d2fee5e84869b67c6b13fc72dbdab9aab06fd90f1dd20dfc41d97fe93503f5a667510815608c7feb81140cb1944024fec6a0911156f60b7bde770269f5fdbfd1366ce063d1acb8053184ef1db98332a888bba29d9f63a88d520d40f167fb8ef40c6ab0bd6447b4f1050f092c24996e16311801fdb87a1647b0f953b6b510fe3c4f41872df454f48ef086784a40a550725a6a485a925cd90b0fcdfbd6335273fe013166f00802461d87d7909922522b0a13195b9381e7ead39dfc7993a4951702a1dfadf79fee29edc250a215424fa486bdc073ca00c6f0c50c7164cddf7051bde7d63d97bf5fd780ab0caae727f72aac817afe71e20987f6e2ae0918068b47e40c5fef233454caaeb087433bcd909c1c5bbd8ab2be682a2b7d33267034ac16402ee7e92a0193e8e43f4da7e6fe5f0fb8689ceacc5f530d615d5b5a5db1155d30e4f2187d721731246d809c4f620a34f0d6393597fa5ef7079393a0bb810536235ba15591c5f6e695462bbdf9f5dab32760bcc1a91bda6aedb4b497889dd9115d63206ea34cc87db7ae893498eaa7842ee3b0e7de627c2a73fa1dab8befb78904a65bacb6baa340235532b6e5a69eb4e27e7bda343dab41c05fe4c3b68d2252db55610e56889dd708b8d9e3b09a0a8c748cfe0eebb07eec5eca66b5016d497519eedacb6bcba9aebf407900d3f690a6fc367d9fe985c1425e8d7f880eedbfcd14abab2847f0b4fdd5e593c87929704ad2413b41a6bb6e3d9aba0378dbeef1f233ecb06db0b6cb55dfd9ae7f29344a01cfae710e9f19a06de10ade20b9bb75fd8fad78ff97592cad02ec2ac0700835a5b617ac64f0a2e5daf9b367e70fb76eab49b36c53c6d3a8e96049b25416f77acd7d6c2125eb2572d20d56e5329152ddb9169b1dc95354c4e403a7b4397495b5f8afe35b62de4bd059afeed08612d28c96d802037e8b4660fc48ba6d703fa4b48d7d4ebc6d2a7ab9146d0e9ec79335395f7fe3b5279ac55245b2b40e8353ee6a0b414bd6338fb46efe2bd8321a9f3f47a3d5f9c4e8837b3f9e9ebb9049cc716f23a877c1db75cc64607cb3edfbc9e3da395d890b4015c5430d2dcf6f4ac29fc555f3381c24001e0688dbb5d1982225172c042a5d7fa4aa0087a75012cc3d52a2ab3f6fcd2b48cbde8f791c8c4e21ae3a1de22f9c762381b2a4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f47c0d5b2c7731d6aa5632ee1cac7d6c4409fb7c2e64772f6ad6df2d9f38d830a963e55c7b80375ec0059984c13627a6deeae3db4013afb15747568915f816e6f464d8455973a1f92762abf8deedff1274e501d04a61f66e2c8db2c88f4260715626970d4f06158f11631040143f3216038b4096ac719301f761308fcd50360356e1068e71f6c7bb22c0c8bbcca3ef3f401caa88fd4b71b382d642f7f1610faf04eff88361e8b057fd2385140260119e222c1a14316b64354f0159007ecc6c02f5da72af3b307987a1a96eb685bd85cbaf549a5ec3761af543e43a661b8f18b845e6f5b8ca18bd68529f93e786e2e2b9e26fc1eb31f8f6c8d4df2274267ea8433664959e98668c3e0ee1a30516234f31ec5dd92da42ade1f287592b5440777118949ebf1ce67779ad7e136337a0499903a4240e4b1894ce378e2fa7060c13b2b3734f1034e09ea62c7ab3b82e9540a4d8a46ccefb0e4ab0acf4416a47ea4f70310d8fce41515d944fc0906a62f0dcd7ed8b4b94c0a108836ba2088d889d3b8dcb386fc5018fc81e86016418f7b05f42acafbaff45963f98916f63506e4b0f5b95b1c72d194070099f1f3063c40c18d89a506430d2783aeb0b4b2730ad344a317ff1ed320d90f4bc46ae23f28679bff44de77fad17ece6fc92634e53660df9dd8ab2456399a123a0687cf2a870ef95a1d01a1329cb342a10967578e57042ae856dc940126dcb24ef8fa3a4bd2e1be36a3b8ec2b90937d79ecc1bc1f0a83f7e99240281758f222889ed5b14e1cd0481d0a4b1d292aa740ad99740f4276405589f9b2cf307911018a3e7ba89902a4af49d5170b698742f6b6fc86e0663bcff144c9e27a5fdd824b12d581533bf42b4d4ec51974ca71aa07e6b4948dd78305e97d97cdedad00a338f497486e3dc02c45b1a5510054ac5cd255fc288be0692ac964be6971d735118aa7b8c0140a41ff700d6a6eb55cb810345b9260838fb266476136097a923de32047798b0aa60ee5c3cbe1bfb9901dae729c8bef75d6dbc830b50b38cc0b52648283ebe41e6e50c28896cab0be3b1130ef39262a3c85fcc50c81d53ad80b6af5fe01f50f20bca81490e41cfd755c68446be3c6bc4908e99ddad9ea4c793415fe34a17cdba200c3e879d233c4f077af252a4fb5ca91e5f151baf6f6f61028e5173e83623ac8c2249761ee79f7ae06ac11d10ee305f20021147c571a26fcd5c836943c388c42fd74f9e5d6176d22f246f22489a866ac5fa9edd375d273bd9d3913d09cebd44153f1828adbe4bb82bdb52e35cd221b0b5ee9ddd2020b07c884b3649a99dfe15e4cbf30a32125928bb24998634842743aa616b1cee296657a8e9e1d0a43d4c920860b68ce0d2cb0cea57a1468f99f09d8a60c40150ca4b4cae16c7292f586ed269f75f275d47f51550d68c701766da5d46dd8108267fd861907d41086746406d5fb916ad3da0bfc854b9b9073afb3a23e5e70dae66c0bdac1ca9a18cc77bac100e462a4e8c25ddf93748afab3b15952634a802d99415eabf68e8114d5b2f0b905cae3a8d7174d5cba05eea9d184695f64fac6a4e2b461501f3f06c65dc52b315791ea35ddc7720f06497489b1344b902fc6c4866eeed6c816b615368cf76f17bcdb12a02834ac2bee3fd3c3fa64060029c183742a756022dedad385548f85bd5762abc43a30ec5e99673c40f5544dafbf679a564c97c85f1abc18943fcef75000dfa736f94e9b38678f38d73c18799c4c0a7e998b1761ee03395383c1f47578f2f8e6c2c02279aee9961c86a3153b270caf6c787fa89b112e2c97cbe4c3dcbe6efdc470c4795660ef597f6059d60948cbf9241fd17d6a8c1591808703312c2019280ee1435be38aba70efd1c83c81a7632fe826b2673b78134ec163747695792008c18eefee0d6ab50ead1aa8c523933b8fa0034b5b54407341da8f7528f54f8bce44608e36663ed37870abdd7cd80639973bcbbceef86702149190a85ebcc25b4fa81c8f34af0e6f5a5969d6392d28b7bc4f290c6cfa28955b7a8571632edcc600b39a1ebb370c6351a08f1d8117ed7a841e8e65225c8650a6c4249c35ca2197b88a1e5ace362121fb2019e505d4b2a2b75d44cfb08bae09bd7b11c0cbcd6af1e0b596f7b1e4f86834cf1224a56aa4c8ed61f54d273e1e950bc75829b6b80a583481be847eeb43f60a7c0b3693945ddfefe62970a478127a3533c906825f411fcfd2d44af091f2f20a723aa842800a0df19fa8618806e73de39001978697b311bb776182ef7c78aeeba5cb232835772d46cf9eb22f7704a885bd7a6fadf6eb2ef4ea92b6ed1a2348dbd89db0eb1f2225ca1f8a6f6f2b40fba42b0d16f7fae7539fee269ae60d97a5fc0cfe2310b2c827d023219cf635ef336389506842995d90f75309fb00142dc995cb883f248fc98c5f04c958836f2c3ccc83c1f35b49f3acb98b858b3b4e6519142521243d9ed73326ece1f2a74f289ce59ab11c6071267406b80134df30f8f574cc1c0f844d4deb642668f5f76fb507cd70711f901ad1d3122a93e5b5114e100ddbef35cb8d8f75f4807b665da528727bb9caf8d370d409b7ff745c50aa026a6d8a10220eaca5a51d3552c1423facc0bb428c70d5f24571efd589e89d2785132e93a5afcd566a114190cbc2819d089decfb93f51e0a0bdd2dbba76fa571b781a1af4ab08b0cae4d9b25a0fa475f40afaac2abf7c73bb340c839d43cee5c71939d7643b5d43e3d6278300b8e087f5248139ab2b3804e77f20fe5a2377b83908f07f99b5bc9eabcd758d322b3cbc6b3073446858e60ab2c91a40385a4ea8f0008d21755b8f2de9084acdb332b0701b476d6bf8dadc695d6d4c5c86490a9e018d05bf57528ca004ee78149db7a5484ec821dfc81cef3ffdc1c01720f77c8d9aa38dd2936d376b67f9ec1f6dd9281ebf0279df4a3b7cdaf62e475b428e47b1dff67f3d2dc9a329cefe8afe353fd5c1d38ecff1e5f2a31c230c837cae048167b3bced8e52717d46c19ed37b193ddbf6bd6eedebef0869e49153d193edc1f0820f7f1143fc2f7e2f2893790feff22d218a7b7a75012cc3d52a2ab3f6fcd2b48cbde8f791c8c4e21ae3a1de22f9c762381b2a4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b924b2028c3eaadcf1aac3e9e17079590fb454a78519c591aa4f434c57d569104b327c180072d7e54d55668159d637b2206dc4b0e62b8d4db4fc324cfd134a9895f8a9d00db48cf0538403293ed4cb278c2c2164bf2c6b73b12d4beff5e13bbda6cd2592afacaf0aacaa0994321342610963197d24ba3dddd4c8d73a9fffcad563e1f3a5c14bbd832bf8b07b3c9b5e9efbaa44c388682860b3435f9692265b615d6002b4b8733bf7d2b7413ab603f64a3438b1fe1c3c642853f3d610f35dfb12b93069e3d73018cc1da74dd95ba798f54a31ba8c5c65d3234bb3786d51e5dd87806cc963a2845e513119e8570dffcf2c5e9debc027ef43a02be40180b6d548ef3abfabe6c43aada3337a9014f7ec236f0331c068d388b121cb13da57e73173a69e1cfd9434b9ae36ea4613b118ca05d45f28e419fccff3157f5af8e33ced2af3ba149a4a08285ff9385955e966bd2af4891d385d707bde3df8cfe2d2bc77527a70bc9ecdf42ec3d046cc9e53445df9380f9e19d11d4a9d38920378c2e6f3185189f5e5080295ad807bf2fa5ae0b777f29a4a5925e3b00b4be8af26aafe9780079ca4833d6f5a1b7a3d498b38d12b4c327299c09e0e18528119fe49c98ebeccc2beb54aa649118870f2db2b497891f6882cf3e2d5238748582602fa9bf4bc20bbd7962b113c13b296d591815e78ed09fc113fddcae7817a7ae323a0f7443aaebfd0c3d19d8f505ddf1dc2f63d2d344c3785978de0e6555c5587c5bc30f6f3a14152764abbdee93671659abe43e6c03dde1f8fcfae36a79981d020e339b3ac94fe435393074da7685ba143695f7d90f54a6f63bfa83a0a2596348bba7c1570d5ce9b2e6498672f30c4d4c8a5374ccfb166ae7a4ba91d439d35c42e73b3b1e925c6094b07a994626cd6c5f1eebe07134fa5d47044702edbee817e6c1a49397838f4589b736f59a96bcff60739231ec325fe9adae43a58f1a0c3c9364510c206bf31453abe9d26bf7180ad24250c224558c6bb2ce74af2c952dbc5c77767a92db8375db70dccb087ac63264968017c7179540fb123d55872ff441233fcf66fc7ad6035255052a731ab9ec2d3322d6d2e167311bd0c2672b9f5b04d7b7d87685e79a322c6d849403f48204bd3e001c300f88f0d84e86364eb6e8b956156ea7cc5c67a38182cd24d057214b02da32cbab42ba240a04040c940d9758a8d081387f6815e646f42867cfc9e8fa08697c3ab66740c756678be7ecdcb0c5114afaff488e9078a32c2c660e247dbe018f004bbdd68fefd8fa68ed952020cc34d366d260fa47bf8eff2ef8ab3995c6f99adfa8da3c3887b46a3fd7a1536667caff0df421e64022854a951e5ad65405f85d245b5d0906ecc4cd59c5e7265aa43d1ea522bf144786153ccf0cf1ed1d0ca5230850be8d1172c35abc69562f00525e02ff7d8ef409cab89a4f5bc77f27604fe07e94ec34c92e5666224784036535a244a6530d6ac6a7bcf5c4b3496875620f2baf8ff44284a1bcb7fa2e740f3d9bf17cc08338420f9b0ff4018bea0289572330fa9fae364dab581b5f8830139602cfa9f00eb3bee07ccaa37711b1c1dbf61f6988f3b42fd965932e1496b601ae8eaa959b9a7db66e03cf0cff91f820d2ba11d74746f3ca61e14aeb7708177365496037bc6f3f3c3d79affcd68fee476c14bc7e530be355388c1f6ed0fd6393982a3bf9e4a7569553de3baac54e6fd144113dc600dbcd7066cb3a6eaf099a90c627887b94b5a2b37044a38d181fa9e61013afeb952e0a0374cc8574ff774c714f9e3bc976062cce7445a7a06c27598f7e7097d17fb02dba8ac0761c63508688d07ccf869853265b3eacadbf3c8418f95666f9d13c4a6e31bd4abf4fafcb02df21370e4c090e4f32b1f03f083516d07db38b5a6fbf9baee1d80be4ef2f6a5e35f66fe9e944be2f1b89252a295365381e4e95b3dc0a6db4f44d95c64e72e8f0eea55aff71e222ee1c69c23a89512c56b59bc24c13c5091199fce38d330dcefae71afedf5b6adb6b929cc082769de544ebe42fea0ee19920688e6623b14b91c050508b65a656b9fd3a8cda0c80ef64da2381f8320b5998e249d3c3a4cc3e43058014a9df9477fab2c0b3c93da8023e4ea106f03366fb1ea35e09b3b282b6d9279bf6f715b519f4f8ad2bd7d0cceffd063a9aa196092a5ae6cfd596abd6d24c6f05e23bfa0bca63fdc20bdafbca6487707fa2a8f258800ccc8e00c761b88923255ed6e359950dbed3e19ac8d3bb69ae1082b697b59eab6b2a101b5d7169fbc03940f4117f27a11a95d162821c4ce463eb69f738d81734616166a21258d57646191fa20e119072782a1c35698c957e3df14df213e7ff19b24980578e883daf7cc9e80c768b44927ae08fa059e1b8d922230cc216e0cf9efbf40d2b2c5a4189f633ac754aa4f1ed0cf9f61423fab0b136473c0c6969748666acd5480ebe08d50003b7ab86feb615c5c57098968c9994a7ecd2abbdb9e9f39f046eef25aa6acb4a4af40834dee75e3ac0dad86d6cad26ff23758dead83c7db7a84e0e0725cc92cffc942c5cdf750bb44bb3531fc90db88f3f0bf35da16ccb56af77b6675b0ed811925e8738c79ecd735ef866dc1ca0f27e850ec56ac2de54602b59e5ec85cf077bc32781c1ffd0a5ed68a20d8d08d7003182d961ebdd5fb5e2131a1f93c1e9110b261b78d8282d4104babd68ae69ec88b0b7cc2103d6966c1f920d6446ca3ef0b9ed0cdb6ff390abf67675d6acb99257c0d4a2579fb53d876db0fe0a652f24a2de95e4c30d9e9b3d16bbfcb4ce7d6fae22ad1bb375a43c32f57d3e7f13fa910d8701d862f0ad9e79448e6e452bd9661667f8bc99bb64eb57f869036f7ea0ee91e2fb24f76611c5716f3230a962d2bb7e67232eb2799da035a536e5325c054a4596029eef23eface2d09b445b98a24d5d308dd575cb1ffc48296635e5575f20c064a0b7392cb893eea7203b42a92081f864c1fea4055fc17552207873c1cd9fc2d063fbd5bff115dbbd8536be1ea6e11989fe66962a32d5b876b18b8c700115bf8594732e66d1726c38d4d835d0c1f4f6b291e011185b7dea770409e01d0d5a455218f1bfcbd1ea1b593baed37f4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"0fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a098381035744570b2f06cdda0bed27b0fa68939197a2a9bc5297fd75fea460d8f473a17511a8485aaf7f4a92526816d0202df8e08de6cbbb9ef233e032e685e660726e00e400631794036889e30822601bc8bc1c04f10f4e380ec7647c05af4ff603bede69f323488e55f2d0945ab69ee50c8a738508c3244594bc607ae8784fd2411474e493e02e5c854dd4ba2585ab96ffdfc0ccb612820f12dea8a4c398b8610183175a1d740b62a3631412ead199ecbd62a76ab4bd009e6db65c537b00c15e156443bee98d0cf8292ff0af572e3ce8153ac58b12fa086d342c606043e3352a272a84ca131455b068b4016bfdecae4c38218991a1488e4480bab656ee22842771becd807afb99f5a036679071b5e4e73f2cc759e357aaa5285456cb90f33cf663498072cb9c717490fc2650b17ca28c8df5ce97d468e2e5998bc48181019cc040915c0f451597a657203361345d751c0ff445d0ddc81331df3141341b8f141b7fe3ba7770ebc107798cc4d6b91b429522f3a07769b4cf05630a663c740e6249c90aa7f1a5aed19de1d1d4e1a18e45bc7241e1681712acf421d13e49e8d6cc05029dd7a2d3346f37d2aa4bd250da3fde62935d83a554dec973730cc9e5d56d377570fe2ab7ef965703e7c80dd193e94e1907270004ecf7c64ab22529e3cf44cddbcfcf1454a3b2b59c29dd72a82e36fb5e86118678cb668f4fdd12daf5ecc44eb65a8213278c831b1580fd553b0b62ca2b1232cdca1b2c06c75c22750967dfcc570c42e471350a2b7e733216b3a199051cc475b6fd92ae045823bf358936294210b81ff83ebc18d4cb93efbc70feb6c1b2aa8594e62abb872c956dbc0304501e0605226ba939834ec26666c2e5761e02348c46cce8979db9ddd303f2d8b6830ca7d7ddbf78a3302ad8db1e79e354596fa13ba392ff70a0cd5d9760a56c74e220c657a91117a3ff864182a27ad6092ee94d9080eab46c6a8e78c8f2be5e4c4343ec9fa08ae14dc56c6689b4e60f243ea8d9a895f7f387e3076f203a90b3ff734b4fa643fe460d9f96969659ea63e0eec63b463ff710d927134609a6880ac61949abea0b5dad3d8139e87be6f9ef063ceeca433f594af823d223b047705dc47955a9a3187ce42ace4b629bb731687c903e2048622cdb7c6d4096473802e1e1ddcde60a1230302e1e205e3aca460847899f4996bb8bd2a53b95e7b3a52e6da59c2ce4ea2413cc8b5e3c610d115977069db9c56f0e561844cc2e5f3419df3a9cbf523f3110116ba13bb6a7a699eb680fc702ab131712d9972a7580a99cca6adc30d3d5f4c61410affa821d267ccd85be6c1779b6f7e488ead2cbe271d2aab5d21bd9cad69c645f2ed0d6c87b27f719b1172d25fbe7a6b3fa87e9b35499a872ecb699b1a6f921061ce70bb71fbbcdc998efb1f85766bef0b82672fc32ef73b0d9d9a5debe283c0e548b8fcc25fdc814d309c4152dcd1dc7f743f03de395d580dfd2890293d3099af9a605a686fb4fca35d64c12d70af73beb36bdfbc841e85cd201ee1477e23da37f82fe2050b60b63dce75af2cc0d37509d2b63069b09c39595a8d77d3f0f7738ea14f6f860ca84ad8ea53a0030e88efaef3866291c453e3a4ec151e4c2b7b9a720e4eb37d0d51d0d5515a04024009afc739930381a85e5ec88e8b193c27b54bd4e0ef127075e944393ca32d253e0da7117e33c77b04d29deb5718414d22579f59ec83ed7c66c7c6959ee54074f0c0d6cec55b9efe59cf5918bcd7f78113ea8fd5439b7097bce922ffb7c7195d989a9d0d09ec5648894c0eb3ac9e9f883361beecf9f9248d6c357b59b8f813b3ab5f882ffa96f1d68e107f277012ebf906ac6b30dd0b807b35f4a3f52e5f4797a42f3bc17c50dec411b84a107303cc86f7aca05e844dfc356cf3f62d8299575fbff3583dd3d5b7b3c8dae1d3a5513ba60d1940fe054d7352e914099c1a3f98d5917777ef98582fd5bd62cfedc0ca10f79d126301b54c7ed2f59fa046f96c6a647b6868c2663fa6cf864e8a4abe18193ab14c5582c533eaa59befc5f44749f55526d9ef881b2605e6cf9f583417f527aa677bce30efe21bca54a11056e51dec9973eafc9bee22ada665c152ac5a06ea981a3c71f33f6e633af2468a7ae2a1ffecbc501b8e462cbf36944a819bdca04bd3d7b21ef11bbff3d003298a8bf984591ce5af6f9d8d2b6b05daeabbb1cd215f43a1c7c1ce67c3e16e7797ce56895ed9405fc60f3a1e29da46db8f10831f6cacd5758e6712e3c8f9d84cc04add1fcad6d039b4aa3289d62bcc7d3253421b80e178b1878cc7c9db39ebc717755045850f70432e5d7628dfff4871b125bee5140673ba5ee5663ec2c46ba8c634f93a926aef32d15706ad18e95b9f7fdb431a07481cd2f3fecc88792aa2c8881fd6bc655cfc92415c5127c4c5d74cd942c4ab43cfe8afa3dfb65bdc4c5a015fbea401e5d8152e14095a9a54338dd4bcad18d322cbfc2be7df4d13cce9b693cf859e44741f1d904591a0d39f262c5d85f2c542f8d7882d41b3ad407e13a6ffb7c3bab2a30141a44910b80219d1e32421ead5530f35aebe125321b510320cf86cc8e68b5473e815d5624be8035651713a317699f7ecbbee107ab4eef59275defb939e02d42b740bf140591bcbba3c4345d440313c7df7c850119660fd9b2daa4c029736e5c1616306d5ac987f91cd18cbc15d12becbd9eb445966445d2d1435043ffb287c6b01ac328e445e03ef24563915d94baa7372f46c1d508f2c8b3428d643d405179b88bdd29bb4f42d37dee05ac3de3e8ee29adcfd70029a798c9f2f93fe818740cf75490bfc43be2373d2051f76832b428fcb52e538dd52065720c6275f2f94463c37d276595be9bc5aaf7df8422c03ff964cd3056717225d8cd020ac00b04edfd498ff0a1fd1cf8d67bb02af36f8b723c155df4b19d672f5faa32885a27752a09779f09f3a41f20b87cb63eb2dc9266980a65d3f206bf5079815e0b2dce5ff09c1ad0c7d35cb8ddf75e7ef52adb0d6897773953b1d1e25b81b16b1182fdc1cc2967d626d3fe0aeb767f3f607cc6c0cd7adbc13cc1f4f6b291e011185b7dea770409e01d0d5a455218f1bfcbd1ea1b593baed37f4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3acfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a996e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfee7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b5910f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6deb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a4161fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd8084095e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42ae298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db3749029ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd6733d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7be79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1bcaccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02bd44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ecaeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e0812f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c179cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340ebd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa517f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce386a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f956421e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf497e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e102093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea970e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b22b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c75804cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970cf10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe8821d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abdecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec22c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e84917783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b5052a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1018221950446e8726a50c6c39dc45cc0f3e07a16341212470105183f4d65533d2e40413796b28dda8e6cbcf11114d7f8541c788de2619b47ab45ec20becc2c8d918ff13a55de58a8f48f2a1a2d49b6bd8b805c41d931c7c98e8908dd84f576a5e5b35154e232940dbb83997f9f3288ef8351148264ba7137a854a401c16b0e4c82b42b97e19b9acf0631cf7c4f919b9a398f01096c577c71a12bc431f95e36482c64a34780fea0393ef639251948e105b304ace0bfe9534b2b330647a50841a8236a7aa6d595da82c5a3576c22c5e51f4059753724f9e2471c575984966533c0db248905969f40d7c0897f7bb747991b2f5d86c8d41182185c540aa7183201b0c03816c62c977925276c75d9d9916b9790d69a7acc0c97d2fad3c9c979bc3bfce319b9b90f2b8748e1f6adc71ca50f5f4e99286e45a2842677c4cfc6749030824832ae49b9e0cc60872a52c66c61976945e96d3d4b3e2caf06d9f1fc2ca85c04542536269813552d51fcb3036215140ec0a5776300ec5eb8fd356f7c753f37edd9f5277dcfd450d21d0b40613657f8f59694cc4d10929c06d6b04e56807358c2a99e25bbfc1074b476cfc609fd25630cc1a7adefd0bd1a70354f52bcfbbebd1d71ad82ae168a0a2761f55ca357ed4c05daa393aeb5401f2abea8d545bd7d7e9e0d2614f9d16ff08ab03929f1dec723355403c93422dca445dda3865136368bbbbf5b20ba7025a773b01c3502c5866cd55db03cc7a7862163d02ebc958253246689300a95196f44e68093f4d38ada684c17458d302499c070892e67b3bb7aad01acdb0adcd4b4448a08ee5cd447466b7f0c9e62dc2ddcd7573d20c5bc32cd6e55ce4303371a63db5dace631894696e323997b388dede168f230ef0463e1b06f507b4e4efb5bc59f7bdbda6b6c125745de752714147b8bb4c7025c9e1e7c4c27bf9eb4e7a73704db66e388218bf1b9a64379879d93d89a72b6fe21f1984ff4061fb7c82172768911c1f5116fc627c1d8fe6243c3a357ddb14fe45ff224aa0bf729b5194258396dc62218c7a2fa8ed2338d4dabf3df8e860953c17b268fb9e83211935f60e6c3efd12980987a884f5ba393e7f4e718bab519d7202d206a54585396230c58d50f31b8afa30299cd3ae381bdecbcd6d1036274127d91378570de56e0699e643f36f8b3ff0abc026e3034c3345ac20c3b7853e004edbffaa2b58f0baed14654ad0e68cab878b3858981d7241174aab9abf2710de264cfebfb91d6e18d84d477bebdfc66ca1b1cf59dbe1b1720ad52aedf893ed1e18bf0a96645d8748f98c03839ec1a90b5b6e44b1d90e58328e9260ef56d5c459df6ed69c8111ed1e9f067d1380a568a26b9a21566f43445edbbd8e6c135d2f8d261261549aed0cf9ccd2cefd51a75b8cd5979e7844f662e9f39f074f768da87c7134fb13c2cc6e117c20d5a7a158e0252fda8304eb79db219b6b8bd79299607cfc225d16e7ecaaccbad45e2acc8208c650f6ac6885443b0035dab6c47c6e447093cf4b6c2fa563419949064dc5e74da1e8d191d4ce5cc0ec6ee5ee1a6e793d90696d28550ddf2906eef517725def5b4724bef626d49f4124df951f0f9ca9006754eb5e192a1e87617d382c73a9d5d174277796a7a7b1a2db7d12b640738c761ebd6b7feccd1fb9ef933250aaec3fe0328d4e25737d47c365b82c47f2874ea09d5fb9fba8e6afe33729d181faaa1dc302ed8b8c4cc67a27c7e4839b80ea9308b9f0f289cf57f44b0f6d36ccd45cb0ae5f06d94d1a0598a4e3079a43075be450771589fedebd9532dd2b87decd5448571db9b01c66226bed20d4bc776cef6105edfba39b37247b8cc17906032ecdcbb0007364d8a12a15bb8187533ad2e1f8ddd31a82e17b99844d66afbbb70ec955dbc0c26df74eb35ab2f0a3756f249ab90fdcb325eb903504551f34793029f2f22fe013b07c7711fa477131dc7ee229af3a075a66b3b9390d70456b64f933181de87bfc61b6d0a50e3729cea96d05d2f51922d193b1493c8341815a3b5dfd2936cbdae30a06105568a0156bd19f752c680bfd2ca3abb69619313e604bf9da49979dba6e84d6756ef54b63192958efb6d323a00b9e1cf4b6f5397a2967f9406b828c43ac982b28e3cb03721be46b0c18718b710fb2ddb29c00d9c75f292a00ae285c168aee071b606877e7cad1b405bd88322c0b0851f92d59ddfc506300ba3506302229d7dc19d094efe359c4f608ca5aa6a71990b36cdcfc5af6afdd84dee63f810146d1172aead09a8f0ae66f06041b030cffe76c22fe9b91bb86d2bce3a205be0d1b0bbbac34e9b3802a0424077508b32acae03f4617ac184e63b45f8399c4001d4591e858a98bdf41d45f36934173e162df807b163b3ca43a650b866a6539728c01627775a9e04c0ffa5b338a525c039bb8c069cc88f20ceef0efedd86254fa5a7ed668da7224bbae2937a30ca3fd6295302570a46ac85b4697ff6443e5cf3669ae25b2e87fc1360b76d8cf31cfa478bdbc49f5ab015581d026fae8b0940e3a64afc9e6344fbf54101411af7119960bdda4ee7610087ec60bf3277ed4bec40cc8f0b237eaa267801364ad865b6d6465215bc39b2018aa06f9a3d3ad3f719afc1144376da88aa9256eeaaeea2f47fad24972d9042e7f3bfab53a9e6e67f6890b651c2cf2aa144225ea8a59250c72072035908bc40fb6cf588a6ff8795a25f6a07c7f14451641bb3c71795e4386331d412e995a4a322948fac3c8607f69dcea30e26a7ab40df2d0a2b108f3788b430ccf30a712e4b065fd76a1185e8bb50c4b6bb3b5d49c9dd1e63781820b156b069181166d5e2c0969e8149908005428c67e6e31199370174d9709397b932647ed8279ee66943410e275f633a578b1a322b4a38863d272e16530c8458e0db07867d2ef233b641f554266ec5b2577c3c380c075c242d71a05d3ea402a783a0cbc6cbf51c6a5606177f9408f56d2b009e8e2fa81d7cfd767aa342f655a991cac57a1d3451508016b70a21d17144a4f5c4d5d7804e34bce09bc3672354124e05482ba2e625070ee4fe2c480c399b174c151a622c2279e73ea1fabf9317cdce950fd50dfc18d95c18fdb071ce28907f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243134dfbdde74521a6e0d8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"112d9f54baa2e5a127ae6876be1157072d10fcb5d0aa1a2c20bdfeae57588880bcbb9a4bbafb6dfc6d5fde84efd20165941b397a9b4252bd5300d01dd49ce04f1276812d6ba47f452046b2d14a259b69f8f59d5f18f7eb94e79802a2e274ef2a5fd22654dc431ced07d39e8f1ceb4ff753cca6a09d14e4690ceb7ac720686e76b24d781cf20aaa868597728695742fdf07cf03f6f0020c1d0d3880644871f23e24a88e7525085950cc70d45e86823ae135bd3cc94692b43545ce08cccf22b40c25819bfb439cf28ad38fdf202b9eb25c918ef8d8c6a239594ee58e72de20701bdd3c46987ef28cef61d77d7aa38670b7aa7145bf83540afde0e4f51c9c8ca639c48133c9d79f656d1bc429b74e28c6ab8fc4b28a650c2dc22a801d89041e2e742692dbcefff895d84ccbd6617853a15b7465cc2c9d31bf4490ffa3bc3b2264b1be3525b7956c95d8aaaa245ad1057c2f10eb8fc5695ea87b37536d2aba97b35246bc76f71493bc5938cc159cdbf6299b25b6fd451fdf2476e031ae490838f111e62b609d970a6848ab72326d6940628cc31cbdea1d165001fefd08a2cb6e8df476b3402ecc1def57080629ba454af2c14b73aafaf26f7191966170a98f65f6c3566c381d4e236fdd4eec5d426a19f3dfced9657396c4dfff45ad72019764a5ca8bb3806acfe9f6261f3c6a94a8015b231541bfc19a86ddb7494994c22f175736cce9d2e25921aafef6f7359a10fcd2f8581a99e4ba34c803c9308d5d0d6a8c8b9a5c327f2a4cdeecaf64a99fb5ea9345ee02c999c3b02c4fe8954255ed1bfc58ead7d40af492c40318bdfef6ba6708abcd0f152b4d042280b9d7c1198540b9cbdef6e7be71d9022256d184c99bd6127f679553b73380b56e341917d0e14ff96d95cad886ce4b23eb466bcd1922c51e0b6e3c7e550755563f582e0b607a3bc7f9e2791f3bcad679139a9881aa43a04dc5fb7f4e18da33081f08edc320cd22f1a38c5f4d3edafc7c8458c7e04669f876ba41388c923506daafa8c77398d292fdf6ea020d778d92bd723ab13743f54c457456712bcde27d2b2ee5e755d753224d2e8f0525d4d3d30efbbe9a3a7967be98e9f3d67564eca294c8a1f856045531d3362abbc2c47f50708b1170921fd1cae4778fb19ef922792ab823196c4df775cc2524b91c3079b03ae97339d323f29e7f0698fe1cef3f782f9052c91d3d59db381d5b31d007a464fa41f18c079c8b6eb113fc1b87154e8bb3007aa2c6cf8d2843a035ca50db4b91b2d2c66fd458deb486e3173a0ffb787663c2f923134cf171bf91c39b0d16f17fa225a7a0f2c42c6c63dda7b4ef56b5fe9a2725fece75061c50143a01c8f659b55a1a229ebe881a00451680f590096fc49a89a8e922863e879f87d60b8d3cf13cc43bfb571c59dd53c396fcdaeb71048a739d09eb5554079e0099cdd864d55bcf77ba1f471c1f5bc0b01e1f64cc092691d6364bfc52806f01c7baf47c605ee7c02df24104cdf996e7bc3f56eb8ebfbedff1af8b0ecb9ab9b155e6a273019e568210fb55f14bebcd0c0701d66188e1da9fd1be4db5720e41aae0579c320c259db4c17f526f67441044f0ceb346cec70b77c522853310f9051aa3f3f1e5f80caa522926b1cafd9b0198433782605ceb03e106e8437b162f750904c94151d76258fc3a1c53bd11a9e3ac96d1509f3fa1556bc8c7470f44e24c94546692ba00adbcf2803843969dc5eed6e1df63cee15d4262c42107e60d5909783cffb3fdce3c1a577d24d63831b887588ec6c94d62195dc8294b739ae9513d9c4d7fc80dcfe3590d1bda2d17cacab8c4d3ff28df24379d63b41501c93e293e2b44ce75e2afc0c5d8006dd2324b7bc39230696215d7b5ddf3de2d0fcad99ae0e0609575dd59c1819a606b155a86fbdabd892fa060714ec49711de59d75b7a5c667eab7f3a81214cfafbda33e2fe2eca7b469d37d1185c9072bfe35533b473dd701ed357926244f824d9fa01887de1b5cd131f7c577ab7519763fcc17a324fa8eb9490ea983008210e6f351f6273cfe755ba4e3ca62acee62d0e4a90844aae7a9e19c1fe67f7c508b597af14be61399218fd9a743c8269ec4d33dba3316f19d54cdcc92f7f804321c93ca8c21595fca4a847e5aa53aeab519b28fad58b3832ca77a39295cfa4857e2cf4ed6f619b73aab40fe2bbd64b90e24df5e5972f5b2d5d70cb404e55b33cbb39a1586c12a6231e9e6b5795156362224796b622536b65efc941567f81c791b188f686d0b8105e56cc075e015939962ec4a28039088474906126fb85dbb21e63ae0d8832ab8366858d9eff26b05d93d14be61c7ff3c58ac7765cea3104bf69af09f5e15579b2a324a6051a5c8cd82b3aa514dad139dccb23e189d547d15a83801a7ba5762cfba46523c80026a5d7030543e2c0db98def7d34ffc9dfe42f471214d46f761b9f433cd25629e423bf92cb01d5202ffa61e7a0ebd8deae02f27a3db30d8ae2ff4116198fe24ca12e1e8243ccbc0172e7949c2a6f40cf0f3f0bcaa71fb9f397a70c44ea2289853bd60ff8ae5227369fac701c8629cdf5e97e8c0acce98738740a360938d3b0ed0e583bd1c6b61669b155bbd3c4ad375c05a7b7d55d5c98be66797128f294242acfaada9fc8e9d8e07bd203dc89f7eb3e1e3e13c58716c09347c09fc8765de3b066aead6540bcc818010b19d37d13da4377f5fe2757d76215637b0e9bdb735d42b97ac586c41c3868b5d1226cb5f27e9c7f37e8d49df4f9a6303dd1ab3f8e24c28703430c77beb3d7041612d79844382c36f7c24aa183e40b3a646f6636be77f1241a4790d73898773a4e34363ee9e6d986ed86c4a67695e14a68f31875208dbdf909c551e75150ec8bb46016e17a576cbeedf95fb8f20bed662c87e011990c66dab9bdaf412ffdc0d9bfa4a376fc75ef800ffff35da20e4bc97a1c43eea60ff3bdf3677f890da0284b666968e769bb94dc63a79d902db591035d449d9599db8bd1c82a8417a52db7cc576db8b32dcbf6537f78dd1d406ecd0baec8d2c31cc73b2e5dfe34b3ead72853d387e4964622578ea79d90d77875e1ee92ed681543f2edff426ca83d072b3d74853c6bb354f3fb304f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243134dfbdde74521a6e0d8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"127e83bd4dea423df79468b9cfbe24893609795d3a712defbe1b6b32a04e273ebff1902425f4d2dc78be2d07af1176320f68b294eea6668d92b31525d501fec8b198a9409d7bc8456b64d5af87e334a03bdc02b5b42db43358554ac50b0c7ee306b0e0b4e7e6a040e314e85811a24f06db8cb36f87108835d7c15e3293d08f943dc720de04dfa6b0fd101939f0383eeedc21aae2a836a86ec029d6fb8b777dc38b371b88d314a2e528f5c93b37d28e531143c81c6fe92dc5a9837ace6b73d4b7a6991374e12133df267333739b2aed412311565f668a65383bfc0a3bed6202cc305e59fa7f327406cae3b53be57ab26a8cf2c5103d5e62e3293c2ae42e071155444300eb35dc575d8aea948730e49a4fb3f2239a6521e3d284367524b4cf9233785aded231643046a5592032debf93012eb81e6322211fef8a878f322c065f87ff26b62e2481f8d80f6580fae5de14dbc17e4bdc782b0fc39617d55098873a83f5d21a53d701e66ab4f4a94cdf13244788c7d158d71d6675cd427d6390f07c117987e46ce5b5b4203a5c7ed6c6397c01fecb40fd4184d704cb02b6d720eb54c660ae3fbfbaa6d5ad4475cfcf144d933e55cf85bdc128c6cab20128467cee7080e600bb960ab459c5c9a4bbece85fba5689b4db7d4484e46984ef468ff6d1630af6074896bf490590ed0934373da2db32a63a0d57a0023b0649d0ec8c51797abef288fbdc35dc2a29bea0712258cbb23071e6c0df34a1d0c9abfb00f53272e770ed4354e0b799532598dfdcfc798f1136ac53c43d1d93185b43c637f7e1cc5571f82d5c4d6c0e77c4382ffdab05e62d4b1eefed6e66bb55c7fbd8914b22ef7c9f97c4672e10a6394770ca4121a41e5ec9f557f9399a3451a3fb106450079e269ed804bbf9ec46f4038ff60eb8fe5538f7e54ec354527e095ba9b8b00b786e3737f335c222281de7f57482ce89f5ac443ed0ab3f109ef444610230b7945ac0c310f14032832ec4beba6d7eae42994b0dd0a5960315379416b93b46560993229c0687d9517c39954f6e4fcf059e971fec6eb386ce4e093ddb512106eaaa3eaee5f2aa0b47ac1d574e06b52c0fdbbaed9bf68e10cbb80998e3f6568fe688feefe1b69f0b7866fcc9276a054e2b907a58445e75ac6197a1e5ea145d0c241ed8c49b444d6121cbb34b6edda5b13d788bc5c5be8776f536dcedf969c44b07c217e6b3178dfd94562766fe1accbfda15c2a839c12f2118e4bbf20581e6ef3503c36f9d782087a5f2bd6f02117c95371ecca6f5ce5ef8b448ae93e22ebbaba20eb8f589d95dc6be819b9f5bc34928ff447021912d6960acc16865a0758b60234dec2ae38f81b301ced861350ab2bcbdf87f235fb67d184bc4223ca514fcd7415526ad4851c1718441cbdd68920c3491e18c686a0463b8d872bff97ed3f4b58a03bbd18588a6effed47f6553ad76c966af3dca17754594ddd29c7b0c3cbbfe322383dc226b328957a3c44a91f4d8a6ee38b96409101d4e400f90b6f9a62b1e3ff0a09f3cda9d65a02c0a099ad9166029ccf5c458af0d1395246905a4ab926358a23dc981c1cc84639e28e48a88823915812860fb4db342c520e4d1fd44dddbd0e4d5af623fe51b3cf81ac9f79e052533aaa468cc2886e6e4dc0b24add5b578dc2d103814db57fd0a165becf8d63628d13d497fbe907e2ad9ec1b44efe4c01183e4770c7f9e68ae5409c75269e4883bc6bedca454d07d7d6b380cab14f8fb670765b62c40e7dcd5ac1b6d9fa6bae26cd04a46f63b77b8edd28c2351455938464fec10f1224c4d575fd2752d7df433e99b9d21d727a9e9c38d8d25e5ff18d73cc01604701489e9ceeb64eabf9585d80270cd46007bc33dbfbdabcb6bb90cc97338e83b39c24b2b7365770cf4a8bd81d142eceb2847fcb9431ac1440b46032232a4d0837ea2dca57a860736ae2d9c18479c542c1aee8e28cb5884c651774539529bd9c930d8e6313cd45eaf7846488e17568b244d35eec1c1c1f5e82b3734051fad22fa92fc1db58b27963fec1ca9ff923a39778aeb90bd6fac28191d23ff2b7c3f1e3548e1f2e7c9e837f1b7c9869853aa2c553f60a28a314891dec0c29fa2c165bcf9aad261374efe3742e1c8a46152fe3b0b6f0055f78f3f630c51c1831664d3235431bee09a640819c66d66276ba5179fe8e42d3a3104ef97aadce12ae48a4279a4687d99f01b27c4956b85ed48ca0b4a243dce98a5c4d5e64284cd2b1c2de43ea906ad21193a0b4011df559c9768cb5f8ad5db4972bffbdfd4188146ecd95ea0445311bf116159bcf5885e1baa382a610a71c1083d6d4cce0a2447c69235cc25f2ab096442a6d7122456f0c933ab1ccbda77c3b6197dbddcd539987e51041fb3a1ec150bd8f84e821caed229a07f493b6a4506e7903f768d8d6f98f723f3183944bdf52b843ca4393ad586bac30f9b8438649dca25998ac65d56a13fabc86b8f64d7f905520a6618d3ff74c34554f4f767fa6d339cbc79a70e8770646db529cfb1b283f348f859ca66349e45506d5a5088eff622db0aece9da781c8df6c0a6ac85fc24035dee94e5a1ec758ea15a0688079213a40065221d55cc188d34fdc75c93d0883f88aed51daf4a9b3fe2f0729bf43ec5ba590067855814c043eec60fc2839e5b730d52514e0c69d21f69a682f2c32461ee1c9e4e82d63ca620f81f7da3151cedba158f0ac33634ef46bd3573940c485749729eb7db1409aec01f09d6cc9ee7714f95c787d28c06ed0b4cb1b209a99a584f1ced187343c7155ea0b4722e4484ffede6013b35a6556df7d300356322244e8ffdfee70f362f3cdebeb2aed1bb066c337ace478158f12344f1210388c47a576f5f1cf1322874944828c8d7d6329d099387e447ab0e3c8b86bfdbcd752bbb0a9996aa63cadcfba05aa6028dd49ffbd2fee1cd8e105ba828715a51f34cdd3ea3ea3904097bf6a71b4a35cfd158bd9879bfc2dc390d6e5d494713b140441eb27dd4f34bc34ee87efcc511777a465cef2461227de3f96d24452316c1490f37898dcfe204791af7c1854dff85b2cf7a147eef4437d1b74a1e32b7ffd97755b8b8bb77ff8e221361df97e56677f0950aec8aa57fad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280bf411e823ca0e4a693dd8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"13e6a6cba48b9f747b6d47bdf7be588c5f82fcb13612f16838d88d7be681ad14ebc78848c9e053887ed436a6470bb99f7f34357811e280409fef6c81ecf07c47772bd52d7ef3333abe7844455951d380c0aa440a11a7fc7208219c1022032fb6aa38d6dc06a5a93c03af45fc74ad049336ae77e28492605b6d429d44dfe4099cd935d16bfd7b1aeb0d4bd06ae0e581497a600805c6c56e214e01b18e023ece14cc72e1c497311d633faaa52c37160871d008c282ebbbe397ac6ceed1269cb19299317c59521b0676e9afd6c30b893393f38e3eb078937f0e4a5d27f8e2d3770d30e4340d0ffebf72c34275a71ea2d9c4f7a9cc88df9c290625a62e47db8f8b64daaddcbeaeefcc3d6c859023b80fb20bfc145dbfb7a1fd179e5e33a54b8a94f686f693523a9897e4fb92cbaf3f84a97f4386f105d78ee6101114557c5033695837c9e9e8e72ebd79f52606b83ebf961134c8fa1c743c23ca98c4daa349df008fe3e01b6c49b408dd7e666108a75082e105d9b6a63f1719d2e209ff4ce6e0599b00c8a6a10f2a49e42f6db064e37d08a240592aa3caa535e71f5267fed1b8067281de2dcffdb5c99f16946ac23369d99339e95332843a4beac243e12f5fa7f7d77d8ef8bf9937178da72bfd616da632505f4ab6bfa2d4af7ccf9e54e5e2dd83992c2f68c470a5248d219db5dc1d27a3adacb2926d03e684163c7f36d5aca0e6c264e7861d870c2dcc0c9c17d344471837430e337e3267ebd4117703d672e9a7172317387cac46b241dea72dd19a2ba88fd6f296074e9eacbdb3fdb7505ca194cf02416a1824c51ccd5b71aae79513880fa35037cce0015b188752130ba1bb4d92dab1394cb79850e837b30c41fbdd9ec6d8bc4c4adf136b4800eda945eac0d43b60bfb29c802779b74d3234809b720d3bdb2acf4f5512b1bb3a2a7b6d858440dee143b8f4ba71972ba2816e6e2480318ec9e7b973be3c4c0e5b9a30e083c58a50697dfb3cbde13074f571d01656cf11b467624f7b1740f8bb114dd44d16ef9d7831af3e9746a1e7706eab43c56614028c44fb0299f558a4c3a5434f8268b1fba1722153b56f5de6d7dfcb456627e6afb1f63720af52d5989e7a88949b9f169831a5e6017931cc201a26bb00babe22c8b3542e9b99f4c2b6d48cf0426a097c4a330c989aa386a6177b17e53c57d0a89694ccc58e2ca6d54cd35eabb478e5f09d077ee482bb7e01eb28d7bdcc824590fe09f255936c404bec936307771078f06e85ebb5152acf43f221daa12632b74624ee7bb49f276c807755c7d735bdd3235a5060548edca8b30de853ac790562c18729a42ce7cadf6349beb2c544a2e65963d7ff135d37f7ae7c21f3952762a458c5005a4be652a51fb9b0a70a61855260785f7cfd6d66ea17aed9d0eb10f296e1f6cd637b3d0a27cce2d68b2d7d194d28501d7ee1aeb0bbf68d028f1b4fc9d15e1a51916294ff658960678128e1922233ba8f53edd120184ad0a84b8c594c5f832ae8ec9dd9f0e543a674791e6bcbd9b7587d70a0343f00fc0d68cc36ec951c965f7f37ea126148de1becce4b301dd8e1e25b9ce2801786eddd06431ebc0f031e5bb2805592c1d67ccab71ab2a7a804d98c8a84f4bf77742256ea78fa7827fabd6c90b58b9637267a16c67f736cb31ed657c60bf2a5984236ccc04005a3da98fb19e3299a7cd5c17fb64fcabd25f6ccc6ebc2fe51c2ce102c368df9f24601070bccb5d7c72a33c4459635e8f94ea9d9880951ba8315e4a9ab6357388b8ea521e0966863c9867ff343c1d6700c06f7ba7a2b60b47f19d18dd045473cc5fa30a8e2c1b24df20b780a5e7c9bf8fa93ff2bc92dc35c36fa52810b96223c1c766bff08f5072c519e8f47d4125e98347ec5b32bc6dca864949b5f6da8df69d469e9b54aca9c033b0461a7bcdc7e22513db7f62f0743c813dfc0b5b314f65f693cc43faab4a2ffe3b3dcf110025dc28d93aabb827fbe9271d4b4018cafc61c375654db368d06c74078ffe4b91604961f8b616165af6e4a8db517cee0fb6c1d0b9e158570cd82a88dd1774a9985e8839f4cd310e9fa7205662847cd7cb0990794ce6ef4bded0440a82562716bb0f0d3a35b3e33399f2bd790d22917cf7de5d10e829bb648c1bdcd8bd08d484221e8b70ef450f9bb15c711e32800539e95ed9418e2ecbea61e9296dc40dd373c21a5d589f706eed240416dc9c0f3a9775f004f53b741ce4208265ad09618ebe9cbbf1ff62b09d5bfe546d86a4e51159e5fc2a3f66e8c765ee84c8d8a175889e438c0e7a8f18ceaa2eb53d65136eb83f5cd6bd0ba68bbeacaeb5f624f4e477b9aa5c50e95dbf2b7a2fea25928228afce1df649720781d2ce81e504bda3a15008f139e96232135116488058175a9395ca594004b9f6417c7bf0b051e9550aa7b51e97ee078330cba85fffcf68d66b8caa8e591e8836d16afecad56205fc3c97eb9f99843507d1201ce2f33863006d74de14e428556f507cc7630d30791e520c8e80a8f0d56ca9222b2ea5cadcb3f94216b4e254475cccfa1d01e7a44c87ed2760798d7b1de7c54246bd9d708b4f2f7be13115f50a4f90ff917149633055edc3084b38cf4eb3418ee8bdc6e2106915d056904cba9ac144d1086574af015515a5e53696683349633e9090ba0fb2c7bfd83cd4e3cf63d4adeae46ba870c3b414d4730f7def55aa56769046686f20e977ba385750a0a7fd2be4d4915eb6458848deb0b6c54f59d95ba69bdc4dbfc9099ad723add5a79753f9ae1fe9f7e4c884fcd06c56bba9dfd6a6eaf7df8fa9b7214dc6d5108c7c5301b38557520a86c514906d839525624ac569543d4a19b0ff6012d5ed5be35139e3b3f6324396bf81d1ff382bc352d91727a843e3730ea0c4b2da41e0b71cf5e6c074fc00103a1521953fa52a3bfe7db0c3f5287a4a89a76c93b429687dc54eb8bb1be1799caf2a6a837fea49011c052ad502112060810f827d9fec19f71a9514049302ee79551c2de36850e5739eba676bc906d0a4939e451ce274906cb848e68558566180f3e4237f3a3332d8080e4f7e570b5ff72481d0cc87792ec9feedf1f241427b60015f4cbdf895d9577c3e6be45c5a72f25d342ad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280bf411e823ca0e4a693dd8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1454676496210dda18357eff3f7814ffaade88533c14bfad4b7b6c0fdc1a8f24bf30dd840a8e52f7da39d007f92b9c15514ac428de42cd810f95ac656c8454c62c9200884b5070147c24a1391706ac006916aeaaf6e1d53d8957f734907e876e109b8055a48bf68fd544d18fdf055f2c4fd5900ef4364b525dad68c3ea2b920010478584fd75bcdb492301e66cb5c908ae7d8b4b2c1f9b21ccf19684392623b1c8f72d7f16ac53b48fe50f109053977b02d7027f6591aa0f33ffd04594843ce08b71bd911cca255af7cfdbea2ebce4d3df1409f501ae5d45015e310ae14e7a08d1bc2121c2b1995781492170fac1d10dbb8a0a9c73ca0b89b850da5e16defe8ab18ba8964191f2baa507a8ef09fd2ab4ecaa13bea6afaf0f8e0ec646fb037c230bba547dc2b4196a2d4608a55dfb6c3c611bca9c8cea05218f7f4abc1faa24ace2c567a935533a6027f7391d4301e6852349a690a3d9bacfbccfdb39feaaa34bec6116e8248241536a3b7b02002f407f8c2dcf5c3da99d8d4cd228f963f9714952ff87796614cd049b05b320e6e66fe208d4a0a507e8ddb406e9d1b505f1295c1c00a8b0173ecfd079c4fe66ee3d9f501b19afa594682cbb19a516360a7de00847892a7c01ba000dd2b27bf0488e73d2d6540bc8a93b6ab98f372bf916e2cacc27c487bfa9340ddf706f870723d973705c027568850b5dcfa99e509dd97bd4385a138bc48981fb24ba3e758a66b19e1dc79044e77d3040d898ce347b45bd8c818970ba69b238101da0571d8e5ec21095287af8837d71b6d58264ae89911f7ff69d0c2f3c188957951ac33a25a65065f5759c28e97a0490507a849861276cb181a01eb79a4d58d44855ed51bdb4aad2d3b3f1277cc918af2b922e5761da214c317f82a18f90b6bb69a3d0c698bfd6d6bae147415168079af05ad566101bae93b9992c62c415009ecea68a7eca09ec1b891b01706729456f194d45c610bcc15e79388103a81706f3c83c853c1252624df7712699b6e00e0b6960d355e9364d1b5246a2fc35823eb5c4752d795fda761c9c41259fd44a1d6f558ac10d01f799975f897754b5dedde0dd8de461c465ad97fc47b970833e880b382fe2e556155890824a019e7fd6c8f0f37b592d7f477377b9af5b7d132a38e0e7f4ddad5aabab130f8c47f443bd3dbad545d157b10096f4dab0b790bce1eb227884ffaf318ba0c83028b2523cba86bbd2fe35e048d03e213c4d46d7ac4f3287df3dafa13b2d8c8aff82da5185e5e3f208b1bf6a74cf20dd872cbafdce3c85f5f8581f30306b8c65aaed69c714b89eea9b2cb3004d277d8129cad0e8d79575c23705850742cb608ce37b5862e7a406e89ee18fabc5ac3d491355a52e2e45037fa6b471a377d217b3e86f5cf9b4d0162cdd1fc458ec05671881c34fb833a37591e1d12f9b4897aabf7ff949f247653032a08d50e2aac74baf3914aa8bef8670a92074c09cd7671e83db2a3a3a10c7afe2278f2247ed71709b5f3acdb0742b5fb34bdc5fc5452fa330ad7f1a1233db65baf8dfbfdec5ff9f6229cbe2f66971e5ae99688dc0f90ddda8a4ab319743377f005900dfe78414c11ad5cfa841d86c62ef7bd9029ab7655ae83e1f46822435eb777f5087238575a2cf5528bd4fe8283d1bbc0c72ef6a68e09ab67e5f59dd4c2bd3677dadc97435955a5b3e3329fc66c9ef5f4922aecd297a69305db66d1552f39709c8448e5c599025af7f1668cb00c42e3438e725432b644972c52a3149b16289319777c08facca8ca8f6e79e9aa0ba4bf513b7ba3aa401fab006c5e1b12b43cb13d383f33ea760aae19c5bd28f2decb6f624f4962102a416be1be6bfef71d705620aac9b1852a000102ab82e3b7e27b293f1c9f623d45a875c4b05a186b36dd67684ae87b485eb840589e95a98416bad952afb22692e0237a20501aacb9b9215076e1349c012cf62272d41c203caeca1ef91a0c86070c577db32c638f4cd32a2bc4162f78cfc1be99bb40a7f1223384c3352b3876aa396daa228f9c06f5c1873c46bdd8963e4f191b6561e507058af2411f62f879ea3dd502097aa2582c3954094551a0ffcf44f9ef46a24aa3b11f802633691d4817f07ff20318bf1c57f61ce24271eab4b70b66b10453d24632bf5846aff38bdb5290b4f3600ccdd1a3adeab8f5ed5455b0236c3152990cd536a3fa19468728553a51daf65e89fe403117a8379aefbbb1a06a732a798e0164e1ce93e7b0b5349b32a79f350d4b80554f8e4b4c210547a6dd48c1a4942d15991500154f2c5d11193925a5eba4f1c72b1ff051c619fde1a77300ac523f0f7698e77ca65b2cc13547a2dad2a2652bb15aa0285d30b4fe3524b13574bcf7f37589e5020716a4df71eaa16cecbee355daa29eeb34b4803bfd25727e1db1f2ac35ea4d2d2e82c41c1ece63052b60c3bfaec2485e9e902ade31e456c81318637b64208f398dcb1a83005237d6356a877f0c096db1985bd29ef4cbdbd945a5c4833222dc5239949cb807f352284a2d187d675ccd4b71d13fdd7d3d250561f091a0fec36d8fdacd2665540baa66adbff3abc78e9195cee255808aa14786148755bf7f25537919b06a2143801ccb2bc547019f4d48cc4745b62662683c1275ea9b216049bc7a76f86cdd3810b9071f2112090281020fd20ae9d4b1822861883e796e5c6e9010658a130e3a9f0fbe7e5487bfb9c7aa742d882baa843ec87052a1e798901c0595b4bd27fb0ae50ab56b7b3bbe96d3598dbb736bfb3eaf900ba2969009268e04f81b31b3f29e97ec70471975f8227fae58e7d6321bd0048a7b438aa06b7390141597033061ea2872eb3146d1d3f84d60a68ea49ba3ad520925169bc4bae951511af51cdc274fe84680152dc3762cd8e0d7a7a5b808e7189cd063e265df583e8365e78d433ea0452412b0e0ccbcac0f4d81f179b86a9759d9fd972a7b6a661a1b1d3acefab07fa337c9c21c6a919ab95580f6f2659bd74e028ac52e9ed07c4287a8b38861e33c8ec934407fe4aba59537323b15e279d48da39c5ce9c7ee641e1a53bed794ef62b5dc24e9d5c871671fbc746439b17326bc00f91c44719a102ef8728fdd497224505c02249bef26f24bfa8a4478311b58152b2cc8099be1c1b6ed72861a5cdda7d7474144c88eec207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"150b194ad8be8ef61afcbab20228f63cf3daac7181e52ff00def82994cac8cadbc0afd2003db8617889a0023296ec9d028cf0dab03f5a6ac2c5626c62d536decacf9ad52f5b4363b3ae3a3a3451658248edbe61993cc7fd9b6e620f40573c307fdd45100b4454e53f2c3ec133a04aa9fd652313b434351d996ae76f85016461773ad3b75c23af84670708e414e6e726972352bff2246f6c0f57b2cedc376a8a8cb1df74d83e7aafc10545a521f31f6e5945ae14ef4f0e973e9fbdaafc57d41eb0ae8b277c10f9029c90be5c50c6b400fc0decae22b5d3eeb3fda02765058b4dafd6d861a54526e51efd1b5cea8d7994fe7293b86dc601d6140b9d3f7f083984c7bddea56acd6b0a381a59db4fb97cb3074a7dce6cc8087c832b5b1c71909bfed4efd41bcef99defd1e6e7b86efdf813c23322cb7083eabb538ee0c105fbb42a33484b2232b4728af68665322e62a515a0c16e12b9bab4443f7aab5d5555814e1cb3791dc3ec2eb1439fe4a7db79d119db0557e3900afedf808bb636e1ddf5fa15e5cd406410771b5de00768437bc29f3242437d94141b28cd5eff641b21be2254074651e4e0e0888d0cf81e6f5b9c3f56b1a932eb852aad78120866511318298437fbd3c58e7af626dbd32e2033e5eaa5fc1686097e515bd69715d05d9eeff926515a754d3d0684696ea28f8eb88bc8271b839778588d16b56603d1db5534f20cfe0616f2db4e35e0624bb03c99329758a28ff722d3c676025e1780ed3a3c1124a49090b0930dd621e43eda3cdd731a46b3a9724ad32fa32c2901ed0b3925acacc9cdd9d3d3a196a60a83d83acfe1a9cbf4ca1bb29ec264b94b4bc510736d9916d8dd8412188710e0ea928c566b95b23eb556bfdce5befd3edde10244e98f07e6e3914f366aa6c5c326504baf1c38c97d20b7f447359a213b8e34c0f34556b19380a5477b768200ddaa2ef6ac4b47b9aedb389309aecd9386e9ff169eee2a6fb5436bf4d11742752e4179bfe90b58b075212e82b073ae89b142069e348af3db79510eb975aa478e4a0b948957b34642e3c521d6f5281d7c8b969f265edc3febff1eae7eb1a6ae68cc2c0e012343eb5b13c466f6df5152dbec1f3ff7348c6f35c9b9dcd22cfe9f6d7ed39653b16c816b57a99057467eab40ca1fbc48e27d2abb23306068b1a468322ec54a7bf26d4d0f716104bd890a7e40bda60ea55ec2c803561379fb4d531e510b9d2caf0455e86d1dc1015c87da07288466607ce1981641c9dc8ef56545e394095e1ae7b94fa672017c5360c7d0ea62f09e96b301f7846d9c949ace8bb9d64bc670531f081d90c8b06b1024c41d98b7bf2d45c5f9eab78f473dca5152182391bae24fec6154e0b52512101065e9e5d627676c1b63f3426a2881b184125b4ed06e85bc69cf7203985f083e7f7c30ccfbabf4655ebc350c94325ec9e4bb84f8c8b3b7b5b8696afa1f520fbc200839da20cbccdc72273c3994bd34721684d09743cfe4c751d9f141679c1f504b188b38b4659018d4d2d3feed1ceaf8725faee719528c697156c59d806ff2a05b74f95c654f301c5ade813f72c9b6a951e2b3d694619aa0825005395282e9bd7d8fcce2c57846d8ce337b7f8d77de1130682cb93372d18120f27a062e3636e759487edb2ac8a5c9e851f58545c218e97e1e5b623373054a70d6a9ee4b98980380130b572e82b0b7041c71782ac50a9f7287c9da9f04ed05317b10170f1572f9d6614df8659704af759b899ea2a5918b885bce49cd218f311631079315566fdb4d6e425910dc6ecfc604312bce7b96121f4324dfc629b08bfe5c109696e7dfb47ff14b764eb0556ad4ce454bb3472322efa28c5867951bcb559120ede8e139a1accc52eda02c78b32e40608b095b7a629cdd2e8f5a94c06e8cbded10981600812f857d8906861a728ed5d7574823be6fd270d28cb11a1b1729387ebf9e933130a7bf73d4322a11a43e01c47bf06d76f2422d8c9079d3e93ebae2f5857d27d2fe33bd351cee66d3ff5906a10165f79f4b383f67b9815589a9707eadc16f09585e488dc1f54c93e87158c5841d32bfc0b411880d3cfc09157b69a3af10bae29040d4db2c5925aa6f4a3cb36c89f026287ca05fcd645c3267d365cb5ebd613311e817d4417248dfe49fd3fafcba7fdeeca699c3c4396c7fd7a1ba73433ec745c9ee1c7f6479be808716eb9ba1e2724a247967fea7dcecf0e14356c2aa863fd5d42d5234679c07893f4c7c7f10721378b2a6acb034dbe4d7082b700bbab28466fb960fdbdeeff90cdd3ca53b171380b16dfd8c7cc560aa8d6b6db5659a73f34f0cfec292f100a3bca4783a168e5cd287033bc6b19b361e0c5f6c90e7f0a21cdc1a0baedc4cfc5abd00eb85c6123f1de1cbe334e1455090ab719545d744c90d0aacc37b15c03fecfce830bf47b058ac3e191b7ffb2306461060daf7ee368775acc6ec5281bc79a21492e15885b238dedd0ce33f73a00f08400330b6df02f9de2519e52c32b08dcefeba3f738be726c7932be0de263fbd0cf26503400b6de7e7842875df7dadd48af7a709797a8268fe64cb0d8ba10ab1d0f7ca396ac707dcb512f753173c2098a5f0338dcdcd72a11e6576fecb4d7921461af44500bc2f64a232ccf3888a17171c6b8b53c28a0c606bf208a9cf2b7126ba4b65410e96dd7e3dedd4bc3bdc8b334b0e2656047eef365d23d538ae179d2657d1da099eddf792a528d7666278c12b512ed604b0f4c928d2ecdd3aa30296919e0583a64dfec68ab2ec22b2ef5e25b0cc1318e9e5c8c75c3c442f642a6bc9b3292e0ae1368d0b4d38e9a065ce2a2774eda18f702fc7e756383e45f595b50d57e44d9dbff60861e261e9ee42c1543a1a2a5a0d3cacb0731a2f56b321faa8ea7811f90878acf15eca61751c2ea90957902f4c7838542466e59918d3591dac0a8547347a3e3a3c5e0ceaece465a8fcf404af49e736d12b01b47573761d7bcb509aadf1e966025f7bd397c153bc54dbc6d798281d2d37ec52f63cb04cbc9941de9fdb2c10c2376ae1b885b395d73ea478891e281d832372d81e8ac44b87a6aa87e6a2a2b3e44431ec1daa25832067d1c38b9a9379e2cc9e2452faae56f24bfa8a4478311b58152b2cc8099be1c1b6ed72861a5cdda7d7474144c88eec207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1685e55780241c3bfeae16e9aa47ca1781a8a32313637cf47a90a8020128c40c18235c7824578da3ed4ec6eff8a0ee0405e65555151d5e247b7d9b8fc2905e20b4aa91eb87381884a972e21020d2ae661b52f31c54a2fc5d1d09aa8d6f3220907c18d08f3b14042e8d745cb7033b1c6a44d75c91dbf823109d831685d77dcff6aec9fab81f8b7d8a07fabfd26f1fea447194d6ea01ab1a6640be5cf00eded97b52c7635aff9a71275f429ec63a292b5a849a3db4e991bc44601b45de6e2faeb6f8d436d0943f99e79827193e66855d5dd6e34a9984a210fdd3a2c7e7fe61eb7404e4159c496d720c50f2906a427b4a9edf4598c294d274e3020950935a05d17c6cdd9dee2d00f969d071b269117304f2edee613f7b1826647a427c53ea7b9f1956153214add3d243adc6dee5dacdcd87632265ecba21488af414f026c52cd480d7bc094d8e01b82c814539db6e7cd7be6e74d0b53e156195ce2b7fbcbca345ff7eff58131d931ee11ac6a46c7d2070a4ed72d3a4d25323b34b793bbe13a6f841639e3829f682a82be037cfef7541a1e211ed637be1e2728f8bb3aae333aff2edb22fa1a832f8075aa3a93e6f282d7e60c987bf0fae9cf5620c3480a5f8f9cf181f455ca40866dd5632dfca464e9a42040de9ac7baff04a9b530ba11d4378b5c79226bc4fec29002a9fd62f2e5d1b8ca6a635296e2b4dfe1daee421157d764f9a9cb0b9e0d0ae66479eb3ebe17b82b78f104e308db92df5332d60f74cac97dbdfe6b6d0b1da4a79e77bde280cabfd11bbfc2297a81bb964b207e82252108738a6013c593d58e231f9830c75f4acd2c44e89af2afe2c2cae2f8d98ac05caacf6c815a7ee7bee050d87d7acee8a38d8c04d5bf42b7c69f72a2c0097087e03e62767acb8898d14f04455f5eb4d90eb0f49981d7e386e956288de2c2a2acd67b4ce765c491e730750dd81c075e74af5f03f3a9a25f4c1258ec830075dfb5fbfa9db24da5ad5028b01b485fc717997eb21692b04bef44305708d95f8394db55e82f631fc0e049915a10bbff2b0725adc407ddf5efde0c9a30d3b101dfdc31306862c45829625383bc60fcef4f2f6415a354d34ac9b3914c1a571f9edb034f87e38de2046210950633239c9700b8c8a3bc260ac013d24a42743302a612434f76b22fbb9f2a6ab9ff0d66f8ce88a176688941f966d79601720e66beab479698aa9caa415cd27b7ab48c5407e20d460c0912630de3d2ca4e9ba2d331a67e3001d8acf56e7f7d4de83df9537a86d3fd41737408934b36898b860543265e21e6aef6992ac39c20def0d2c58339c79ffc8a021604098876327635b53db331cf6e4615251a5bea6df9fec81bf9380e2706f17368de1d931be2f60c002b259455f10065e701bbf4b39303b5c361df424a95b5941ae2b648ea58457d61ab4b7ae7bce45d02988900bcdb4d362a9652154cf29b90e4525c8e84f9c2ace19928b94554a5400405c405b51d1f4b081fed1f0457cc3ac0b30cbd20128ed31d10a2fa873a5304f6cc51bc724b3e27c177d7638b236a10cc918caeec08480ebcb120d911b02d4597383f5433bb6ff487c64d22fd4f110acd4f786afbdbd1ca174ed70b74aa6a60ef1ebd372e78364286f358bb7e8bb711ef30bf6d5d15c54e8ad357e63572bf5c731f7b04c028656676af572578926e51d450146cf9f3ded5cf658ae5fe54012953c5a107f49be08b18595c8f50524e422cb506d9cf9ef89dca3a074f1e5bf693695c70ae32996a50823ec1e54a117025e514cd6e13c4fb89e994b9572b64e4424506a442bc95f1424da7f0288b2ddce32a7b9cd2cf5358b04dba85baab9139f5d3461ed62b335ce202396450ac163dce5d4dc5285d3a458bd615109ba569ebd03f9c3b77aa5ea33113d8802c994db68cc201aade5e713cef415eeb44409d534e90a7f7ae6602d7d5411640d6bb86b6002d3c5aef6911e5f1bc9a268be4790f19fcb44c36a5eb3df8a2660416381e6c422579d3b17bd7e6b0dde78b9e50c8dd97c760a646e21decd60b84cef479d5eaef1a95dd2c82051a77b7758b63e0549d0f9f05b21acfa684e76d94f2132d7f3d14a0feb639b6756f3f44e33dee5c08413f1cf7a655ad28cf77113578d846eb3071bda1a46d5a5da44503e972d4c4a30107d586bc42f197080bc98dc286a6baf0b90169bea1c27fca5cba27f48ea2c88c0c5010bd37575f1927e7e41423c849f6beb420b408421b231961afbe816d4cf184613c8607a343762ba7cb8918b81a437b9ba5607da28ca415cb0946416832cf2773c60397c629780ec97d12dd387e33225996479ba51eb0420b37a91dbc01cc02ac39443a2f324bee1e242e40e5dbe21e630f5acee6f637631f07add8f5e080879067e862f23a07eef9fc995295657e31557e8f83b1542ee598ac37c7db0b4c4ff9072f8cca1a250f1a5bb59445e2d7539b598fe73517c0ed0814a19db909f4cbb31f14420c7b830784aa5411af0c0e44d577f40f1db3ca0b107385d7da7f1f59d5adb6afc6c284e7e2a64dde3ccb4332f89417e91fe2c8d734147310e697d3432047c9028194a3cbc00c4e9f2fa230ee11e74bbbfb26854bb26bb8a82e362ad4f6184ff77657eae62401eec537d0f4c01a66b23f0a4d7d96d46ca904cf4a4d4df15e5160f7905fc367f72ced01f1bb918b392376b0682b9d6c4feedeeb675cb3328abff32659632d2258b7a984ab8bc870a39b6da66af28bed791e285bcf7f964857487d20781e7e5663b71972a5e79faea12f557bcbde684f10bbe49265cbb38a7ca60c2f62a2f0311cb9625f873e43e06f1019d0eabbc3821296b7f322457b1c05e2283b76b0d4cd21f9c9c5ef963c3b16a37a3149310d5186bfbd951343a47b0bcc6163ae38751f7ff5a4c97a3d372c2d953753821c2a048e203bac4a108db3efcd6bd176654b0e3b124871be273d03acb80f076f80868becfbebfa3fe1dca553f11995500ae6b6ca68ee57bc1d9b8490a5f1bbf602b1a2b6ef0e6cc2d7eef7da6039ca9ae8c5d102f840630ba4309335c3a5194bbfe4592a2d03d4cff422cd59cc98f59e839c0e119fe7776ec0341ba89817f708105d398de01b41bfa2c185c2001de672d8f9c83410df28d79dd6a4244e5e902acd5d7818a01986c3689be07c82d33c207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"17e9855cb60d93bb2d02481af386e18eda208da07a6d54dbc916d1e3adef6a0774b5147a2c4c33b244babd82ffbd7b427e5dba03b2f03d37cbc67a77fb098e29afb7093ee22c3524c2d9102e86c25cb10969f4242d8e2892749f0f2d1187a689db7e21462253fc4295dec37dbb5a5ef7990f6f133e76a8f8310dc53a2a19da091765dcf87b8f66fd203309360ab016cf5ea6cf73565502ab2353311a9d6f27c1b46b6d232aee48756c44fc161e78a33b253f10c9d5b7d93ba81b6658f4b08e6823c4639273bfa5052047bc34cea1c83f4f5843d05ff0f60a7293117d2efba1f4f5d1c1f0ed21c57b39024f8b53d31c58bd8facd0e781660000e1acd360966a1f021c9b4399a4e81f5ddb877f76949275be657444dded8a2108c65783631f9ea7699de7a8a45c2eb8f0a578afe09eaca68c14dd287fd82eed58805e7e5ad848442055c4ac57f775790b7ff28dbfe44006fb9f76a13f928f02c0d72062baa4b0a85c1eef7c3e794343ff8a3733226004bea8614444ca071b097229d2f394ea7815b80b1c21553096c6e6ac0914ec22be15021ccc730959c8a350cca9e0ad2076017102f1c06cb5651ec5c18d5ee6bc628728004dbb8479529bd317382fd2e60b98328c945e3a500b7c6f979cc64089d839a163be6e03b235670d66ad4f77adcf6f5f542280a21b357fecbe4f63e9875893fc9e3f6694a73d1e3a2a14c8ce8e3259fffa8593aab69f8d72c10ca43f7406634f7e2f14ad4be425aedba884b8347cde4d16421e65392d0c634556a31d0c374757141a759287ccbeecd1a7300e9933d98a8cce53d28a511df7d1b9436f7f1698e44e46a36d41500ea04428c634cfc45eb5c8320b6f465bb309ce57cb2202e5fa3e7e96985e1e8e92091a9c80862660e4c992f4d2ec3525c99cb167c2de4aa94ccab3903f394c944f34356e551c5f99bda18687536859fce93d2b69345b43f550c4f41192c81720e8173af23913e1807657e8bdef97847c4f1376663f41c2caac249c10bf58fe9f64018b0aff45c36f1e9a88fce2f7dc53721b4e009cfebd60cc75f438f32785a032059f23804c3990a5ecf48aad9eba74fbeea0c8764af325ef39d6c2d6c5aa2b16d8b3d1b5a34a379d14e7c6d4d292f6eb8db6daf8e3a54e2b94b2b74596f0c9cd605be31d2c2f48ae41e4b63a149a46d1f6246c99f80c8663a52e07547c9915433139ea009d5c04c976f039fd77c006cca0b3d52f4cc5a5fa0d135ec5d503c474ccbd248871cbf32c83dbba1f28a2f6ffd97e4409cc471094b2d17e0ba0bc11e3d023c055695a881dcc35be0e9297688cdd232a56377757992582c94b0037afee28cb74022f92b197fba4a39864b767649ac2a59c1c3ceb1e7397cc977d7e4324c497814dc2361bf5448f4fa97ddd345bd162e6f19036140e158846d32d4d764a3248d59b11a8fcf833ab9eb73bf41b0f14d2f48189951d343192e5f7975ec2283270fa63c47e2bb2e20599d7b2fbca5e6c8145e9570cdc3fec9f1745124c34c1ab679c048e4a5d690b14ba4d5df6329ecd9d68351fb80fb6bec68725012818c7dbcdb81da42a6df3f2370d23c781313cb6f3c13d6e2044211c6ca9aab77ae19b0c3f3a045b325c0d55e374992f19b508cc25d84341accb92c5eb07a49809483219b16f6020de242bd0e11ee6062d2e7873d56035d65f8a745aa94f70a76f779c379b4d3aa07a70f00cb89d15634b88fb7bdd4809f55f24277cd33ccac1d41b2b850700b71a0bb460e73e84d42e51f513d94e9320050b5e6ff308ab7ccbbc6758c8d4282c17963b67326c513a753de6e14b2778dee8538fdd6806a7abefcc7d85473444b46fa263201c27b84ae995eeea5ec0bc3d2e259a770cdd0f53fb1f5e5118f5de94b04fe96d3b22fb5debd36e5a398a0a630203be681f6f3ffaea4b86ea54b4c34578d4e3156863387936a9d7dfd5fdee46dee058fb9ae38e3d9e3c855846ed5a1d94e46e719c9ff53b21619c16ded399df4e08413d3c5cfa9973aea539b54878f9f23dfdd9e86c531d9e5355256730649d20464ed112b9bd553c2b89fb81d12e622edb5a09097ca7402f19a713202ec57a28bd56cead573b1033c415f5fdbcec3bbcf09e59fb4fcbdab6143dd20dbea544aaa92c1420fb90c106751fc049def24fded46d55c8262d8cd70737fd50e0fe0e3605015d53545b17164d6854eb9c402517c9b1eedf213f898c9f0a8bc644bea4e6f05b210cc7f0668968ece6e831e5d7c01fd5ed88e127c146538c42249ea0266960eb011b4d444bf114b654ceeb2fb5bc38a2c033741f0b2779c9e56d6e2715cc7fa639139dc065cf2e9bcec1eecf01b5a9650a6a6833f3c23503c6518d151de857400c69382b2c3d4b2fed81ebf0d72a71ac7e14105bd5aef4f612c1a23be4471e7623bcebeecb3291426582e59b3dc027ce49ec976e1bda4e2f33528ea2a1a6b960d14478262b15e736af16d92b2f2b4cb4e8c9d2865f33f29fddf4d5f6a4b91ac1022cf502c062523146f9fcb9c17410b45a59e7dbeb911cd22c742504ce7fdd3cfa6e6c7b243b9fa9117ab459ce5b1f829954e9a25dfbdf9daf3e6d57e0216e13b3ec68c87e31ed1a4316567e6a8c445bb8df2766b14a428f3206c2194eeabe9d4ef53305901f4cd344da483876e21fad770df3e86736e264837286a958b15c78caa7812152a59e5ec6512a51aed06d3678d90a040e738710410aeee82d54b8681ac4ec6d0431fe3ae4221a634cff6c0c32a50a1e6e80b2d0fc95573e84ffa136a36f1e362254200ccc23471cf9ac5748edb87b3269e15471d03da47eaef41051a7fe3c3b13daa81c05e12dfa53f699b2b5c07c6654d7f24768fd86e5ac62356df93b621cc0981c4f5d3125c704391be0077934134f8288afcbdec8ee2c17bf3d3fd231262ffec37c4bf3e95f85b4cc924bba067a6470cf7f13d6c5605cfd4fc92136af2c464fc5bdc81209d732d95ac4dc0a43391ff885b7b2837433b61cf819c11de8d1c09f28effe0c2352dd0d1da8fe2df05c54c0150917d6a86d2e3779038133f233ae3a4f3cd8504751c1f4e5b47a27b3a80371aac2386785819e5d9d46511d2c94cc54ccc05d3ec86d98c8ce8139fe672d8f9c83410df28d79dd6a4244e5e902acd5d7818a01986c3689be07c82d33c207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a89280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed248b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d65270ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2fd80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7abf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b669657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a93380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634da22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f24170d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f61486cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb039641113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab343b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd612df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6cb3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f58410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccadaa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af26af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df831351b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d779003792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b456971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f857c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf54480184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedbd09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95dfdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bcc7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639ebe8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f5246de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c2245512af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea7262c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1801c8a57f1d1ded9d0b09250625293a2b0077a3b657e0d05a2f2334bc7f5c1bf2c5bad820e92b262ecc22563d59767592e4e57ae62a939f8bdb1e6470cfffe7020b927c6678deccce203186f411321861288fb7c6c511b245669a379c97a57ea9a481b7bc8086f80a4359ca427af425245afee5845530fa792ef34fb2323f098710acb0ed85a25ff101b5bd6ab3d05f28b979622d91ede3c616b79a67166e58037793369a03c20b1f0519567805089d14ae0379c91020bb68652bd798ecf63f72865c517629abc0764b5da3a4b97091804a355fa23f3313b84e0a9b46f1976e3d4a196e7b9db6ea0f09c16f483e17141942702d7d54f08e5624a3a0cb7801321f0de200f75ddf848554dadfc6f4145d943860762097a52a5e53c3c5b07b32a21aa9363e2762c16c590284f83136e7d97a5787075bf4b427d8d02b4bb2b2ec08da17ca4e8ef862deb2fd3dd74d4f6c94bbc94340f8a100e0e8015dc2965e973a972c1bad8de8bbf8d3e3359afa2924ec71d00d84de2740336e73a50aa200e1fca4ee16af6d5ccd9c1812ea62874a93e33667cd8bbe9e4d1ccc70adfe02a4bae1d8f685666d8caba1a1c0f9bfd24aab462bb52481e7b3c119d4f2b4432edaceda7865c42034bfbb926cd40d210bf83253d01e2b6454811579ec21c1d9b4204fbaa6405ae523d862763418b872c19715d835abfbecd2ee30c4a4f1b5aeb777f87b8e046906b21e242728e51f21f41801ad72ba44ac090f68fa86bc55e6e59a2ff7366ed8cf1ab79bbb4c5d7b95f06ad3858750b02b03e94abe302e32cb2e742488e72912f66fe8a2aaf6ea19ed4bf6d8d5d1e2353ca34224b1506aec88fcbf8816145f8c07a4577d299b4fda5c03b6a5fdf18b4d40d0d91f21f4141e1abd66e933d75a5734ba1c3adea089d3d74561ff757aa1587cfba5ce7257602ec99713949e153dff27665c7dc6c8dd460fd9fc06b745ef32edced2e62da31ad2627544ea794d03e7bbafc6229ae50e2eb0012ff0b1fc19272985f23deecb0c9047fae214a290099539519e742409561408f3d45bcffe71b31c8eb16af21c5cc1f5eed8c682bea75c7eceb1d165aaa928485cac93e94036c488ff224aea7ae89ac52c56f0e20139a6daca48c937455ce342de3251c4fb599f965f5eff6b887a12c2df124f536a8d2ca8f1b0955467dc50d30d5d3f0163d1239e495670598e45a05a415bad2a4d879eea20a2bb9860be2ddd94811b2505957f5eb3b673964385241ae4f2b0df8a887580dc2b131066a559b9437fdd3802518a79309143da5393b534325c5f928823c1704d0dc54ba36ea5b6c7c6e8152bc250ad6493401ffc0501b0712cf2c4a96059ea7e0305acdf15eea0ca7c43a0c5074bbc14103519c59aff3590404525bae8ce552df78559354d60d36aa6315cad27dcaa25ff673b2dd587e4a55dd78040b7dae6e24ca332ea30908abe9e943e0a9a0098e196f6b00e13c772db8a96a72ad0d64aefb66901890227d25291dd44a91123bde979a31e23e41334d6369ff0f82662ec93ea2728de895d82280ce13bf6c1293e36ca4d9efb45567057ce6f151f39d94c564a975193a2e09fbe7afedc7d1e387e8fb20a78e20b76840011cec23c0d1b3bcd4c6dd99c3388187760d5448d26b8f4abe29afcf4eb84aa7bbcfce9e7034fe03b16b201035d4a476093cefa4c636c24abddb1fecaf20666aec676df9a35fcc56346bb1b541837a38f2f6403b364e3f51d1080821614e705b2322dca06712703c8d1da1995ce66e1033d2bd06732950cceaf6acc885d0de8b4353a749905407da79b9ad1901c5e69f3aba9a85d8c4dea4d0fac468e73037cc2c12205fe6671a3be8f33ad83896876509a7b918c969c310f6b9fde22aeb86a4ad0eb8160f8cedcaf2d77857c84c9af63e7d539d0dc968582e45bc4c29252d1a33940dd73b0e64242d65e94b43c7a3075dadbd1b1986163a0dac36176bcebab2bd3c7fe279e885e22e3586af8009fd064b75d1c84e62fdfc7444a79c038f8d6788d187bc123d6df6ffeee5f6615f51c6c3a1817afe6b564fdf9820536ee3c922f98051739fd5e5a7c36dcafd4c948527e4bcdeea40ed157b05477890fbf505d5bc24d026fd01316ad7a6ee81cd6e92db7d47fff83efa61e1684fc901dc57c5cc9a1e5be232db5e4fc72ba121ad43ba379a14a4061ed7eaf68b564d79dc27ed40c027b0ce2186f548c0c081a00cff2835a0c22bf4b4246684cbea03d967dd6c928695c18d14243f7871a17350bd27e147f7114a27d2639b2f4eb1864104b180ffc2a7937441de99b90573462815861b672a8b9ee5441c246521c200a5a79767d9abbd9e4e736b0e29158c43e07012bfbdb4d07f6e77834408e44ad6eb1a6c9e8721153db53f58d46457cfa6c762d986eac10f725f53f7359b7c30d6a176d70ab370c837d1f1356e9a9861d0014df9a253ec61c781ea5d0bf252baa5c980d984502f3305be82f8a8d20e7496188e8243a0a3d8e07e276d4e3ebd4898b885e723920b9fca0596557079698af244456f09aa365e72d9854c3082256bc8d6ecbeba1e215fe19e1cd02aee1a934cfcf88fe8cd9f237d6fb7f3cec60ceb15968fd88dc52b166e71431a0606aa7db40f555c6631f6b01019c274a5e035817fc6a46002c0d1be9ef69a640241f5a5df1716fc331f9c5bef0b7ca0d90593cc94ecc27c526aa88565c436a44e28a981f100a5ed0dd32f78963c37f9e58b283de5248d980b62bf798f1fcbec1b18983cdb66fe532409a714ba683aaa24f824aaf9d39b839b42e940dfa56ead4e8e814c256d12b695dc97050fe15804c425950d8b12e634369ecfac032b35f176155db6d8cc92cac661fe97280467da2ae8b8f0ca86bd62e31ec55dac2184a159c5062cc2856efa777a133428a7e93c4b2da399573b267e81e4014e1b7be77beae8a909fcde9436a66acb8c3d0eaffb1d94396373fcc47c162fc7ddaf3e44c6840540b37f80128092a71dd68579d20fb19ca698417e6f36130a6061d89386098fd9543bd9af63b0c9a036a35b15733894e99251e8a8260b6b15afda7da017571a6c396596b3676e62fc35d9d336dfb10de120b730f5e668ea7fa9ab309702f0030d0436282667f9f2b4fb849088b139e035728c3bbba3e613731974cb83198d1c50173d66966ead89987cac392387220ca93481f0be699e2bf139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1906488a14d9b3e773cd340156d50e69aea8d1e56a889cd9a8196ffb0daf83226b1b50fc17e100982928a7bff45ded1e73d891929bb6d57150ba61c575f2dc6b97f88a1d681aa8bc75416a9be5d1e478f6c8268172110c1dae6c6821f4bc3b36e93e60496430113054b9d55f804640296b412d1651948b7e9229ab6114e8fd72b95619646dfaa893cb81993ea95911edffb1d340b725f993e044debcaca3897700c72cf633b4f4ab891df6ce001e699bc077f3481d4a9d0887bddc533a383c93578c77c050bb4ba0137f2b20de8515730414cc79dbb5e528b5cef3adc1c71960e2997cb22b8172d906778e17e1df502c25a439b193e136e926132154fc9256feecdc84a0bbe507ae1afcca1e36461d0bf285deaea9b3b51fa8ab43d64a6f959c2364c2c411ba6347cb57bec581234ccfb23d87d3aea815ed33950b8c057302d22eec37c6652fa5b590edf8274bf84414bb84543ca06ca72a1ee62c7b53d27a5de9bb0112d9f67b50f566b5314da5721f93d534aac23d6363703940e66b1966a3c9855c167a82d06f42bff6c364dada10218f313e8b63c5d4ec6884c4b3efcc30fe9bf94887610c5f89ad2f5c4c04109fdb53e04f47d6abbf5d5d41ad2e48d8eb6e225201aa5216b5acb5dcda178d18f0dffb43caa5f2cfba3352932a092b4897cb9026f19f8e2c7c902c08fbc6a9d7d857441708781f076420a1fb606c517d65fbfc29de2c1d6f935afe6c1ae6ca1e299a2a5eabcf1dad2361f715ff6571ed59ef25c8ee31c31ddd8293e034eac7d51d954034c852228b87d9b371b196c278470cc686e452b73a82a3cdf049c2e14bd006e5ca7cc075e1ed5627b65053a83db1496cd921f95b8dbb3ea5623508e1a4d8d4d63c6fa5283b4c5e6ed3c5e838ade06eacfe623e00b5e0b6a9cff5e56fbaa92c221a5888830fea13f323787f6ac6148dd9be520bddb2f1584d02b11b0385a4e011d27c46a5aa236c0bf37901cde0437b8859ea39d902f540eac55b1e3eb04d398ecfdbfb1be2683f7b13e760c526637f33719686f4435a5876851cd656d39f26a9dd66a11530ed9591fe5d9cba3e5a32607d591795b20c30495c3c97bf67e6b0d3b7a37a4ec8809a231685ba999d4786bb8f5c00910ec601b04aacb87c9a7e8a49e2b14e684cfcec971ef8a90636ed01622e5a43908d4c0f43d3b9e2166fb99fbcde24a42adb7c46792b43101ab56485dfff106bdf8c71d9926ebd5c353d321649eb7f16c46e5d5c87ad9b729e1abe4cd587a76bac24a21e2df697f94b3714e5fe3ba7db4ea57b59c3cdd994e939f3e7130f85cce9b4b1a86f32f0fd5800567a797ff9a35f395fadf51c0687a5096c04b3e3930b7493a6e916e6caef5f38cf28d7ff1fdd97f237771f06d24aec61c168e08023a57b1c3f5914cd37920f7246713387391ea6f522e222bdefd71938902e5ed70403966a3af7254b5333298881d7fae9ababbd043b8a4e2404859f1806e0f876f4c907d749b232b8b537de2465cc9bcea607c0cc912b3d331a6f8a0a6d384218bb82bb5ff8b5c7f754a6d90acb4f2981a5f8676b6fbba58398a30e49b8d49accaa6f1051744a23d9947d74b8a401d9b42dd41e688ee000ada73c10fd7f6228056e2a5e9c98c1ba264ed0b6b10359eb8f4dd9acc3214ad9cc3a9aeae4085d3f97b1829a902be22a9ae7dbfd8347220b34b1d1b4625a3eb5af1d6cbd690682788fe71bfba34c9af34ed4df7f9952c05ae29e6febf0f463e142e344aaec2bf039879d3a036440a4840d8f3420dc04294bc64abbc33c7b4832da145f24d9036798db73cb99070f51c8d414e377d9c059060f32beb25694756154568fafe52f32a92735b3553daf4c6ec45e9f212d1c0f50beb38a12cb9c643101c1232febc9a82f30ee657bfe0563c817fa995342a3428173113ea350ea121d2bf0757f24c8035b38f5abecddb67260b24a1e124353c69eb02d900363c137e82a869ba34abcdfb5af1c3d8f6158170f238082423f72513ed672d78831470aa681626615d0ae6dbc91180b674e000308277fc0cc7ee964e9ee24ddae6d57043298765da895d1644e425aa100bf88e19e3bdff4204ce5e15e11d24a8b4f53a36b41e6865ee97b9035e0f60bb8078198e1aafeffab03a99b826e89be0a08b22dfafe1dfb58f2a8ee24d0d709558a2e1cff8887b5a3382493658aadf7ccb2a09143bc320670836e083f6c4d5bf4c8841de216639e02c23cc2031af9f34145369384484feb23ba731808a48f3661bea0257cfb8031cf1533b8421ce4e829084d34bf56a8cefdc3b68e850cb9a175b61778c1af6f71af59b3a08cf5ae16b1f94ebb630f5f320f28ad44a46b19605ea776b1c85819c716e51d4907bd24b4598f9a4090e322bdf36302b008325f34fa3aa1645ed13cbcc69907413fbe6459b1faea31e08fbe88102cc3b1ecc24d8401e53bfe7e4687a18cdb5170977f86e84e4249558f0a1cc6095d1e273c086cbbae6526fd81b303f00aad9b5d1fd25b772231a613d08f020640ae675b189cc203e1afd671b1c024841f24c9c78a78c3a2635b8289cfb7f46500bec7921220c128b14231ac0fa8af2f1a8df3d0ecc407a43e48b5aaf5dd823befa8b94b4d9a57ae60f587c541d2fe403a60f0843262e367952a576f2a7d3401353e8be0c3e3709115d4c92938a52524d5334457b2a8175f157a4d48429e9edc613956151331f20a504c448c0a6d6dcdca8ce9a36bb159a27f1d44459bf22e3ba24ce83b616c6829a5d9699779a27b5c63a38a8b4cf5111b54c04fcf3c94a97acdce93ea5e0e60b89de0c1a053ad3bece4995554a41a04cdc198addea346fceab7092657954890d70c1ae74662b749fb9316cdc00989253e3f6fd2a0fc5fdb89177b3de8635ceef13a2cf985c7ce2bbc2a737ca426bdd24533b9fca54274928c312ac7997349778a685acd672b0ef09270b693bb00fb4f41578380d43540834cf990bbe7894aecc94870b0ed659eb305438dbe1f954ac4556e3c07c09dec930891b2fed95599c76be50c9ce888e90395cb74e1a99e94bc42842d153e0c677ec5a4213ef1cb7bf48452d347dfbb6c60a28d0f34a475c24780ed4ee656ba8ac4c3947667c12f0030d0436282667f9f2b4fb849088b139e035728c3bbba3e613731974cb83198d1c50173d66966ead89987cac392387220ca93481f0be699e2bf139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1a4e0d31122fec26b4016b4cfafa9f87adf5c9174611955bca177562dfb72522266ed362136570864a62c3110b99b8c90b88dbc1358c6a04f33fa55bf7bffbf0cc4970ff2fc02e98cb7e38cf8b761f8375e82ae5ca838717f203229c716615fb3cdec7afa9d4c215524d55b66cc0f34852055b40a7d0709d9723bbf0bacc329d6553a3647c44f9d5201408c9fb7f6cb3804ac0b52330fdc5a62d6a5f73ed3b1edfd2281d6786060200412905fa788dbd7d66825418d407857b237a58dc4d34a7e29feb7063452fbf7d575e67e6c75bba19a704d300bbce99f1e0f4c672082adc9ebfd8fc994bca9585811a1d5e8b9c83727fd7efecf4c643436d29324db9e29802e061a10dfe9b622d34944c3a16d55b3b06ca73d32499e5b426c5da9da6ab5b359c16801f7554fbd0f964ec405cd0be2c147d21d6ef3e440d549c57af282b2e187d5c3619bcb1f01c8e87f6d46ccadaa159bc8074e33dd1c4467febc6ca84de82b2dea3cec684baa0e86b5713103f483ab0e493e95bcd8304c64894ac865a6706a7c389f0e62762a0c90b0121c610bf8dd96d7549cc65a4fa925397c22bfd747f3153677c76c5d44c85caa1ef98ee8622201c6c931392d54367176e82c230c172c1b5c0bada8155a4edcc370a89bb672036a849d6074dde5be747007dd2bfd1a02f9ed00c58fa66d437b2ac651cbcb23d6692cd0c0afa0cfb2e7837ee5fdb2ef1a7e6e16cde5d78bb1c40208bb2297d555bcc6e0dfb90084a76473d07b15ad3689ab0a13cf4d56f36cdc39749e1ee297e4cae1ba93aa3a8e08f00733614cf85900a4541bccb4c2d9fabaf565f70e803dd9b9119e834b908eb254b37a1a5181499a4c2ce2455ddce3fe8e306158ed9d5f6094a5d418c406b1bb7b06d50fd6c8c91a799ff146381db72d5f240c9e7c26da94b015b331b8a0a039082b46cb9adc30c1fe0ebe3935b7ff9b0c0d4739ed39137ee55db1784e1e3c30bde911b8ef76d5961fc93e9bc948a9024b5775344ebead346469611df9b4771bf8d4602d36c8a32b01195f79b9d2ead9598bc8ebf35b35627aab61f8c547820a56b1e80150570a7f43b9c4530b1dda5267f40073316e63456f60176c0ab2796ccf608303dd2fd772bf4821c4827eaa99db49ae6e567dcd56aef1fa5c0a63be2f8385edeba27a96466cc7def2125529fbb53fb23cc78b6c021d9c3ae3e32816b6377b4c5a72285911e550212aad03cc8b5427692a9a6a536373a91d689a9318446a5aa58857327145f1d5c00d7e8eb8685dd077e4377860183293051bfd5495b50451a2fba1487d2ba4a74b8f74e2aaedd698278c161afb39f611840cf32587218121cb49e68d42cc278065e3acef23f7b93febc0ffa43a6388218f7e142f46e60eda1d67e7f5bddfe68ff9ca39cc6b773cdeeece61690b04b2d7a13996d94f5cbbaecd8d914b3e9bf63f72430d8b8a226ac2e8ca5eb388d33c15abdad62dba3ff4fbc1cfff5f9a51b2db482c95142a4dc58a14dd1d77c2a24c9f60e3a721371580be29ce7e4816d9d8caeb72bb865a330b6a3b35022789eb399b1a4e14800dbc20b56542f9c268d45d4b32077520f4082a0a49e74ea51105d77ba95c530c1678c259b331b87d920b04771e5fd0e145f8ae9f7b9c245080d7a8f58678c7c0ba273ead2751ed4d5e3d53b79dceea6d18fc25d8aa71fef9c6d620a58f73fc641cb75244d6431055a90da70d1c9b914c4d213a058c03c7801ff0a937269c32c728cc74344461893029ddea3d32ba7787d5e4fc530f2d261c69b0d881893040f18f2e64dd4c7ed810bd90f0291030e9ba618985ab5e392061bfce8060fc9698056c266cf0c93bd82f69989b2a273cd7ad40ee31d344007df6888bcf87f966f04b50bb6a32c6b521b216398aa7dbebe49937b07ddf3902e55e3ec50227e034c1621a166d8e166a210eac960bac04eee19e7de8cfac7aa1758ba39aa746a0e9ff139bdad495551e389e2b4849122875c19e430f9b29d13962298d8fd9cab42338fa458266ce00d2da579e87130479cd523ae729216f62285ddf34674b48fac75a7ace14c057c3ff8fe644080a4b92f75707535e9978d61d01ae043021f4b096d7b8517a62c05485467eb5152b4fcb23d67e8070c7387da5012ce20e90eb4a8ffadbbd3443ec7cf68be83042fb0be1e5bfa3d7b7185f473074557a1e2e65a6c747a52bd2c8066cdbc9a3fa6c8167e42af40fffff2ba86715c8cfc96edcfd4397186d8ba1677674ddf22e65565615cb490f29bdf94a224c42e68795ca70d249b1dddb69ad90c6425154eba691b75d7c93f0b5f36226bbf58a75a7d5eff2e2cf978a4cc3e8fdd2f7c2160c2aaf3b6334130793c8f5924a9fae96c15e68880adea2dc6b99688d61e8d73fc7fe41ad795ff8c82d0248fb22c04da38fe4f57d31c4bf7f112d30827c1f8e1449aaf77e03e0e2f265f72dd3f43ef47a91f6fd54244c103b00fce084d0a5836ef8cd89d581b1515d81df1fbad0fbc03e3f140733c17818c66b0f17215858cf1d272853799b936761c3475b98bf5de6351d839bdc23c502d98376c20a3a368a4bd4719157b16552fd175e9dbf0752801a4fc4566e6f798dd3a0644c64b54bc299fbec16952b25ffffcd0925d1d8fa30d84db0e4ea1bf4d1fecf705b23ccf8441f5eb043c79b544186b237e8cebbbb62199347570be745cba8cd885b803343e3f68c37b3ccf80671bf36fd21f6ebca33a91b9699f3f4e9c98c470579c52e4e2711c8f958d998997d6382687a04304257b049b26950ab6211f0210ab1b4ae826bb174060d1c4285eaf5f2478f0ea1a2fe2f2a533c8ce346a8d1eba50ea9e8d607af43070bdcd297f4c6aa9b221cbb0c64c594b658759e80c95a502acb4a9a77122397d4b38e1b7abc29690f7e849cc7218b5728ace2c45a0a553c837b0e02c99a71b145cf777d70f9cd958ffac019beb737f653a5947ad23f0a5ced6114558ff99d531af775324573445f772d2051a6969085f2469938c7a7d80b68d2e3365d61f423767c207e96a90fcfb8538d4b9cdb1fe0755a3d0dc112bb47777345ddf800b9b45c71e842ac7ed1525144747ca737fd05256c39c2a349d6f6e45c0feca1cea47d9c51aee2513e2dd53597593a7cb7483192177d7854778d249b181ecd72cfccc0ddd98d1c50173d66966ead89987cac392387220ca93481f0be699e2bf139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1b2b918b4171d73759c65d75782955f2ba009c93fbaa8c12fcb81915c88946dc173c8d3dc60471c74346d547a4178f3386e4623167a672a1486e9876af87cf5d1b6e3c411807afeca3e7b0984c1bc41573b41c374afa07825a322aadbd66ce4a87dd29d8f9d6a7e16fd1a219f1df78f29665622e726f3fb37f8697a31b22722d97179730c371eb6d0a241ca407f3d505739902afbe5499d881fce748d0108ae5b6f19cb4199c54b2a980d6cdbb61faa61b797284610d1b8e06e326e3113873eb9b4313b696ee8d68154269c2e2fc51b3e3717d2bd4b915472ace0cfe9a390f3f4db0a33967efe3221c63bab18399694ad8e2117b6472be24559eeaf7fe7ace99a7c51fb626f0dcf3191997c39040bec3050c9157b2dfc9cb187340bf12abb56500a84021a15998cee09e52c2ddacee04c82f7565cec229fc945c0590e5a534828da5542ff110177118071bd7fbd8f7dbae28210cb9be39da41a9f26c8e04c65e984ea5fc737ddc9cf638cf68f32a532177fa1e0d1738a3c9c1e275c824657259805759cbb2773018f546e67c136e7911bbdc7917e721c75fcf5e79d26ae31b3e12c952b3fae35d61e95757c4389ce5476fb04df9ae796d7963fc97e63aca9131c87bd7c58765c72e564519919161293061c2aa5890b1a2e43dc24ea5847f8a12b2fcff052dc33dab4df119ff7e08d22dc9c1c15d2795091d14b69a8e9c078c4dbe167a1c98ebffac10cff51bb7b74b59ffe216d925bfab8f0e7f82837689d25d09acfdaa2388c9002e58c96d32c569f3b97e12c9da43ef2661183caf6d8baab3adac1c15387e481f5ea7c8d93bb8c09a70d662cb1aed7f2012bffa891748719b64f71883614ee80603db3e4b44d91971bb24ca7da8b808e3857143cc0e594ea2166aebf934994625bc3abf28b033769b02b2d1dff3be3654173d69f16a8774aaf71cc175bd33b53dd3d89984280297e32c4f8d5b68dac624eb8bca8d686bcb372b7459a2829a3d4953aa30abcaf27a57e752edaef88e7b684211eb49d2c024ccc2ed0604096f41e924a3e2767bf0d9c50b4eb8b0590a9dd312c54a362ac2584215fa7cd7b5a57aba251ebcf5e96eb328b368db9c90dc169a69649a9bd76e9b3bf717bc6a9b14778ab7be64ddbc696b8c78029c3fcff2971bbfbefb1dc850b14d29dbe563ef00337cee0fb49fb5ccbf90fc7b44f5ccd1f6c5701002a1c8904ef0e4d16fa0a67c73775856c2c8151ad16c123c1b8f293bd57d369ba47002b02c51438f6e227870c45a73ddbecc0e77b10a5c3074a260226bbfd388ab2098a5d6434a54e9a284898272ef57347959bb208cef4146a6102bb3ebb6f1d7f65e6c2a7225731d13fcc3d712a79496331bf0954db6aa47ef73e96819552ee743cf48bbddafab47536a2efca6aecdd14a9cc780792daf6b9d642d8ca6e94bffee8a22ec57356954bc6dd7c79db3ee0fd1918d1746a5f63896760821a1f28697fbdefebc5aec52df53a080f7befa05fdc91a4d0ed245e7d88104f4f9b5fd27578249f1b1ff3bd065a420834e9288ec22eebbb606bb8c17e81866855a0a802bfc7861cddbb82aa895e4c0ea0fc5ced262d509db22e7844044b006bc0fa961127f58f157a8e42e213c9d443423ff3ed2a7e185d89c2ee2c1272ec979fc5a681bb166a52471516a99d960f843fce245574bc837800e90e602c32c3d85eb12ec917d9760b1b9b0f82d356c7f7508b08db62b42e4248ec0bd01191fd137908b763e76b6fed6de739e89731d2596db40929a4ac10f36fbbac1f747077ceb61d6f02e7937e7d9fa1e5134394cee69641008fe2228493fbebd3b7b80d4867b5207a0a34d64daa6b7e1361285dc0394f6165f886db4c45c75ce04bf6c3666dd18c3b2af56d4f88aec604ec681ab08f0d6f67f1a46148735c165c34c8f64155ffb695d030b4f4f25a403d8bb6c0b129679a719e35df213522910c1a285934b6a5701a17a0a3b35f828f1a5a7562108d5ef53005ab1d662a4fa4f3327a0c48509f6089732ec543e7ac8b8d782c9d59ca8b0f8d8eff8908b0f3dca1884e115ea799eb7aa4a95ad1c55d07350ceb4119ce578c217f5138b1d83500b7b102113c859410213f2bf10b28cbfe701d9ea3aacd257066eaafdff54175bfc032dfbaba39e434f37a67108064f348d3d24c824ebcd77814ecbe60829d5843776a3e416442a9b7f17c46511d3951105c68315a44f753237d4a17cc63e0b47289c08b5021ef09645aeb0904a9a680b6e689d27b13e70753cd6fb1db3f29d24fe60f30c264c8c594b0af315d2a93dc6f6712a2e936e5de69d393fbf6225f272178fc9838c22fc6b686e5282adecaac981b2f3aae4ab736179f27da92db0ea2034a08b55c7ccf5b14f3465017753908e2e768d1e9961a26377ab9683181251659f4bc8cb59cf5072adb581377579867d2e256f4fad201f57551d4f333129b7b01aa5f7dc29163f0b4871e0dfaf8908b63bb834c73fe6b458e6e46bde92b3c49a0543ec56ddac83ba7511e6b09f7c29307a2e9d7f2e5c9720a63cbf7cc422213e7a9dc348d67b70ca6c31d87410f5b23005c22aaee9e4e887d474bb2fcec35792377eda3411246aefc49558b2861b49d3785cd596baf60ab5031371d0e5463026048a2371718527af7ff5ee855b9fb0799f970bdf13c8cbaff36b6898d7f536dbdfd30e2bdfab680bfd5538b5bc758b9fc47a90ebff9275a01e9fa867c56cb3705b0d7cf56b0582992a45968847045b3f9efd6d69970b0c845eb134c0d4539fb717b36ca0f8164ce2d1a268b5752816052ee3a2c2585b7a3558ce41e5b251a81afcb148cf30a759e59a8cfac9f8752e2fe32e7eb5a633629f2275d86d2d8a0bff6878df098a70de89957c0fbead36c1fac1bde02fc77348efddcd75b48e22eba37f66d89642e2914a2594c3928f195629b3bf35a0bc95248d7289fec8a1625349b5fb9dbd2962e05dcef110c68ab5fefb0b0f969dbafdfa2675b6798b22cfcbebddd430ce95dbc278dcbbc264da0dd4ed042d2b08c41618a99e822905317f8822ab3492c6901907a225b13973e7336dc3c08ed13364742e3c49d378fde024816d4f2cf36f6550821d8f68fa8cf602a2ccaeef1aee2513e2dd53597593a7cb7483192177d7854778d249b181ecd72cfccc0ddd98d1c50173d66966ead89987cac392387220ca93481f0be699e2bf139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1c9688c985e26ded52ff556fa3b01a27a8fda7cc463d64b4f07a3fb90d83062439f4002651ce713e125ced8f7e16a1ea0519c20b64efb6d6cb83afbad1120ee226ef95bef242c1c08a4280a708c583e8cbf7192912dd15fb93e64fab0467c5c6fc37ac9de1c69bcc5adcbfc1f178d9468a615535c321d21e4d9c9af61733c8f9536cb0003c507b42d497c58a6814bc0878b8a5bb3a7ba80b61d31ad8cb039b379d22fe7705b6de3ab750fb321ae7890ec95f021bd9338199cfd20de8f1498f8f9b3ef901cbaf582297721403d424837c7fb401d98ef4835ed5e936f8841b0f995acd973db8d991a5a987b642c6425df4161b1b2a401adb47987b76b578dad70f5831d0d4331410b22ec32686a2dcace9115bef85e028b02fa1c15cf75868a21e0100b0e713a9a423ea92f59584bb140541be74742335861fb4f4bebbe503c6d8055c0fc3b3d6522e07ba137314e3faa8e100d8de68896761a89eb1b41cb2d2947c96d35d7bab34876b1a74067b523218afdcfea9fc61b10d979ffcf25b001fce8c0918029b86521320a3ed17f5b6082c754ec6ead55712af81b160236d3c53c8174578309b298e3b6257b555497a7462632bff88bbd2bbe9019f807e883ac45ec0c33aaafbce7c7ca2f1c8f46577430406422477eb3c358a4a7c289428d08f5ca2cd7af9d007fba1ab06c958ca3a44fafd19c119d0f6e6583959b821e86e33f0bcbb3978672ed26341627e7af472c3abe6921ad36936ffaeb9fbaefd4a1bc0295e23dd9039ffc4fc142ba10811205cb84abad25273a7215ab590b5f050aee0b27439e221ef847e881e99cb8e85407ac7e48d94b05b9ab62be64fc8c513578d88570a2c60efcd258e563ad654a818584d5ebc3d803b514175479d0db8c29cd97def4a97a6863c010a5c63a174da196a256d7cf87a34334091086213c5e9d6e1736820c10367b1c0d921f252ec47c0ed5901c0de1a39970e7a558d616d51db4e27f9c04e4508db3c57df03dd7dc2977c68cdfa84dfc6f617ffe56fa35a50c3b584606a89d084ff06cf6c5c83ee81a891ad5c3a1d5efd2b46687ef253f941160a57a509a50b57ec8b3c1af2fc7fe5494f61cd3b06436dcada345be059d3d906053b9e2380a0dd837f62f5955bc2d7341b5d65b0de40cfed159c74140d1810e88cb4bd51c0e9451f02349cf90da67a0a0a4e71b989ca9552a3d608f798c0abb40847792c9ab05c4479a1e61242124174b5b22b3a96c6967aec9126860505884854ed1878901e77f16603794a5d04ee7371623640e9ba85b281f3988582174183e57b6a7ee6c18e29fe3b41f31e12ecbd5ab3280ffbce4a9b7958f9968843d5b7062a52a373ba3ec4e4cab684034d4dabfc99c985ea059c7f3120dac79b927b0c87b9c0aa66459f138d5a667a721681dcfcf53e7cb3583009cc17b6ec3955a68377e6794ccb2cd9773b265a8e2e46f8a198387085ad8a5a2643191e79bc9d4aff8377829ad58bfaf627b559752e08edfabc029ca3542b1b76971b685005b6407b9423b88177bf72c76a0e997ce7ad961b154cbc6feaec01ead8134e1c71f933b0ba4b9e74001a425437461df854b5c3c53a291c6ed28ed651cc4de3734b2ae5a68fca693c744856adb7721e6ead389ff19344bce24584810ef52c516f19c311de335120e8ef1be0018728c10cbbc4c0aa8cf751f966a6aaf7e6982c910d423fd8b8107c51eea68586161cf8737c44cc157c248b208b80b9c2d3565f08517abfff5f832d95a4b17d79f4ba2d0823a0aaeebecc4478113bfbd7954f8dd3772fa3d18440645c0447463ac2c3c8913a0ad3ccff05927e1f9481ca09451ddd85229f2a192b1a483f42498fb7160fb2815f362e698384b451a645ccf87e8529af9a56cd5af76e75ff2d875ec024f53f4f3cff75505cbb1863775c3d13d0e23eb14cbb78d49ecf6a848d79bf43b482323c03ad84c21141e35124df31221e6150347d3b5277963f868ec19254569d0abed6e4f9b722d242ed87c5320ef3e68fe5ee0ae782d91b71e78f1a2278b366db912fe2d8db1767e25d971ee5d1b81d7f85b58336a942514325d3cc83756356e19fd872a26bf2d9f2112763908dcb4c73dbf3bbb2418a2efbce380e0425328ccb976b86bc16856af2eadc003a273ec247ebd7badcb64fd52d5eb669699dd5f058a0d1612889d06b4832b30eebfd67d14e467ec0fe1153b563fdae846cd7054c2452cd4e616ec77ab0f326bcee96849cdf8ae27af125e43e32d588c4032228eead9078b991a63f12321c544520388dd7eeed58e4af45d6dcf12fda0b73d148e0229a34f3dc325786d9ad53a35f036fdb28f5928a36e458521e76e6bcea421ae024b9262387c51707bb350f9fe2cb8aae61dc56cd4eb5d3a6ced26d3e7a2c2c424e52a5243e438b3a686a88ca04e9feb6603f4f89bfd261b81960fa2f429eb9ed291c5a76345bc83673c8c782c2ed18062ad3ec2c6c5c5afd685757089c8249103c89ceb72a3534bcb0dce8f6b3f60b3f249658c897e7621bfb7916f74574cbb070af6724f995d4b2a69f59ca8d785715fda61e3a6299df4dda2306f53360b8b8b8d87d4a5c5caefd7e593861ed6b72e14c8940d4cc986da45321bbdaed3bb0e86566a90ff4034d7a1cc626ff926d1ee7f9f9a2eadc23c8e07dd994ff84bee8ad71ce1311d6764032d2de9aff4e7db890b55f8bb3941f9493c0f69afd75798b697e228846a55993b5fd17f2ad5a36343cbe79ae04d5fc3f04bb66d8dea940aea18945fc31a8f9a8b400d44168ce1398f5cf36714afc0328663fb3e3f336d8d23f05a65273bbdd66a4cf566f3ab291f7f6bb966ea0116f2d370517e933f7871e4dda9db394cce178409c662bd689e4d6e0f51cd309d1458c2c554f0c7ab3b74d7227f77a9ec911a1262692cfcc6e2c36dbcdeadf1935e1addfe9cb2b8f3e86cf50043b51dd2d04f9420506705302721d191035152a18e22b9ca28dd7e71f376d7bfbe441587b4c62310933fb0e8d4e08fd7c622294777dccae6c4e6b2df88f68d4345f4f0a7c16c697f3c9139ce835e61488887b18fbf5a6921219128a51264bfefe4562209013a1ab67d86ffb81a1464c5c2f29f71b4a4d4f5fe39fc069a8280d5917f8b4c3c5402a692748121102993dd600efed27e7db5bdca9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01cf6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1dd47d0c6485aa0bcc09a5d4c0edf959b04a8b1c9169ebbc7edee5035b8ae40333766c506a1327e776fb2cd16487a5517a309b72d1b5267c592b90a7efbd96b5ef0530c37f8a0684a8f0ffa04c14f5e839cd22396171e395d3467918d9ffb780eb304f12afdba17eceba96f448b2d0791584465f35e1db8b1513bc824bc7ea7bdb1bfd2e12dd1d312b62a51381e64fecd684f83030962e682b89e4e5a4cf257b38b76f7c448e9350d0028f0b9916ec645f980c4ee12630fddd8251317b8276e1bf5ab6d32b215902e9049c9a8b260fdb5d20d7477776e20819721e09e7cad5e652be88a0cff32a287be534d796e1062abb93df619a0912f20bcb3988884bf9ea09cef7514fbcf025226d6e178bbe369e9eb093568b5346e8018edabeda909135d8add2af2130b5028994745c8737ef1695ad6f1ae9f572297afacf4a85c4c9908055fdf5198510827ea3f46bc3bb7d4769b523a367e5ed9b4dcf91da183f9d8486ae3a02827332716d5650c60d3812ff12e63dc7207d3d60b39ab744327f9c3c7462f6e1db9da77a31796c7eb77205df0ffbc0ce1399889bc7394d9b8ebedd15a76643786470faa650a1e84f742292e6f064287ee8d32a201a60917c8e5a8f4c9ed35a012b89618a43d390fce82a7f960d306bf5512f2e7182e543f36113b7bcdbc2d895b96fd730012b77e4450100828ce7e7ba9cf301386edd45f410e532b11ebd5df6f2e555b24e2a03fb6e4b54c87a03bf9b100b933b5eed42f221b76434192fefd06a448b767a3192f768c827d85bf1f5d60747171c2340d85a694f45a1a25efa9e33d31b1008e823aac06b90ea1914706035c2f8d71f7716aa2965aaff54d02cbe44a85733506d7c03721b1c4874f89d2cc762e81271d1d52fcf99b008abf2bf885f398425fd80ceea997995f15f36a77063c491c46af1758b789a65d105741c021fce4ce4f2d6e17c9696ab023af079d4d6279428024d60a7cb08cf62688d83126cd88e280a813ad5287be0b44e32cf7a8ea65f98f9e6a6d6e20469b977bdb2154351beaac6a6a4d17b5d6883311c3ec9a3a43265347a9f8febc467ff1368542a35b7e42bc69117adf013982dd0ee5c8920023a2d66bf1a22aeb1554550da4ca807174e060729e262b7fce5f1377aec232404633e535e3295472260e72b3d795043b2d3c494df16ec6bee7bee350469c25a486d7d5b40d298cdcf85ea37be74a57f49d8f808f868190937eeb82c57e4353ab978c753662c0afe72b993052b42dd085c94f921f77d4db8f8e61de611a6f2dd09a1541fbc9416c2a19dcef250a8b0ee570d5fa5f0feeee80fb4f0f3e03a0b39cbbeca9c1cab024ec5a582465e82a35ceb91464f2225b1a2cb802d1e82b23970071f267190c02d47c8c55654ce59896c2349cb601d383618531fa7728c1955bf3b2670af0a54042edad7d0447362e6e7ae5989a430f8055ecf1d4eae64069c179919600f4b5a99632a82fe1dfe61061630772657b1def4b97431b23b24cc91085dca3b9b675b131183f3a95a0ee1fd05b3faa1ff8ebe16b7f013c434ffeb31f4a90a4105a6b38aa7940aab83804163863ac9f147abfdc91d4303f88e14fecf3c56c273a02582a30a491243a2668304b023c6325049fd8d749592295624dc05514868fc2c3211b02bdc6bb1158f322a76038d55d85c7e37831d1e67a56435bdeada7cec00987c11e499526a35fb5d13c4444e9b5016e973d6c357801ba5af59360c48b1ded87fbf57fba8be1bd067e994f0f2a2f434fe0392daa4676edb00d50c21f27180ed48318c5236832275e79b9d7763f60ee79e69232cec39cc0047a9124f3a903302518894878d55a937787b6e1eb44467fc4b4e03f6a08a6dc7e9928ea020e1fb72ed5219a28c5d79d582841ed3fa23119105b7b8cb27c80c7abec5ea694079793bb2eadaf26fdcb7566b6c987629e6957392d038a05c87cb99c1741fc0dcf4c038b2266577a9fa21a672a0bcc14fd64aa8a6a0925039c8d75ac291e34a16dec33e7974470611d855796664560065232c433f51e99762893b03a8364742a9b48156a519a8c61bcb3cf7a0dc573a1f6f277fc62cb50d53257de38a8da32865df428f71edc7b566ab11dfe5997ffaa30027c8d0daa42aa30a617b253eb5a3fc23f4eab76042915446502d962b98ecbd69423370c7b6a32f0382ca4b7dc0b2e28431bbdacee01f7a36bd6285f1c6b81d0c30ad368f402373c70f6ee758d41c163ecb7a7c476b38d2a0a06413da78c83ea27a41cd80dcdd8124569e7ef441c2f13d07adfc6b87f1eb69dcc47705e80fe88aa88110f64551015ca732453da4fe72d4dcbba759561acf96ab6cc48b0f96bbe0b8bbda54f6c9c7e93a951bcddadf1d4c984c70792a715cf9013e4c640c1e61fbe5ba009ab3315cb9852a9288b13b8904a6bab441657f6234c81465ed880be61bb424d79a54c7cba39e839308c81ddc985666ee81280c179b82695a9cc93d9f8e21419012d4c879bab349afbc72d5d6fdfaaf8ca597685b9785f93b0776dc06b2e0fdb5254922e0366da4978173d63c213a9df4ffe1a71e7090e2670dc8403ce1a9841b811e33a946a762dfa764083f84451b7061284cb86f5794e26688585ae5a05ae660b8085a3fcba7978a6054b7f0826f462b265f996a48a648a542d1271c6cb0aa9404f36970bbea185b82777f1958bb6c04fd367355c072b3865a744b6adca31e2a41fd06496809fe7589d5891b7b38049b2c4dff88ba06754d3415e31716e66aad3b967425161a8b47d252ae4b3d8cab4f386bccb17643fe1060b8df9c483fc02751a299f53e83134105952e361fc74d446829ce420a0c19fbafd9e996109fd8b3b12e9403c97d08a89a55c7f6dae301de716b4790b93892b7f03dcf36355e75a9dcff3d90c3609ee5024f304a444f935f98fce6dee0954d6b7e79612bc371e5ef03c58032222a211092c5245e6c6ba8a269edd5b9f41963bed7d9bafea184c175e264fbb84f0c259a9b19acfa3cd6e07dfa7f4b73c8d2939bd46bca5bfb269ed6f1a963ed2d4a8565fb515baf38391e1fc99bd42c535fcf26d19691b52f03744de1a06f53fb561a943dd7daf341ce64507191639ffbe39fc069a8280d5917f8b4c3c5402a692748121102993dd600efed27e7db5bdca9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01cf6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1e1629e7868df0508b5c5feb013e7e3eb75a0e6ce8dea42e10add7b106a6fc61f1821523d62c55c57e8a3ee2e02d07fc89781bf43f65b77e454199e3cead123d59b8abc721476e4cbbb13111e3bbcd5da757607b403ce507cb18a55736e6801cb264e571d668af57967f031328acea75d90872b55d339a8b7fffdcf289c100605d10b24f0c2568c4ccc7649a192c7a178799558ce4f14107e6ac84e647340f423e6931a003b8b80798085dafbdf9ccbb2cdd48ec28596e72bf5b1246c17a7b39d579d86b6606821bdf03af5d92410b4bc109f6cf938afad4a4b33e729f85d9d5efc64c261e5de70ef1c1987ad75928ed3e028c6e42233dc03635dca6caf890febbbcd0dc9807b29f9f437795df16cc165f25761843f5b6ac2688e07e1f6b43fcddfd42d993416bb55afbfd9526c643409a02ff95dca4c645196a6446d02430fac0f8ecc88ef2d9541d55a15b95f7536ced29b9e97d3fa1bb630193626c1ea6d35ebd8ffac60c1e433ef9ce8e7df7a83f3b4daf0487414c310db829fc7c4286aaf822e0e9689507aba82c9c0209b8143974374910f7651aac5cc60791656d6b5f8b3cfecb34d20831ae7452b2d995a77e818d93615cff046a74b87f509eb46926eae38345f89d2b460b7d5f12b3fee6d4c1fec58bc13ee88852bc3c9a077724138962cf9238cf1c7fa428a99e9ead6a00375b3108d20877510822fbb7c5ab04d0c653784cc355b43337c3f89704b0469564e0c40223587187dd220f66e5081a90268755e8d7f46d0d54c3c8aef3c5c49341fa4da879f866e4b98a4401f54b4da2dd04142ce58b3871e5e30aa61383de8161b0785e839abdfb27096ab95f7277b148ce539b57b98aa79c874e1bd4003c1a912333e47b3c41b50822715d83461b1dce5a712df97f5f425dc6e399af15312ee21b283cfb9916d13ebf88b0b467b1c14dd600e36fe0fe0da545bbbd843101683dd4418a79b60f55aba747d873fff04e7c37b576f1dca7319f367dae50d131aa7e830e18002394b0d882b9467331be11dac00ab7a034487dad6f28c95a6a253f1200af1fad0fb3bfe64c24dc710f19d52a88cac5c316a2dc8b82ff68f84321418164bd1d10f03236b5f6b334c01a66332e4e1c4f7f78129d61441e2b3ddf9eef1c1763706ce79607cdb9bb270c84487b7822ad3f9275bd34fbf44b661c60dd2338577d657956db1a5b9d33484e8cb4c087be62a47f22f01ccb5ac308d6f34276987eeb293265eb5ed6a9e20a33f6605b33eb22a8f384d0b03f7ab613daeb7dff38492a7e7857f3849c017bd21963b4598a534506068f4df17cf48fa9671a9306d927d14df11fb277d8a56a28fbfa1c37a1122176513e3e5320c0953949a4901045e3f4a9fc70438bb2034c01844bb380558499ad97df77e0987373dc7076ac39496b218f9919b2c693c6d8b53a0cb66a860becba37a901660f30ee4265ccdf50d03d49a0aab69b3b0f8e34a732318d8b76bdd599ce580ab3cfc7abb946b62fb29a2391dd5922229535773fe41acd802ded42f5172dc552540ce81a673a9f22d021f9d689c977e0350e14db66a5ac025e8d5a43f1436af84a1fa57e03e6489bffec04d69d5db2d3e4d5cf8bca0b576fefcf2d88883be0333584f502f315d8c47de319ca62b9a310fc0ad0c76d9ab27b9df3f3e28b8e8edaf592755b56c2dbea6f50cee95056a3d3e8c82cd61abb76a1fb6914a872d90bf5dc5f57cae9fed263fdd4d85e65fde9141e80f397e427fbc1a5186ab1685a64180eb27dbff879026ba3a1bacf2c2e1a559b854f7311f98aaccc1d65166260441201f28a380d13286810c1ed0c9919f7fe93bd35bd059dd57110f6b4a46b59ea30c3cae5ff19237735ce7a892af297281f3056de704b6f93505ed24ff6f3f4a512563f9f2b3792a524bf5ca1be336bdd5605f3adaabd1231ec9df1dea585ebc087332c821097f96f1258cc86923e92d9178af779ac707d7b3c6c025e7fabb70977bacfa25c4b2dacc90b533961ab56df226d7f8749d243b527b60fa8550a8f0ca1c752ffb5bdcdeb6ee176872ebd4e6e7a4d7ec1af7cb76ebc9b308149f697db0a72969003f1d6a4cadf796a76fb5f2cf0c6a40d730d557c33686cf6bfbddb7513a102c63f2aa0a814b00ea966befd1527719ccb98a834d85196ed36ad9813ecc690911ce8dd7b08665b20028fed0f2d4f90c3d9620cc4413a876c515f2bc732c512e0a4f7a1aaf72f8dc3f7121f96ae96b30ce38b27732db600520b5549e507848e8813e50179389f6c9a5ba2f8776baf4b559ee3964edc2bc88170a8fd5733cdc9265110fa821727079f6df4385b5a239a3619b2182e0e622039a827bdd83c3ca8361104c078c5ccdc095b92052f445e3c1c29125715bd77da30dcbbee14318e1ea521ee50074edc20332753ece1e8a4ebc074f6ef1922d96a8856a7a9a9d9360bceefb3b0402b48d3e3b4095e10c25dd22580bcf465649770304049f3f9a1e7e63317527b4c03b1d10d8264d78e66c782c9b7b134c44a0c391c4eb63ab90c090ca108343660079cb7402f5c3ec12a6ce95809ee606f2d3d0625f0e9f3f926e9c8db97386bccc188df159b330017825a1e9f5ea0b0a3b6dceeb3956c3f149582b6ce2263ecf0ed4e5e7a55f6dd95a12a5f5744877bc30704d953d6ea937acd9f3ac344decf67e4e937fcde3dac09f3d87cdbd0590f4e2b45f6865deec9a770f86bd3909d3f74effd2eef6d5a68d53833f1cfa9cf891c0a4fe101ab23a3ef9e752c44080d32aaf2d92b422568939b7a695d2c51489b55fa0a294ef1f8df0561186bae1b4ef49198464b0545f14ba86a32e89b919e77754bef20307639411aacdf49de6633659a5da450fae82f796ea590aa543b509928eb983cedcf4120e8c711e4058517f693ccdaad4cd0eafb0ac0ef04cea258e2e285952aba9f75ed5e585311768318e358b41a7c0061e21c69560f6602affd8864213c254ae4db4ae1f0b1a27ff142518b8ce301471c235c32b5b19f4cd048ce99e4b40100e63c90d77fb9db0664ba1919853942855a4f14979d60fdcb2936d9b2ca49cbe1962b48fd93fd84faf8ee8952a52664c1992ccd51710edc2ec8c485c4932ea0cb4819093ea76a9606028cd2b9a60d20b1fb593d2b74056ebc71b79ecebd92b02da9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01cf6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"1f066969a289d440bf5f3b9a286fb7adee04c6efff57e00ee2797c7e18d27434e7664299daae2668304051cffa860a76a0558423fb114b48516087c19cecba67df642bb788deca20a7deead96ba181dff639b9128feab51785d567a1986c3d864d18b8d3e5d5a97e9a82dfe6331ebeed86cf31ecc554a8322e9a0d4fd70b0ec61c88dfd3185d46041f72f8b4ae7312a083280ef4cae700aa9b0e094b413b9d67876a99cdb59b5bf0f1136f880917548537b028da6cf281db31064d83bd57f676f749d0ddec2e439486a1d4c9803a75486fad6e64ac4bd87b457624ca6506bb9f28a36bcaa47ea5c32e8562ac58b1f540226f652e7dedeec29d11dc9c9bf65358a544b6266d8b17e9128ac76b9960760cbb35a9463e21ce83b5e4d3123f6c8c9d88ea9945056f009407f40ad1cf33c24362ec7f210b6048c82680c2302f57e793e8c6633377374bc585199ba3e4ffcf31f1f22f86603655b3864301dced34f3567ec64bb419932e3ed73612feba7e0deadd9e92027872d7d67adce708ab151e7105a70b50defed32e2c789ffe304ccf198f3fbfb685aae71ebe28459d60fa51f49b645d122d1b80edf4d500eb6db8df96c2fa9af3c3b884332b26c1c5b3cfe02f714bab9b6eb5e6f6f2df23a886e6d41c7a33e7efeff7d8123d9d5b9439ed31d3d2ba28a54209548d587795601ce5a005b33eeb4374355bf69913432bc06412b398fac79614a1fb8ea1587aec0929a812ff8f42b86e8d4b48829cb6e3afca6d20b1d9e8e39aca72b44fad89df86d526bf82992d95358c05d9b0727f64d25a578230854d976d6ffc4e0db1ae52c4775dfe1d3beb77e12fb7e26f09b0061efa6e3732f4e12c6dd945b91eeeda2e28257de0260a71bf0f1dcb7b705f4f630f1cf92777a182f574dec84a6c0df3bf8a4bd1f59cd8879e005d93a86489d1e4add2e1c34b1e7189718f99e93f72f5375f4f1b6c4e6c0bfc4b1acf715466aa3bb6168de261723c4f62b5e69275f06224d283e468ca3ec0c2ec77c6148548081b85ccb43c55dee73924c1da8e39b81496f66cb1d815c4ec6a1e34aaead70f77707602af27025504808464671556fe26dd9f760df45395cd3815d19b63548a4bb868625f76811365cfc61a646c518b24ea0fc7432f177ab918d5d5e824e01a4a952b2f682a28d9d65f20d09c97c720a795b1028cf8e402f90df3a331949c59470a7f375a691be2ded2469d54050fe52d9750dd80ba5a5e250039df62d051abcb9ca26c1ed30c413f865493c033cc030c05ce300e3df778c8d40d30427caf3202448cb9a7508464d998bb0f50abd7064bd5e8d947471b935d23d9e181f68a38068630c249caf0a1899c0b0c19735d2b8ab6e333f92297d816a2584038dec9cb87fa2d780a1eb24fdfabcd812483620260e5dd092af4ea2d7f3c308e5057fdb70a090140d8bdf7ee760faf670a1079d8556ecf74885eb54e47d8b3ba853e1f08b7d749114df5e2eead50a06d755691307eddd3c16bbd3ff4ac771d40e00ee0226e300221fca671b345c2d9ffd35ce3582858024828c8bf16189c8617a0d1849553c54e2c26c12cece2f27f2343f8adc8653907f4d6b461b23671ec476cc3ed935fa07b5926e9a103a561d9fa1f5eba80311d7fc7e7d6745087886a13e7529c8525cdb3d46d011f689f1b26d0f2101780cf1fc2da4763ceff1234290056d573972f48e733f66f30386e793256157ff5dbc47facbbcd81c45604dc6684293f2011814d275dc317538f686c6ecf166583937b276959f4e62b0bc2f7cb0c7e160eece99cf2254b5fee2518be59d5ce5dfa7959d5589bcb33776dc8a66c47db3b930361770f8cf5d42cace66fcaf0dbf263b7d9f3631963c18e460fd847c70f29371983b811f4493d20fa0339280f0a2ac6cc1566e7672b658b6efdd6d0440f17e6bcb4b3f910c96544d73b71799f219798556d5c5f76d60036109dca3e3c879c888c1a881da7668493ab5c1536362f812955006da85c6bbf262c44dd489c245c7e8e0918d624246bb6dacb905adccfb22e5ca1de871b649d7e66d2154052ad1e808e476679ef8d2bce71dadd976310f27832dfc524175a31491099652374cd3d4259605de42cfd9afdbc114652f4a89c7dd675c140cb56de3bc1900237d505770f5d748b17473daf14567bcd958151763ec99b4511e96a3f84ae718e3a2c5c38615258c1064cfd4b7ed8177da403e8bd480da010b8f7d3ad9041eff7aa2b9b6079c5958b5ace63f9e4e0f22d1f4f035e362e9e236c2d34d8401bf5225fbe95c70bf556fe2d21150b6d74d9dbba4216ed05aae3af7f23d51d69c0592f089450ef73f7a5a1bb28fb754dc77411c1eaaaa3abe834cac5a5d569fc4254b1fa45f3b086fa9a51fbf1edb3015ad0a29d76bf86c73a5486b35fd1e1bbe12f6fc7c0dbc783b67bf3c1738ea4993a79a1507835cf52586ce69e851888491c6935b74e38d9f1026cea973193e2119af0ed9072f16dabb9a7d16ea253357f48a1de81e605ef7f485404aca430b2fbc41328dbcecad85b92c0ba4ef34055bcd9849b55868d6f75a8df14ae707b3e446a74783a11e76772507b2e7dcc38ad895a2eebdde58af7eb1f2b22567a2c19a08eb06f93cf0cbe14528fed332325bc479382375e48020df31f1de42c7677a788fe2f1296288526ce1438bde7a9c20a2310b73f99ed1c1ed824b89679b435afd0cdc79218949567a16c0e2e03389fc253186e7b31f437cdd05c0c5bc09852be381e8d0e614e765b9d97e684450f9d5c99e9bab9f4afaed205ebac391a05893efbd6760689a4cb542fdb6c1cb62be6b9bebd21d2a1e9bc80e1a9a9139062d112d2abbb946392afea1554ee98ce41ee1694fad3e32143d250ee2b68ba1430d40b0a740c491eda5a85dc72034037b9ab8d75c52049c0d47e886aac9df86dffce6a8236ab68f4d80b74653ca0dbde02437d9dfcaafca1c11dc919338a2c39bab7ea082187766c4c598a03192bfcc5cfa70963f31db89dcf212d60e1be9ccb55e5d02d07fac9b90ca508e0f6c897184fcd40d00e058de4c59eca0b4dcbf44b46926e2412d9e65010c44d508c3d07f84a149c7ced7429a050b5facbcb0e129aa17389f74819093ea76a9606028cd2b9a60d20b1fb593d2b74056ebc71b79ecebd92b02da9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01cf6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad1649a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d410278a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d088a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c275219467845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4ee7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df45d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283de165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba4279d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a676dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df6975fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85abebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c01001e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae20340514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe9652a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc35f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e814611cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc518e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369ebb934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696ddbdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a221c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc8596cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a557b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399fc827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26118fb08a7fcc497a31c96d1ecb",
-				"20a67e046163c0124c0880a830ab726995e81447dd7fae4a9179d94f4cf1f82c32786d58785d395a7146617477965e524f8f5b083f11c3a53bfce98e91d084e38efbf23ab541549c683732cac86994e679ae5b1a0b4df765648d4a6e630d8256a7ae4ca6c680bc350062d628289ac12f445743c9dd77f128696418808b3ab396377363a8791e63c28d01e335f6c608b43fd96517fb7362c39d88a478ee9b9d06e6726a6ada9c1b647fe222592387997f426fabcb974e0841bed598e143eb976471747d7d5e0cc57898c5c89b97c14d49ad1a113f579690c2bc150c4148b2e5e52b1d21af88ed78fc2bf5ff523da8fea07e95223f440314697a388f11b7dda55eb83c1b78f416a86d455cc30842045db5d3c13c9295a45cf2c59d785923c457ddbc6494b3fa043fa1532a69b5ea8378f6eb08346c4c8928c295dbd2c42699e7988d994e7d79ee0da371ffa1ef18eefd8be8f4aafdeb7812db9605b38a5f21fb484b78c6acb40cb06c2b89d568ca8a7e84abba4c250c9859ec3621bf4a381452f5ee8d094aade480abd8e8856aa74dfbdc1a465bc3a2baeca8766b78b5e1a7398292a4156c980b7d4d13d7e6a287d481b8ada7ba93d65140cd860554e11812248a89c5b43a684fdaea1b682246b4c240f27828a40b2ef5091c32226e975384fc2d5b2584b347cc48165c64f394324dc31d17e46974c75ba1d17a92361e6d7c611683687fd9590763a1838fcfa734d3df875dd116d6cfeb42bf442c09957c351159c93a2876360982bdeb1eea942bf54a6c841d205609ef402026579d8e6dae8551b15d863fb90c6e39446867ddc37fa600ce563dd71a79c072fb54ac9ebe97113ae860276f2dc4adda26971e0620fc23bc0bb80a9f111a385852005dbf8b700206efb77e0383dc35d0f5e07b167e5479b4994aff1385f1677c6facbc723ed633780154249fea62124778cefff9cf3a597b0756b8bafd1299d8ceeb689a7e6fac891fe8c07e43269cbacf07d534dc0ffbf4bc422a4b88bd6fd4fd2e2426fc72532706e17b72ed6f8273f393d327c41fe6271692ed8258d4821855eb9c55723a293dafa0471a177598f65d88d116b407e60d4c50c6c3a7c716c9ebf63775f29290a57981ba88bad16535db2b7bd9e8279c08d9e330754656a5045602c376266028dac860a2851c9cb26ff97bb91a581c20c29829f74b234cbb5939fd0269d5972a472910a15f622eeeda8d35f30d4f8120fb6bc43007aa9f6c460aa740c664426653b969356c64daadddd43716fa6bfa07066101c38a26c87e936c58fb86157875cb72a5597c9aba74859ca200a037daeef702b52c7d56ce2546e52397eaed94a9fe071273b7c8113fe83af3aaf3f4e30d37c54b63e1377d89d62c6042324a466405066a077b81487997fd226ce2d74c6277f8cc2444122296015bd9e8d4af4c49b6deb43a2f84c5a01106f462ec269e5a5285073da8211f627715fd1845a9e0ff0cfd6bc0c37a5374358d632c90cab2c51c90fbfca63478fbb3544fbdfc20b4d0ccc8e690b82f29c99793fd87ada4e48acca9954373f1cfa81c9e450cd0db12688e9b760898e559f81ed6c316d6eb49b4932dad8c7b353e1415e10100432f72a84ba2586053b3fded33142dc1538bb790394c835723bb48fa8622b974f99a635d5f61dccd92bc9efb590020920d57a2b97d0d15200dfdc0e0930bd34affd8dafc80e32b6d0be1a39dea5d2945fff384e2dc5a35cfcb92eb5fbdebdf65408861c908f33c43df33633c6856e5e6e1c712eff6282211c8c266b3b00c7aebc935b178164ad43bb79c8f31e450c273e35622b3de39b32758a523464d3f4af869d4a1b2c495e24fc768a87782a610fc680599c3fa4e431824a665b067dd11d12eaea4e6eb2e105616985fe575f64cfaf6f117be2c586e4313d2b299185d594765eab194447e320f8a92068bf032cdf3593977f4d25641f58422a07be3dad73313b83438a3a9ecc7dddefbe9f805601056ba301b3b27404a49e93bed6b650b5748e2e13a42288900318fafa587a6aea21f58f954eb1b969fa101c4e23fa1f6e6bf566e0eb7436e12b528416b24fa5bce206b4ac219069273d6898a016385090e65497d8a6e08664753dffd57c4397abf3e24ce306d45257428d5117184537a98351d3cbe5927024a9f71466b3cf6413db2c0a99245a76f282ebafb1bc95be5a303901d3c6407b43a1a3a721ee20ca14dccb2f53d3daf9d60a6d555041cebc302cf4137cfc1a233195ace2a720bff25ac17f9f07ae0f4d3384f6c319f019b96d5f219ea8fe76c51ca352ea8e7366304e340303764ad0d07f668bcc40681dff8c7c72e7828fd779930346d25ee515cdff7ec505db8430e7eebda1bba37c405bc61f141be0933d9a23a00f7ef0000b0f0f12f74e9e3a3110f611b70f02b25bffe5d9199ee61c06d784d1e2a8f1bc1bc29bd9b2fd40b54fd288461bf18680c99f3180d7f061a512575a888fb04361e1f1852713a8c0b4e7679d782e2ddb9d5af65d0e2b64a9a36c15cd2c9b3108f7beb360d0da0cfa2b7be56d4b3a69f904a388af2df126f2bb66750885da0be0ed1d5a8cd415f1e490f750696a2dbe35c7e30db6f63b04a2b9ea5b917d703c398fa810f0ed552ce7f512724bb60fde68081953e320a2a5e71534a7bf252f50c7d87a74e474297ca3d5454901ff8c73baaa72c4a2dcc58ecf3ae31f96349a0ebd29439bb172e838d96623d8a8caed81613a34c3ab9eff73b6c793f5f49468be09d81931457bc7783e09fad47bfe9d0e10575a046251d9d7ddcb044407463c55be2002f5c3ea2f52bd0d09af9a392feb64a5c20aaf59b4cf4c2fda17c244dc4a4c0ca2a46cc184ea541dd3f9c8b3a36145bdae9e746bd0b3bc86cb486b816bc60576e85651aa11cae0b92f55282700ea838db05f493f682d11b5e8a40299d52b10f828506123ddba3b44cccdcb1c23032a561bbf2da50a3633b0905a1fb959556c7539a62d1dd43b6a9d3b9205b09a1addd0ce80e3fd79ab5f75fc6c44b371bcea64fad6844f21d33b0cce0817bd0ba55eaf1ea319087239e56dc4f88c338317a9fad36b5e6af699cb9644de191c9cfc76273f8f8425c1cea9649fbfd16569879e67107be9223b0638e6a03d86660d878fe016f36f76a21365bfe2885559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"21eb9f2ed7230ead8fcf75de95f05f7ff9d14b5c45a43bbc71d2345674c6b0d3fa8ce6194baf890e09236a1a6a226a8c86831ec35e457fcb59a47bf151a18531f87625fbd5761889186012a02a72d03ea585724167a3070513cfb8605352d2e82cad502efa3134d75041c0cc71cd1d7ac6399ec1398bf633f4109d368afb179bdd7032faddf3d192560c1f53715e1f2ef6df71e1f24a85234d8628a107a6678363ecbe5e21a7cbebc04776e3e70a93c3e10e666727a5bb981edcc0f89d1951d99ed4828c3ddf23d573c358cae7db77974e54e16c2af879fca63372b539f84589b24aea34062b498a8a97b32fde039303daea1f00ee2143e484b145a2071f357356a76a0b22a60f5c084993ee4b51a56e13f0b35bc41360100aea1dcfb0b1ee001efd5411c601d5b4fbc21e68ab03c39597a47a28fc93da988770f47cd0e342df01a3da4a26784ce9172ca3324fde330947a15d839ff0050f487ff04cde7f63d3c8e1fa344dadc38703f40f76b879b229b272c6d4b1ded01286868348c3d9d5049e8c581d114f6d671c75360a14b706dc809f0ee2499864b85cd3720cc9563a5e1f282e0bebcaa47b1131108df17bac526aa57935c12c1ac792d423bad2817b8852fa746c6b17e9e9561312253e4df503ef8b12a1c5fb94a103a08798060dd945ea0e6519cfa9ec215cf3d34d30f0804da6a3e8faba9e9323410dbfac3814ec213e7f71ba9f8f574429fc2a5086e9587becfc304e1b45718c168609719c9c6c3106814c242d93cf636d11477886a7701643c9233b814c1298d154b5e50f6946aa74a75f5978e6dd36e48bde5d3afa79447ddcdd98fb8396db095e99ae28168c8f12e3e72782f55e1ac04a2b44599876965140dee01fb472bf6d05f4e002c4601ee72ec8950b8fe09f2d34e0c308f6c4edf6ec64523af813d929cbdd91f794c8277c4d3b25df8e972cad022885e7bc1489614df154f9fe6537cb7dd350726659fdec3821a09d59b731b1c597264035a09087268a324cbf8136719500de1a1ab0d192a923e99902e8e889c2b5b064b80217e2e97c53ef228472f5208d88c9c06bc69a58967dd5399b47ced84c944e6473ea332527eca2bb874db89d43a433f3cc1029c00ef955bfb2255793dddceef6a4969fca27e8c235b76d3586600bebe781f9f59d3e69bd45f58b5a32d6f81831a753bd5a084e7b24274335b761d6aa3deb6293d4a46664d011ba6eb1055270bcfec70f645da0005fd1caebbdf183fa7182957885e45d8d2b4365ebb92eea2b5746f2cb3bc1070065d6e518b9b238b2a28726b68f6ceabd9b6061832fa114a6c6b9604426b7eb3fdf5164687ee16038a8f50b3780272b9cb19c99686737ca417684b55d39e99db3ea90c1aaf23bdb71576bcf24f2dd7e239c4ee82e3d0874cb03bf3ef362e17a6cd726650d3b2945d687457e880137a112aff4dcdbdebe2827e1d7d01035ced33b4d1b2e4e9ceeeec4952686a718c534f6a20111220bf4d2f5e6ff7fbf080b0d85447cb9b5c1e052e0428939bf31d9a5a2ee84908fbf0ffee84adec5063670a7386c51d4a7f00ad10622cc000f73f00549fbb1ec1d99b9fa4d80d3fcf9ad85b42d3eff1aea69b6fbef367db442b00d158dcd6165fb9694e18a4aa77c783629cf32982a92182e6823ba04b2e6227080e9df4c7ada20c88ddb75104d56e0eb18e447fc3bc0e9322114c810ac2974c6da609d8863cf541da61c2d87c73f3335a5532a6c7a02aeb7cbe5750657a38b332efc88a3d246e4ff39f49ff619c388103341a96f8667de37b69c80665ae8386a95e4da990bff87b2e59180e5859231068557e4b90ab3bcd1992c5ba738d361852c6fbbb18ccb29da5c4c23f0d4c12febb0db5516df2be56837929942706d0bdff1fc0609c9fcdc47e381b67f8c25630f30374f6cfffa0d188b7f374af14e56f70b83af8f3fc668dddeb028a2b241df6c74bfdef0e6102bfdb27622675d14334c841ffd689ae0badad31e25584c0be435738f4f71c32327db51a88237f9792594a0802622634875c17f1b554cc8be5d48afb5ce6a8c595b5aabb81f2b66eaf7f2e0dc411f5d04d8d92ae3664055cb9bbf42e8790773a41acbb37920f5a9e798dbbd4e1cbd7174cf38ea83059e25fea5793a8231843a82d17b181d4c5e514f611cbdbdea7ba5658f386e437838b90a5ac236ff89bf523fb374d3c99b5af4ea01cd9ce487b278925f2ebde33147f04e0e8e5be0a2c6fa8379f306a56da8581249dbd3fb2353275d94fcd6357c7a8f9e64bdfb6446af8421147069d7dddae83b7030415f79ef596b4e61e30739e4c4233a1e7db942be32d080c309d6ff011ce503f56f6d17d72dc36c203c0d072267f97d85f5a491a5208423db79670b5747ff7b75cab0573bcc40c77f56f8d3e291141d63160e0da5fbf68afabdd1c06b93d193035f4fbf3cca82e3aae27ca96bdd6e03872732e456f7e95b1963c61fea0f785f4ee970fbeea443560b9415b24c97e747ef28b6b7d56b8c006bcc6b4c080846e2b93d4d458334d92628eae7dcbdcf78379d953b4bb09118d3fbef0c33ba72c99b2778aae182bfda7bf7fe17fc3569ac6b172b52a6f183e83650215a1eb92ac5f7fc3653ac58498777c4784c8a0e830bde345180f88fca2ceb4dc6346db1bc40ce66fc9ff84f3b31ac222389f95c71db212e7aefadfe458fb787ad65491ea72c1ce797430da23dc3ffc3a682ab0cb4fae8963bbb51814a381fc36713856fa8f4b4d7f70ae0c91864e51e17d60133719ed0efb4d143e3ceb05a2e8c0c1c80e0091bc4748496bcab653ad84634ea2998bafbebaa3c61c2c4b646b62caafb5206cfa2429bab20dcc858ec7cb5da3a1b8b335b503cfcc809193a781cc8f6def3234f2358e5a199d7d855eef9f7284657a47c9fa8d20a690a614981544c54764378e94ae25bcda1e7b99da7415d977ecbf3fbd104052deb9bed64a7ec0d97d4c0cd56ada74ec954477410c45116fcb7916c9b2c8eee79470181ef8ec402d0360598262fbb8b87686ca134700408b7e1b0df027cb57dde058ab2d28f0ae85c6eda55adb1a6f5b5552d0d571abf7a4a989119da61e9175d357c78a6c29ade27869e9c0aa1fd16569879e67107be9223b0638e6a03d86660d878fe016f36f76a21365bfe2885559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"229abe8cbc651af0bc92fb2f812ddf8812f79935e8fe27c2d64e9a6ca072dfaf70825a3fa162ac44504c1f18cb849851e930af1c1ee4d202ee5d24783ab8cd16237caee308bbfa7b9f2f187a46b2f41bc5f6936c264eadfc455d401b8face01ecc000b602ee6c9708f3ebd209145e8b83939a15aac858c0a5079281cbb14d8c4c318a047f813f4a72b24aa00fad2740852c8a07085da8bfef31f3c1cd622d725bb471e9a67252b76d90caff85ab401a6cfab6e6280039059331b889da600e552422dfaed31067e8aa61729f7dffde62e7e5dc60658d50cf7d2c54e18af4f2ff284eaa1f31dcda001b3581c0a16b17743052148ce97dbca4b81ee6adb5c20303007cda5baf1dc3821a5a311818659c802e997fb1c6f159022016061195773930216a933c563c00d0b3e02d51638b0e49f0b5d8d119285553e0d3fc34811a8a28d8062558d37380a6db0a2cac2cc0c434fcf57a1218194725f8ebd9b3edb7c20b0d13c6a19ccf69a95471901b70c400ba25139b1de0abfe7cc5972c188b44c53781d9b03b65dacf29964564cec50176bd0861c38be323b626eec673fdba9a483b56192ed14c256ea2cd081d98ee58f5feb04b0aea16b1c644f61ebf81419910c06b356fd7ecd3fc5b785676a2e9f8c674d42f02b4829244367dc4a6b1c8c9405e584a2d4a68e96e80e6ed2db045f59b3b8dc0d92f7f83e235745d6a16e26af8d03131c5f418eaffb7c608f14732037f0de18350bf0334c101070243fddee7c9d64cfb178f2341cc8f85b5714b8c9a9008e3ae1fa3308b95f5d4390e7d5346ba2b6ababac55d3b558128ed833b6dfc691aefe873e1637b3e7b75db63aeb315452bb5438e6ab5b492cb4735c1f2fead844455718c4b7d4d4d2330879ab3163c4f56556c0c14eac7455332a9962e6b4df7a79b60ea3e2c63a0649a52b22713d25700d784f6858d87e1d2746368251413499101eda13fbd5257887a7e45abcc997cf7401949dfe83cc0108257a9076bad42170753f71829f8993b9a08e7c4a03d447470b76a7f494308c5cd051d129d239294699692887dd112740f200992fc8be845424ec831f57ebc32cba152808483076cc74688cc686f7198e31e259180778f4f227a0887e6a84531446c8065962a3b3a3fc662f87cba991f0fa5cd1c8ce1d24c948735324b7623834cff51c896093e25e4acbe3d375c5dbc168a9e985847cd15b2aa09fa8d73321a64a8f52874550b8739e344ec8e94c5722d68e8cd0b821d5f1322bf3592a83ae3fa605ce5cd232a149c51a1d88abc682403f7d843b93196cdd92ed403183905c9472a8981a836e7e9adcac64dc9d131a5b4fad91960e1046533b5064c151d7cb95a074871c46900037e7190ec97190010496982245f540f1ea7575e38343618d5996900172da9f2ef2b56aba7a34892bf8339f89ef2911761459bafc703b057ba700f0ef00d454d4d2300b3f9dce61279561a6836ca7a28eb8139fd4683a517345862960aa92dec51bd4232c87e31aad3e89beeaf6c0eb7f603741db9c4e7261d5ae6425474bddc24787bdadf5a7a5cc4837e6782b9602ff797b501fee984ae91d1a6cf4e1da4d7501495a9cc423c29a1e7ea520ed0f9d679360b903ddf6bf36571b59f04e61c486e8443be6254f73a2a68a51f6cde4c9845d46df7496cb80372a3f82fa7f3cdea330aba4af06e28df020f24ad3be8ea67ab382867e2320b7e0e4cc5264b3175f4c6724006d584fd6a9e47fc298f4ee0537908c0e0a8f0b9a6815b28c8de8d0fc6244bb39114c769b614ae7d7ffa1d07dee38f7a5bfd9725bd94cbae22f72d246aa062e28bf2bcd98b3c8ae0f6b90b676a9d63fa29dba82cad1d41ead2d4bd2e41dfaab342174936bdf84e09bcd25447a46b42fdb8559c0533bd858617d267428bbb0297351a343e90b68761aa841237fce15327da87bd818b40ec3453b83a598fbf4ce1c87f8c22f9100f8e42236602a928ce69ecbfbeb5e2b595bae50e3386ff453c6bd63281c8aba06f4345f8c833ae53b89d124ba89b3adb33838b94f2db7f897a47e1cbea2f98e7d2f97717325d3745dec7a53c261be24c58d3da2220b0ddb55e55dbbdbebd599494cfbed180687dd3fa4b7306e30949f489b307fb1649e8a930c3b9ae14e63883b64ed46f13545165a1e18efcff6a3c032e33cb145113a7aff7b891b7bc2747030b10255d61241b049473198c8eec0e4f5abbbcac6dea28e994d022f7ebbd7a023b52b2c611cc3b5e1baa55e9df23b5f426622b6546944681328f7308f4672cb8f18870590623c08a378163aebd33987e0163ae06a787f192e6dcf695faf2ef03775250590d0fe5aff8fe5f1b029d545b6c19836e55b358512945d12ba68e57fbcdc7e3acfafc9c6d2a5aeec28744ee16a6354e9eed0714b234707adc2b2429238941513d226cd2268e6c50c9c0376ea8826941ef8591f55630baf7f2c09a6347295df96cd7ffc058257a827833968fd6e6872d9f7955a15f875dc5a28b7c8c3dffe76f7312fda0d958aca4e4cada3b3796bfee90d5a4b912926c630182b1ca26cab9ada935756c748f92aa8146fd8df58d8a99c674f62af1da4c06feb1db5f3dee0a5cc286355e80918608305952f9cd1717bc53a5dcc2c4ed8241cea86a4ade03dab2cf21857e02007bdeb47820a0a01d4f99c1b4395aa337f698e305dbc0ec483c9ad46bc32cc621ed7aee830cc07549d9ddcf9925e300f7344003d268d7a7a53e691db7050d4f9176191bfafa1a3d6e0478afc3623b0e84104b0277ef6f5063ea1807505a6601469a7a8bc6a3733cc0d4b897b48b8d282ff473f63b824011a6c57c837ea493c6bece3197dbced0efd8ef1ae79ff8af965a66c023f6b9eb6e7f77b4919f525a81727cf088727ca6878a03ab558d996ab4edf6020e11e266e7deab0de96c22610617254005f02e85a5893cd120a2ad94812ae1f46ddf5bc1d48e1da2f9b61684d39945dc9093252ae2600e385f4e95012184bdb9abd782c396ea283bd56fee00c9d96579b465746671013cf23537dd5dfe5046168b7262567060cbe00689a709493c0f84152d25ef350fdec045994210043ffcde66e9bea0eb9c150a96e94b684bcf63bd0ccdf13acc0251763b8fe83b4f1861830aade0c337d3b4ca6bf46b51f3ba485559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"23a2267c7d68930fcd162fb7b743e94c520197ce16e57ffcb0af6790245d85cb0563e06fca2249e821a8b102ee771c4e38d797bac3a79eb26092deba93d6cd9a698cdeaabf442d792fe01ff325f998f85c34398ed4856f070eae3a2e71e44dd427a34bd6a721102b0ef32c33b49db2d8d6f43e024f5cca67819829a650268408ca49c9a013770a979f6a42f39f7d044a878f7a46f8433f506b426b16163ca8d5808e21f9dfb39e339812c452d68e4b63d1f1059d7bd9b5ce9ffe38d687ea6b9cc6371e8d0d5c2536420a3ddaf845cca2fd488acef90f55364398ad3d1d7d592a23c11a1f0c368201af728b81302f1b43e7e4158b909089153cc5c4d1941b7df2a309c44e13d83de34201f36b1bf487883e04e2acfab55e91bdb56ce54e0d61df000465847f9f90a908f800f6df66aa100e2af0d513cb720e2b119cd454f93395aa216558c4815a3b62c0829dcb1ff49f51194c8e376b8bf273ace333c60af6ac0f79e997d059dbe876290efad863d81a7a31ade170e0b4aba20f529ba6e1f92847baba3b7a62b5abb2b4d3f477e077581775180611977aedc9b65db157549202ce291c7c9ddf32fe6f97c09e36d3db1e7da7a364b415b41ac0a894ee2650955de6077f12ad30d3d750aab5a376783c41eab41d759875695d61f491d7c445184337c806f4709bb052cd4d99834134c1efbf00c68d9828c595633a9b4565016d2062f1b863f28c12788f0f123324dd89daac5b90b34350817671b47bff831934c6d60d30523f6e4b903a0c56d1a7c558ddabe2f9b80063aa5036a888d98713e627ae8d654261e31aae7c0f631d18085c82d9ecc8f1548eef4ee3d89955597a056a6593b6fc77ff7b0b958387bedac460637e0b3f8963e80e84da162332c20652e0343be9f26c81dde308b33c5170f1547433f2292ea696db9b024181dc6b0e9b7c979dc78fb4a93bd8b262dcd94f05eabfb2981c83b8fe14fa9b2a4983246a506e66bd05d64368d31a60034226c7ed2d35e3eead5b7a719cf7b5074a16ba7c7149fb2ed242ae3cbd0a3f84d37fccff2bc432a26648b883aab754281fc0cf00556ff74efb73f17a4e2e9c8c862cb5ee0efd886a7a80ee52f8533f82d57bacd5ac1a2318b5cffc9569bbca5b27d9b329fc68b56ae8561ee188705653c618e38ffed8acf01cbda08efa0cf6cda609c9447dda0d74fc383f17b9d81a309dbdf5c954f6a322ea6eadc41bfd3681cebcba78d440e779f61bfdd203b05069231b33d146a44b34582fcf63f1e2037988f14d29d6dd067b8236fa613485a6a48ea37c71d58019b6dcce04fb77f21d23ecc6b9f734f996ec571ab3615cb40997c0acb18ef3e70ec8a804b70388049b20f64efb4935e4d3c62c132112297793776720d8494c7921f547116a4980086a9320062dd0c916113b242ec234e3940114ce49de4e9640bbede2ab573725aee1d32f6ffe91d38093e4d9830b15bc6444fbe75023e26d348724a434c25259c2abca46dd6b4762ae3b886221a81401dc4d841161ef7cff07c243ed9685cf2d560f66735231f7fd297e0ede53559ca3b035d6f2f8950e1411c29a2a01a589793b4fd7d0be45b105fb9ea9f494ea5dcda6f7656ebd3d4271dedb62605527591e21f2e45d44e21aa03ad8794579c2d93737ab092741ac153a56252abba8f95ae2557447028a60a592f3118f348b42057c69f4ffc8b022fe5827e40964d5c4cacef85c2a32b36fde4b2bcc3973942fd67fb147fd3f1b26b1d88cd83f6871c424eacbfc418b19804cc8fe9b48f85121b375a8489680ab5f602731e71af1832dd734eb921e1fc6458957fe106f5df96d2fa0920997cfc56c1c7ba4c13af81c90eff571050269a9e6e572e46389a015d48a1372aa0c4e7e18d98efcbc2c2d2be7766eed99f7bd7807f3683d4311028d1ef6b0bbdd556caf554eb4d7c1922c16fee10a4cc7789266fa21f3e1aceabe0cd1bed8dfac4e790a22cd032ef7971874ebad19690198319cdc4c4ccfc8171d7cca7e7b1035870af3ac0b095853cdf7f9054c461b3f95c8ab4c9923f35b097fc9e7e76952025f95faee965d534d4f8f0a51e8977331b00160e4ae5897defcf8556afa12230920ac7881daf5ca9590c523f230f2dc9f504c1316a5f851e507b5e5698f9f8209e66f0d91ad287b434f49a99bf159b96190689732a6878a64c890807ca862fb71f91f5b8ddfd58ab1d4c63773c32989aaef8f2987469dea29547e7238c19f999b7b3e9e8b5627545b9881bc0af6665a1c730205413be5529c41cb02a7d65d7f1d4697bcb7cab8aa9af608108bde1e31e7275027bd73e3ea60c10e0f9d6f416939d3848db7a8fc3bb47141ce105388441aff4bd20fb28bb46a68e3f4b614d4a78e4d5847a03c400665dd785371b73085a895e9795ccafc72e0575f771603c9603c4ddb5f701edf6483f2039d52bf8360d946c205560c9388398f023023aa83b3e641b39e1333a9d9fb69ce1781402ef0be8aa2c5c880e5ef4b03e0d2f54f74c3f54ca4d8a82d2471744b69e54e883f921de4418df44165b1ee650c987f9541c4774da12608526661398f2b4d5dec9d767ff154e669149e765d5e4055f0fc981987c40d9b033ed56efb91c8f547ddb33542951ec0f9586209ef2f801f0c33881de24efbb82076c2cba55bc3402c2a6eecf34fa37b7a2c18b04b72b37f1927f3a8fb9d906cb08da0a6a41a311f204f4bb958f1c4447d7040e9708920dbaaa054c690683a197723f8e07ff4c62c6328ed66e0f8fbef365013f8346ffdad9b42dcd747de63584450c10a6ff0ade8c7970388191adfea8db6af91870e07bc3f0dea103c0cb0a0f1c3dadc445859373c47acf20e5694c23922f4e10f17d2b5def9c298a5ab5b73e8b6702670d17f91617ee8ecbf11221f8c25975699ed9b76f2895a3c2c9a16f1ea96b5c893039eb6be0177f9a5f2dc65c5725abe6eeb60158879a5ac732153f9436353e72e8ee1244045305dea94ab66450f1cf78f1d9ae05636998bd1f29717f2acc7f01695fd9ff9da1fc5d22fd5b4e3630a90520918a84c31e3e3cc4e93c624e75747aaceba351707d39c56962ed12cbca38859dabbc14ccd23af96e5f99a6557cfba7a3bd0ccdf13acc0251763b8fe83b4f1861830aade0c337d3b4ca6bf46b51f3ba485559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"24a6c9d337c58d1908420f010a3df1fa396bcf04e3778ebb42a27a4084c17ace078f5d1d36f24ec0a32aa45ef4aaa51d44fa71689cd6dcae06e6f5a8f5b0145a241b9993d1c4719c07ed2f571abf0852f6cc3841c1b7aec3f8bbba9a53bf378b288880d0df18517cc8dc13b25376fdd4dfeb88ed0254a8f902e5dc5a31af4e42547dec66873ef0803bd6e377168fb6cb55985525f0b71198cc8352e6aafce7fa80f3383973e22b4f66ed5a9239d108baab9bee4fd73bafacc376a6fc2746210a00facca815e05c36a6468cd0aa7a22ba61a8a38ba0bea83ce589749b028123aac322f964efd0c26da3244423a7db19f3db2142bc1d64c3b8730bca7b5accf58d07a720625ea494513299dca973fce6ddf262be932edfeb04001a5cfd2012f03d7310383fb6ec85f76dee656148b0a1a844000aadc8ce76f8127bf74bc1238f15a03698cb013a42a8a1f9308d41aa106e3417e8630e62c452b4e8e17be0f3d23fa07e6442518c4ea3f11ed25c1ded6e905b5696e1ddb9e54b9b38e1b45f5fd8df5351680d71abd851788c82e15ffb4cdaf9d17f32957be30581c39cedae1bde0c48c31e79ff5bcc5c8418a59efd867f5eb3a2d0559d3dbff1712b9228d45980f4f8ed3e0bd714daa79e23bdae2d08f008188646f76747ede5014792d59cf21fea6dabbd3586ecef23446ac7ccffc01bea2cc393300d6125a7e7d21a60503ee653513db94eca370ea3c55bdcd5b726ee3b3162a6561ec2ba7102caa325f660587b298ba33bf832d150a3be950b55e0240259440daa2252a9408f72496b60bb23f6a574fab99a8cc7e069f22bb055689130bfda18787d80a20b01dd1ee46505987ed1d545741909539fcd42793084b6cc4b5c49b4f685c41834c97223b8d0fd81023670c1ebf3987fc589506fd7f97fcd5d7f83762d73fa97dc321c3d632669770815ecb501acdf7cad03dc91aa9859ac710683a4389fd84f3a78d5784b8d4c497d8dd69c7747fa98825c01f48d81cb799860b478faae2bc065849e948dda865e0c71678eacc87f9391dc6ac2ecb82c0f47baa472ab8d6f6f040708e7c998f879bf3658ffb9a975cb14d3074c44ad709b43796b5b5e8710cfb912c5c2c56169c6376309d518184b1851ac8c2a8f49c08651e4ecdb59f078e93c647288dde9aab5bd42da6fc24a301294ea8d9121c0cce01b17da3754fc424882a696b85d7f7a8fcd85c5bd49c735b3b707e21d8c58732d56291b751f484ee1dd77d35ece16138aa89ec68b3ba08856f61772d6b86d1d4b86568de014638c1da03111bf1b5337c47b1c44f88215487b66f6ce39aebb7d84164d97ddb095d15942dec9793a79f3967c78cbf696d6b572a6143a00d57fb5c7185af26c2084423720c47e7a9352615008bf2808d1251363f45a26507460a54795394832d82ab898b9aee8fa262c21edfb2e3478c78f61d5da683406557037b3b796d4fa57e4a817a21810b01422dc0b8f1bdb68113f5f50108a27905b646fd62a97072513c2ec632f2185bade6fefc0b2c0836380d5231b8e43739e4f937cac153330d263ba42447b633549ab00d6c898cb7cbabb86c0a28323cc47f2b0093652de36ecefa60f87f3de13a41cf7eebfe685515c07a5f893da49a3918ae3ea4f51df0d9e4cdb3d311fbcb717d4e48e39620e25302ee43d505fdf8ec97607e198e8ffe7d09c864f9328b57f5b0596981812b07c4d74b62594e836125696565a262d88e47f7b7cefbec6a94f67fc7429402b7fa4f49fa8d0e3b88a937cd3720196615b1a48d0788a7fb527fda3ea5bed71962ed519447676b2701c357e8f3abfcf7cdd39d7d5c758cfa4a034e51f57fdc8388a86faa94dc4c9f0c8fbe1fde47e9231cebdf83a512dd95abbee28eda1736ff6173f589f8b99620fa705f127e44fd217607498d89c3efbf08107804958093c1687bc91fd1c7c27b85943b01b7c3cd93bed1f317c552f4e149e91a962d16a0c3b3b4c5e9fbc026a8a1b527f99f445f312e7e585803790291c9dce225772490c35280a1928336d4eebaf558b76490a286d36a6e123fe9026f7ad1276cd829f81b8ed8415f95cc8fcb5533137348d609b8cefad35a9dcbb1947f8346bdc7f5d7bdb3699340a734c279851e085730cce150fd336597c570a53567e03efb985dc3032ac95e711d87ad3a0357c60a67779a02c141605e24cc2d68d065a95672c069690184563c889e103fc00c5d96093b3675adcc439fb1464efd60905891217b299debdf1df34c93000214f0a2629d2cfd9d770c0a4ef2205699d2219c1c00675089156da99d9625c485e4fc36a970ae2deddae5a2963f5ec76bdd02508d858b54e8a65842fd3953f3dbb9816a6f7403e6798dce1b455efb3fcf2fa169f5546adb82abd6301b06ae084cee5db07415e2fc9e0a29abdc316ca95a310544fff3210117d805644e46cc5604e8ae9415c34375c251a5169c2cd0f1e474a1a73da0a307e9026d22e5ec40d2a8ad52e7ad1409bfc97c1310e3e69c96673a9b63c22ac2ac1ad682aab9738273c4a11504d5e106084c2afb7321dfb200022ac7ea0c7e018761cd627455e4de0ebdc86f75c7c54b1641200f8daf65274b332bbb0f949b6ddb619aa434b350f980f7683564e6f7c9c611abf325af905fbfba3f611c6f1365dd55fd328ec13fd1f5d0f6931a8e6c607e22a4706ca4df0ce18cc21c0bf4afc24e0391573323dc3ab7a090710ba77a7e9e49c0c012c91aa4d8bba1a3016ce6bca023da54ca722f060d984ee2187105caa7a38108a644ff24b2414000a1baf66bbdf59d81a55761f4992af45104d06ac0205bd1868801a76b9e1f0cfc3798f7121f6dd0efd765232e71275d2af100f8c56b449e3d3fb1d50796f6b5c9c420844581c536508d0a0134b1cd4800b4fa2ef99fc22e9b03ab345a4fe68f8075cc4405bd6489a7e887439c04270e4afa11f29b73fd59c4afe884e70729d5d2ee673d3930c503501acabe2891c08697a7013fb3d07d091946c61314729fed05122a4812eb9298d8ea9ff31b5b6bbfdc5dd230889454ec5aa1a8d2831269c7824d5ae5c828085fcc90592ccd17e602ef38da81e87b7716669909c7b8788687bf86d69dea7fd514426f5aca5713d2d3fc976247402cb805da7e28f525cf9ef0646a64d9f9200ac85c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"25bc9978864ca6448a943b11fbd92a84d65662eb7b0157d1f4ca732fdb3830a745f9c9fc03db135321ad591e2e1ecd8829d7370fa4c6aea2ef0da638b328af9b9c56caee4c87a54a2d229b8b92c90f8e46720ddea820c65a3adf256cc2d003e0db9018245411561967487aec8cc16374870a4dc332c23d25645b5774a39ae6a7569f3ba1a2d478c9a8a506cbd38ba1543b339324cb0246a38f5398eacd44c6c7233b0024e336be86b1df3cb70d6cb208368f3e44f78db80604a68f92ad8e2af27e09ef43e1c7322b84cabf0ff6d4d575b57dffaf0a6bf552094f31857c755bf1ab4550a79456806cd1d4672e579781b24b45734317b2258d5f6dafc8e4dcf4a4dabe2bc562acba272387c39eb5a0b0576583378582e81930eefab9d8444689af9d8c1b48095110c2b2dfa047a0c1d01f89ae4f7254fd3251b2da6e9c2962318c4bb2cb755a05b8d1e0c337a745a813e1e61bc8dafe542cd082ffeac50e263e24812fe07f99db1adfb2a47dc1a24c0dd4928d6e6f2d3513d10c4235b952db87fa9684daaa72b7e112dc7d3bcc4a301363a19ed1074fd97d80b4b79292b8dcdcd7da0fd016103fe56e81c5751010c91c6b9bd3389f79dc65f5fba4c94ce0a10a95c9918bf85aa635f22effcf3ea795d6d317b7fa5086fedb79bae6b160a34a7738edaaa0316fd0fbc0edae6c73432e2f89c2b2b495ba90de4cba7d3ca9ad05a32b4d8ae47020e6446e6a90c8ed552751c880a88c689d254a8f5557e4285a2c6dc7ec608f2cc35a29262b8b3d5d0cb2bbd6a0e446435abfc8379cb843706777269ef111421a3e06d318bd64cce886493f99cd9a835da82d4e750d9b4c47c1c131c1168c2b3743301a4ca9dd56f51e1ac56e177f9306565ff64354af74abf41418441c999cd5a02a86cc7990deaa9b1ec97709124c4f0965fe2ab9a03b52663eab3b7638fe6e04852096cf63c3e2937ab402a1572b24a8b888e1ab438fd03f288e20ccf77889da53046c142a59cc4672188ae881f0557ecaf4deb5dec582ecc2a7774c3b4442dcf7648adbc237cc9a7c80d6a2a07bd66cc71e5ffa865e19483e4b10a47d06e5254aedcab1114f620d4d7d1133018a9ff5839d2941cc919ce7a059f058bbd935a6c67e19d9961c42793e08e5256311274b22031a67bd1381b5c88bdf969b8c3bd0f3eedde337a18472fdec1daf465775da3341679b814a33c6ff93551c029b1464f91937ca4c955e664bec9a51eb382cf41bc628b2fa88438847c88a6b6e606f9e47b52f17ba463026ddc83fcf622cf7d92b10af7055140da03af2fb3736fc992da4078bf12bd7f1639d191bad4e5909ffe5cee6295e3f9ce76aeb254722d47bf3d7139e35d65553aa66c9675202e86f9023bd509de53052c5dc96d5be43035f2660862ce704e987e3935a9f71cb08f83d649f535f10c4597c6661ba5565df3aea46a6818b5c4a32eafbe2f4fd8c980ca25e55fcb333b6ac0fd7b05643d0248abafc29fd0abbf170ad451167d491e18d75320f9568e486fce49e977b1f525e7a90752c6f699731b9bf1f1fb515d580719b4afad0c4c104f989b56f91f86b3b9b19e04704537bac918c2198cce83d4ea1e80f9a0cff443ffba78ba8154e7007ea07e442e47f0454aeb7e011e962fac88a094c7aa99c5bc134cc19eae2b3c601ed389d9a26e065225cde17621097ea54c611f7b17189cda0b030768eaf155ad69b29dc115f4ec3f98797cbfc61d8e48a8def905ee044334d6ed003eb3515f0c013a90705e05005d85491a107bb4164932cad16fc225c36711e7649a23b57187cbfc3542271aed653acc7aaeb458d93ed03e025b0397caf9aa1a02aa7b2d7e2f79a55068184d6ed4ba2662c2219c02b58b1cd5730cc3c3580cbbab4e648aa2f3a4b0f669f08b7f772163157e98ae6117f9033a56179cf1a8e6face3ff9c179713340eef6b6b077348ac3fd77501e4548d1e7d064643c66f954e568482d4abc5b531852206093a9b774ae8a06795c7b7af2d1f6bdec69187095e83f78c5e3f437d87244c1eddd1e3282513e690b71cb8c9171f384c0f1c586d7af9f54b9072893d51cb64ee22934e88aa7910949108dde72dbb2401e245b24a4f85957eef16e71fe822f3a1d9b3549484f8ec5cc2292287cfe7adfdace67c91deadee2978138b2f0736cadd71b98d55ab9e7d0fb7b49a09475ae85ae88b50474f6808e78a253ebd76a8d62e53bc459024614f7f2ef6b3cd660a44d1aa629fe5a1e193bebc5a6872545a3dc3a5cc5dc393571860621566f122c37f945888e7b3a1f1843c7afe5764f08a4e27c8e11eb9e9ab65c5aaf436ef3943ae2bb1f508ee4c2940557b1626e40a96578cc987d4be305e817cb789a26dc8fd8721d83be2f02c71b841feefad09273ba81eb066e4633c4cafbc988def2544c9ce88fe016b225451961dbda821a95a45f5024f8b517dec14b8dbaf36d9fbf5eda95360270e91839c7bb13a5954e5f5d19de8f9ee69e6c91a1f48b1040cdb8d3b5b0ad559bd4aeeb3441ada040012fa9d571c0f4f5296cee83b828af80558521b2e48da78b82739204a2f53057ce8bb868944c8358e69c9259f6b7ff77ca54988c72344756a34ed807c9be718dd3a3e4e489fb94909681646f97f50e0553f63ee9b3a9e8d05d106744f8dcde86f7e59718e9bf5af840ba7ba942e5635e4697e34d8867106eadaa88e941fb300056f16cb3dc12538869887663376bbbfe9072bc6c70fb44f8b06f860a23ae140935bd7e851306d1ebcc5eed8fcf5948bc66ddc71bc01d7231aa871c3933ded30ca5efcf2d7cbf52913854268344d5688c67fd73fd4e50eb3fb86775c7c2e59e22bc2b98c4bc6634141ff0ed3c773d6545ecd560347992f385111e57633159c9201233cc9423f283dedc13f4aeb941cf5d28c44d46fb187ae7528745780fffff1d6f44b18601757fe3057cc59412622871b572c227512e355fec914fa8a7911c691fc2ff7f28050670ce26d198ed79b6d67a8361a7dcc28cb89e75d138ca7a32b253ca9fb4afce3918cfc9082203a45e9af91c8676debbfe6c2481a184c21170b28e273afdc5e8e36ece68c2e89df8a3f8f600e1bc5f06ef191c3b27ab3582e8626f5aca5713d2d3fc976247402cb805da7e28f525cf9ef0646a64d9f9200ac85c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"26dec253be7ea1b6571336de8917b8b0a4d24f128a37bfacfa0557ed1fcd72bc3f8ed7d83417e7544a8234a6a3ef50c96bd3e3e671fa2975b1642e13e8a6b9c80fa17b46cc77d902391a2863c85b9b5b5e4e73962aa90d9a322d29457bbdb18b92e844b7a1d4030e933fd748cb2da3bda8d97042ef8059efb433c9cdcc71c4a5362ac20167c299bc373fb4c16cc38724eb8e1c133773013f821bff92aec0ba65ba8687830edbf67817e501c56db9d00bd85288c0de09bd0a0e86a46f63e4e508dc04a352f383c9074f41051c79cebb7504cf10176ad2abe778481c08b4e5dc503f82d29cef99b6e6a2b321339c1b12b1791dd1621215600f85ff8190e93fbe1cdfce14186b12f0361eab462e9885b32d2a551f1922964b7db20ca60e25ff6fa65a53e971531aad3172e11ab7503135eb97303a34b31a5d9decdf0c891993e76ace5f36671311d2f2cdd07bc1bcc1aed0b7e2aa26b50c3244a7860290aea8794471918e02a2eaccfc329285880a85bd43853455d248bc05f2345cb5abf19a0c0784170b11df8751e0a716263a7a37143f8e5d71847c7762ab69ca6e9ef6852f84a0bdb56d194d1289787a1fd84373d66a6629f634f17b63b91e42a2f2ae7669846d9d747669829fa4d2061847c483295fa8647cbf3e7b0cc393ee095763639442eca202e710d7038984486bbb9d6d8d6792c1921293e5140597c2271c06b26e5125a3359dffcbf307b4e706934cfb986b83de4d288b0862aebeac408c229a4726674a8a37c1ecee1fc00806c8336e69ac2a6a03e987801d05abdc00b9a77a7f63cc130a6c952387ec3cb49c12f0fe8049296fe9b1ec3548827c6c729bb8c1dae67fddac8ea477bb45b5cd321fdeefc7f24fc105501a43364c6fcbaa646508963f8c0848c17c182ee295d99d482624d2712b7a872adba3792b5822a39444d8051db8185b02c4d3042b9011a96f91cc98fbfb6630ce6032a63395b5ed566ec6b7abd39718f6c37140e555db7bf75b5808abf866a4f55f428d866109a668d3eb0d1ebc7c02e3a49b1103e73d59d16693b1389f8b878eb47898afa16d3e392691aef932591f5d16b2b0cc38d56ae8f6df19490e200bfdff185d8630cb2e720b2823adfbe3134fac6ac012e7242c22605f193629b32ae3a9785a0fcb45208dcf6bf81aa123135a9b87e3cbdcf21069efc95f132d6c21c7e768152c1328d669973d9940bf162bf1e45f23db1fdd5a44424e3f22172e3575ce5bf0a791bb32ba274f72da601a20309bc15f18847ac625f514049635fa4bce766b7b692147f829eef763ac52fc93d3a09d8fc10b7515c97e2f666a9e372706f6b3e2d3fdf69dae11b98266ebacbc9cde2bce799e9184d0c1ac77dbca2576b1a177a870d205f78df26e3e02ac3cc50ba05fd69dbda7f0a2a1198f8b90b6975e42cc26db94fb4d79a028d4c5079570c538e396110a969837326c12cf6e1811af9a9c73083e3ca4cdab8bcfa5a952f59589853e73194d1187fc7d104e8fba55def86014f3c3d4973802fbf894ee4c39c7967328115878aa4e8268e705d5c248087de89197d1948d060a56f1d9bf022ca31e7aeacc30057006b04b61f9e092cf0a5ee38e5b83cfa46157793c46170d7cd02db1215b91c9e73920af9bb7de5d09123bc93ff719f8c8a2e0559121897b12a1101a8ae9b8a908282cbd9d526b3ad23b0a7a0fce05eb567ddfd52b5fec693b3ae5d5ba07176160eed13eb1d01aa6ac91f15c48dd0358c7a3d7d02173e2eef5e06a21f9f0505153f3e84587062101f922b3cb6caedef38372106e454f33a66e64a3056d443dcf9b3c1b628e6ecac2939c511c196885ea48f4a20cbf47ccb0ebd247387c8a34402e66885ab4fe9027101c79957e8026396d2dd7ea4ea5267dd3fcd0bda0017f9a8a9b4f0d0100936ac65cd9373920514af897bcae920c81540c1b966f22eeef25b08cfbb552b9fcde5509035d4063e2824d9f5abc014420626e24ae59eb995f453caaf0885b8068c3f23b8267a9142c25df3a1d731b72a9f8e3c77a9d5581cc181f35ef03a219679c11b5437660859efb89020dcb65ea3f1a3dbc97af4848f946caafe150ca8198808d4007f8f3121c26a33dc6492a855171b809b8709e37d090cac44e02c44a3e540bace6fd1cffaf93d653778a47d5360e7e8f30e7c11a9441d552a486afbf0f0a6053fd16b739b9cc2fc36c789cbc34722c68d4c7075bc23ddf45223d31691dae46daf3abb6a9c3c196e0421797870f813e29931384d069e6227c873c81d74a8488eecbfdd6ff198dc0afa84eb2a3e6c4c6c89c8cbc172f7c8e2c252992c693424a9bbe9e25dd1a34d5d396ccb649049ea73843eb2b09143d2a6380f6114a1a3d7439adaf3b9d26cfed71eb2e1ff902b0d7cfa09200db5805cba1343d61b89430defbfe7b2b8de584eff8d448f69ff77faa6101819f8fbefdf279b233071d6857e5ca55f35150188b5be235311b3eb632f7a9e8bcbe6793e1fae4fec9bbdcaa899c8169b745feb48ddc2163fd6b356e2475aa7ccab3ba0112a885905fb1bcbfd7e2904263b7a0263573d3e289f5aeb69017ef1718cab3806adc44b84627a7880c671f97a2559e11a344399d67143dbdf2f43992582b06d9cf5e1b7a1c73293b12f5da236dca67b83140b570bf511897da0bffd8b098626bf76d1c5ec744854d983529fe80b6718756338599e83a1eb8e4915d60329e1a6d4ae845771330ac40c2fcdb32859cff4f0d0564a653a9d33ec8a650e8419068f57ea36d36369da36c70f53f54b158862f0cb2dc7b96259475a340b0f844c0ce596662c8fc9444928b5d3b4b213679c3545fa78b87bf78f2be27f73357c6d1552b05b17dd6bb541bb090972daa83262ee8cdadbbd6985385dd6daef5a23c21673a3da91631650dd0e08a6ebc8dbb24eebb8c839e47b3299a197f182353494e387d7f51a8f8792a09f57b08fe33ca019f01267a97028af7f39150e11513830078e6e2f7b51876f7f602c02b1d07ddd2208088323e806f58ae5750074f4d45afef7cb652133279b140a214c3e294b4d07560cbc4876ed10648f5e3b21ecfa2064197ba30bb056e2405cb818bc0485e7cb3f01c98a69e763ff979d290fe0b3e583882e5e3fa0dc92868707539d89c205fc09d86e964c764070361c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"27896d88eaf84d8fcc3610f36b89b94d85da6b773f457af8d87a576277376414ceefd4b05482f1f207a1ee7116e3cc4fdeab134faba2b86d865c65f133efa89fa73c23603161940095e5c42debfdfba0fb45ef3a59c3ee132de58960da06894753232d1cbdb0efb7500b5ed0817a119cb165f1b0b14cfc057d158069128926253730e5b31386e239f5fa240354d81895d0d8c105c4e0650a16c50a246d82f36ddffbfac557d062818fd1cb990c2847d9870db0346b9df82820f421377dd8682d5ff3f7622e61e70a829626facc4199a44beafe4e8adfc46171d8e33dcc7a9135242ea336b64b1604916af8f3d2f49c6d6ef73621914553b7b8efd22fcc2da1d6dae332e1b3a5b770e1dbb0f3025e666d5a0ce78c63f9e509d8464921a7e397d582a9e9fa7770e5065bd3232945de15606ac0d5b60abeda1b2055019c46065f74ef1b0d1ae174423cfd1000c07d3173fbedb2130e527ae0250ea85550efe42db9c25bfd82de6569b3e82b4f6bd9ff0405d79376dae0e762a0387f6ae2bb066ebe2e20bcc63dbf3789f0b8fa5c271cd92a3d33ff0612f6812d47e454414de205ff6ffca96d46d2b9d2300d2dc4568f429a13f7c887a55497188dc46c6bd5f556ce103af84e16b187ac8db9707028bef1acfba12d98d295c6533b54314d1868f337ce08c8c2111ba845e2f026950d083a925f9b84afed1a077ca956b3d73f0298452ebe32be8e488e262da9e3992d154fa0b89526bc66b1ef3af36ad21a6f785981997c55c2d67a61aa063e76acc173af8da8a983bcfea9ed849e1f6b281c8b04a86351d0ecec00b42c3b69052ab1808faeeccd7438f930210e94ddaad6860325b72a0e84026a134bcc61c959610496d094245fc944053ddc5eff10ad14c4f93b1e0773dbcbbd1cb3485d4263605e4fb3026974989551ee5a5569a91c3e146fb125e5206723316923e453dd8180f9f0f5c05bec1b6c462a45b16edb7d8040a6ace59ca5ccb329f4dbcd73e5bb556542653e17b1293814a3fece1c9725115f1b70f7d8f5c99a9cc86166c2c064fa7c19cc5c3870c250437191b00eefddb31b97f193f7c4ed12341694f47f01015f50c6f1eba41386bbaa21baa30bd510b501fef2e440b6c0b2e298561696fd70691778c430620e9a0da8fed2817dc2c19fd2589f3936a6597f00650670ca87c22d1478f0945868ea3846900661ca44192e515158960008f14d54ff2fe85e5d1ed562e4862d6f4e7f45414dda58ffac0e8a723e3013013bd72bd99092f05e003fdc0ec7c9bd1f9421c765d07f2cf342ad85eddd6c976a57f883ff9a4980cf32a7cc79ad4dbd6869a73df53dde7898ad8ccd7b4064a9117f4b00f807c133c374a069da2eb86b2ae6a165f43e5e7fe04938045a7f52d58fd897e5ca7b0ea913844b1fabfb6fb05013822fdff60b17d852aff7ae28886fdc2670b9f23d07e1320aee3c10ee2ab2779d2897ad7f90e4d6998846e494728c0d94124b77cc9190d36bc7835f8dd8cf1781ade3ef3a93569ad9a768cad18fd1c3a658ef71b7bd257d569c020408da1164293bef004668cec618dc31ea70ff6d2bd8700bf77780cd3b5c3534a8f8754565d6d44b82d497a680c8138bbed767dda8086d67c264b2568f7829e473e35602ebc5cbab38a94a2be8807fa396ae2fb8da9a82d02a6a9c461760709a62479531f08b9bfcf6d0ceb15fffffe56d6c6674dd7fddf8467cc6972d404965f253cfca82ca7d49a79dde0ebc38ba00ad7390ba7ec1d6a91b0ece73735022ccd84552ff5dba064f247dd2e859104d5eb2077b961f61762279a7c289031c083edb3119b4cf3e6cfa0169eaf575ea3a38a26e7cff6e559bd30fec423c7b4052073c900e03031927144bc25a27baedb07a476e15f27082f97e509b229b8ff829dbf8bb07bd1a0d31bfb0922f3f86bd54683c5d35600337726eea57b17a5e98faad765012e21522c80c7d90b6b0a5e0da5db701a464b1a2e85ba50162b5c3b5863ab63a619620e6397b13a7d7654fea3d601684f91740de4a0f19ff671cd58a91022c68f4895b179a709e85637b5197ee7232f8862918bb976606e1e491b86fedf453c047b2582a5258e6fd76eec1eedd74c7e53c777f73f222004965e85df5658079e21579b758ee5d24a2f68fabff1692d0ab7d359ee5618e5bc4d249f8ad1de74a7b33a5d9e6b5961ce7937f9de4fe1fdaf136929625a56d734c4898665e55b29a5ae90c2d85d92e77c8dc866008bf375932d3c42980da4544ef7db288ded83dfd871430b8d7cf4b8185184242586ea3a0029598226c9b614c18da61453e390de05becc7097988ca1ab912f960a705d024092a6c35fde00eb05744cbbf7852d4874db683c6f6bfce2f2df3aa89f3e6cadba90f4acd63c42824c78942fca3d5dd5b969158ebbe0e401f7c3eee1eda2945e2db57bc883083865474fba7ad3ccc1831639a1669c0421989b7eae327aca2366d99767f48517743169ea17ef92957ea49e3a5a1fc1b77b727933651ce48eef10c5bb15687cc96ee64ed432244070eb18f7f6852f5be55200842b50bcba50afd03db33e4dbbfab7813614cdffcbed89bee7800a9e10a9697df80a07616372bf6126c7b20387ad2a9ee39b1662d3c294dc177134ab8607c63e2d5f123e39c8784cf640b13ba019ab5e87369181711900acb140b749f841c7a66785c23ef38709bd4ae612c11ac915431bce4e6e4c0a05485a06b70e4926b04c95e410e62cb13eff48ad7252360c8eff8e740769990ed552aea1fc83eeac71711668ebaea2e404eed74b1bafa297415d010577a8112bb2de93cd48d225a2613b03084929d306c59fa97025247a49ea36c9d10de8fbc4d06ebdaeb4894eeffc386d47d8116b5cf7bcdea0afdae378d9628a1fecce0015c1ffd617a9e6e0254161957cad458261aded95f3c432a9220c1ee88f9523787cf81dd8872a6c6c32896a558fd5407f25df779bf78ec8645eea6cbc7b2df8b6f680e3195e1a8c84dd6c4fec740f66d874efed2e1030c5511db3422ac951976709b37d864a040e7b022a8e0030bb6d34ef443a00b1f8adfe232d5a30417d9ef8858afac1bb7999ae39b1c1264de0b82410b34b5cc55ccdbd290fe0b3e583882e5e3fa0dc92868707539d89c205fc09d86e964c764070361c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40aa64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce54718a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e10e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd75df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa09965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b998cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5efa8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf67515049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75bcf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b18973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a054d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c276ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a676b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e846cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d15168cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648dacf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce224d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca70dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f99170e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8aae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"28f614afa0c4a5b3087844c15e82b74d0bff64ef28d6e4a761ccae96a01b15b578838099feaedaac93320f35f6588319c44caeadebd9ae7463e217b77d8cf4fc025be780e037da76abec3af5c412e9ff86285bb088ac566ed26ae1862a1c49359f557d8255db34394b1fc3ad8cb7ee491282aa19765b028baec5ad47a64808a5ca768fb2f0ebe306e015440d2bca571aa8175e3a3518751a50ed41d3a096fe6fb3abb34448722c9dd5bb306585957381288ac3c1304944c5c7fab04f673cf0086d9507527e374bbf77ebb9cc926e7da8203434a74aef4f0e656cd55505291dd3e488c6c9460a134a417174d739c2801968e831abefd502e72e232e3eeb4768588456121ed8471cb8aa09c54a7b2a07cae0b608b8f1002c9a347680f4cea314c5b60fe880520fe5c9644a02792e089476b45f3758d64d05513b1e98a62360c26df59d47c6788ad9133e386b92a185e8e8a701da6e0bda5ceceae0561ec6983ab5a369d27320d73f3a6ab6f3e068dd694da60764abf4f5fc9417fbb398b74b1eaee458f2b25ea219814c52d8898fd5af90d2f4693b44c2101ad493f1cf44f130f3a09ea215771afa4b7a8603bbae25a4b6be9db958f929fec62746036fba2ce6e32cc689439d87cf15cf9e5bfb66470951ae4ac3ebc66eb9c946a9044a6dbc543fb97029161c8d588cc6725e5027d443234f63313d9b8a492b851d8fda68d44ab61aa15e9a220ac9bbce6d67d78e44216b8ca813a1ddbe3d22bd93f976c8371f5f75781d0881dab889a8b025fb34e8935b326d4ea655c0af31c78d96aa56caa27b053427934e23bca4dcf79e84f2dbdb40b068a79b541f92a5a6f1d49f7c3ea6d29f1d0825e5d6a1dab897c7b619467f19033b8b78f91a21a066634827f825e5d3e8db8262531516052c123b47d28f4c016a06dc8099549f4c8865dc7c65652cdcc965704fae5ff55107f44e5be67e95b3b875d5d6f824050fa3f811a7a68a2f59becbdbebbbb0a06917e5fe7bf25c76b21e9068deb8d67960e0d903c354b7b6248d3cad4db0ed732b82efc012ea84a7aa82dc857ae000b989462020353ef6c7a8ec0c25aa6a804b33128499683a4209c83db87e5b5e9abe873ff81408f9fc7ae93f1345800ee097d6a61079d53e551a2652d6d9a102e761fdbe420b9708f604d9ba79c8172efd9b6fe8f2d948c6bebc53cd34b977a5262e2f3377550a17b70761c1f6dff57eed1e3f884c8685b9d5783b7c0a2102387ca027a215433baa249538b92a65e49fb3e5a6db27fc3c2c6149bb62b0559ed4558c0b52de3e2710c1bf7a091080081863444472bc7d7d9764665a0fa9cb97c9587420c5d5e704a254c897e0d5c7c77a8c4b680d54d18493a5d7b5583c7fe6531774965a09d16ea379b2ec371d06de939b867516dd46bc7d08cf7d8fb23ea733dc2ae68b4485be6ec26ae9788a3bc7083ad7efe0410f3636b9f83b06c6165fcf224d793688db04b2348956f76ef2ceb09781ee2c9b0f56de6180af828e69c718f5c5eb3f3ff269e11c8fbd5c75a8561e8fc24b230a0d7e0499b28f5ec8d5c2e668730dc16dd349d20f090b16d4f88fafa6364a31f768c1f721b45b75f2638c7e5a747079ae05aa48d52fd9991b5213d59c128db177adf42c49cad07d5bc4dc8f28bea9e45cdb4ce466647ac19b79b8f727c55e889bbc1e3612fe0d65de2a04e47070336a1b16f9ebaf3f6aaeea519822236d380ab62192659a518a76f4923b5380e7924436d1785884d2e9249b149340cc2b6279de7fc2115fde0347e7fd9a4825a6996b7d2023cb343178adb38b2c4564813850a6c63c612c1853f7464f64bfd92093c615e8117299e2eb788824f3fe231e34812baccd34c96ceb91c21e89cb1fbe62673c60be2c85e89a7805462097a781c008f8d424adb6b55c53f06979388c3297927c6c21253dd0085c45e497a550f8b6e56a2ee4fb392a313a22d00a83b7c7a95a06039040699b1820db1fc30b69643ebf76035bdaa8bd31258054db6d12f01e5fdc06b1414315b4da16d4eff65945df02ed524971bc07d54937a0372fbf3407217d5e73a34fb84b6e4b12793e25739251d3ae6bcacfca804acf0c4f257a9123343d0a98fb672eed3aff551a3d6deddf206787704a910ff31090d6e3eaf90b3b7c92ee708eca6e0e91cd9104cc4c9ed0c9b41a3cadcafe0b1b6fa6e54f65a32726be11d4b80019a5575175002e93a162ae931a1eafa5c3d4034a544f1a41f8404c0768682241755df7fb2a0b413318eee9bb25d0092b2b2ea48a9940a4543250309566eeb4a4a214ca05b1675a5020c1bef77ecb73614ec781c79c18d9c78016dd90816c9c953b9a00ce97048e8228fdc31de8a356356744eb106e23a04fb730337855bb52a70a11ba91a4fda9b9d32beee354972a477b57aa95529727c4981d4cb7b480c2a76d78802c380e78a4f73cd51f88e8ad8595df5afb5e2bbe49eff97cca50b02b265f4fd80cb749b38c4591051c7526618d8c8485c1854a150d070c43f140c11f2280dabf937203b9ce6b3b3f6893f1bf889dbd51c132f65dbde9945ec9152fe246fe8596669f3706ce89788970292f11ec149b781b57e04bf8d79073a0ae66e71d5877f040ef52f28640979050d3bcf943cacebd61ceda0414fc4a876c987eee793cded762bcff517e53e505e7da0f4e06f38066bc3d31a646bb3470d5e07bb8b964b3ef2719b16bd54bf8ae7564ca0409ae47cb799561c87f05ab1d05a7747d1f01c7f18a3acbd5e6bb46809c10f387136a628b3507577c11d1b878caba4264a10538663b55f56c7e5c16df59a9ccb98ed46b961c6bdd0e59f97e11b9e58cc71a23837b3d3d7e7cdeaa1bbaf2546f631bd85a66cf45ddaca0e17a3ec8bf2ca003ff1f32fe41e127414d5729be295e5a3aa1065ef7bbbdaa4e6ba1249809991955d43a3f4759617418df5b55d3c3d83094400710d0495d7fa48d8e90987898b0dd9f6905091016633026103fa2c644e0f3aa680e546d5b439ed6a6770b3598384bf8681ce59cf02ea63656a31afa309a490e032d7c7a978f6bf722cd473a9fff34ab28697e6cdead9091b3ea7c789bfa93e20d7bb43a9e28b454ccfa676c7fc50bebeaece5c34830cebdd739d4d24bfeb876b74e3c8deedb3981cdcb91305caf56734a42dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"29f0d042cf382e23c516767502496d166ed9a17e81a731faf1fc47a00cea19323d49688ca8e7f30a6c0c2321a1bf0f033d2cba1e46cdba5af5af42a554a962e4b5257b7938958415771299401c194dc04b8fe43dc464421de90659708ce19cff38ec7de41719f1893d38a26eb2073b6e90da4aa000cf4568591d0d13ea2483e186be9627ec32c697b23a3405384cec5f5284700bb5b0ef84339b5df55bfb61b76201c2bfa857450ee984dc2f006485bee0602e4074b5aed91c76033862bfe5db796e2da13064c65914071c0f17aa59dba9dd7a72c795c86888cc6ab7331f8c64c72304c06e6a694556d2cf8ce8fed1a318bd99b51866888f1ea372d497509b5aff1a5e3222d864879b9c393d4308b738de1359994b29cfd2657a808ced006d434cfa205adc51568264e125becba025baed15789b2c237a01ac3d0051d61d8e507d5960be40bd95dd41723cda68435e583b4d5238a650c9d22652902830837ef98f5212231552b1c82b87ee0b6b0b87fc091d21f1ddb10a3b1b8d0725366649841c5337e08db29108b8fb9ca5a21ebf696fe53dd800c80ffd2b6411a6ec499e417f21c48211c66f4afecc4f4b98aa2ca08622ffe0ad103dc3cf456c6dacf1238da3bc2f5bd8bc4b30a7d5c074d26dae992f3ae50d9c6653c376981d8f767e7e64b0a6b1442a840150c2fa4479c4e6a8c5a17021519aa5690c2a0351688714d0e6f62d5826df5a9715b4f1d2e0ed41ef17386f63fdbed99fc208d48ba0b72efd423e7afad70dc39b9ec06c024f17b16316571570b58283bf374590c2e03106660add77bd5ae124e714d36c3bccbb64ed81b2a94cd0d7907550ec86a642e33a4278be937f6ef8e717aeeeac8989e354cb7447fe9494271822eea7978e31212d7286527480d98d10bc7e49b85884089162b650e0de0ee5477ee48c59a4af8a786dbe4f07c8f68e737d6ff6886e321c39cdd7525b2f81050947d00e4df3206c32224048fc3fecd59dbe7416ea4a95cc8e4cfacf5387f3c3d31cc5b7bcdb8b3aca033dc6d1416d69a9e19800d3603bde59ccf70afa44da6a33545591685953ceb91c589b7d15988b62a42b1ce798ef204a548b037c1786da93fb24f206801e892444bc4a7976938c61517e1584f74435f6ac0baf814686859077b60ed259b14e5b173e73aceb7a6d1d1416e1208108264d6e83aa9889b2d70e477715231bef262a1cc1fb5155841738805095cd22eb240419d2de6612b0de92dda7dfb76d062c7719228f15dbd7412b949cdeb4ce170ebf8a2c6c09de11a4b8e7142d95f52d8f511261761cef86d91f46e5b8319345f6c774a1e9c99c7567800e70c2107a09c12d1be41f1f88a944d6007d1bfa93155a12d08c05dc223961b35a29882fbe295a60992aa6a4d8d403ac3f2ca52a425a084e3358d7b9b08cb9becba6f13b57118f97442e4716fe770774751ff703fadf90c39ead77b1ab5ed9efeb3c3254e7d0abb84c164b0ef9f3c64c91530aa2cbdff8e6fa2a9b1a2971f296d168c7b0f0729de63ed50e44035f6b55901d3a5dd95e6425385b963d0136100c94a58cd1796cf82bcd95a91c3932aa277c50cbe7f57386119bbd6ad5adbf4f2bcb10fdb8eab08bcebb8d6f42f4d18ddd6e98e62ac65bc1309378b92b0bf317689f01a29f9a81156537183acf982dbb25acb4adb9d52dcac3e36e69319d988f2d61b82c05a5b6636dfef4430181213e7ba7c2a27197c61924e5b96b37caf5f1a3c528adcf63a150e9c924e5a189ec2e1fe74e0dca98c349a90e675aec475cf17e7d52aac6451b37779be64492fcbb9b86fe3b309356489a7f6560c334f388fe67fd0d41789b80cdb335cff0924a2d95b2f4e3b08d08c432ac510f2692ab308de59f5b8dca50f902fd194035a8ecb00249f2b9bfb02bb94047f850086d802154b74c87a07c5489c92fbf0fb73a63e82baa3edc7955bbf400c01664e05c09225f79d9caa05fb0c0338e209d5dc7e4ba45bc53aa628de76996f4ab4d9a006169c8d572670c0834938b7eb342ce952b3eef277875900b2f6ef16a23100464497decce0050bf15def0adf5b56404ee94da39cce349082162e5ff4c32884264957b3c1335829e29a30a9db021c29067becf1cc26602307c6ba78b92b8018f9b0b531d13ca343a3ea1c7f3d777e23b2c9cd565b632702dab9d1ca59094c99bed6a1e068f6b72f04951ad79249146819ba138f95a070c5b113c75fa91afc897017d5c27480aebfe92af5f2971435b937d2c13e61033b4f2da6e485465521f52f5092eaa757cb8fd2fd0df9bf0d7a3666a43fec9f6086ee06ed593f5d4caa1322797173549e084ff1e2f19ba1ab810f5730ef4a9fdd0ac72ae50ee315f86daf7e41f4489b7f0ab09eff3db4a51533c36d96850468ce4195d676498ea2080502cd5d9106a87eb7a173f783604167ed4e3b284bfe2d60c54664a658cea446f8a46a70c239be742e8fd17d29b1fa9a366c18eca26062049223aa88a27810827c89f5ef96dac2a80b86239b9c4cec7abcd6891d8f87813c443d20393028fad67b8e28165c5514ccf02efba80dc4a1cb9e960dde0f84943ed546cfdd416231886c1798aab9446603800ea463363dfcdfc06ec11266c4fa02148fb1e982df1c442165fe329c670901560fb13295827762a6169c0f914476fc7d62d9a2493aab61523bc72fa21b79b13bd998c179dc45918eb3d47dca449f009888038e6b00e641c70732131635bdaeb58dbc65cdb62605e2f5b0de90438a3bc9c35075cab86ef37727f447c1e40998d8f13b2681264515f35088fa3ab956b3686513546a29ec30831b9874b01d38c2b363439bc65ea9042f4f5e84f1588eb6e7f3338d0cb1c94bf6285b8125b74de6b2f0f185b734237441918245e1bffee55d22ce5bf15905dc12685acdef155a2dcbb48fcb6f1825a73541e89d840a940226e1a6d92ce58a1664f58e68c812a27e68c6d357fb078b48aa4e3f75df149b6d2bddc370933275a55450f502315b066bdfb7b9ae752a06e7c8df2de0beb96ac46cb4f78cff84766946394453b6e2905d1f80a24658684e2325798ca417832d9c4bfce5e3263fe0f2e8273f302a12282dea597d85a8ac67e98e03ee5c34830cebdd739d4d24bfeb876b74e3c8deedb3981cdcb91305caf56734a42dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2ad8d36352d84d652b73759aa716ccc320c0ee46d12f8cdcb844729ba3576def05d765294b8ef5d97ee07dce30ab9a3f4e84dd3cac9c6639e63bf85ecc4367bfccb8c3b33016c4bd92ce370201b0db1791b00f6ee2891cfa299b8f89b4714069bfd06a3812906683efa178c8ccd0f06b3564cfc0c1b4acd70e88ea1b77298c447cc43e7371e567c01c8d723a615b446e5aae9511aa8a4a994318c7e460415011f6459a1c3e9894b45951621f01f3f2da2e1b27e3093dda70b830d80bc90ea759536725b3fe18cb7bb8716bf944362dfcc81919d559125087aa4e401a6bb03831ecdcf4bfcfca85625643fe6edc18ffdd73113d8800b614037804be90e527e3a4a387964d923f057d2b56e4d9c3ae4530ccdd5bcb3ed791523de6dd3470dfb5fb20cec162d410ea85bc00aff738f6d6617e41f239fb82e9a90f942f90e71b763c158446939cf133df1b6e65ce768af3076bc77052d96a2e439f174d961a649dbff53f63e8dfee7ff50b62dcaa44d9d2b5b5242d70acd996aae16c127fe57aeb55babe5138a67c7dd9c8fe97fc7747fd1dd8e20689811e0937407c133a38ca7ca1dfb4a2e3e3f97980274e8317132159cef36cd08125330d1df3796081a8ef3c8f355461b5cbc7eb65d8a1a748cd8a4c8437ad017da281c6f7acd30275d18f08be8366b6593b5e44365960ef219c01f04d2605bbe6ea21c097e86e6f7ab6fc17592281229017d8d3e23ca75685cb44b99c0c204dcbc6b9519101c19724565675cb15cbcff83a1877ccae3409615373e7509ff959684be9d43862381c63585e53f225fd895648f237afe638ef6bc2452dc4bf7258e695e3af11a51321d65a9b655310c31b347f274ff83edddc17b7fa957109022911afa5794810fd8f7985b6c070d81b920505db93075cc1a392cf6af2433a4d8a1f2f3050ccf9be48a60f5cb8d82600f15c0995de49675a553b41534c9eda7b78018c65aa53dd0cc52ea9d4ccb67fb0dbe07431b4392cf1409871ed302cb3e270ea635d81145bb28be8fbaf1bbb3eae47f3406d42a56b172c9912b936712c5c5061e7e1c2fcb6d9d2ffbcd1afb2b377586faaf2c8519cc0f8bc81581bb64762369811cd6cdba40421a21c66d20aab11aa99fabc94673e6c26b0107932e2c9220170412ff0e5a758b3eb50e5ce0e80117254b4139d4eb9fb0bbd9e2f58436fe6a79e14d7b13ec634a1e67c2d803785e2a5839a5a389be0cd50112bfda94ee4a213b6beb8604fe1f4a4a1f3c4384d6a417a5caab90b14ebb5936b037d2d7bae0e7e45ad8381644bb089a0ba0a8cd492e24fa2847bd73df9c5c7d534f64cec2f0dccbbb6550f163963a360e0eef7c8fc3edc0f61a7ea314c39563a5861705f6011bdea87b163d3bce94c1d1f5d416f8d675c128e86a5186f87d3fd3fede2c4517919a6e089ba5efc0651723f70e42fc0ea0305fd1be2b368696595f32cd0b16de87b44c7c72cb42b64906a997a5e2b6d4e340c51178513cac57fcb40a82e1fb7260f2f512b00c7c032fbb3eb58c5133876a6f6ff25221652c9d34b61556d1bf3661c58f38078bef7842f8dd825fb815f0d37c8e2d8df21a4e4614e1708a40bb5c2fe6bec4a3b593039a114258aafdf669be738db8b3f3d0298440e0bd9b4b90e085fbae0737a9734f79bbfe9225fb4aa350b7e90a82ea7391b32be8749048d55e8735361d2c79e1649ff3af909829054a9883a8de52f16f5f884a99c28f769b353e7679f9ba147a18a1d2fa12690034239d8043c1bf0f37955ac644936a99a70ba6d02d96283223bfd4e7a5ff0e6ae1a24680a67545450830b6ef4865a914b3afb295dab712529e4b194dd3295ad9b18c7f412de792e3f07a25ad30a55bc45bafdb0234dd702f3b0e186968d9f190cb1edcbedc0cc0ff6cc10ad10c8be092b396d450f2502871ec6d69869ea303953bcefcb782fbba9e6f905df7f43d01c77667953a321deeb72e41650caf960552859d1ad6ab68159c5a200b308b3bcd8c8b8d44176779447f853b07f730090fe14b6c804411b7516946f87b65aeec6c2b5ba6beb4f135ea1d82fa4f3ecde1c7a8beb8cf32a725c96f5c0de502137eab3f083427437d3bae5694178f92ff0da401d29367ad86852cd246ead40b1e669696f39d5265ce19d1968fe23deb81f53a99e5806bbca13ce9c50d313e67789b82c6886f7dd0ee88eee9043af3feb19b931ea6d7d394e1c8bb0c4305d39d4ac4be7f38f0f442814045c568e1b7a684f0de2ec24deed0907ceaaebf122d20c61a739d61810df0cfca063e3646f4cecd7eac1e1244d2fd3bce1c04af45862dd0affc1c569dd255bdb282045e5b53f5635f43b8c326555131dc8ad4bbee1ed78f391ac36914dbb4ef5795eec30f58a7148006010bccd94191875236a12c70f0328213ec66e20add51b7622e6757f3641e6eaab27217ff5ae298cb35716889e9dfbafa07622d4001416a1ed8c3f3328e930e229f5d52c48076a56cc813bf8937adf4bd5e3a96c764daaf2bdffc927a1ee03052488934aaeae2de9583e207dcf0967e6eae4471a8991921f9a0c621f7bb4e69edb3c7d15d5d49ee4d0e8fe9f01f4dd96cf0ce1770250c195c5f2b7c60525f1fd8ebd57c81883ff3ac5e1ead1d25fd4b1c28e4cae1f4f0fb77057c5df4a6290c1a97ed9ad44eeb1a323bbf4121a15017ee33fdba05fa3c714b9ef9ec8c824a0fb225b01019135f03443b094c8475fb3893f60deb50668b77f13a268e389fddad5939f38c0b556b99c0577c1713a55e68754843b67c740920dee90bb90198736a110a448e64e73f53053d31ac8a0fa2acc04a8fc921213bfca3e7ba12cc75af6a993484f486889ab5e28722ff744466b79a72cd93288b889e89db460e6350287610ccbd34f8aec4ffe4765e5eaf4ce1caa0ed8226537a7d2c6fa51f6a45f95b35c25f0d537a133a33d6e65d913ca92cba6060082ce95e3d5adb72105d30aea51a6d0c3adf9b72f53b0f56c7d5d510e7eea287c2ee01cbae259ade57fbf4ba66d75beb68dc466515096d6e2ad123589c1522ad8a35b6e7a10e8856288f211c6fc71376c60994b2440aeb4dffd8c3d3dcfac0d07481770beffd462dc7101d1924d878bd950e456a454a3fc23c07e5a2c526967f21e61f01ec768939fa0ba0ddb69efd43144dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2beca89f874df15e8d42c25754e84604d14579cf8cfa0561cc0438778d0bebd805515daf36ddad53f3b5fd4216778d2c9d7957c73509f43d4f1fdf7e981186cf1517284a2dcab4b6f658ac0334baa34268adcb084cbc7829affc78cb0fd9c9bb5dce848441e1b2582f30b4b39bfbcf08efb24a406af97c5ed51be2c057ce0c250e8175fba83b39b47c1c690bd13cc3c9e51c397496759f74579bf6406f1643396ade6aa5cd22e9e3f6449ce8f143c1dcf03303caed31c392add1bc6e49ba4597d84ff697c789d134a818479cfdf25514c7f16e282d88704344921a6213a0c88f8108fa62f934690b22094e7c745827721a5d8b3ab334568e4fd0f0361c73b673ea3dcedc93d73d3e2767f4ba510715b25d90399cf8b0de92a650d2b55d22437716a9be719f17b8a3267735f2837b86a4cbf9e412f392f337ec0bfe1b6b29bf1eb65aff1b07cad4f7d1bbdd614ec844bcecc7b35962b631670a8bc5297bde9d5ac9b33523730e37e4e83dbc9ffd367c4f7cf3f143279e4d43d4eee8257dfe72accf65e34c15b8bbba4179c52b38cae85c01262dac2408ef238031ddca9f2719c8b4dbe9891ecd9a3f47a7ef91fe894de950f771df97ee52c1df1e3aabbd41a050fdc7a10d2653aa81f0463bd7ab9c2441a8fc6a0d3e14006038c3f2cd01219e25b9f98841e25cccceb78b67ad986cd89aeaa4548ecf11ff0983bc2e1b02f62393138c29b9f0c18538b3f8453edcbbeb44d309cfe714223455c69b84b204b74204d1a0ef46d30b36feb3ef409976ce56ddadb84346e90d387d5fd8910ec407d3ed1c632fbfdb58e85fb797419d49cbc5b902bd3806c483994033817fe9ed6015b4dc4ef5b35e14ae5ef1667844cb570a2b2f344dbab4c9f5a7a33a334e704d3a6fedc690088ff331b371b53d2fb0e8f8570fcd4250db8340ff677479e81b92deb5484627ef3c10c210db151eb667c20c52ba4d9ea83f8f109d9846a9dbf51a061b42ee6e98fb26986f87de70ccb22d7ad5151c9f978882be28e2a9dce608cacf73e15c08e8f5eecee0e45ba428730e619bb25ac943d93de46d122fb2e52d992ffd54fbb7d4ea67a6cabbd175dc0146c82ab5f5c248f51660bd44d24dd9f6e40ab28145003e6c1925fb0a0152dc2dba0d92f20562a9adfe26eeb82ad6424c805d95fb494797cf882d4e5a11438a35d2e29ee22b08c62980d656176e6049def1759bae024a55e24a01f3dadf1953759017f48f30328cc3c1972dad13ebc58ad36975a7bffb3c1a0f08d84ea7c8a6cbe0c0644fbc095291551f232623431282cd0a5ff1e07ef997f78170d1e61c6ed4995a02d6416f5090f8c254107b6babcc0d0a4c9b8973228e35a4fbfe1c3117e0ad3fb7dddbba229386e21ba7e0f4c22584cd09839ecf3e432a9ea3d5fe0bce55e410abeadd51242484c48c464a64e3aa4c440e5e0b0bdda97f5b55cf175f2bebdcad0dcc95633c58037974fe5621561e2a72b4d821a816c2ef87544eca26646ad262efe6d8715a1594c12f9ab1b5682c3cedacc2cb0a8f2ce7bd822ba6c09f7acec55155eb045f18abba40071cd3d9670e64eaed9a3ded5e50fd09d2a2f19463358bad2e0206f23c683aa36d07bf73f45dbeb565e35d04e2429552b5d6b0ee3e5eae0b05fad9ab47897c275938b7c1d4cbe27cd2a8869eca252a35c64a92d1915957053063567adf1fe1e7c505728e80b5b75232d5468e703bebe81d8f8e4af4956e31fd908ad454dc3852120e14b73d43d09022096a67b0ceb33929d71bfc521f06434e799ac52795a99eeb6c37179b3c477e52aea42b3389ae6c10f88f0b86fa4f15d6ffec4392fa3e15cedbb14ca9bf20a0e8b4428942b182c9e7ff55034d3c00647b6f4838758f255cd788407f2927df0bec4ebb097e297bcd641c85eccfce36e238da7e5659a2c86c2997d9ce43af5028b97a93d4dc879b879e88329853c2f828f451d1f9aaf09bd250c78a972ae65f927e9fc0d42b5d41652fd8f5981b6a539a05bf183a2a9aafab420940839727b3e1abd10607dcb357bbb9a42873cec7b94e07d89e62431cc9359a6b21b1b0d188c7475dfb2412412abcebf7362e9237ce23ad96175c494cc612bba81db7b1e25269403cc7c7f9d567eb5cec3468c1bc4bb2909fe0402e7cf6ae55890cdcaf20925be73ccd7f5c4a901fbf5dca81fe4addb08f8792dfe7f3401d0b773508decbdd7a68a6c65b0406e5ced5a8d600bb2e0dba6c62c81c91b2c104babb238e40a2501eec80eb2ca5926d15a3dce16969075dcd4578bfd10798bba7deb1185d54b9de44f2467f01d10f1434d160c70414dbf8f470ce8055188dd37877b76483654249d0130a3e2dff9566c8c4c0e4e7a49c3c9566a3514230ebb3c259d023a862d6ead2b6c039bf3d625de70a0f949aeba7d5ee3e36188d03ec4f73b7320a4a14917432a2bc8badacfdc44d3e27a67143d803f3b2cde15c4c3fc0c44d7217db4263079328beafa908e58f2cc50cf3ce8ea72e3afa2116e1029e4272a778d856bb554d301c51c612a50b1493fffee9e1af8661957542ec9d0e752fde6ea1a779629c8c420a65791479af9d0bb3c6fda262f123fa31caf73269c5e2b225f29affbf643baea8f9476d374f196fcf9f84c002fd94cb7b962e7571693acb3218bad4d84b7a5059b514521ff73bd8fb8b3a56ae26c9d887c487dc3d0a7efb976313fbd463b8155e8b0b9a4adc4dd047bb801c4fbfdcf40a36e34c514f0a30575a249560021679514c73bd9f8ab4573361fc154a8c4652e2bfd3321cc07f8414149ec2fe2577ed2ed555bb185152cdba7d261888ea25255d9f6cf03cb6e070982d9c96df7d1eb04a827d3b8363db91862511325650c883a0b5b7b859582abefef6e53a40fd44e0796e77b1f153335ab5ce46134c70250079ca2203cf669c46c83c98069eb250384eb0c2562d637173b62bc7a36f45cf8d6c30db080c4064405475aa40e56286c379abc104fcced45bffeab6f0cff7affc6cdb11b5059f58c29b9aff8fa109dab6aae812f98dcdd30d697ae85b80fc660d4230ec4e4387a0e782d0015f69fb3748359e26c6f63c5448bbd2a42bfe51b09d791bbff0b2f01af9929ade978d4e5c4447e456a454a3fc23c07e5a2c526967f21e61f01ec768939fa0ba0ddb69efd43144dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2cf2f2b4dca49dd5439887c17619ad19af1875314966a1bff213fcabcd840e57f9758a2f491ee30814a1200ff2ec4e995050854e19095567497e1b347531064a0134a1a78df4bd64942e69aa9229b50734b7de1bb27a357231c7967e7e34180cc2eea4806698a1797663a60fd7f31312f95d1196a8cc9ff3afd0a7f9f2413a7b57ad277e722c9579ef0f1e5052e1a8c9e14487489cc973d39db6a737c22ccfca16e73f7d9995b0cada5f12ada2f363eb60f631e9cbabbf60c54506228d05244f77b31516d13d544e93e7c09c9a278c7e20d08c827e7b5d6cf9045867af2bbdf67351a062d9b92cd0bd0e25b3117f0226a94a5e702534a42dfc7540b05823107ae3e862f0313c4eebd51c2a7ea2529ee3b772c0b5f0481d599d9877f7fe2a17c9e68d863f7008303283a662d858b23b1ed7eff18b4e6700da4385f3b63a50afb69f91699d67d1d29de92067d74fe3802701b7c1992cbb79d4bc93c060ed7853e21f52ff6038b7f782d5abc83b2338e6673a4d279155aa0ecf1b413bf5a40350efc125d5306d370c0098a4cdb0f90c8a56ab83eb788de80ba3f0f72594bac39e72c9db5fdd6a5ce756360b26e02e44582ec37aaf2f6b62ce1e310e610ae837f156fea983e3b98b3f423a05e921fe128fcd5b77c8a9b6e83bc1e7a3da672fbb8e9608f73d90c3131a3105fd5e7a28e55c36bcba0ae9cd81f3b885b3097922d3bc38c0a07c9b8d5cae72735ec42b7773736a49742a1a874798cc413a8ef699c93721d00fe839ea658629ea0d095220ef8be586b3ba3b559b3b830af465bae074e6c635dbe792a00dc34c6b063df28e7b590334de1a1168e231803d24e20a7847ff07d84cbd92edf06748d9e4ed5f220c6a29768aa0868ab913c18ba8644e9231ff6543f0e8040a293b021bab9aaf3c6ce2d0e9a00247bc599c039b8e7f5258a32d4266506d8982ff6477d2b33f86a1b49d6f764565a247b55b99b3bcc495f6229fca3c25ccd0fc0b80ec869676bdae25608888bbba32c0b3091e4dc7bca1e075621b844ac68a721e8e38f8509920dc9d794859ca6a55197a43682948aa36e24c7ffe5d332c37f30038a1eeddc1bf7714153b2de07f0270a45d86f6fa4f7455abd53b835ec2e6d7a3a2701b7cbda1dbcff9efe8661cf979ba6020c6ce1348d917235b97733dc7e1719c4d4edaa3ce565f734cbc4bc9c15d62710940b5466fa3670c58f8e379e7e1bd907f341d98057d9b2d9d96699a761a4e1967080de5cc9063cd226cc32a2ecb35c65d649b308f78947dc60c5be1c41a86f39f57cc97c5a1c0a3a1ae24d9340020fed8c950467208a8153d25d57627636a3b658dc33a08afa1289b8b995b868bd078a07a04f74afa3a2cbcb31f83a923f73631ead4fa1bcbca7cf6d0da667da13bbb4e81639b60b66d6fe7de1d4fb4b43a47ff5fe0428bd83d81d25b500d5cd45e8423b3edfdb6bb0ad1b0aa179e69fc9e7761bf1d3ad9bd6b61cd45ae349b41f80ad5a44b5e016d4edbcef1ccb2aec372479854569927fa0763df1d79d64c64344b1d3548c79ae18ccc1c9e8260c19442097c7526aecb2ca88c90ce83c6fb20351251a26765da8693bfe6e19c6fc597d06ae8392051cba13b8ba340bcd82c5a618a0bcfb1b323da718ed39c8f23bcb5ca9399f6f553aa64ddc8c54e36521b63713571cbddec9c9c8ad0ec0816931e508c510453c61cd6b1e4015d11a56c6fc51ed4f109a2020923f39dc973e36563a8623b2141390c83a9d5f862657547b2c03ed44e414421be6310e6c30a56a537712534ad0d1d6a0fcaef476de1547947217bb3784b7eac21531900483163a346a98d129b384ef76b8e5635eae20a8caada944ba3a3c8f9314e72364e8036e65dd45f805da66eaf2d103077530e51c8fc24cb5aa80e71064d839e51e7181faf4aedefda99ada1c4e6d983b4725f06585737fec97c0f1305e8d9ab6ffc9e668814a0b724adc2d32ce75922f31352b06ab88aeca21f74b1e395e7ae23f0f5ca522d9aa891269b066d6e26d42a8888a6c2d5652c66bee84182e04f301bbebeea210e906c2b4aaca884b59e05e49d4676358d31a2bdf1b1df8c1f1a64a798ec4164387a46f867dea6eee27f55f8d7cd7db706b8728f80ec8727bb911e0f40956cc5c64b627d8d2756d3f2f034b9beec2ca8e020161886c6b2da8ddedf295d203db75e3556137b7b06bc3b184c47c67c1d01673dd9944803b5a94c00dac3b6906df70452a63a17d34fc4ecb6cf728a8af98b2101ba360dde85861ab5f4ea8da4bdc21d1150a0920b90c6c4de2b3820100e257f26251b71f1b8f626b624125f30fe09e67cf251024e7e38872588362d4a2e0654946153c010913c268be47a5e813e4addb8476477bf9872c0407670bc0a7552cd12d6f455315d63fa9ff564cdfc91d2d5bd44223c79b82532b39f1c2a64122422665181bbed5a87d144b621afc2c75fd07f92e44576b076ac2568fa2836ac51f92fabd5cd74380df0e8fcff0fdcfd3de1aa1ace9e448dc1ac7953cb4bd9daf932247a66793d2906f5cd80c87bd1504c12fa9343efd941921829b5e5905bcc031bf4d0d25d99c8c68d87ff350f1a6b782b6522b2cde82fe50782536b36050f8bde173f4d75f9c44ea13bf9eb2f97ee31deef5a2f4f888401095d3d7b9193b6f6cf7c38691d84389cabef5fab1b00b0fe2da794e65cc6303874c157e7977ee723d832cf09d24b296fd7dd4cdb568865562a55224204b3793b94a4ac073ff112279787d17954bc1c1f20c87614dbea1b9590f826df22d39c2f9226c7c3e66de14faf33ece2e0c7825d72b137e1e507e9f7dc08a1fd37febccd3da7e4aaba3729dc4357bc6130592452c04e26634c9597e2045ffec98d064ece0b6257c3fe89a1fcc84389c477d2910067b4165385584833264b9581dab39ea7e53fe69ca5107f27c1ed6b9f9171f85d5bb463f634b561a079de9f27201931cab50b5c81c77c6ad22d23757ba532ab133d217307d8963a97e7900d90b5dd749886234172753e7afc9a9ef72500dbbd6caf4e86a58692631739c853a3fd07e2e1f8a7d6356f8e292f350f2d8707521323d56a9f9549808d6ddbaa1a1c12848a9430818fcbcece279b35d934b705ea39d108c82a1950f093f9c7b6f8de80f5c3cd043c2ffb5ed3f8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2db3bf4734a965fa4770d979ebf4fdb3d34f4be95b71eb847b49617f135949949f7cc0a03bf7dc34ff6f252a1ba282de8a5e9ab00ca0360287c3f6f0b6aafee8b8c85cab6a95e4e98184f3274c225fe09652529bcebf94e875b3f865b90731a09ea961957cc64f5cc8f8327af89d7aabdfe674b4ced15e518d2858e56e75c1390929f78543a9f92fb0e0f8673a6b1754777f8255e18d3ff0910152f408b01618866f7dc543c774ab7ce2519f2a6f79d36dc016e2857ba62fe184fba6a3f67b6b227970d66d8e353ce4fe8ef2ccf1287709843626a0daff308bcd43da83e97b361bf2e8def2653d09e55959c4c869a07330d4402b891638909872983938c70b0642f8abdb60ab0a7ffd8ed4b1b8f70a46e928a03d0e39b13fccfbb6ccef36ae87c574ce1c0c8457984d11a57b440be6e9c4b59cb6f1cfc744de6b6550204859f4e4067b4d68689acab08c9ab6992011d33b9fc151a6e27919b919577a91e832fb2be49da4e5338f1edd826ca2c3053893dfdc1d5793056d25e086214ffe5dec5aa1f714455d217bfcd6eb15f971aef90918a3df6a5e2446b79a0403583986856d6ddea69c19ab9b63fc14c5735f170935f69463b00ebb2bfb532170b532aef338450f92dab4a6f137b3cb0622b3a0c8a4788fb6f3cb9ecca94f854ace5240ad1d94450e890a87de8c68b1bbf6ef25c27fb4a048a964074b66bd701e371697f6e980443e7b73f5472a70c2a90b37391ac097e7babbbe638eb48f65c6575a4d17593662228e90befe04756bad819e57a023686751eb682239a476a171148068a0d1ad6be1a41393cb99b7418c344bc5d746a0040053630ee07187481d42b1a280dc7e0d627973217f834e5ee4fd39077a1513cfeb50f43a3e9755bc76f77c322b41fac2ca343a0ce83b53d817590035c1ec93807637647e95b9c6278e1ddfc190d379a9ff08a0a76f99fc39a90d38c6fafc30e93b2674f7aea3abf5837f1ed1669e2644b20645bc90af5410166a9f408ed87726bcb97680a492e2dd8932979c902f548e9d6c08fe9ac5624246bdd1c8c447bd3dc30001f41306ec3a4376f5fe84948fb47293794aac5c7e232277a666c9e6b89164f34e3c7daa330769aa450e4c8e49b42ad6a89567133b15c1dba1e098bed5f8e11219c6a924bb451884b9f913c7e4e6520d979637ff2f041c6d509e9ef9a556211fbd87637f9631e96e7423edbe6d5f159e6d764604ac6bb9d2f817ffc2d972cc8833caf06c3d82fe85547d0d616904535c01d0b33428ffdbe55dc75a2e02b5fb475265cc05252b50339afaa24107157a59e6d019c1f97a5e501ab70e1a990bea653a3a81a53b2ff363c86eb0f2faad2b6710d39350baf21ed7e37c7b2a641622bd81fcac28fe4dd94ce46573a747e5fa5a8598dc75250e3d163ea9a5d6d69b641ae137f226fd79c5dd5ba5d80051464e07ab54320881f2528bbaab9e0cca2920f2f226ffca4f3226fe6e0e69e6ea9e29f7a22e5a438080366bcad562b4bc55066d95597e8fae2df05eb1f4ebf31959483ccd0844cf8e38e71d4d7adc5ffd7785b9184c7c11e9c801b8399155864aa0415204202aabb04d0d2f67ae6c749a94181c31b7977e67ad2727ae6827c483192b0f012838a51c99900d4a46d28d391ee156b9bcda6de602a234b3cc7dc64ceb5988a03b66cd8d45d3680dc75fa49647a104fc786d87b97defdc85e0cd02480dcfcbcc2608c483b3026c6121623ec37a21d75043854b9b0df1da09e3af9f9138e9b2c9a7265bf955ca0f2bcf06848ac369aa9afc6df9eb0389ac1e496772dadf04bfcb935a14f21bfe7768729f100e3f34ef640b8db629c49ed3d1075a232f0a3826bb8df7086b06ca3cfa953d506d613166f3f951ee7370ed35f8ad9b8379c38c0dd884d282060454a2d6bb1ca945c2b80d0c2f45b243773aae5773d11a0af3e8d43b79f11f0fe02a49ab580b0ac2a43a875a07a0c16307096a96adcf528abc00ce33897b1727cb14021d78841e831a302abe431adf4996a448889ae8936a7cb6f76ad44f27e601ab6b4ea43fc40889a4079359f3cedce6da4c7b9bd9085ceac4aab78a0ac3582daa5450cea6fb3c9054b052694db1959e75873ced1a02ec2d0c982e0b61199a6f410869672a869ff299154f8ebd3bab0015ca265ae7cdfc58b056bb5d3720bf8aee6ba64233b4539ef1327ce171d98862e833381574d7863c495898c78e475d17d7e10e38c20910d685a41484d66d70b19776e411749cf8ef100afd72cb18c34d5e2ee8a10757a108d84467b3226159cf2d91a5df24dc98989ed9d10d796dbe8d4cddc898dfa3893ec2da267f187053f8a22d70e883172f3767baf3be2b5246d3f3d955bf63e49acebf7f44d69de9ca3bba482766f04c76477019e928008143deff1e464c63845cfc693f0149b365c109fc496d5b452742b97bc8c45e5a65e5726f468526472fc59eb3585a9796ef0cb295bf561f6c46770c7ce9486437318852516828026b421d2895609bbe830d2bedabe22053804dfae54e1db7985166c9389010033a8663b2c98b726c67171f2c10d36d05cdb68d1164428992218cf3ef2d2bfdfe4a5d92bbd21a718f9c2bd649bd12af3868c257ae6ece7b6e5b17c9bcd0807f724c80f7f030bc0738488a31ab47693188a0864cb8019cf8e3de831ee512d2bce9e73b33a1b0659626a66eef790ea7e7763bac3aa65ee3ff12fde470209eb8b4add5081c88181b64355aeabaf9e2857aa97666cd0d89c7267c57cb162c74de4814c7c5844df620cdecfa65f96d6dd5ff3ec01016ae57737c293270a3c02b360c5953adb5799a622810df0db8570a7c68d5a31be0956e0859d967c3a12a9a2e8cea8847f6e08f9574d9b1dc275c28379af23ae9a583beab9e6cbad0d7dfa52a92ea7e50cd147e6d2a92eb6c1ea75ef42e57a8ecb89156c812aebb380738dba335696c98ec4a343b4e24e7aac3ca13162d970d8c71c3a3ebb76ea8571be7678e03ad52fe131c357025f8183f04555ef4f846b09e00dfd486498cbc4465abe4360e05dc380925cc32cb62315208f2b8400b5b3d7b09826dc523eec6637ac88fdbb3ab1ee394c1dad03099690a12ec692cc215c46a650d279b35d934b705ea39d108c82a1950f093f9c7b6f8de80f5c3cd043c2ffb5ed3f8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2e9f1b2d971eddcded8bc3076ab9c3f0031ee792a50ec50af9d35e295521d775eb3476822cd1421c3490a34c4b0d6fdfcdaedd0e3a991743a3bb783993f5548e593d11a5ae5ffdb1197dd6ffe6bbceb726f7abdd1a44eb544535dbba01f5ed986d03782a404780b43e5914b7de443d989f31a87986ae5a30daba9fe9e8b3888930467b9d29583345bd1be8f40081231195acf2941ce2bd867ea15ee4689532a226965a1e4bbc70ce44d506194dea555e86409a462f2f88a449266d7d07d3bbf0e0e39ebe9c3cc34e9f94a494ff00ecd96d1b4d8b1885eaa95fcb9b4bd93871b7e26011a863168c94b98d883d278aae9a8512ed55c009bb8173494123ef610ae533550df03a089a0559174df73556477a53177030e5c436b03637fd759b82cd7c8e7928b72a0224684312469284f82c1f5bf38c83c0d3574c4d44fea4153958c514cdfdd00382b4a3d210abc66c7a1f3d6ebaca64842964fd85b8681970fc1e369b3b6a319efbb09ad888d92d12ae4789ba8015f278cd815975d0f2eb57c621766547b8a532d3fd8b7851df30b491f1a408b69c0c6e56077e55dbc3003183b9e991428323a7ed00c2aa31adec9238e7f6332663d6d31106fd9d253e6351f25681cb81934485373e525d4bbdd0fda723b556f3f7fbc097ad8aef777b9bb230e5a354c872aee55a9696baf88604133b7c876cd54e1327b9a924294e4c6b37c26d6da633ece6fccc6aa3116ada930b16931b87f3125be81f277d3ab0bf7e7c9f38e5ba0a1934e36841ef3ce31e5aef293ea2eff2bd520225633740b167ca5992b2250f914d0190865190c36b54acdf4969b60e0027f740fa27b1deebf832f5fdf9b41ca410c9f1ed521a7dbd8a94b000945bf515bc137785bd44fd54156e2c97a7599b3a9581412f36599b771abeecd73bc94d63f74e38dd524be43cfa3491e9b8e5fb8b88ae117a10bb0b14b21c326a99b6ac6656a8cdca2364be8605bf580376215685215757b7ff8e3c23a59d33ef22f06f42cda43bdb13a8de132b46d36ed91acb3f8e6275746519b2729a3f22660713471defac3b6a55d3cad759ce250aef3f19c07c69e445d880283b4ee7a14ac7f2521bf059e13f7d10b889f98054216fa283a9f0ac22a32ab33f389282b3081eec97e3d3111d2c82712a28eb37d49ed36799980c5c7ffb610fe79ae13acdff81a9f82ebb8b8328e01367c9fd889a1a31e702afcd5676368b8222a5b8601767b698f2ba702c6a43965db7c22ab7407c475d4f72d79161b7d2ddc44ec73ff6c7e9e09be8f7c2bf9d84e9e689f572f3073ccf6e52c92a0bf83e09cff8e434e5807d9728a5e8e8be785d95c2d9a2d73084cbb226007e70c46a4728d58e939b77fa65dfab5f757646b6721cace23795072d35813661890e743a806107a40bb23c907e70918efd8562b56e8e83088505fafba9a18c5615c93f98cb0380b19a3795c7c02928c648220e84c114a1f70d7869f9d3f91bb72a860f2e124a10b47a4f01274fe26e0d2d5629a1d8693d79352e9cb2ff7b1dc63219ea3ef709e0e63a2e1f0f465e6af54e138635da7185c7595bbd148a419129d91b1cb3a6651de9924786103f5f6d57968a2df5bb075e1a03bc9b536e42baf093d02eff946cd81174160ebe1b7d223d2cae0e32018551b31ef77b05ae19041fec6006915239e7f793d033692f02b85eae37cfa56e1d808462103e2eb1ec002eac45c9134c27b0969b9eaa3d4842a28d44935c2c76f811860898fbfc2e2e9865bf764829c8790c0438ba4757a585f84cde8d336956ce99a44a65d5b328c847a1713b0bfbf43cc65c8da9520bdf625c31f026b802e571a879cd0709ce71d28ec4df44d6c1edac5c35c03dfe7411e60ae1aca73741d7507d4db28fe59df2975af6f5c10e0e10997fe683ea7d30616c416282f4cc40dddca0537c4fb20b11fbaa0dcb479b18d79007fa1ad2bc2b16f11dea199c0542f026e34f136da017874f58ad3e1ba48ad5c173b97a70b0a5242f4643416d0b4afba13d0c0ec334e761387a787718c35db2c635d30c7b475e28e33e3afee773c599636d76b08c13707ffe390c2ad1ccff152f6dfa9176cb1d4dfb7effc7b98488e664e52c0217ad34ac9f18dce1a19a61f6e531b378155f7b036882bc0bb338050bb8d57e124ef3d70b88ba4d82d1b70b7597462588d7edb5a94d19a39ee73bc4adea9f269008526b1b2945137dc660303cca7d38f40c4aef3e48c509a7e4149324832e09e9b943ec43f9b18facf6b91f9bc292496b0a88baae59e94eca7b560111d80be1eb0e279402db9847965db6a790522d605e0dcbc0bdc5908e6b8f68015582713d9e5ba75fcd2c926e1c76bd9e929d72615be75d044f55de47c604ca8a2929c635b23ae361264ad9e92e701b4133e4a4e3de9892066c699df6df836e1daea7fe1618261764d28dd96037347c4b1573354b78f8a5e315f7ea4b0c388dc3c25e286d5613a83616548e847cc9d356178f432b348eced18dfa43f7a29d03997e4d5a03bdc454a66d901bba01bbf99faef5a0d10f55fc24d4608e92150a5cc6f1fc94239c3781cac1fedfd490609b9017d1712ca97a0c31afdf00722333fd8dbfd197b4fb2fc521f4ca8f27e3d27d138bd880a0d52c62b24204aca927e5a5b03d523889ab9af5ea87d633d641715104d8e925a61250e07d2e102c52ba80c0d0642ae21ac66d00c6591dddf2335704b10f331aa3c3c488900285cb2abf93f4553ce4e88d9ffde0afec35663f03e2779a8ff5ab23807f8da6354b6edf5134a605398068a071c63b4ee493a23105c3f6d9fceb477198ddaecfc4ea69b07b32ddc7ebddf910de9bdf8b6c9ec249d8c1ac193b2bc5f11787bfb7b2bdddc32086ffc61d5b30c9df5c0afcdceac1171a9c1104141f57ed63483a9c37f69d97b562cfe20c377671bb5399e63946578b059898d93f12ac02e6457d215aee32de1e299eef16980b908ac956e909291c26b96f0fc30b7cad1659b45eb9a96e61c1228f3830451c46495b49557a6c2c84defaf3aff36d5f83f45d903893b0596739776e2f619644f65ecade5eba2d47c0f4d6ff83cb37516285a6367f93ff7f51199db09f814e363edd1a0c499b61b07a1ac9cdf31321bfb6444a82b0ca889596a233822d16467860aaef0287eb6cb30cf8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"2f535cf82959171786811979ba0f1de0c7083d6e9ec6b12847b9e1a5680b8a89f84dfd4ff5f72c06b1cc9272fb69ddc7f13a646303f5c69a54e45cbc50c0f14fc2784b045292a241e50702cd1a8f3f3e4c16f8ba2e1473baeee96494eb59cf4e9759cf91821f86373f185f004fcc4d3e1c7e4ff037ccdfb54bef431f5a59d1f039e2a6987345c29c71e07c648fde68c5e76376378b7c315c1fcc7cbb3b82fbf9c47c58700729110a3e25f47539dd51250e2194b6dce4b2d5252abc3cce279effb0af45ca5da69a5077234afb1a9c8d1d53e6b9f187b6e945b5ccd270b59c06ae33d1973b2019db39ebe80ae603b83f3197900631b9e0fa43cc6f0f580b0c78448291fb82c0aff788cb2202b1d343d30cbba61c27ecb06b8e76272138c0480fb7973e9aae8a25992883955c2c9ccdf3c1a77f8f842ddd1494a4c5c1f6633a1388b04ad1e4aec668d41aef4fc6915fb996855e399636c4ac111d1e72ce2016989d35f22df7357e36bdd3dcadb6658aea1e9114c94f2094f95ced96db9df9c1d19607c102f489cc5f02abd3769b29449ba792eb010c7473a1faf6783e68b83595c66edd8802eb7f7cf1a6243a94649588f390f037ecdbed1c74c2e88ddc14f75c454640e50ea2f409bf617ed96eacd44e6d43339cf9bab4cb8789b4dc551ebcab52524b5ea03a4e4cb2a28ecf34b3e75dcd7561e279fd4a3e37499ab29e753e86468d00f2268842e8dce21b4588d56f78d87a97d189b05553a80060dc753d86a0dc448915e764921f337325b5f7fa0e98b81c0d65d97c6857ec0cc75387cd9e8236d53bc91c3db275ef75d79f49cde5b209a96d48ca44635f38022628a307daf7d9c4cc16cbea074da62e9fc97ce2a337feed8d25c5e5125512d467af34c98d0e32f8842fcbb3acbe1fbe8713786f731efdeec117aef42643bedc5b9bde2f3b288a4937217c05463253069d3301be4ca7a2883f6b985e77e25de40f46f4714883fdc41481ef55124789538ee10d5c1e7c90294dc6317337f14b0a15a493b82d2f918cdb09e2015ec560e3e1d5c9ae39faaff8815e838171edad66684026a46c1417362ed89eb5ee6594b87eebc522aec52e81856a72be8f99f14ed3d2365cc5954f4b35a181d7c0dd251f2aa3cd611d2bca7f0045a9d44d9152a849c3c6cbff2a0b2110efff9b4d335fce3e1026023b31909d2b22ac9ccc2b47a52608ba942369b5189b0c3936c23a1b10ff0e4b3c7d67fe8c94386c1c43d2b49b623b561680ff9ae963acfb28ca64aa22ecfe37afb4f30bcd3ff1e1422077794ed989864e36e2ae686cbb1c30154d4007ae500ff8aaf32e722ee266841222a00cce8f98585f196ad63102476fed8aff61af5e5f2cd7d8bdaedf45e0cf20b340ce5cc9af6f3b5b85d0446a110d1894d2ff0eb48a3d8a8258f18f9f2283940f695de56fe14692eb40a28a1e0067e81d67b9e247bacd73f76f88f702b3e313e26c2b76136a0c02bba2eb80f060aeeba71753c37f08e66d4a348fa4a91a8ee7bf48893e7c14801adebb11bfe445afb9c2f27dabb62f2ab77841a3f3bc1ecf0ccc73cfdae4005a4cd8419aab19470a64508d9726ab8adde326f009853d4d986572ada479937611784c555b8ce68fbca8af09a476e0231d50d8237437426891a630b0ce9391aaff2bd905d46106d42e64db9ecc551c63c34c3803783766bea2a9c6cdf91c65203fa87d11329d1dde05b1c16d59610e842712ade6692d796fd7a861d37af6e097f8fa0f291a1b94cec92e5c6267d91cca29ac31460f45decba1ca21c8d81b3d2d473d3856675b932de5545751317189905e703bc369ecdf83e01785dbc463f5b296af31e935146b36a76faf15d6c194e0f11ce13731965dd6052828602cb1ba8cc340fa8b6cabfabb819aab0481fc0fa7b1be844c9738f6a16e0ad2e51609fdcfdc412ddec4e8ab1d10e8ea4984d13e44c60bde906936f55735925b27dae549b10604c5b119ae1f2fc66fd72b87e5bb2af592c1238908585e021b97a5d48e5592d9369cee6b6a86820961e177dd0e78ea5e73e789a8d26e2cfba0b0cab1d1964011dc3c39cf7d35a6c451a1f90e0ccc257909509fb4eaf8ff82d2b1946a3c6ef054e094b0c9dbf8e6b836f4a0c2103407eaaf8e8ad1b7e16098ba80c1bff2276b8800814f23ac346d63696c0247978fab01f358168d0fdc718d068193a2708745682ab0f29fbd1c35c38bdb8924fe625c54f9e1c39b08f0178a1476679c59fdb36a81267c1929f915157353b5b01bedba1eecaa447e93ab7263d24ed67e4a94934fe4dd63a6ec41730b1e42f7950f3750a62cc94bf6dcb72add306161a188c48db97a410035d28c42a24bf6137a56a1b6d04c02d9952f8ac6008ef890ea5f432b67ac498954d220d2bd5e930c8c6de9bf830b4e067b1e932c881275dbf683f6b32e0be3ec59d582961a8d13d16a9b8da27b634037624ae948177eb704239cd18a366e27a99c22663a31342d255ad3b51062ea14c5803941cad125bffb5a70dcd82f0f9c723882b27ffd16937e24a75087de4cd4eecc494e88d1388a3291f5b2fc69434bf54acb8f7397d461ffda0a83c366e3a22ce0be8168daefc0a5dd6c58a56b63572b7a780dfbae8232959439448afc38a56a44f295c692fe5ebe5405263dc617f636f07acebaaeba3290272f83a907eadc203b16f405b6b95070ba42679af1d848b361c3c6a61043776a3524f4e758c8bed6770811e67adda167e1d33b7acae6c2fcbfc2633c1f502cca061f23466463bd9e81edb12ab347cafc0040e9235302d1f8e65a4f20f3d61d1626dc419c9f55533882ab1c38e357fad85b24c6a52c73b88053afa5fea377ba6da57c8c7fc2f9777bdaee7d43ba4f5f02f5ff5016c27e526de7620bf213fa679dd56351a8cbff81a23e6ee30376f69280b005540a375dfefe0ec78e9994112d65c25346b1b239708f62e48babe0b856b2e939abde9f46919d11a9f3daf978b55fae2384e5fc9ba1eb835750c947166880bdf85c15834b25194892220248657036d6fb2897b70ec185c85cbacdfcaa6243507156166c50508c2269043ddaddbd2952390536e85590901cc5e8e7824025b42a20c3dcff4ffb9bd11ac9cdf31321bfb6444a82b0ca889596a233822d16467860aaef0287eb6cb30cf8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd02366a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d333624965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570badf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b20261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b099342030010c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd307e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da471843765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c610cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e87c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f4763d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b576090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db74454951470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f08cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d44b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a81efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e246505501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff8004345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"309fee5c1e2cc2cef7b5a4797fbe6a3da2be901a4ac73af521ed419ffadc5d10ace0de7d20d680a4cc3b930d021cd107f900dd335897c24827e47e31bc8ec9d0628e3beabf160beee542a20a1c6eef06b31c0ce63341a32281ec045befb784b6e24db918778d44138e54e3e2c33b8c350b387118880d0fa20bae46ecfd177740d577d95d6271d76bb68fe0205badaa73d4ac4a0e3f75e61da1da2e1e37a133f378e6c4f1ec03460788140b3fdf8de15e2cdbaf65c7ee820a5e6766815c6c3f0e558e1484b000a88cd2331684b717f824494693f8dc188568119f1516834912d83c084b091733c8dc449846a48973e8f44137c99374d2792b2bd7424180c56cd33cb815b9217b37752adee64dbeb204e542709ea6e15e2df867a368e6d5ac5ac0a48511164db33e1717bb626b01cd2d2ae5b0940dc3870be595a8732daaf3962441b778cf7f4e876c8fc32e87769b230d59826306ece603ceaa4ca25747c46ee41b0c69dc8cdd69cac23d463a9c4341d3b5e6a7718299c0fdfd72746c46d8f2fb2acb1db7f8de60a124ff7cf53f006ffaa8f0f2b4926e9455a47bc371a11e19399f956126ad4d7eb007dc9f5a4429aebc7da515236f0311f40cf7d0bc5a570047e9610c078de3cadc301c0665a59bd290ade578cf400142f7d6b8e447783a0f96cad5809db3226a80b09e9e448fb91caf4d91a8f955b2e9233df80017ecddbc570b63dc75473c421f09bc49a37a08b8c72380828981976a1a93aafa30073a7dd068332aad496241fbeb9d73c3279fed29a8fb527eb5ffa762b63aeccba8af59723bb01aab70a48c478898d21be15e64ae3bf1c706d63a6251ea2c9f20cd111f2d923743bda51cda858bb50ead68722ba46ba52905a84e5a04957dddd9009674705e6aed7d886fe038fc5f227db6bbeef59aa06741c73ab27131c04708b57477a8c303dc68a08f0f1e1b05cc25821f49c4d6defa831d90923da9206a1cae4873f0651d6aa09881c8d1b53bb0acd24dd763bbf48014d178c73e38a3a77ef89be6c5b5121bada30cba16cbb0074cb4080cf2670fdeadf774b6f862a047122b9140c6c4281fede1159a3bb7ef6eec308605cce104b1acba2c865bcf05b7f2b2b5ba403d9a81c5fa3cd93cba798d6541a7987930c515213f45bb16c25dd356b8b906e606d02a4d71bf99847f1c7765435f05e7b6746ff56c583b0691bdf434ebb896d42abacf853ff5dc78583b8d799284c869d373cb9be85f989ddfaae821ccdc650088a5e3033bae2893148473be9ed329969e5bad3352991e74faf15a4062f7de8d248375a49b626edb51ac9196d91ff330c17fb4a32cceefddb31e6076f272a82e584be63c33dd8105391bc4ae816d4bd284f62e053c63867011e55f4f514125e255910b902a3f767eca6f01f4fb077a086f6a4be2f15313f6c38faf6ab1b9687c031161758fe67cd5dbd559f603cdcc74043d3458f9a3c5d5e110892d99490d265a21a5101a3a406825fac120b523df9e1347feeb911d988caf96e0b21e3789fc86b5ef3f8e797cd76d5f4cecfa51fde91b2d2c3073a20716b969f4df25cc1a8d6640f0283111623b3262e62b11cb9345907775dd3a0db27cfc3d4e4446410cbb1e42be95f592f698f4dca34b4dab324248cff0c71f69c83a69f02112c07585ae38afec6d83b5e49945affc207d127683875fdf252daece5dd7dcd6e97181df7771cd8ef89d0291650fc4d4f1568d15c155f7c960ada659a58d58f7442fd85abafa2cbb3077f9f507889cec3337da4a58dd3da450339c1f072d6439b9a908a5e3e1f947faf567d2562bc44e95b2a7ab5e38cf83638cfbd3528222ace115e2456708c9bce986acfbf03e8b18c81e0b62b3ab1bd11c29b487f2a305a5fa36e0b2d8c003ee093eccedde0ac2572c8ddcbcaa60d05250ee4144b522ab6bf8c4970bb7a828d7dd46f4335baf14f17c80a82f628affa3e45d10c0e02328570fadca943ecdabb9b41155e65ed71712c38d77a80ad5c1d5b52fbbca45f7e0889f6b118ba9e3bc769d9bc5950ac8fa469801ab5b16cedbf02ef4621cebbfe0f4ef31939334f8bd87f50d9704ba447be0f283e9b59f665f2aff2d03dbddcdf9742ce4e48b91c8112dafb0c2cc57f130f8ca25db59d32618ac39b2412ff2cad3028079db762f9b4cfd70d6e94725b7ad33bd09baba332845073401007ec1dda4ec2bba97dae55471902067dfa8a904c2977750113258b873b10a467aa4a551c4fa286e3c0e425419403471a06392646381df3155e418588fc1b2c4f1df5099dc590ed4a5a606552b4e269ebdcdb1986a469a5111ca6a63384ae67c4604f56765984ff3b4d4e9c5a3675f2fb0c3239d7b5ded049915c35d7fa293e0459a5399e7afbc1430e73b937098f714739b372f62610ceaad184e4f40ab2d7f48b7883f1d7b8e7d92be6c7c05ee3cec6ae37be9c934ae01be26939c673c10b38007640271db5e5e0b862a0babff66c120064edca4939f9a3724f43a8fa2e37d9c86836a43893c7486b1561a680e5f21dad8f9676535562af70d15b02ab5e38f974fb1d053f22fc138b335fc8630e905374e5290db8bca0e8a0925e309337a1753f12b0d8d7c087df190fcc7b42edb0dc2afbd614abc4b105218b0c3e7bbdc6318370633da5e45293d24a9eae6229fa098f51b714fee2a6c1e1e71aba828cf47d332e6bbdd51819a80ddee8613ba200c3e20f290e70e6532f44d1a81376940664a834198e85d08b33b958510dfe7e0bc77a354dfa5560e1b289fbc9f5af73487db7f4c29a454ad5325612b10ff32b4f1626229fe25bdfac0cb8ba6462f16f633fcf11c2ae575b2c48ae24290df29b4de509280cf34ec200c2e140e4d436e9520c9f410ab5fb2cfe9a61ed0d3931b65b65ee6424236bf8e7b2724d36050e823bdba0db911cb80e7c6cf297b2c38a9df6a0c4ee2c3bf8c48a0911b872b9b34be40dc2e77c2de49fcf0c11f45f16523adac32ea7c3e8ae4cab419e28762e448434359f0298e7ab23da3e24b1bbe8279409df5470c1a97b6e80d5c292fc7c62841f1eabb8e9656078589ade0bccc129cb899fb6e8cd4a6a76908c4d8ad3218d485d8c22e48c081b8adf29a59ef0ec3eaecf757df6b2eaf13ab35b5cba745e2f82af27c34fe075d1cc54041f664f3ffc6dc3c298007dccd60e431b9670c10124290c714e7dd079da82801b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"314df91d9c76620d31fd9df274da9d6ab40fea389a0438c32ffd095a689008251f5e7800bf56cffbe31cd8ca089951a2d081f534a98f3e4f347336b30366e8f4c993bea94c0a78fb06c87a5ebfab7fb1b4463325ecad94d4add155300dce75e77b783a7c67bff5d6616ed6967a43e2b63052000f66007dfbbae8106879cb41580e616518ea83626825a6f3f76a2821315a1e4c0c3861ccfbfb19f8fa1724e6f2fa673d6389820a2e6d8c555d847a541de6af04836a6705146a025e56121d51c9c7faca2526e83ed683226caa6cf8a75b7ad0b4a05ae0ceead879d05b2692042b41e1183ef95707eb4c86a9141eb9399e48488fef1528e0a8a8fd476ff07c290758c1dbc06407b6707c4a908421ff6ce07e3d2fb14777beb13d6b2277585c7c4e6bc478f5e372d816181f40236c1b5b71a56d742b0aee603ca5da3c341d571edd3ee45521da43f2941d1ef089dcb71b293bb6df38b5595b789f53eed87359b8899baf2937f5bdb0f0959e1bb6b36769166dc01c115983e7113c53ee17cdc8d9d2b680f1531994b3c23e0d6355da0a11792853da5bd5d1136f51efcb75757bcb67aec78e1dc393c5cff04ff523a4c03037515975acfd42b95f3f8d58f49952e5b549cfacd37b8e7c10ed4e1fc52c26b4ecc9a9ca17be2465a990ba5c0544af9938bf9c7e874952c2ad3326f938f4f088042467441b39a1781bfbb8d2eea743c7d8ea367e5a0ca7ebc3234c78402637cfc5e2d3c97e964bdef6a56fd3979aad16cf5672eb3b1a505846d452c2ca0faddac75ba464763f279cd81451165f4c7e7f8eb749f14f8455c88eca24b22f55459f9d396e77d162274f8c3e0ee57576f08ae3d5dca76f2827a45e91a82cd04002e5f3d72be948d6db1046865968e6ba438bcd58dfc88989607ba5bae3fb62197faf7cef67fcd4367c63771c1ccde0d896bf056aa7c86a1f81c4137d57aa437267e039f6f49a3bf92fb835e82fdb7460577d122774149ecd86b0b34d6d5ed19bd02d50fb7ae4f455136396477cd4356a011ae9a414cbe0f3de4f975c19047a246ea1e8605e43df7f5dc0380ca7e77bce9b2e3a5f7ab382dcd83e12b244bba20d90aa8ebf4c471e3e3b7473a60811042de7504b4518df4372e801eba9e7e80240320f4ab4c1595fce18860f9f0e926b11f5790f77b84c9cdfbba1b1f72ad666c214686c13399e005a709e9e50ce143d8e413de962c5b6c3cdfc8b78aa93b303ee4676aafa9ab5daa87cc612ba2277af8333d0c64d69e8757972814c3ac90c4e3286f733c947135438d17b63e50ff91dd8f026c96fe33640e313b3fefa7e1340419b2c57561c41ab828acc2373aacd7ca5d8ac5babed9dfc58204f43a6c5062fe710614dfc0524d0054edab65dcfcb43b3ba0591f5ff60df99eed5d116b8df350c4fb2829757a47aaef623796c6f2cc2e05cbbb8371d1bd3fa10a25403e943b302d9d72cde3a575c99bf1e73234d03b258f70297b83235f9ad8f79b6afdf4333ce650817c510bb243c0d6dad0d37c309bd6feffb8fd1dea6170c57ac2c76677045e822057199dfd3a8d8dfdfc17e5e72ca740ba2c3d0a17440774e1ecf8398a4d08cd7252da99cb660165a6b5bcaa64f6bf552cb38f89637eb300473b3364a2febb6ae3c52e4ec62b01374368d0f70dd4355ee819ae60f1e3990bb001f0b457fa33781fb6bf60acdac26ed021c528902fc9a5faddf0fa3ebce4083cf98070a864f7d299847a63b1cd6b023b8fc4b5e1514d7cf0115f3b77420de6e940ab7b592994010e4ecdf69a39cdd6ad79dd31d7a3b92a3749b9397c7a334dbf86663d31519e20b9ab9bceff43e4a1bb7ca14c90877120d7f331f01daf955868390ef915498f49ce81d04d86debf1af753c3844bb27cb2f18b27abfdd95606409da48e0672102f1e60fbce631903c5222b78152aed15707b68216cc7825a7f91565537e5c4150e64cef5b8e1b92c68d981c8a11fc3a4fcec354f094f8ffd80eca71d59418f3ebef6769e9457b142d524b5aebbd38c8123210405ec4354a7c7e89ec33152c25ce03bc0b1bcef29802875b8fd737b55e29a5a63f1510f2e58faace726edb54a27ba267156865551b16b971febec03c9042bdc9688513b640cdb16beed1de56609564f55d7c2ce748874cff5affe8d167b4d908351b0182796fb95dded6ca816ed75971e1d6f7a3ff841c48ccb886baeba8de6e573e5d63c871e81e6d22380bbe754f32546391a5a03fdb0123c8f03b5dec6f6840a9a2121a212df13b079ea331362850ca39bff92806f6e6622aaf1c5af4dc045d750861bc5d3bb6351fe889e03b0d1f6ee362c32b9add1a4aebfaaee4a23aee7f7555b2959892bef1657c6a2a1059a17559447520062177074f990a3ce356c62db77f689931ded997f2f2033b634807bfef598f7e1152dd10958e18b4f7fb90b203e2063c6a84620b19f02593410c2aface54e0c5c0a29e70e938714a02ea907c85f6b8219db9c98495cb614e617e6ca17ea20effa3f25f8abe0291a657fdbb7fa7dba2068784d28e38139c4f9f830386309e33e0d205719ae8a3c2763af493373430dfe1a57e3c91064204a095d3bbe421ca21985ced88c7793cbef38a54f8c93289183674a3aca4cae7f0c8148bc80882d86fa0eb2cfa45031854f649814dd067bce19a5099d2bcfa83d3eaafe2dfc41dee917290d20a4ecd2fa7d9ca71b80d7e446f6c4f0c89b8b390ca667c7f7985efe7241fa37c9318c03e246cf9af729e8a1dfcc83c85eb72abc6b31592f1e71afceb0c2c9aa5f6ae3fb3f3eb0bdd49d8313cdd54453d24557e21b4b7803803493f7489ba3a64d9ddd6edb6cf82e3478533b56bc7904c14046530a6656417b1f22202a32d31899f9473e152855ce0ccdc19552bc6355117752c3e8d915be991ca16591b3bcf921d8270522ab0ac93a29ae688d3ad105bbff03412eccaed81e7e99de7bb90be6100404faf570d7f8a0514623df64f168d849abdc707ff27ab5ea5c1ffed66e7c3af713aa6eaf525ba9a4edf2e1b63408507a1bd933d43a617d44b68a319704cc69a0b9c372e6e6c816363443aff799acaddd65a7b7dc17dfbdb832767656565cf5682ddf409891025569bc3eaecf757df6b2eaf13ab35b5cba745e2f82af27c34fe075d1cc54041f664f3ffc6dc3c298007dccd60e431b9670c10124290c714e7dd079da82801b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"320874fcb8dc9994f4df4a2aa37998a47a205b077d37bf1883db5884e8f90d7341cd254e777b0ee7241ce0ae3eee042c6c7fc7d9a28fd15d05844470767b3972ec05e9987bcabab8b31dcf2b6b9901e458aec7470c7804642bdd474a577736f8880e8a1bc1cb69fc40505247afd2ff5e2f593212521a9853900225abe6d962ecafdb8c3fdc97545de2ea474c290ff766b31d5883d51158cd7dd49a6e00e1a3aadaacde913a70cea960a4ed2658874f08793f08a8a476d3fa5c19af8395ffd525c0635418bb6f4a6294676e4efa561de44bc684484d03a3dd1204442b8b90df192e2cacd8a41fe5fe4835eafc607965b61de75bf9f9a4ef7708ff2cfaa6f30af90340adf57eea5ff9094f21c9dfc6d5f06c42039e8dc61d9d35915286effe7ab3db9c375bc024772798e13836e744159514c83acbbc56de22ccb2bcaf733f6f0691d2d07a290de613ebc5fa0db39078584a6f506e9fcc5f45a3132fdcbf3906e32abf064a113bf9b75109d34418aeb70d8c2ec1e08eaa559f3afac55f49faea78e37b4bbc6163b7f9392b2488de06d493c495e9baa5cdbe662b78e50996f946278748864c9f36784d8c726323226cb0c0a38db3d8f8d036775ac026b8bf04688fbf1e0b520ce96df7450bb3e6b17b196ef079ba8e24e9bf6231b9d9b8e84d1b62252fc76bb3611e750dfc96d5007624efebcde1ab4b0326efa4835067d62c133d141bb002ad7d270f160081b80191fa17da3ef8d0af809776849989e5d64a5679be20c6027cfd0159451f0d3abea77046e5407cb7019b682b6b5be39f624e7e98a8513701994e8d4a31552d8569d78ac35f35305a7ae327a78acbccdb784a2bde1ed81ab13c6f6aa8762d0da7cb4f142768abf4eebb0b4be340220175cf2724fecbda8b5b31033773733c73089e46787a0610b1554681066779ba0897bbba48cadab026d1e06c636de3b864a8f6aab94e08ac5dd6dbaf500fa33783254f7704bd83272cf361fdf5f55ddecbb2ff085196d15a833dedca487de51ec3508dec5d34e2bc633af681a9be23fbf7f6e29e4e693fbbea80b3ff5ad7d8cabc4f025ad1b0f3630ad866be06e716c059af034b4e1723f6d9e4860ec711ee749299c5d83da242928c99fd66adfafbf3fc71eefeb313d3a0cedee00e44ea79ee4896bf0c4ed046ef86cbf543f62b995ce14c3ffada875efd1f5d3f00b341a44cb73a4e83d9a7e21074616ee9adf9f194ffeb7c826c4e0770733d5cda58ab58322180d767ed9798c0ca9f0f78cb0d6b872d0966c5befc874080e46cc3717bffb7d24551889d75fcfe0e7489b422806abfc4322a26ffa58921b796ac583c09dca4a9d647dda7edb9f6725b0317989c7d76ba47dcb471dd796bad6d1e18e7b50c774298ee343de23cb4e186d26d8f3c2d5755674e814dc23d81a892d55af912e8d29e9463f5a7ff0d526715c69fe2ace6ea24c3e77a01eb253f54bc87480afd3cf3b2ebaf55e52b5675f0799caba63a3934804a5b86b1cd8b8962e0d70d09328f64a7b49eb63863695c098ee411843747e703820485cd65a814939452ff29b6aa080750e3a9765b4930c1123883d4f8655be28732dd1e3e65ea7878706b1e397e79f0a345e5c3df2df0630df76d913aeb05b335e9296c5cd0c2d9433e599050466b12af93ab07b5fec067a4a124ebe8664a7227a151c1a7257a24b2d34399a881328e1ca13ddacaf5c9aa3cf2e6a565d4a98e8b730c8163d2d0ff4e9d918754022456eb41a8d3613659c1d0e39facbef92ded92d973f031922f8fa8f70d172796ed153f9bb0d653484c9672c01264b2d92f6bdde727d518ab66bc331fdd7dbf9c34b2442283d60123e2931a4868c961aebb001784f58716b37db82a158609751bf6d39404fbc457acad24ec84b85a26e490eaef47e2825128e36bc8e031660d74118be8c0718f5b78a35f530e941d46a83eada2c8a5b4e0fa4ef4336f866744248246e4b2c535ad8f0ebf36f71bb5d458189bbb1066d532ce9a9e84dbe33ea8e814df2fd53b3ddd460b594d54a80cd1cecc1dcf2a48ab00cdb8b371923e667d625c8ab0eabed2d83ca51b9b19909dea6eb15efb51ef1db32089b07d2395834ed5bca43f8bfe21941f57362376ee081aa6a5a043db0a308b5b4b905c65c837ce508e9fe314ecd532970e0056d00fa76ee13ec50fd20731cb66a286a674fb7ddbe61ec14fd45a88437aa033a9b2123ec8d7ea00447c86bbc053f553d9750c518a0310e5013256ec640ae8353597c54a054f079b24c73428546bd23ed297bffaeb55d09ae2e9e02cce16d4dc5a386dc6072b701233d7a565dc9d2118db3b718ffda5fcd236ffcb5f58a0d864e484ee7d72c02c9ce3ddc6d5aa8c61d5026d659cadfb5a978184e7c47d9c4607a6ab0d8ffe48f91805ba3a9ccb4e7ca8971864f01eca98e7b5f43a09426d60c5cc87800afb9968f1094b496d15e92e3e559db5a3f90593eb9bf9c19332bfc5e328ab950d080c96358aabc623f683c491eef281ff790d84c080c75f5939a0084228666e9f1d2ad7613523b2530302c72d4916c2d183e44a3314be025a0cb1d9b857ceb988b58bd39055e03dfb3ef07502a2dff9c1d3f40f2a3cc9c7c9a6d7bac943c2c990e45d58f87e1c2f9c32af6356134f5d610670488c9b43e334b11c2a7f987503a71cf8c3bad740a470d3751d11c8f1b60f0aba7f994b7a6e75c0027c3b5f2b0d733f17d08db11ac4a690a7237241ce4d8f3b71d743b6a71baf7921dcbd1e41d4387dbab201d77c0e256b178f104c5ab0568a66eee0001da790953acef406544ba8c22b3783059bc780f35b25c21304e41f63a4c7661696059eca432aa5142fceed8bd2c368093b14110deefb8e051d5b02448de35b29180a7b14558423add3dfbbca1d619220eaf90f06bda0e9a0b47d63e6bf6e3d3a4fdd059a4435103b43186bd569622be3791f6a5b3f6b85577fb80333f563ac75ca5f61f4e695a47b0deed8c6ef6ac0b480c0b339d57ba46cfbe85dfd29733462db7908327a5d7ce129ab5afc4590cb3ce0edfc5342564b1de184dd54a0a4375545ac54a3639362ce704d0d5724ec8cac0888608f6309c7b85658e6cb4e454fe265a0d29ce320446b5d8d9347689d8aa1458e3c9ee89cf73cb635e84357b371064fe2c4532effc6dc3c298007dccd60e431b9670c10124290c714e7dd079da82801b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"331fe9494ced34e060473f28fefa01b5cb02e18445e8372df391c1227f4b12ff0996cc86c44faf7e17bbe36bd38f0ab3a7f469ea25688812a991c7fbd270bbed607f88e3bda40e3030290f6e3b97aa02d0d3d576c098c79b0e863d02bbd48225636d5a41eaf4d0af02f1d0f80a62e619fd159f4cd3da90f45d60bbe4a53b7b0b5f6bfafdd8b6749d0c17572ffa54245ee02b41cda56a1824fd3e8066be91ef3dcdaf77e8fe9ee96416f0e1f04e331390882b6ed89ab64471e112a9e6ea0c32ad988ed2684f7e1541fee1c9a6f486186fe8a424e2dd724dd8dd1d8fd7af219ff865b0cb59085db025caf33a3af717f3f68c97f61ff6187896ba2e34c454dc0f36ce327aa6cb4fb503520065094e52b13bea3fb1d7ad251b5ca9a1279ce43503c984d92db5cb78a40359fe3cd33c509df30aac3887b46bd2ca57754149a98ac703addaf4cbefb20c834d37e9c24d4bffb19c802f01ea263880ff85c927bcbab4dc67fbda82cd05b83b1e4e0fb81dcaf52eabde2cb94371ae05b22bb603c3f7984a79a705a2df8745db3f577304051b99d591c1652604edab44a15fc5bc56671c5b4b02935e65e952581f2927c8b08d14de1052c7aeed467850a49a2143f1e66305e4e392f09c1768433d3e914fa4c77355b49eef921d7cfffcea13406bcbcc5cf2cf711bd6c9fe37ac7ee1c617614ee6550a90eaa5d21d44fd139fe6018d18379a75551ea2b9cdfdf33c922f22de13a67e740bb02ebdb0dbbfc13741d5d420b83826023b3ccfc233ecc9808d933339a8e6e4978d42a8b317780c76bb11c4c56887449c629ee20acd95a737416fcc0faa4309d124820c22f1203a3214fba8fb819379d85216f174f370e2c7479542baf3e3e4f4fa6ccf31ac2dfe53686d7e9f80dc5dac4cd9fcf475d3f6b1dbb7b0e3f9b49a08a69da25eb7ea74bb58bd550f2485161b3c4c52d87dbd84744f36c6af3648c26baa1d11a2bfac111a8e7a400f8f8a7abe9777ea71b3ba1acef13975da13bed9d89cfc69ecfeab08e851e30018057b62241b1c614dcdb2e78529e91c65c36feed296bdcc12e740d44a9d31784e2613195043643d5e9dde4fae17279cdfa164b1d00b928a8e3f85b1e8f74ed124bf9bedade0dc606c0fab03e6a421a7f4666bb58612fe5620ff9723b40f751507978402f2cd4526057552d6d48498c3d5e2f584b68f71cda408c2a158536e3c19f7bf198148541bbc6648c0cc39afaaf3a9519ced58563e4b56a1adefcc4810a6fd810ff3a9b883974eb20fa0b08ce0948aa06f45fbc6aafcb61aa59dc45281c115c2a10c3ff542ce805add318790f8d803e20676b9216f92c0ae78a69f17656a2be2e69d565ea2349696c59937cfe8be1415367f17d6d9685cfbde200b37de94fab635d3a94d6961421816c893fc1a60bc9b7f90b9c5211bea89790006e9b0a87563fef956a088539dac633ef7125d51dc650d39d490c40bda681c48f4402bf2ddb6277b3b6ed788d64fb47324c8fb7c6768548da233fe2f7d0974b8b6b310a5d37bfee558a3d725710a90b319ce4047e35c2a1727e6bc81b3ea7efc657ff9439f9ee30d335c20dfa6ef7a1f50bd478d99f162432494441448a27b7b80cf89d1a347917526e5e0fa319f3093043ff2d27cd38ffd0fcd321cd6687615698573af3ac75a7e66dff93ee7713d3dbb195dfe5e038dcaf8bf27709afb0692d814e768cb08c92ec4fb386518ecc082fba4773bd58289e3549396984340f1224743826f46b2953d1c60102df9d8a110a4505c06b1f352301bc9eba14ba6a74ff0cb145a653915a413b73ae719c7e0f070e1234ceb07d00417440c38cd90d3a10107f88ac0a3af5294d45568f8795cb98039865347fa2689d3352c4a4e7162eead6e2b00a86c23d08308a0f3ebed0f9d1a40264a05b08ea15883c5335e38eb363f8dfa949bbf010d38cc1f1aeda6c3433ea5fe5b7f46b646eca7505813428b562d110a770a0a4427357011484f277ec9efa1cfe23ce439704b72f7963ffd290422670c6e7dc80d4625badf29e1f85405b96847eae6efab02fe156ae06f69145c5b6ba49ddcee66ad9998ed271243bb62686f1637c5fb90ad2b20346b809e811ab221142ef81b262365865503d0d684188c998dda9532eac6d64a081f9d1eb007a7e3c005d998ee24e794dac515233471b796f0de1b92b54d2d36f0d9e569f518c6c06814f538b2e46e3b1104faea2443797fca067e74dd54a82f2652556114262e58f5142100810431b120a2e12272b8a112962c5b0cc23bd6bc213aaa12cf9983a2b58fb30250dd92e144ebdec0e6ecc4cf9e04aa9b7ec937bc84113c8c2dcdbaeb837b9fc22e7f5486203e8868cfc6c95b16f9a62a87e6319ff4a0580592566e1fe62901c822ca5d331a7e99c8febf9f12c6e99cdbea28fd663482135d13a22b8d76f22053bf5c9b34f6642772f7f145cd940506dbf30605e001824f473f38c4a493d1d69f06fdc81f12a219d7b3e2697b2de85fe3adc9f822139ed427e40b3c29f94a50b02c9cd2ea9b4478014f1247d851f1f41d148c3fa823a36115fd5120e1c47a285f4c2c8820ad04453a619842a2791dfeb8b58d158e155a6e6b8866ef0284e277692a95687383f5007cacf8f22e178aef0763e2ee7f1d7dd7e94e27b15b8141ee3de32dc9dbde52a453997e426d22f85667bb96c530ccf047e2efcc503d78b27d45caf6f8700858ba3c8141136001e3de867dd72f0c0a3bc413f54628f0486fe550773edce53b9b4c0ae94b9cfb256de489b40dbe73ceff8f696b43ace21636e40a189cfc51436bd809dea3060f9df9fbed205b2d06aa3d6cba0192a6825a50f089583de59f7deb8e93432914f1c07115cba8c137f98684d568bc0bb436fe2e67b21d684b59738efbe8f112d7c522cab01d5351fe2f9af1f1d88917ed97ca4772a5ff8dc7182fb8cbb2e4fe106baa59bb16c2712cef7b641f3d2f49542ebde1ad02787a0c597de245182f4f12225eeae4579eede1451c06692e6c84c9662fa562bd2450c5b27d928e95535203f80715be89adf11ff516ff6dda65d692beb555ccef92a5cc4ed87ed878a6eb3f0dd6268111bc50586c3fe0d2aefe7e2350366984ddac20446b5d8d9347689d8aa1458e3c9ee89cf73cb635e84357b371064fe2c4532effc6dc3c298007dccd60e431b9670c10124290c714e7dd079da82801b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"347a023354bdaa851c37d97e0eac48aee083649ff74a675a622ba3446bb0dfb1144046f46682e7c156846289f1d823513c585800861380ac890aa8e4a39233f46893b696c00cabfc7dbeb88e58afedf2e328277a30adf716c0640b19ba4fe3b3846d8b2c060ba8692feec9c1f9535f95eb17161667435d52918607927290a8b947c439dccd990b897b49bf3ca5725f91fd75e164914ef60e4962acf0360b897e1af9b9258aebb3c00ec447a070e39fffea7b6f63ba86532621a7f0b9680e5293e92f82ee3e4fd312c91513fc114e79e06607fe29e88c6b8d3a30f28697c32fc2cf8c1f926de58d78618bd17ad06a7479a17c59c4f7c38342fce17180a3bf60a738a1c343857d113cfc6e5818b8ed5bb77e5b2c87e0471d07aecac1f254f73389515e0358149b4a379ace7e93a349a03b3faabf52a5589a64215a168059e1ec7f87f88aee71ad873c85c9cb0934922a1ff7cc5c64d2fad5bbb390022fc7a79f3033ca765d266f050e8ec86248d2d59f4873e0c803d5bdd45a4a0a3ea3bc72a9277b951eca9cd440b97e3216c3046a22739770158a0e09d12a77037d86d4034c26dcb4162522f0e3d0dba3f2e4040c23074bb333fc8319a509bfb98c6d92be011c1e78fa57219de8a0b3e813f682e97f12020d89b7972f80149eec95a6c0b830a9cc05a7d5289a891b06257c7f2a3b20fdac93263cbe63683194d49d38e131241fc25a20ecab58a89f9d7896341a7f536140b3ec5caf7f276be6e0a8990e322fcb5fd5b3fc5ab547358f04e0a42bceef264feb6dce6e837494502f00457840dc461b52aeb7ccc0415584f7319a224f623f23630ba04d5c52bdcd882acfc3c0a2de37b86823b77a569f2330c1dc07259adff7d5ca8a1926861ca6f46a68877bb0ea9bebb680b1cc338392deb6423ff01bff5fc6f4cd311dc2402ffdc0a0596eadda53b18fc8d58bcc1b7e6a3921820b332e5c89800099d02bd15019e8db4e93c2e0bd2e10729f26d9fb419eab99745ad596286714568480c94b78badf2de4bf773ffd10a037e0c6305dc55e1593d0c0e1a25f82012c491af7a14c5ef983070fadc4e2be4a94da2112d27662cf2b88db58ed107fbc54d93ca8cb047503b9abe4d62f5feacf960514491a1e3636d96e34e1c6daedc9c6961f4a4d749fe0e10d824d94f143a0fe9d784584040301beaff72945a1398c5c8c76cede10080bd45ee40000bfb45f0258ecd46560c74344d11854da6af601512fa0012b01424345a67153fd7962e93f39216d247f1cb7a834cbf0b390fee3f8927d3f68257d0f4c651a01d184f532f423081e9d86f108399039e7826368d3c4a02c0ee2aa7d94dce4c5e46a49aea464aff0b1b472aed28a676af227015843433b0bb41e7e9f6c470d347b22bf7c684e4743b200a148a26e9a4f678da3d197fd3e173dbb95be3bbc4e90c62cbd308a7ad745d92132999fdec824b670f4c0a641a923abe4433867cabda18a26c376cf4c69d680558484f14b273683421685454d4d7a8afc11d4dcc16af943eb14bf432ad94085c8040527bc859989fbf526905ab52d25e643911c81eb049008965d85310994e2adfabf83379e6d9c9854eaaf4219108f7b97e13447ad789c29e81b3ad403c14286a539d41b54f7494382d566dcd32b0772e73a0002cb546a599f9dc1ef1d7b653ec45d927cff09a5221e60823e282485ead7aa8143e290e72a474b21eeb9638fffc2320729fa3f6543a5fe472e85f6aa93e3f22b5edcc3d8ebd5ebb977733466f27e23da82e9f8594b90fd55fb7ae86db06341b21403c5abf8e30cce009a3f76b613f00669addf77a531775116a3ba5d7392c9cab6a37d39d9c26363a987a94fffe4f23cd2d37f78535f65f504b7979852cba2857241cfbc433da9d623a73292acc75f627a3f7bc04699e3c20aa430354f7292804a6092672b2bc1d6cc7c8ceca571f7b86545741e8c8f9e4b0bb1915582385879db65566f2e6efd470cf01670f32a2efdc662f0935360bd7f70ed01d6c53f62212d1ad912af40ee2924c7b3390cb3a673208efdfefc45063866e59e6f05159e66f07c405edeb3b6abad09c5b4c338de9692965dcdbdb0ad038bd3896ef936aa4ebeca0b6f8d258d18b0948ce6a4a6b40cbeb1048d9bce6ed8b5a1fb64fdd908ead8a70f5d96c61e62dd21d07a067a86fcbe7825d79ef9587502bcaacccc93bc7dded6bfab474da4a2bbd4730a7820772462076af5524a7ee6af75924fe994c877866814a447d53cb96ad1c78815cbeddc09a48792374c0166e793ccffdfba494db96c94ec2080ddd88619747f0044c15e40883fda988ddedcad089f72af47f1eafa6b8b08e0d9e21e621528598e2ea1a031c463372bfcb537870a061436333ea3926687a33d2e1173ab48a840f026094614785274f406e249a27a76a5ffcfc950d2032fe974a426bca632971c059d02f9baf0360dd6dc6dec76d0d296dab14d4623ff123af284e32bbc3c7ed30d9c94ff5edf1176868171afb341e22f6b774b9db6897c3e6b5b1eb31af465c43802460a39c0567dda3b83b48f27aba336025e07b276a49463312309c8f3a5690c1f1e509593dc0ded8c96037bc59e8c7125d88fa206b0ccd279a83cb9169488bee7518333b529b31bd726e0a105b55ab1372fb550bce50daa46be89e555f29799103e2ea92814c80a6ef7faeea0090ed054f5665119ce7f6e5d8830e3f8efce6aa5ff9079d9c9dae1a29bbeafae135a4dbc882f45044c4bea402e91ffd37600d78aab7f69cc8314b5819584797cda67ca097f1e5cd47ec508ffd15cf3019f62ee4ec5027c856e779ee549c0f7f21d5c2085d3c5c2894d3f35bac944918531ed678f7dae724cdbf9720cd7191e3330efd56e45b767fc303ce2729329ddad4adfb094ace50bee7d7102739eac1f43cd79a9dd76599e135faa76ac7cc24530b2230398a77c140d0a4b820ef3a4cc6f04335c292051cbb19fdcaea3e8f0e7c9789ddf4779e0c6566a3b6028e7bdc0349a622c7ffad3fcac17a9c69cd9ef798e90a838ed67c1d110ce040b9633e97240fc33a95f056a8211574581eec69f33034523767e9cace448c213d265fadaa5ba1216bbd57225a3ca6cb3c48bfa5c28194f04121855f7ad7f60e901cf9867f858b716c5ee75956f646dfd2bd49f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8fa7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"350e70a98dedc81b5021d82b3eeee8819b65a5d26d495b3587604e16fa81c191aa8c2a6d6616a4f7e2aa79404b54e90c5dcc4e9f36583b98849176c37c81837c6c66cad5490932033afdbe6f938875445809dc21af838858319f44d7adfa6699002f4dff8eedccd120a86596c6e22e137009195be5727806b263a59e99379c40d36dc302ec7cbf6629f6387b7bbd249e89de86a1347d64c58468197c808c446cb790ee82dd38ca35b0dcf38f2c523d36ff79d2db3b3696bab3dbcd9df2fd0cb2dda70e1edfdabfcb7656c981f56799ebb799d337d05d559845d6d39cd3f82d75af9029ec9331cc9393bc214b6209829a340f1127506556f6b533b5bc4cd124281567080393ef624e25ad69ab0f49ea2508563b413c0fa15c4b3264d2a39e485251d387a5f92f43b4b992a99c625cab324b2e2c84de5982f61eec40186209341cc55872668be2c533f94caf3bb19b186657aeeeab0ac9fa1af9d65cee92dedda88a48fbaa707dd30e630c1d79d5dd56f692b5f8ceabb1f09316f280c52c7f58d02012b5f9b18408172269c1466b966460b8ed16e6cee281cb26f9fbe499e4245e13d21a226f7be2a19a4b67513eaf212a2b587b64ab16ff8380645eb82d4b23f5a5cab01e126b2fdbdc55c4b0e2d1dcfbb3e3f253137a067a4d579921424401d7bd0da45c1747ab9f6895ea8b5521d9059aa9c2b9f9990afaf0179e736490662bfd10343976b0fed72b0bd9731d147aa4a0a8a41cdceed9d608636f06a3bbe3d4b7a654855715b0f053233a3f82ff3e2076faa5e1b747c59628af3f6df02ef50ddcc5d9271a7af4ac7133b0b6f607aeb0435ec62f0ae7855e248f47fa94a66ac0232950c8776df2ca149bea61fa98a5cf2e4c7cca0a6087b991c0cca59a16067653e4f6c9a3e2394ebdfc59143c83c596baf7823c1c8b5af03da69922ad11e6e4b63f3222b3befea14c9a5442270a74661a1a946c877abe988b59f92a27dbf8fbe1dbcf89f82da90c40531926eb626ad29752fba00f93749719bdaabff32cc9d577d58b5ba874bf44948908da85c308d91fe85aaaf2f6e9776858c6c29a6febb9117efd187890af5b36e5f545a3ed0e904240701b1a5e25344936abad9fd87e644ac10aed2ce607980e85ab3caa1d7239432ea50f29b1644a22b261c1de47922264595de355eeeb028e2ed1b650ac02fcc4c3c1fbff39706498dd5f1542b6a3ada02aa063221688846f0bcb9ebc538a2d576cc31e3d172ee53490327727e095866ef87fcda773fb0949a8ca78d38bbed89278c0365f13446453798e8633b31895c0afdde5536009bfeb60e785301fbf8fe2b1e2b8ce38c9c8ef2760860f26beaf7a5c102fb94a5b3774dd00a4e7d827433fee3dea117ac9d4705ce757cc20992c6ddf503169230b0de8d53e978e392aad37b73a11ef9fafff808579386a6035dfcfe5372de95234e4358be3a5aba209eeaa91ab848ec0ebea4d44c4909d234989ea41ce83d69b67d5d2436450632c5d615edb1bfcbaa987615d851a90d898bb5d200f90bcaac876d9e7b41935f502a35b74816858626e092b0c304595e84a73495d2e772d33b415e224e4b48fddd51aba6454b7c955ac6bee3c8ee091fa6a3b7b75edeee9ddb4496d2ad7e69bbe0c24a03310d2788983528d552cb511e4ae881fcc2bfd78b1e105776e9e9a23d86f16cd943a395a10a279c0b07499017e2e0b8b6bd04d4a7b41b51b6036d608e552710401cf90dc347a6cf758fa3f7ca9b23b92c1387e80613cff3b713f8ac41d055da4e52c6d326b96e502fc8b1d80e9fb7d42f7775cd2e2ff88c25c1764b061b45bc7f090567c70b87287e9ab7709b71d29be9fd8ecc7988bec715f9ea6157689fbc90c76e23bc9f556acd19dcb6b5d4a3ce208565881e65813cf66e96ad5b2423594bb6956d15c30b8f6b467930a95df238690c973aa95661d8c3cc9a231decf1c00ff0751cc0e1ca4ea99203062927de304c870a8fa40198c58a12661a9ca6526b41b6640c5e4833b65d55a9f4d959cbe0a5d0191fa71ac803b13b8c58208037ce70058acffad8f5f0161ef335282461bdfbe15be7f6b7fe3747eb78eb401087ba30956d8e8d6b9ad6695406a5b12706878b41cbf7275eef0ac512af69c9ee03f6b25e56563e8448e0ab42968d6454ba298baa3f2e3695fcf7c2035c64f4f2869f4d5953aa61076394dc366d03ee5b0b12974254a3ef29553c50bc6d770387963d8ce6b4510e6572371c74e79c3ceca57d99d623cb85d05d9e0bbe2361fb69d987c060064c6b0dd58e7fc3cce979ad82c4c0c80fbc38d868be5044e69cedf2f6fb2cbf6576b52913f40a1118801818f215106e67bc398861a0c9182dabb4dc468a032ac2407a113dbe31c73af51c99e63620927f1a0edf5adb8570f14a1634bb33327d968c7a2cb54d76fc59da7981ea145bc5860be9c7d8c380cc540c739ba0c507ae511b51d86644b5b09ee52c1f50162c77fd319709d06ae01c3a5785e47f59f730ab363ba3666b009b5142944ebcd78b8d1fb0a71ad853db97099b9250562464190683d42d83090df8e9d35851aca7a3d71df9b8de19009dd6f2b859665675922b9b769d8f22840f4084b7c888e280a59745d20bd0dd9fdd69f4ece06dedb8bef37bc0b8ba358c39eddae4d9946999d6989d30915af788cc937c1d067a6edc240b37221ef78e63fcbec1858c437b99424a53df8aa077e11663315d83f786d611f87b742e359bcfefb5563987cdaf10604b5fb0ba0d3d94b75656c81d526fef110dc8417f661d075af7898296afb55639fa3dd4e9d19638953c67ea7ff42df9f85b048cdc41406f7c1dcd591326870b4e5728c8e1cadfe4c0ece72dd79bd7db5ccfdba4d74e8f70b0917ebd2840ee09a82e8e4569ed3e341c0c5e5ee9465552ba122b25e3a61cac494afa6c28f4af2687b51600523e572aa86c2c0fbf1fc51a3729fdd01d0b3732e8ed37778ac9421db6e8cbb062e6caef2e6d5de026de22fde2427bf458ea6322d1a979bdaed5b9b61b3a44af020cb494d4e2badb68e8a820d1225df50e4124de5ddea4b335a46b92baa44e4b2115f99086ad91f6b504a9ac356a008da67333bbcea71095dff905fb442fa5c28194f04121855f7ad7f60e901cf9867f858b716c5ee75956f646dfd2bd49f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8fa7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"36ed2bc0acf613c41413dd1e783ab698f8edfcc3ec9fff7c40fd4ef85c7901dd4b4023ba8c8a24d4d7c9a430726dc6fc3fc6d5b3833ad49f7674d281aee5c88eb1842f95eb4e97352ef14f491bb1b2d42250cfb97a9e29e5513528ba2419422b890d9eaea67e559c237a03a133a3f3c4f81bf56865f9db1dec717332be0a779422de955e939ac750dd2e2a355a36d56a2525693938965b7104bebddc1ec889f392f3f4262bdaccf9db79e41e4993eb9f4d5551dcc4fca2d9cf4cc5ea08af7c38506ea5da4b4edee033ef10c9088384f9bf8c3982aa083e3e94b8479ff139b52bae838113957c63bb125666a5df6b32b53021e225e0a32f281812f79b1595a4733d946cba1cb3eda1ef756a341699d23d9e1dbd8c577a07e6b5838ea0c2511b382933f0e6fbe969514823f4df5dde093768971e1438367148731b278d119d944853e1eea7d1d487c3b11fc4be5bdc612da29ecb161eb0bbe05befa9b2c4906bfe56eb5049e1589ed1946fc87533a1f5ea92c97dd91db479eba93a423d1aa98fe7ee7c2338eb0ffd5b09144e64d791ce3b65e71f5a26b87789df79a510931997b28f92ba335b40bb1b23c5c63400bbdedb841d6571a90e4ebe878499d8e47ea956f527d62ac823a51ddca502ace8847ec79ca7f21f95ef68936f4141dd4fe204a4b284d7c8db0cf49193a549b2258d11ca0be5624cc6a7dbc871cffdd98673e72672f7ab6d23158013eb4bbd390f24d2b213a03f8baceefc65fcefb408c862824c8971223847552681fea3877b350fd275adf1c5394e9089256699542a543ff7941045faa11029ff0bc731f0c43d7348786068943de5ebade0484b82f71ef61428af4cd43ecbb2d70abd9be7134149c95e51fc7443d553a49019b8a078b56134efe45e02bc8fa492fda8527e6b80f9a080ebee65ec78a9fa114a542c843de794719189aaa7320a7e6ac1e2e63eb12fde40193c76bbf7362f8246679ec03e97b538de7aa7859713d002075585d25d81bc226998d69d87909a58128065ca98be14714c47e8a80e39d261771f0065250d24294339d01b977cdcf33b72e78286511f8d1c2ba7ae91ae6debf73c7aa8d4710ff0c32f84ebb5cf08f7688eb7f59b823a8fd82e038bc2f82d0cf4527ee6df7a25779bb3ab7cd77c46d1b70a2862a865b714717ec5ef1d9cc03c9786abd6f3162ce710c2b01d9cc38c5b0c5c36d6d1bbe8a212e2a1e7bc04925c8d6ed3deb0c76914f75690f88a0140fcb05ef4d8708bdc2758719faaefd3633f470c4557a165c6ac5ce087dca8eb4af0d30068dbf45df4481be08bd7be1251297cdff338aa036762f318cd60498cc6eed61afeb6e7f74c14064ae6706452762b9d06dd8158a3ef4c7a5bbe6ccfce722482a578642c8e61f582f4b60e01919c334644507338a6b98cf14df6de512a4a66e3c67f38f63499e1b4e156f5c071a44da4df91f66b096996f4245d76d4391b4f50ac343d67004fec4e2798c2010a6a4159e259efaea66f5b37b00dd3ab1e5e43c476e439c1d905ea841a902a2dc4e02a524e7378a3132d03b883c899f07c7df0a9b6a6e0701a9586453653eed97a4b15ab983092d2dd49392d7ba674bdcb8a5a34d9f08dcec821d32b8998ce20a3b1e66ffe3a7baa55ddf7b32ec9c5f14ef8de72eeac9b80158b5becfa0b31f9865a93b8318cf0da327801fa46d658f7a0a8c22a2ab121dc2a3cc5f6368def8b0f93c3b4c1c0e0a14e6c9b53a0e542b7ff54e85b00cf0f4ff68f5508be62558503a3c9ca67927403dde0e1a1a842d31a731d4d1cad8e7106f3e32bd9e42051f2cd53efb4a1638bedfa792451db7980f9c199237971a153916ea40e118edf5a3d7ee59bed0623c5aefa87d729948354910447f55c393c9343af03fb918431daa15de1343c0c813dd9c6159ef2744516a05f72b77ad31cca3d9c60a4f19b2c243406f6ff69bf41fa0ada05b07771e30f36507751ac4125203feb3ec4ed8f7d961afbdab5a6d3217ab74cb401551b28621c8c975556c24d3d6deaf4e300bbea75dbc7f048fcc667c9daf6f74749e5052fc8d026e1f8e1b62f1aebfc58c1161b45ba0cb7f69173219164d1bcfd1754a10d8d1ef42bac2b61766f12067ba1de99c51422f6c14c6676285c6629232c4a694398bd935767568ecf5f94d60bab325a41b794d4502c48e25b62f893a68d94c4a03130e30a13214e6eaf69923203a83b6ceae544b70b61c3938c64bf529e60cd1243c6c126ad3ed3e934f2cc762e86cb0b52ea22f2e9ee4b0aee233a4caee4c916971c6ab0c8216e7edc410414a56b9092773379fb008e4aa66f4c2562978576858be544dcd4e7d25d9d13dac7cfa6a4d79d13e312234fde7521aca571f4862477e00a28b510d4fc4f7301ca56fec77277633a81e682c205653ce650a0e2314e6ad561a78aed98c5b4250a147f786d0dd8715397149b2f9220fca8dba19d77b1b0deb7434974d9152dcea158108cd39f7c500d5d9176ba414c7bd6b055140eebe229962f56d3654b167dc2ea8e26821678f76c9324e1c0ca538e09a80f2ee6985b6da9177ecf677a1e4c7abba1eb3e6193385fa7119ac584aa1ff077c4f3f8324dab3bbfdb5e475de20ad66a96aa296c0bc302474827cf44e8163ce83f1c71a0f54340e6815cc0b10432f84ccc0a79571f16abe50ddfcfbcf9651cad5f09ce62c4b1c73d94ac57aae66f41abac602b496b4d9b35e66efbc69938a8a05474a7d5f32aa872b1130a914d8b4fec85270c6d3be5ecec8144b979599f58bfa221af6042f791cc49f0fd4df3936c51362e1cc977e271f51ff8958878f27696a651cb1b5308a4d12b2e99d7a45b2c8a1b420302078c962e2218abfdc5ab2678567a88f0b36e02a47275911dde75693bbcb62e44c05543866d1d10c2dba7f4bc112e2ec3baab01db1c4ad549ab4af11c71a14853b744f9187209905aff3acf686b02212bf48b277f6f5c6fb979c3fa1662814162f51c663ffdf7dffb1fba987ddf05bf8c21591fa9e6f3811b1bbc19bb29a6ce58d322fcb8269f532067ce4b16cea3c8f7aeaa56171566a5c99d9bb1dc7aca36f181bbb4b03fe7204ecec98ba4f29dc58b3aba213fc63e28e96a2a6a8cfce407ed417873ae4e136f2da4d973f466e5af7caaf930bb0c88981ff76a037043789f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8fa7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"37a48a8a9954787f1a81b61f1e9c5bcc59923990c9c84c573552923aaa625e2e1a384e3c516072471a1161241f32de43e46f4eefcf47fb1171a21330e0fe6a2d57b0caace60c2b1a4c2dc4ff194fbddeb08ec103576195ab8de5c9ede57c34ae5ca05f5635d5913e1cd7593657635773ff82c228fcdc69dc7d13014d8b23a013fcdfae8c2d24e8a03529fade94344611d094121d9d75e0d5ffb5c044bffab36e1260401e652d0651a6ef0079c68e20229252eb591859c7c14b281eec17a53ac706c3413308d4bd514882010b823a40835bd21ac9adc3b881fd5014215bd58487824b2473fc52b67eda1f875709c88b54163d0e2cbe6bcb8b80f2c9234e45ac17c614c21d319aad3d290c920569b677aeade1146b1b721a4046beee62a2277a808c3c4f41dac0b01d6d4736ad6f560effd07894500b9bd4ad115c57c91e63c8e637785a321578f15e8809c6b3d9bba2e1982d63e8e1f27969338e65ad8b63d57c34f10dc0c5742bf39c90aaba2c0c47cd7e4f53b826bb4f636647a43e88c9539c641ff544caca870aec61c15429c7f124457658194448e6327c6e02aedc08ce7e9903150f943b1fa9474fdaa5079f327f1a720599be73f7abfafba15964793ef54fe81eab3e203de5ecff682e443767f896de1d7e0a04b1e973bdd4a6fe640a28f142081b6909630e1cde17c34229424e6328944459ce70ca1c17a886acbf4acc0be23e3480bf8df821fe41a5d715b9400f60427add5135672c85ed5994ae1ae3f1a4af8b193c48e40ffa37827ffe87cb7932f385164d5e8a21cadac4f67cd016df022db9483d9f865564f4428133794ad4e5bae96f9bdbe55eae220107f574ec9f12a72af278d15cd0acc60474e717f83ae5d012b08aeae4c3021e0a9f855dbe9496a7c7289001296d726618dc82b3c891f94b33abcb282e76c4d1652868d0f6ce0d0fc6af7e7f265d5f057016256b504cebfb0c675f9a25df344bd9a15504793aa9c8100f3e43667cb5830f1afef82939e014b6d0900129910c94d9d5ac1ca1058d2cdb4b027d5de5983dfa5dd9e623b5bef7454a1efdde0708b1b2447b909413e774f64efd0da8cf5a8e1706483c62ddcdcbe2909a6f5941336b851a8e1eb23d0cd81291cd28f2073df840bb77a07c0e1f4e702597c1aca0366da3db5664aaaea319c70792312c18f58f4d71f78da25ff72b9bdfc7bfcf500e09b3c947175f2f2db565b7ab32df569ad50a4a9ca84763fb9c1f3a0e994a4f81470ab01b32061ec6984c9dc4dc6859e0636318dc597a3b062153cd50116bfccf2c0d98af3c6b63a87b6502a0a71a6f30720e7b3a63cbbaabb658105088c9c72bacb69744ebe63af8ca3529ba7a17147279a95220171aa671c60b1f758fc9c06f513637d5a07d7d1ac7c024309d1a07b5f7b64eb93bac675fdb93087c3aa84c4ff5f7552bc53faebddffa8cbfd5b953364015afc6d3caed38b5f017045ddd2eb03f575a39beab4bf9ed77ac05e5d760b5d3d211fc11c3536c5b0ef60d4b9e55d9530d099e8a8ae615c33ba0dd933bd71426276d05f06373b8f413ac3750cb4aec5bd2b4fff3fbd20fa2c063cc3ad7fbf2aa4fab7560aa34c887cbdd9c6cae42a34ad8f0791ac795194266ae927656ec62bb8a55baa9b5716c207290cd3df2d8d09a8e21a1cf3e0f8f0d48209df96f801d87e05cb5113a4613816398d02433924b81717b593ef3a9ae1e8bd58579cb62de7cde3e788828d03b4afc80360c99c04879c6505b02e5d7c5bf924b17a473062659d03cf3c6e8018159fbbd0059cda6922257d9f033aa2bf98998366ddf2add6dbcfdc147bef81af2d87f09ca613e55c3d171791aaeedc6da951bf7c9e3a0872db533bfe78ae2d5ef43cdb50d1c1855c907b43b7410d82f6b5d99648a9b6bede46d847816c0da41ea630b084142f509bd407b13373505244d5ccd6162dcda183ee0a2792d8f81eabf38dd97aa96a9b1d65f40be5488a0acf3699142cf6375f33dc60986f4b5cec2cffa853bbc1080384dc829d418cde1ec40942f6916b4418c882208b2fb158c28a8a806e0767c134976a74ade423a914bbf68375d15ec3ac9e90380e59254952ae443186293a14eac84a0e06183b2892818ee51348feb2bc1f87886b3dc8b06b93f1db27399b943ce05aacd04bd6f36377a9be3f035d82e02da8fe4cd38e80b9dcb1eb4b0aac9c7b54d4818f729b239b865a1be96e6c421fed296ca8f3e257a554fed520ef3cd1e019a167e1511a281306b995c010e57671725dcce14b70a0222910098483bf2a8ab87fed47cfb4a20749d66f5d90b1afc68d8624a7fdcbba594c7839d019386dba72863e305582cbca848ebb045a87a387f535aed00fa8f93a1847ba39ec4a616efac860b43e8c7ff77cc17d85eda912a4be999e4ce86a6bf84512edf8ce6581b155994b8953fabc457af3060c749d5ceb26b6ae21dfed753dd7f25430a6042112104c67641d0731bf43501a0911b7428a5ca375eac981642c2b5cb22d5dfeccaa39e1c9b8fa811bdf2c8f760ccfc4ae57e99cb9e92fa9d13452fff8b12316b66eb66154afe46af9c8efe7393cd13abd01f7e974180f235f4fd1eb12c4b7c2832579b4cab47e19cb21c895acead8551e1c2faca290d4522f9f0ae42197cbb3b5b46c5d970356a20ff452a080a19d1a9db6c8a9fe8a9b83c56786a49c89663c48bf1e02540d96d8e24f16ab887f3f7855d35adc4ed9fb18d7a78dd13d046f7c736929dcbbbe07c725f500d93a8edbc3be8d261f27b6f6fde9ae6484d8b008b7abd42e29e2b9031b27bead901418ced3a03aaf3caf792cf39f956f247918006ef874b67f2d33a180bfa5d4e33b13c8d0c65862683607d93c3dc476acfce20952a259b395eed13bd542f48936992df61de8e1437fa7739fd5423b43f8ed900e09b5cce1b6edde567cd046983fe80be36a5cc0f0ecc4e2ba43eef2bb4c5a4a19822fe3ee8c487f410abc8367403f5536be677a79c255225aa930207c43b1e7419651e3c600c77fcf6728a429a63de4f6d006ffaab687452d65801ed53cd68aee6cc7c4373a06bcd4579035276c43cd3869a6691aac4c019e4bb433e518beed443bcbbf9bdd60a1245df257e07ed417873ae4e136f2da4d973f466e5af7caaf930bb0c88981ff76a037043789f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8fa7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d624917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90daa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea995833d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d9327d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d42be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637bf56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d28691b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f6790067f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc5883ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fbb86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9ebea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b3288054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf21692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f4580131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca127d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e4235805e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f72093d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734ff8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edfe69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d33336500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a081c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef3087f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a2187509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"38f70027d489f630a20da8764d4d2c3ae1e2aec8835f19de51b48cc01c20beb5213e6871603738088e0969fa4b87e18a027e037c0ec0602258943ae437031061dc273656a2b14c29c90a568f465b87f8f3ec305e78c1bbc996fee42c85ae3779edf6a02aaa7f6ba65dac45cab3b1d5796bd8219ac8034d419524d6485903a6d66ab0ec43577b5376808c10e99516db9908fd743c3f2132e28378278470fa852137679ac5f25f9fada9bc596c126fe2217196e9fb90b9fba8b2b1a924aaed580840030f89a4a782517a4ac33f4b46ef52bc39ca88cc76b0965a7797554fab30dd887c305c48545ee7677213af71461dbe7a9cf9e0db1a4740e5ab030ddcee14ed49694b6dd5eb6103996ea68803a417825821b42be08a18645484ff2042804dcbd2386b167fddb429f401079e5cb5a1522c8c278ede64289e9d97289f428f7824868a22170b0dcb5851a1beefef86d9bf3c18e9c59a9ba884a738f242452a768616cf1ae5db937a58a89ce213fcdc544c2525df3d1c89717064f132584c0fd86e58ee21f5d06bbd36a62877cb8d7e41972d1e65bec1ce1246ef00cf1aa5878713110a89fa18a3cba679664985be0580c481b7b1694340f8a1f39ffbf07ea3ec489e2847d1de83b885f9a74f27b112088aefb856d8fa98aac7197a3dc8797f82c2ab2ae2a0e0e286478aff121012ac6865f2a6f3fbda2fec6162cfce60f3b53cd04cc0636fd890b7afecb59092d5a4752533cc3a4302394399978be0bd6dc441d2b6544e5791511dba63eb2b935dc45395ca656befe64118cdf2acdfd180c5e1e8d5991b64bd4b59d06522908de0b7dec283163a0911a86fdaba0cd4653fe44507a480b1ad878ac9b5f7422696a0e01de1b84b0db8dbe630dcb1d6cf6538e546ecaa3c8670b049324f8ecf2f75cd2aff73a0ceac672e1d6477d13dd5f127a564d1b8e64464d24dd0682901a21a85386ff561f7b2f735f95355cc90d76cc828804da20ee52bb5b273033a70ca66819086ae2373efdab77c67b430104ccfdcd5bacf87cfd6d058aa6ef00cf53b9ca42f6c11d0f5853eec1266a82477a4a8b0034feffe3d24d2a6fbc396f085832ea59863bf18e32408894e56854479ad24f2bbbb2916df0c2688aa14661bead8f3d245a7b80faea3fa52c7c5ecffc4cb9cb0d7f2a0d787c1df3b5ba3bdde55c433ee51f835952ba14ed5c991dc52a4ecf3d1b32960c7c7968f0e0bb710e306dbaa63ac4d0e049897d362d08bc7b6b2f19332c24b26f500c1cec58a312944a02580bbab54e751559de1e168aa31a594684ee9fe1e8e982677d8108ef54cef267372e76233fb6be4efd59da9c8392bfcdc8f889347cfcd0b84fdb95c8935115529dcde567ddcc5c7779209f6aa7ab19f383c54ed5ef0667d6e2979963c66babe0053f3a17c41b417dd6fe86a03b6e12a3514267f9d2712f85a1c70c20dc83b08eb3997055b9a17b01f1a9a84460aac5f3f5dcbd17cbe965331e30eceb8d15b9aeefe009ddabeff50ee673ecbf2f50f3b87c08597cbe604e88624d3d98af06fdafd476ea7ce546215025d12c5a303d4f743d2910e9097748ca1c5252cbad7099a4850f4183a5fe07be119826fb71c580fbdb1c112b76bb205e51b1ddb270c9597e52e573172286add0cda49ffb089cce8fa0e8cad1659d623a39511b17936660332a1ad8af60f04d02f9096ee965144824f8ab92f2b31ed17ec89b16d93a2a7e468450497a27ee4d4be6e4bc469f5f9aabefe9c8b41e91212c05c6904cc33e71e33e8fe296ba0e920fc8aecb20203c804ffde759b1bbcc48d07ddbbd36094e4d2f919b8f90ce7a928d279600a11891649d5014132c3c094994bf6d9522716364299c00f7a28e017ff8a614ae8a80a2a79757543332beb4cc2b980494ad13b9c0de125d34e2322e9e03e3c2b5c143426cbb6e52a322107ff13b39c662703537c8bf93293464c428aca016738bb1aee0db06b529112e697b6529e5c62d8aecaed87bdb4cb98d26ccc042efda10fd2bbadc1838e828d37cbc978326fc83593f76100de20903c42658130c7d71002c112389d792383a1d457c838bd9511703776b7367052c97b5a64edc62eb255c69ff9ff6506510c065672e52f23f495321d4a7709a45e94960e2d4e5eeaf70bb9ccc65f9942bf2f6618f24935b02843d773d04afde33fcc4cc8cc271470010e30aa0006cc48adb69c33ded04e56e7fcbda14641a81772524ecb211b85b50d2589f47ddc6cd5eb7f0442690f244674cb4e053e4ec57df643f83654295807a41b1a237d98470e34da114d3a86a02af2aef9a62dd5542548160b2c8d327428a19a9d5df3e10f8294632fa9df68ae61458ded056233039e125b58152fbf23f9acbf8c01fc829b5c62234b9a29be37658204eb9622efa647b6c76b0c8ec6368664a35e5c2daaefa25153633ca379237441aa9fe88127f232bac07dcb10c152dc4dd0cc9f58349063f4e554b6941d69da2dd26e76308e7fd413acea557f9c12c8d5530ddf1bd2714aa0c89cc4ec00157d08125f22f2d08a1fc2224f4ea9ce714ccad43f562d5c82b66394f5fff4ac7bd2eb96386061f175e1dc919f78a969ece6ac955b6a37c8dc9c03e654d84993c4266b56fbdc3f71a55c241c0615623cb403e59f8395ef761a3112d7673731f4431bac19adc6912b298f4903bfd0664cfd5ffe7927459a47d73bae35c090a5efccff98a65219ed3cff613fb43460c9b031c5d449a7d4d50c970d5dd358d896677c91e40f3afd1189cd752514f7bcec89126648979f17836ba2116d9036d608f6f83dcc945f859283f27374d6ece760d847611e8706cc1f6cfd92992f7a746ed02d609942c2a3168040ad66f84440c1ab9f1035511db2ec72dcd86667c28bf4d9397cf877d0b6c1f6be25aad63e1f227c1a185801ba6db815f7b8b6b8a0163188271993ab0e96656c3cabee65450b7ed4fe62c831103343fa1c5f54a7ce6ea2dbbf70acf501032140de08cceeb883dd337747351c36b6b5d6449a1df48f9df9ff16a2842607e1b08be8ae3d69cb1ad730e01a3f03267946b3d07a915ba014b63da7ac44f6082252e619a7ac9fccf585dbf15b2ac57eb660602a077e9c32f425d71d15f449bbc588283454d94eecc36b06287d874fb9747277065aa8f7c0c3630d7a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd47636b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"39d50598bf0157950c6f626fb8cd21ccfa55e82a86e8fa45ae8eddc18767b1b0a4a5d5080511c32a0cee5631804458861a86fa4b4c73f2db345b05b2dd67861046d4415e87d0b0c0a639f74c9fa3d4fd8bdd56e55034ea341787b565b07c76f2bd49e5cb57d10660f79dd06cd4f604f051d01b3fba10f9285d436ee7a8d8d61791e006eccc98a2222495ea3016365bbfe73b94ff1202fea5cea6aa20b5f82b2497e066e6a803533fa6ce70317bed8e652f470202c9b8c0cc72e2573251a0f5f70e89c121993eae1cb6740088010e0753b174ca4f6fcf236b1eb55915de61b079c9c2d05a34b274fc1915fb44ba6681bae28a7644f49fc8493a4df168e690eb994642ce1fd175951338e54cddd103223b8824c68804dc62ec754137132e3779b82ed74b123fc52943cad0d0bb06119ac8fb9b82fe302f7641af849775bb1db27d01e0c5311b9d03a6bba9719be72e337cea2209b3b9a0e400f6dae6dde69e618c865aa3af16ef1ba4d9b48ef5594f7670e7c472e2be49a6ef06efd2ade1665e8182bce220eb9f0bcee0ab1dc67a1f3657764fb8b2ca62e212f20ecdea55d7384a38907d7b757d2a03130c08abf65b56d9c6ae54e1d9acd994e62453fae9c43630079502b0cf33d22dcfd516b8d2bccde2fe70d74fd4ae24ab5444e6ea299e13c5448eed5249da2600012fb95b4addc28b3a4c15689cb416c119d73a65f0fc35c7c7a2b0fc365f8fe1915956d4d70e418ff62612364a9650bc0d485ee49373fef0226c97d2a6232984fa03d4be23251c677a8022cf8a9212d0de901e9d06187753f3e22995aef87284f0ccb40eb92b1af7f64294ff3b2bca09456933785c06f5edb0648c2d86fa5c2bba934cd2c4c4d0149e7f3b9f0632406d1724bf1674d4053a42d88de9ebaef134adfef817b1d6b026816830dd87e2ddf31717801b93d59f2148c28c678f17a1534928312194942b6e9f89eb274bbe78347f5dba939206081df701fa0c05ffca40c298e8e4d3edf4e65a25a0a813b8baee8d82923fb544755f144b49dfb812a58c0bacdfd62bb9aef1a09bcee725f34fa2b8378bb45c428915fa52d2c165c9edabe3881dd97dc63dd7e3a907c1ad3cd2bf3ad41f3b52a3ca21d3ae072d981b84162a02e6556afaa22b593cc56dd8569a18cfed408c0f843e97276f98bd492e7c7f6277fe1ccbbe779b1dad3ab9ea2eb23868933fedd64ae9ab90ffac1e5b4e7ec86eebc685d56abc25a61ea63ff2bf5b5abeeb8fcdb861cffaf00cc9bbcd55ef8b23cafa0669f61f30851a0fd08bffaf7b1cdce86a403c5979fc5a979b25727089109b566aadf5829119af553b5df31c298c2860e6ebf236483f4b7875b314b909324ae34f03a4c308730131a8a6aafa85ef05da56897dda189568062be7e791b66f62c38bf7363069ba583d7caad16787ea288e04bad498cc581381155b98c477f1008f484c72bf1843c3e86cf9daf9a1d45d8d9d6cd55d95d541fd855209417ce91875ca5ccc92903ef906a34470ea818f4561cede77117da52245cab83844f696730ef096f3d46a3abf5cd2888700e2d38ac8b6614d8da0fd2a05c934625af8522fff875d907dc69545173732a2f8030071577b13508f6aea3174628a22621d32da0e45e1bb9612637654a9f4809528514877d5742a6d4cd8ed6f6441908789b4ec320c6045f7f88488be698b4e4225c794ceee3dc1027e37010182c62e5a8f5b5461a50ccf2cdca8b5e6aa72cc29fe848de1437377685712783eb0cc215881a1792207bc2081b9247fcbad21dc6edae817f4df57e77e3dc67eaa015e661e264f939d7dae79662b373aa526c229df7ff15db854db59e91feda24fd3070364e1e9f34d5dded17ffc68a4c226e4586c8e85d1f20f7cd36a99a521c0156afe65c65f95cfb0aa1c6e6f8ced73a726c79a25be125dbfbad7683704f54778d14aa5beafb745bb9c4815a273581a2c627b1a6758f1c09e46336d10dfcc31a832eca9b8fe61e2c6f97923a7734d8620416cafc788cb7c53cf79131bb048b97f3fa372a5e0ec01e677c41496e67e6a585eda533ddd76ac06b05a36dc541b2019ac62d32ef03967516e86c09eaf8a042e61213e4169690d8d2caad79389c31c97ba9efa2cb8e4a396b197d41e394392f9e2cea24293ddd12f62c75ef9f172a08e359bbdb969cf07b07eada3daa5226070522435dfc897f0ca6c25baadc22444228f54056a87d111b16778212f32ad49686bc6d7f315519dba787d7913b46a85187765b177be0eaeb422570089ca9a3ce2adbf4d4dfa35fd990f00886729bbb70a025feb80b9bc5aa2570523b84ae8e20bb009ad9c61c42c409ad74f6ec88395e49c1859ac7ba6868ea786ab9a2f435b9a9b800d012c87db61ac9ed69c3c743e2ef542744f1104258d470ac16b20fbd255ba3988344d33f24ff3d1679e5c4957596d03c6dbc50f68253c2af3d7291a0048a19148669192fdfdc668035aafce52bdf1ee47a7f957e5153c5093d1d77fb54b1f19398c28e20ebfd8095c072bf1f23c9b637936836bfcd0786714502aebaacca8fe85c49f9ea3c53e0781648d7839820f7d1b0ee07f0546228a688941d3b438ca4597e80c481107b6d74ef11aedeefbb3a3008dc50edc88ee44abdc645aaeb2ec9f645dcf63b4d15bffa3898a07dbe708f9d0e3e5cf2744bc1159f663a7048ae8d23e8c9a1b9eaa0e7c7a8bda197ec90fb6e3614507880770710f01634bbe64687a6cbab73279b15ca5e1011ba29152e4bdb1eb8996b6d66b5799c4d46f2552dfece1e0bed9a140776dbbcec0b03aff2243cb13b81a16881414160c920ffcf5938ef2390b44eb8b84f277b5256ed0ab7324ee6ef20b0789b8dd7cf42ecedd24f099a978fe22dcb604fa3430078e7f0cef3045cb8c2e0a647af6967086e2f505fbe1fd796b00495901a39c5c3ac36e390917723fc9296beb0a1d13b0c768f1a08ce67ce6013bacf95a4c8fa36cb2f46f022b758fc5409163e412022e82260778e62bd256ec7f29a3694a548cb0822e0b387df2ede2bce510ad2185fc289af1e0dd71fc5308ded413cc1e51fc39197cf57ca965cc6ddacdaa83cc2b25e3bad5630fdf6ef3974b27690703d8809c1d15f449bbc588283454d94eecc36b06287d874fb9747277065aa8f7c0c3630d7a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd47636b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3ae79b4aa31f135f434366c323eb977388c1bc0b4e05a8092e35993d6d16df215d75ec66f68033ad6a5f5bae6414a4b4b221436a64a451476acf6ef3963e5760f67a8033ce9d73ad802f451f5425278368afb81acfb61e3d09810be734d88faa0bb5eca1387f9c6b0bb323accd504d3dac50e7ef0dcfd25e5f395bb80b7b0702544b9e603a35bdd362f280d9b3f805040ed0ddb35180bb102c63f400de1dd8986def669ff9e2dbd16471b86f45ddf9e42a06be90fc6b950a2051af2ab5d6e7beb57a87f29b82d0b8b6939ecd3ff10160df6077e50c4302d2df4103c53b88ac48462527223784dcca47598ff252f12b57de026d4baeb0e5e66942824c2b47f0ea0f42d6a26cfa353ef8ec908458a93ef834f77868007ab41e12ddba62692a2ca89d9c5bc66cf98871740bc35154f73e614199f6cf833536859bf8f8e0f374016f6b84d8fb7856417bdde9070f12b5aa42c78657cb7f837670a403201d68c920876382038200ba14ee49820cc77a43cf95f1624beb5d5c9c38acdc65e8de02e8802cf3b21326e941220599f1017b4230889a169a6de74bb100bb71527c1ffb70f0fcdd98ef9436eff6c91701042fa77eacafee9daa8bc453719ff9999a803f27a388995f2fbf92f8605af9a92bef46185765d4883b8bf9d75417d3e2c549d98951420512867fa006046c5c78ab10f4eb707abe84cd20491386d09be4a4515e633b978617255c90bc0664ca6ea08528b8b9979d1281721f54fd529203129d42a0a5f56826b02d3a2106e5429cbc107f470ceb14085e1f568d4313b95556f4b2adbf670c1bfa37184a9e5896b57630545213c23ce2de0144051891db66384aa8bf8867f3abfcfd300695b8537b41cf4ef24a86628747dedd51ab8061e612faa1f1043dffe7d60a56859d96c63e6aa61e2a2c0e6ae4442239216532ebce1eee98f174ffd1c316f0d41bcd674cb0d365e75f85000d47f20cf22fc4689aa1679bcae25f8716fa6ab2c72d041574c9a699abc9c72a4cbd568ffe8d00e73102538dc4536f7f93f377d2de03d0ce9e52bcf51a7047c5e07c7afdfc34f567b9355c190404a7b89c5a7b2de7d7205c8850f3621053d7542d83a66b925314210272b30c889370bf19ea42feef2a7f5231a7b5a4e87411039379faf42294807196903a501492372edc91d61f0403adcb843b9c80d805899cf5383c638c439f9ff096519e0b55a3004327a983e0073daca1b4affba6dafce68da9e39ac000a2260db89abdc0e467ebc072e8e48db515c95674cdc67273afbf2acad7ea9a8f829207abd7b2caa7fa8467fc20317304b718a2275e9c3fe31f7639e5be5d71d2f4b641ecd0a4ebc9004753fb809715b11c827ec967eed4251649ea8a03c0a57aa5d7fd2dfd1db1d55ef4871c1431e7cb4e3d96ed9acdf20eb6a027b6fc23642a66b85276c7c21ea9bbb1c85edff31d0b33a011d608990050d92695f799454424d0c989d61fd4f3506523c50d4bf75ce54f160f3c78e9e9bda923e294ca5ebcdf3a02b89ab56b22e000a8dbc5838a19690bd2f8ea8eb0e8e7245aeadb7dbddaef4a492c56544ba30a1faf8884ad284e6df0c24f21c55fa2edfaeb8355458fa2a48b93cd096d4546aca649e0f09b3f64a1709ab334edc92a1148edf8f673efe7489d01b90eac25b26fee62317f8dc4071ef834e507f47c41573748a2f4f26b2dc06fd26fc12fbc5bbd4fc401e263b28e866472258d09dad986faf05683f75116e98b19d51d779ed5f05ada29dd0349dcbd3ebea78e7a9b55827f0fb61dcc55a2f1b9648c4fecbadd0dcc30e9d3dc5135200f17cb1e7881de02398c90dd6b5d202f802e79dd5e851a898a89d862cb870fdc32279d3819f5c92f12a9343acb9c1a8277ce862b12e50ae828f0e375b131affface7b0b96fc6fadcd9dc7192be5d60b2eb7879e52638947d00eabbdab6a846d32d2124d6be534b8a0b86440bb72ddf1693b19945ab5e60b71b68dae0965e2db31e4cb1bdc271f95d6736efb2f7a14ed34c7ae2ca3d13beed7b8d9801e0daaa09c89225ac30d6032c1bc5bdb2088e8522a410b8dfbc38ba93f22700a22e3a80803000b68a737d4ab684dc1f96e0e6f447da1a89f224f81cf780f169c25757cb8092aa1f5660c5481b60a2489ca486f55c632de4d427d46bf3d93b3c652c8b8e298052f6f9f0e5c969205f36b8431ff454d4bad6349cee13fd32c97f01f5c821602e9196bf37f1036b0e85dfe906526cf6b7f4ba73b4497db11210aa3d4fa9403e3fa1a1b4bd9e18cf0b0f74e4c21cb6b016809a3d3652601b7fb3dd0e40a63fb94321c2536115e93ebd1ca98bd939531654d4d26909fcf4002980d78e924956629a0055d552773087b99fbc9fed7129882be26754ad527483c43cf1a7d3a7149d47b399bcb0afd7bc71f4db394157931700ff516c684ae45e55e51bd8ab3176235afd12432ab602c846c3fadd9afb7a49225d7ccb34acb792ffc32b9060879cf56ce75384485985d2597b2fa96d5415d7cbca80abb71c496168c64c17775af8c25af659d3c2e9e4ed01d886db90d906eaad6bd5ddd8487960e404154ae68eab74c1662ea8d324f36ec16869685f6713cfcc03d6c20b142f284608f998295cbd2fb7d612993afc4b62b77ef901a93592e33b761fe9b5d782aeefd89d4dc214d42dc645a1b5a358c608adc1b27df76fe09a905a4e232c4f2b39a53300a682619ae7462162011eabccc1067e493bb46ad1102a5d7aa6312ccd3a20593dd2ae8db98af243f778960db08aaeb7e44b78275924510661ea33e6d5f41a4e25f35a3eac895a05af1d9ed3de5fd14c33604610a333060b72b259bfe63650ccd7800a168eb9437071223821b556460f8c2e9d06d5c0d147b164aaae7fff7c8905ebc879b5ee35d8524b02de79e7243a95217de63a06c680ab3120eceb35391787d28ecb379270fe13180b99eca4f6bc8ca3f98c7871e6ebaf8ff7c13062b45cb6301e9bbcc84fb0499dabaf5c580f89d0af3ebf5e28b42fadaf0bc2f67e3871342c96eeb35351e46705f79a2ed2dbb459924116943a1da7423333bdfd083e3d1e997b74eb0999a28d304c1e2568a6a141b066e7a726d7934a131eb6cb5550eef75d8ddb0b6246b0f0d97240694ef176fed93e932017a5b1e933a870ac08d2797a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd47636b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3bcca66dc56e4d69e2dc766efd99815e13ecc8b908b208ddcc0831d97766e2fcaf81b9158b31d9df71a587e0cc7d7ab4df3346f28528bb654e6f75e8dda12c1b7e2442975b62429aa3c3f2f9325979a627100a5cb2a355301d015d35dab75903806a65ad852e6ba669af1a16879f887ad220af632f20a40969e7f2483da74bf65edfbeaef9936ff398aaed04d990da1347df039d19d14e64a524d0e3f5608797052bb076ac3abf87e26b70b81ed1e7754384034f22b68ca8e4779c2154abdb316e77db52db58bb386780d11938e7738f1bc84ea01e76d01d00f38402d66df7680923f6add5705cc0d768fd8d7623157b28bd6a4798b8811038b8c27646e2d9bdaa09fbedf92ecbc4d741fac0816f3aface039491ae7da421f5025cbf75ffec0897b1dee6606448f86914d5c34fcf8f127947fadb239f1884efa189291c7cde897db67d0966858671416e4d8d595148eb4162ca8b2c1369d564bba4cd328c236195dd02a82ff26c04ca55c21dcd9ab5605e5a7c900ad7c75dd82102f7a38a4ddf4cfb034036c15343af617748ea64e999000659fd33e98530ed27937a68326060cfa2c4d8e797a47b93dea937d453d5c8c42725aa5b93b66b45ac37563f8a2cc85dd1aebbd21978156c757b255ffcdbb08315b15c57379fbe6834ff7446b20c063fe7e74ddecfb28ac34baf32f696a83e286f87c648421ed9aa150a0fd3d3fe4fb8bae7326313f11425510bf53a50721b7f69cfaa50be2059fe57b1825040505b174674efc41dfc160e32e9e9be6cc7ef36835f32328decd6526203d890631850e13d50eb9b20f660c66f4ab69e4b525049cd45c0c5e69afb60ef861fd42fe311add049af6ed681297c19175d97b94cec700b87550974f4d431b295dbb62068c9532ea5c13e8aa9de0fb824425155418af01307c0bd234ddc1025bdfd2067be6180b4a3494446bfb9d2cd17501616618816e571ef74644629671d57425a3b1ed6a1c7f426d3d50da4658b25c3aea5b4d9630e35e5f1b91ebc26c8b76e0f35b7bfcbc5126bfc8dcc56f2ed98f286dd73f9d8da7bbc35e3d98b4a183dba3960a3a6f0b1bc41c25fd4747098294aeaebaa4b4363ab81b11b75a13e7d4f600cb567dd9ade91d1d547e33f9652434f0c12318b9f3d7b18466de939ef8fc1c59d06fcf98f6cf8c657a4950e5b8aa1a27c6fe08383bb7da6305d7662675cba75c34e129715a501b434d467612451008295d23ac48434b746e51998ec85acac5936e404fe5cac4f22bae2d1fb12539480541e94c571b490399f20f49b0b897d1efba81a06d31dbb6f35c5d1232c2093e59285850fc53f6c82056e07de5131594dcab7b13f76749abcdfeffe7290c771b9452050a9e57d38da52ca78308f933294cad6a4ea1645f8f6146e9f675a8720beb7a8e3880c69dce9199e0342d8b636c77f28069988d979b490377db0cf68e4c92b77474900972988d3adb9bc783ad42f7fe9b96d8862700fd94b142b1e1160bf39eef164bdf42f62c9397a217d1dfc147b7f9730e1b2febca0b25f020dda4f4c148bc1d75404817833174f5de76d38666caa7da8317788cc9bc78159d64063f2c02b9149728add0a8461ca3f11d3effe1941d9a70dc080de74e830bccd2ba4c231f48d9e1481e62d221d2a2e6f405c2f0ae346fede6db3c5199587e2a19f9890031db9763e9eb40a1f1c71412dec94dfe674b65da797a0aa59a4ecf9abed5f70fc51145501a38d161a6f47e211322906279ca7da3589a3ed1af38490968630c6d1f98ebfe523f7a357effac1f5856d522c2f27938b981099f01af267d4242eda979aa3ab80aad7a61f406f8bfa4a02061f6726786cf52464ae690c5e8a9f6e84e28ab01e4a26a5433120fc843ee13efdac9dbb854c31c75cd405c840f8ef0d32577c14f2e8098d9ab1a05e50f8c8921a0ad256b771be4611bed6071eb130db0615f6e1bde04ed0d452b9508b42ad970dea429b6cb8a705d15197904e496b59e84eb7ed5e600230735e4193a3b89c0aa6957ba228566ae6c2099a156857845d0738b7572c82795c54ecbc3f1815c76fd019e2a281608df529d4857f6a8faf22726aa221afef6f3a9e9209928fd5abf5c04913e30843c9f0db336881d48fd4c471dd71d1983a582f921f4acb9a9c8b267e06b7f9d030f7066a8de8450ab9ba6e23770e60903204054b75c2f7e3de9bad06e15fb67cc3b233d96caeeacf58b7a342d96ef00bc96b28e4cc2a3f9736d290e1d69cbf2bdb70dc0484be1972a37fed3b03246eda3d08bef7e9d6e617d82db9e478252a140699d8d3d60f3fe6c67ddccbb741bb735483a2c79e5201bb44eb552e64cff06ee1aafc56b382fe6338923a7db762a35ecfaedcb2af74ffe79e586df5cbc08ac0135ee8b2db8a97da083b24c09109e545753ba604df4c48528f9bc633054b56a7e6b49bdae64aad461091c70ac9e9fcc517c30c9112248a7ef5bcb2424b97243413e25916a2f0627fb48d2df1b3f7d2f7122a96743f19552c65f74d6464f73369c6007dcb8926f8ae35b1c30ccb66352b50328e3d501a67751d5dbab5794b60bbd2fa3f20292e66399d7a00b61996134b6dcdef5869c4842de92d4a66bf4448e7a0ce0d05532bdf7e9d27fb19209596d308d7a6bffdbf6f4e839d61e2ac8b47c396b4b38d08d3d0bfb3d27060af247a36bfe817cb5228b7204e332b09ab4e6b25ab6cfe30711c9291f9df937f4a45a4f423e3c9271ed0c3fda502f1bb379c1a458ba4f537910d29b14e25047abf14bc5d1ca5fc0b56d6773b7a6225f65530c403d6629c2e815068ec8a3af54d717d62a90ca3a46a1db12b9e8674bc86237249232aa5f9c5d72c173b0c252df316181a7133d2f6a89382273fdc90cf3bd61c07d1362dedfc2c7b1a8ced88789ffbc487f986c9ba9a793c517381e6af3e815179e0c735fd6f118fceebdc257b9ec7bd28100f4d025dbef36eed8b420603f9c7232f4efaf27a5ee591335c35aa67963add6781421a23be4a82ee188ea5752c2783c37399478490cb656506d7b848af42d4bdfa21f2cf3f7eae88070d2db67002b900570ee4ef34702974c8ba1f9999f9e1f4b9ab6ea906b166f50cbdca94dbaae5eb51f75d8ddb0b6246b0f0d97240694ef176fed93e932017a5b1e933a870ac08d2797a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd47636b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3cd85230cc10db3d4466abb719a3875cb638e81ed02fe0a71e3b20067dcaef30d29f0c35a511264910a10500349b382e6b5ae5a642c9dd272514f3038ca463ebd948a3fbfe80ae34ae4174f5170d175f28a3548040bf0f0fec06c6e9a84f3e98d9916cda47d55565f26d5ca5b199af689e506a2573ccc4b0f9be50ab503689c11ed7293a0d6a1389a1100539afcb21b4b25fd855801e9a8293ecb1516a788bd15cacc07f7cb8fd78973b7b372d2452f0e5e0236dc11a99c501b773888b57d230aaf3694621e3df176b7140dd426cfa406303d3bd8cf6b32f1a3e09f82a109055dad9e72ed39b8685fe4b7b14d0528440c95c3b5bdc71a94b57d68d79bcdf1d73a08601f65cacacb4380f74a3d3f2af0b3c01a177327692bdce417c05c5d82f0a4344147af0f10c34b5b63fea2b83d87f83f80d2baaccce34f210d50b5b4bec594bfea6f2302d1688372b611a91f173a3cfdf09dcf6818274b6e95327c15946cb44aea7ab55c007bf66db1f7be7f3822ea4661c65734a20bdd8e7616fc8dd93647a62567cdb4645a003f33de68d9a8288848800688debfde91323333010b7d393b6cf1964c4149adf494066e85cc96bfde2fc9e85381e7ffaa840f460ab9b8ca8aedebf1a394ea3f524aedb251b57f9488c9bff6c8c9177fcf7c87a57c0e7c7ec4af6b259ac2ccf04db888c12775af9f29d9c626c2fc1d4f1df52ef33cc4996435b1331ef12b3c4775aa648231681676e0a020a5fa55679e90ab02b8f42de8aa65a35aebdb9c5b99797fa04eb9865d38ea9937d6d6a9ba4d693daae850d66812d3c66cd227fb3f2599f4c0e289993dc04b23fd2fab7dff749434a6ae7bbed0c12f1069d6203c35537191a7c97f0ff8a7383b838c3b947d1750a35d1764acabbc92952a82addc1a0b04c0408f408a50b148211f0a729caa55b64a4d81247164f2f39fbcd521e60708f1a6ae1f714b2aeec1e4d1bde130ecb7fba85ae122906923344e54c649f7e9fa358fc67f1baa59e887391b4471f9b10e3c77f68b47d44911d2af5d9a5fedd9d54f8db689fcf8f657df60aa16fd59b7d59f165a393481cc1a7a1f5cef1655f489b9ef8afbf87eb2039ebb3d9a2c1ac416687bcffbd518908076abed309368c3b131107cb2e9e3cc177eddacce799515572cc75fee142a69b6f98894cd9ebbf6598922b989a37506d9ebe39bb5aad9f7fca1b4173b9ee5b04ba7380a675a1956e30cba3b918ec5e2d54468f9bf7d9e3903477b3da996059555fb969cc721966fce8ca58f2808b82721f66b4350ea59b11b1e799cab2fadf768aa0b8ab623fdf6403d8dd9968301573f0655b86d2f147d8f41697101b12d1ce69b6012ecfbe4ba99f3bba199bd3e1594918c21ce8be8385489bcaa168065af2150581b5dc32455be124e52af9fa84bd95a9cfe82048d3cee501193c24b51435983ae5de14ffa694d96dd3fb326c7d43a137e39794e7fd97639b7858b875f136d36734ac1c71300dc35acb201d00c282ab0407f4a253dc91f02c9930e6d087b9aa8562a07eb5862886818f4bdd7beafb6575a9c8a87b4eddd78e67d400bca1579ef60fd480f9cecc32416a8cf88cacb67c7ff84746558670d0c5ff10da07479b74ce0c8ff2fffa9358c36e5d1b9bf9196acca0063cdc38610417e7379f22efac9eb76fdb24ee8388270eac02df2d96b8b4e4047b64a433b1250c7cc8f491540c22b6903704c663847815e84bd53257934665e73cdd010f9d461123657abece1c6c6bf7b0655de7405dcb0977b84361b85206866972f90f1883b325a781324b160499010079069ec84c33170088d275a11f34fe480efffc917a4fd3e40d12ab2408052776884e8e273cea029fa08767bf473ef6a49f4642179252ff17b62098d8acfb7ec28d6430b6f88a59e4f68e9187f3dda9d0c19f67c2839eba3bb633f136da22818662538c904b8989cbc631e682c767aee49088b6c626ce204011cf2813e6f7b09a5147e5728548a6c29de164930b49608db0933ce2c6b379018a8b115336e70ae9368d053457ec13758216692032ec4d404c5b2592c8871ab16f39eb70a0bfbb82fad5a8a15af3964bb3a5cb8c9712a57def48d2eb5b0174bbe1ec7893c2a78afb05ba31ba398e8589590050aeca8e8c8dce0b85ea218d3deb9a51ad05c6cc99dfae70d5bcb4d265796aa6f9398d36d324692e7ab89fef4054b74c6c82dc2306490040ab338cfd71b817ce35d1e644cec7621cafd31afd54368805f9cecd34cb96d1b4e9024da1a0e9cc4bca60ddcd685be2d862e9ca32f69f6a1821a05280fcfdfc6a54b3b69b29ae2652e8d26bf30fc84f0455c4894bc795eb6d5c569cec9ff6295626878de95dd9cec9b383364fa1c34ab5722fc23308c4b2f8f3de2115665447695407a7248d002c1509f1ed64326fa992c600226db40d205a8a472dc178c8a39c853f61942275d1bfcc31fbb24d53f86eb8abd2690876cda8be3ca24e972a9334b9952fddf73572cebb122cad90eb394667d067add30d54008409fe19f8c97375aa2dce65fa602b805d798e70dbcec16bb96136922b5ce09cec1dd27cfe43df0d6457242e6cc4b16992fbf3b4befc32ba334d36e9ed79645445d40cdd6976e5ddd1aa68c9b9d4633ef2de20ceb67dbc5d994eac71c0608387561211bf7b2ff3dd3fd794f5aa3e16d984a1d9d660af05b177d894392ed7e7c9df1c99c713c4715fe05eaa1bd054f7ad37df92f67a2800927a9a9b28739e5f9a0112dd2a3da29692a8bc47a9d169ac769fdadb139f17dd9cafa31dcd1ec90e83e1f6f088a389c318c18b8aa9e902c70f035084514cbc5673b57ce3682d5b32906ba8f90f55d47c1498242f54b2e3717a8e64baecdeb05b087007b51542bc384a04b5df27ce33b24ea7c4792419664c667d76ee9e3c17ebd483f5c9c24168c325bdeb90d648d2263d97173ac074a4be9079e351f42ba91c3e4efe294daaaa81de0147b1964388805ccc89b346e44ee04ae9ae1240ebee397f7ab93ad1d15d888428fb5bcd2706678acf5843eebc1a5ba9dc47a12aafb61dcab34ed6e2aacca70fda9618cc6388f6566eba9dda4c0b225abe20cb0ea4b0b3b7623d6d4a6a1481002aa40f25a3190c49a1b9514f9708280f6f1f3b4d9cd2b0579cfec438b2409433d71c88ef8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3d457fe03664d78c3e2612bd4b90cf24e06ee2a508965eba3a93eb2feba43bb260a297f2c91a4a679226d94ca7eef815b0ae0530b2d91cbe839bb0ec45293544573e79cc9a0d567cc7916e99febdadada1ccd6a6d76118696dfc0c1cf25fe101692bbfab466f2f39cb2ff4d923b356d91896c39a1ab0370816af05a6abcf4110fe7f4cb03a0a28b2fbb68a14af8c9570c6aee9f11ffe1d6c1dc5ee9451ce99c09f93d337f3a9520d42fac43d94e0bc971723c931e834edacc08247574c3e7fe90c2cc2af443bf5b64616617e0f031064aee320a7886e4f6e692234fa88d06afe075a0a8aabcba73c083150c9701b057e44ea7245b41e4e9076fd747ea5cc40e33a6f49ccf4ccefb84d6794362dbdabb2814fe12b4bd1bf93dcf643825a70e57daae1fc94c588f18a8bda7688259228f8d7c7a923567fcb2b93c24ddb2c311d02cf4b7f9fff0d0ad14aeed8cb2d77bcfd5d068a2baa0e5228e38b909106fc70110f9040f7d3f74103da5cd23eac1c0a3ec0f218e9cfea7ced995b79377bce8b9c18c2da819c6b0821afd325f0c6293dfe9de85d17489b0f2730854212dffb1daa62e8aca1eac2bbead63e7ea4ec9ee231b7e626281f6298cbc67abcad1b6e8af0055235a31d8e09b87c11adccb914ee4e82843d9ca4a6b500cac2d8c6631d58960f511dcf053b518d040461401e4f51f10ffbdfb47559b987bbbdff8199e8af80e61aad95a2fd36467b107c0a61929d2777f3431a815e7a7deabdf4665a91e3af0c17b0f42a4350890e56998f47bc825d02bb5351d480f394561c3d70f99e5ee3172ccbe9a0f7121c1067d925889e4bcdf00e5ddae09ced2184c0ae6e565e546886d41465aa9e32dfdea7a14526c07dc7812e68d128df53e534e361a041c57ab051edb47a666e4d7b73f4fb49abb51c508256cd8a64641f34d13d9b99d5860e70868c83e796144fe9aa6426b137f16eeaa402d69ad8803602f83b81c88230f2db65f1b6f613a3775560cf299c8a607542410e346f25a0d8c3a8875a46c5aa6f102f98b46e2f016d6433a6c7022ab0e8a651158cca07cf64c37b010f87fa957391a0be18640e583ebf71067a5ef8b58135a7228065e0999632d829a012429dd90ce506ae548cf14d82950d78ece1d2f0a1c820c53555e108d1430bc71b6e88242bfd1d3cb6f48961e9dd164b9de15ef17ca2785a8d68bb89cbb42f0fa6f0128d6b8e714a8b31cd6e84da4ab56fc999d7f1cfb83923c7c7c3a05d446cca5879ab94ee61452abae21c3aa0f962c3b18cdd0cca4c899e2bffa4f9cc999d0e3e39f87cc2cad999361fbb0929f807b681331dcbcdad5eb877920fdccde9d380207b4456711dffa26948784bb612db776285fd3a9534e1fad9e4fd692b2a0e08004d41c151ff4987b016a235d57257d8f6b15c3cac1f03f00546b826a8ed31c90f09005916853f3284be341033332469de315bee7040c106826d00f0105044fdbfd67ed40b40c0a3fd49dfed38aaea90886f045196cc1a9f19cf6a25a6e804c1c7c39691f1f1c24c650f45b2b1f1a7d7792467f6b089e56d56883f0f50f660216e4ddf42cfbf99ebcba6016c85343375f604bd9231754a8216e92ebc17c0778f319b2c77b2135b2adcfaac33f01736fd0a88e1c3dba46bc5a4de6ad1cd6987c8342851cb837a74859ded09d4756dfbe167b0fc58d101120a82104a39619f4a2385be909a9d519768d17b12609724457dbb7b5f6fe902541bfa3c56947c00631fce01070311bcca5bd957dbbb7f8ac1ba1e42be603512dce7e49195bdfeb9e132db3cc6d5e9bdaf77bc26aa01fa37547a598d796a626c0e86bbce1e2775d5b68436995b9cc791b3f191aa60122ee7d5ab4e362d9ca23e09cec8a198f6225d53bffbb57ea2c522b3d255020f9ec437bb65ccda9ad44ac86d19bb7ec4abb8ce3c0014be5b0f2bfdf7106409971578057d355dfbde8f1454b685a22de1d8bf4a125150aa6307868029b672bb65778b88b156448addb5e2982e6b175020fe7116d2df25cbcab9a0e5315cdb362c81d76756295a78063615b5bef6f2f5fddf0633dfa0acf1047669507aec90101cce6443ce92a452db42948bc8191cdfc744cd1d3e792984813ced7d45bc3cc1e889e9d7fef41fd20678727cbf0e31373c2d62425a06af35f0c8735014dfd1115eadd4a3f15666295c604a9a4a13395a7715c6f2394ef34bcdd20994a9a8950aa4abe544d9d7c294c1a4c77dd447042d3de6c3b65d73a2eb89e5cd3eb8dcf2d21feca7a54cc6f3d97f9bdb078ba1c13f6797b1f305f6e9bced0c760a525c819faea7ab65b9ba6bc929d69382c99085110907d5662a5fc533359b5cd2dd9831e4acaaa00b826b379ee1d52325b4ed14365aa9ae1a03409fe32ec9affe033a00d42f034b3708b49805849f2bc83921f26e60806eaa0b8ed6060f519086e5776f1c33c0a8033d01341662c3dd6011031062e55647d2497bf9fa24ea0977ea8f3a035dce97f66c3c09a1fed34d2b1de6976f0a8adbef90b61e68919cfbbdbf824e532b52e90f0eab396bba5e467245111e717845c894746c139b3b0dffe9894f055d7e8660892d2a65bb868567a31399379c5929051f82d6a448f979103c8646168f97bd9c43cd073dca6b759d1b71243cedc028209492b624a70239b7c0363032cb19cb0d05bcddefecb4135647352068e0cfcf98fe8d68b461fbf2e6bfcf565e53cfe201dc147fab49af5112dd4979bb64bf1be7a7f01253f5fe175fcec61c4a3e00e7f64b6bbefdf7945a1aa947ca8848dcb3b61e62be7e8214c99f09d3365d64e6750d06294be65af13c9e897aa3350a6c1a990d7cf6047d0b078e5ecadcab7afb0a096963707f8fe10a28296a5169a805840af75d1cf2033f2d54a0d22a884d583d12e33ebac282e700b130877ed0a295c02c1729915a733c05c6ee6a1a5d80b8d1854f65d1a89ac8eaad19ebfa163bfb641a139c5441a3e8e7c3f50569b13bb696e132c40b032cd9738451f6bc9dc8cc0050e48600018d688443dc33a5cfa90225904989befbe701fdd71f9b35a3bdd5b60f8026ca5f2840fcb3f6d65bf9dcaa18065f82172f97801e81ed561b54c439a8360eb0b9aa493e5c13a3190c49a1b9514f9708280f6f1f3b4d9cd2b0579cfec438b2409433d71c88ef8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3e0235196e6f140740d20384df775060f0b670ffdad5ae27c7f1e01aa7eb9f41e18341b8c8f4047c0af138b15fa1907cd2734f8911618eda1e43e15c7d8ff73e454cb10724e8fd1feb90ce35bb254681ae792500e83d86034e53eed6e3a716fb1992d3af9aa0c3487e0f9ac2d67ee8bd9b2366e6ceb1c0d280c743a6c21e24fc0aa45cb7c318d0790146506dbc31cad4019c07b5518d5c0644c0196776cc5b26c38c63b25a86a1adcc156fe5790e2ecf55f458c231daf5429e237978ce3dede327cc4fd41fa67c0c2ad222a059aeab1f6c4983a431c0d8854277b68862ad12f228e9d080ff335b724d98274aa65a4c6e1054c844e9507161eb6a6334d9a289af4a74482537bae5363582c446cfa05b2cebc4a0dd81410c529054fa95031c196a892930ed831758a4aae6cf1878ffb7461f8da70ac5edea73cda28ee683b774d2d3e8802c3121a05b2ad71d8ef8e46514ac924ca33f20053adb86e369c1dfff36b9892aa976e8686a4c8572da45aac199ac1447f3c10371d4e294fee1ead3852b0bc97a8851c2a2caa3d367eafec272f37f26650ce70e318503e0ee741449524fe9d290d03186769c9076c2118a772bcbb0c3774cb3dbe75d6c06abfe22f17d14dab2d8d113330f9c9b441f8bc52503b3b6e253c55d9327dfce18f34346630bcff7461f82b2f221188afea86ae5883d374303558818847bdb295f9e27d3b7ad3eec3c12c072cfcf58c228655aa844459e050bea31e892350f1a8bf1daf3aa87ebf6b1beee2be98fc33b86de2f3b4b25d95803f8706b4ae1443bd4ab3c92505dbc84ede3670ec3b99b7471b7917359782281c32641737db3a177057c13f1b1e56501c9d65a5fafd1c2691ac2620cf95c7a61a45509b071e6ae415ef3be0a24b6154d8f6706d56b0b45c9721eff7ebe5eb8acc9f241fe19c437af494251c2261b5846b88297c0176c081c55b5c627dbe07befcaa6af5f2e406c767dbf2eb0c74cac95f548837a88a183f21a4a3d2de1e7855d56a46aa73e6a84234c7cafce84e23dbc24a2778551341efee584445537330b06f61c9df96c1e2678fc1d5bd0a067aed3e0a4ff62fa2266f9e988b4841126728ab81f63c0dfa9528168046829806bd608f8c497878fb1e4138be7235a9b6282cdc71e3eb7c7477d969b1170103ca1008c0b56cf9e7549ffffa42c6a399e40331dcb942055a49487e08ce3f5bb52bbf47fd69ba9d4e81e1bffcc6771ad32d3c0bc62c5365fa5319bd719fdbd2592a8df6ab9509a59c277994e4fa0c84375227a9c44c897d13ee8f660bc3d204af198475a0c4879ba4cfc351d4909ab86ed68fda690df2df1ccc4e243718c4af111d0e93c20191188fde22f84851d5fc8b8c2f077293406ee1939161855c5e434689ca0a2d8b5fbf9a740e6eaa7f3a6ae694265606a32e89189c37e5b09a7cdbf682cf68c848b361284eeae0df5b80e8912706c89472dc860c4b7bbd349ccad2b4e1ceb9dd2ec5312fe905c9c697a4b87eaa7f9152a173278b24f36a8c5430805bfcc416bf563b6260ed3599559060d05ecee5e0f078b86691e36c52818d30773403625a547541fb73bca0cad366cf3429aa41f9f11b0d0f739a889aa26d9aac663cd9299a17eaed69a4b9cda5f3cc5fa3bbfb30e0694bf55129955470e44503185c32020ba243498e03dce9ea5e84ea83c9a67c2b9e12e2c3d21c858dd675c28a89e91d1ecf3586a35e7b07dfc8565fa82253ef1f5ddf6dc032471a35330e67520d81b8482ff23bb1977317fbc5fd6bd8fa234dec9404ac26f5482c5707c92468c6bf03f757f8c0706c664adfdc8ccdbca14e1d325b07aba52ed3ed78307c7ca448e7940b5171808d1e93b0d55e13e607b05b3eb1493dfead1faf8acd5ec4c5d4a69df860f109c8ab0acc3cf861de49a59d279fb316cf7eb92701be08e2dea72f4413bca856e6ad55ef53327290f499e531e9c217c68d4cf89fc47046a8b11f5f5ccfa9e276a8227a99e1678f1abe26fd14f892ee5b57789799da3538da453b436de11fea542a7687fe0b0bf776beba1afa024ea3307d274d7f19fa24a82962a6ae4d4c021ddd84f9cf1151cab0a9ec8c968f7e2ef3a9e528ebd3e983188ed52b2c05c30b91afa2e370e37d11523286209bbb7197efeb2e37ce222afc3be3b94ef40770feb8d88c5c09c119cb3b3843ec94c074c258bd822af0f3b023c924f3347515c83a0b98e672c5ad005cce80c38390c067529bb66a360c1fc43538300625b95b808de3bd9a07fbb4795223a2465f107ce4582bdf8438a5846d4f41ed9eab855d011de761aa4b6cd3a34cafab4a433839473e3d55aa1f105927ec1c621450b49698ad8cac67333cbcd853314770fb93551c7ff095d3b24eaf7ca4e19e8052e5b1caddd9d75fc76cb7e8ad0a2c3f456dcc9b63b76006d4ee0655cb2364147f4cb370a202fe447700f32483dffd8a46f33fd2126e98e0c0c16f6bffec18553a8d0fb14e1d3ac216fb56919b508918a68b8724bdd54a1a76ed6cd6be093d5d63103e6b5a12ed4986a79ea20358320e0481c0a43f1eebd4b3ecaae048c62a9195126d055fa693980b1fba8c6db7712161aa4de92434ab1b23d887fa570283974e05cf6566b58bc60365e981ed216bd1953a1184a66e7c1de25282b594ea2a0defba13cfe505aa27a8f3cf5c896032d9555bf5bf287db048225cf09d664cdba2a00f68d27ece641bb5197dbcc670bb10be61677f41a709abb12bb2e7dc0ddb073491650993a2469b24735a35f4ade6a03cbf720050c3332a708c919fe69bbf998c267ee498b4348b12bc666afe87faade80a495bc30ddf9b45faf5cf2a9ad119646f4afe519c97f3a2d174e802a63b9d7f947f8fb99e77bd58b97ea55ed588eca27a337bbad054db58be4a02181024ab2d638427d806ef50dd70d53ee5250f82dd931589f2fb7fafe9e8084c8946ebdcaec2c08963bbffd65e86338332444ec414a4c8f56b832f868fa0c14d6485786cd339d077adc4db997c38530b4604dbb003596c5e867873225c4581e66fec82747cf68234eecaa5682110a1fec712571f261c60d706e423179f9f0050bfefe53f353dc306916ff02ae1fdd98e5e2565f050853bc3cc3f0772c331a214b9cb61afe06072ffac2b86109369ddecb321f78c9a814b7bc8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-				"3fef199d1e8f4b5ec30f289f0e11643811de32d694bdbab50ffb020e02ff6adb4a68e5ee7ec242301d1862c6c9de81673797fc08a73f006d46407454fa8f88c05b8aabfe5397541cab33d93e9ad585d4fc580f0ea5cbb8d9517b075149c68278f1aa76f25ad625b54fe724665df891bbed9cb52af8d1768315fa3a44956c3531d8fe4631b6e575bf332f6b50ac27744d4d4036375f1fa6c6d341193e823cd8d41ee035d08f77d317dbaab2480f3d780840311a94a733bb7235cb021167ce047d7f9271f7c3a1ee7e83879ef7fd08e313b88fdcb68aa2afa43f051ba6a86ec6fbbb5d4645f487cdf01bdea7150a6e5c78758f249b482ce71c4f4223010e6b70a6261452dc9037e4826902b768e326e979bd1f01bb1c90160975f3ec123c03788124ee4246ec64293ce913b7288e760c556dd693bc89a2a93ce8e920bb2678c8fd8f7e7fea68f3c8544ed3873dd39bc7152f9b4ef7c472d77bb60722ff65638ab4584e9c728486c008f998627da2ee446866d5db13e32fb7b5a30bf907bcea2eefcabaa7f53e9a7c94df11ece802c62d376ab8dbc396d7109ce9ce232207b7cb8cd3a6b127698b4fa8c1291a09b0f11e0a9cdfc1b19aa4f6c7a002028445edfcef35f1a68a51bcd3d9012c486abb8da48eb3498fabf8ae2d21afd0cc52a654869e67cb078ea887f68ad5263a57475a1ed1f7b5e551fdb2d967dd082da4d30028d946277e5c406d07a29e011bafbf14e5e51da7867ff76b4705ae059de46f3b9ac922c53db63189c154320652d6066963f10342f4f20992271c24625e8286e918b05e37c287cc0502375786fa930cb526bd6f3413da7e01aff07adf15232a8513fdaf7ac4e5a73f4e4a540ae008e4e52d44adbb1984c08b85540440d56fd63415ae56d978cd145001f8ed780665f273ade06f72364b5f4d180637a06156b60e75247c81d0a37e9a5fde226cdb595c3161e3fa78ef49afeb1edb2f2bb250fc5af575fada6c11c137b8595fbf57c4c8df38320c29787bde8f4608c3964beefc2b9f04585bdc6072720f014e5c7cbafba340a5e35ecf2dfb0eea4f1cc49f532eb8ff4e2be928de92336ddb0b3234b5ff46c48f49b2dd0a4f58ee3391353e295843d8d7461deba00102e2964009099804f0660ea37026abcdd7d4389460d69d51a85895f7dfbf4a0605a4d3fc6dc5c83d6e15c4f04ecce15856a1912599d331632bf1a04f39310db0405555fc62c59518fa8810ded2ddef7c3163ad13b9b788a163fc6cc2d26f25358562f95263acac6b56c81b8c39376162a73591b1f6a2626451cb2f425bef9455fae7e22c250f14299ee90d9aa9d548a0d1196949d8e72376f1cdc4f56b2db1786d77e8c23ba5fa8a32fe9209c7b621758ec11b4c0b784bd9b106aed25dddfaae73e501f92d6100ec158dd3a981e5311b8340d5445d24df4765d9e6496700124bb21721c63022b3546d31130c88ad2ae927700f3c2912dba4d84ad136c9c70342bd42a458cf09aa3fcc333de09db3ccd5f43b3317630e598b45717e7491582cad253942e5fa36464cad205bcbe71504d2567fb9e1afdf31c423833fe63d05cdcfd368f4ec6d69d599825fc48d901d69d1d9f192062aa6aae0cbde1f1b94a3b88869e92cf52bdb0f0734f2e2298747823dc4059873c0784be9285786b4cce5fa81854c2e19f6804ea3638b69efd10d7f1918d9c18e50360be16f4e968771d537c989812948475b4218a0ba4e889618cbef4c0c3f50bf63d9402b451c2cd3202b41720965094ad5cc6938c9a9e9716fa599416410d434fcdda0340f2f021b45553856a4126b03f932d242629e585993c58d71bc6fd95e11848a6092bcdc7fbcb706ece63c89548413238e3d7be9c3e345886af0b081254274622e10913937492368d29c790e8e338d7053b33cd6c1654ddad73476b901dfcdced535fc174b06bc16f183a2f67545de2f325aad3c07d4856eeb5876896c88677683ae72f8851cce00b8472703d88305950925c70deae29d726c5e2258edc84b71f436f7bdb339d1e9f132ded0de3c0aac035f2b93e8d19027b758e7f337311e2a8556fc50a0608df5d93d45e7bdbea1b8d42d5654d515361804d79e88e1b607ef3309f610f605b9d18bcdae8a7597a6e334e5eb66d7fddec12f07df0bee57a873945320a5e88bf751a4299eb324e07106e6ef26224dc3388ea127930d649276db54f5dd705e3cc94d0d56447aa9798dd62804d493dd1674e15ecb95fb8a169b6d74c2e21c3b84928591585f26773451edf872063c06aeb3defbe7a6f57d36a195cf16dec3068ab8447808a7c49306f7d8a6b7dae9fafff813ad8267756cbb87d4cd5b8c398e2f3aee3933fdd651a0d52d5ecfb9dc7758d5d4813a67ae61545e9d7498c1f6b1713cbf5fd72c8d81ce1acaeefb67ffa0104b8728f50ccf5bdabec070d653a3eb9a97f5bafa581aa47924fae03fe59d7fd76d6c370ac35c5471ff830ad655b3a619fe9890a62577d73ae56cbf1ba3a17a2094304eb9560bd68033cb18d8824ede87878b04d98ea715241999aa9c78a12682e4e4d2ecc11457af4534d0e21c07781bd3381b2dcc3c7875e788eb960251dc7cfc6d7380ce3b419a39ac545c5752ff83c298b264539b1550055d9bf70b56e94cf8fd2bc69a70a0da71d0c6b1d9c77a9bb858db24c1597f9ea000465f1d6c4329b9e1b72f3d97c8e86630d84d1bb76f211c6254d97713207b984a3cb98bddb7cf53a834fc34d2c7e6fc7eb41544859899e4ca5f326f3383c44f509ba8139cd3af00d47e6049dfdf820b076af9af90340f6c0675a6136f7d00c4c060167758050915eb64023c04f733695f8e4fa42f7f965dcf6b8ff1bd1c15c5450410a556b23b6e78337624634b691397b02c66b83cf3d358e2750de25b7e289b4e9d9774993977c3dc0c39161dc684718ea7c68900457b06f8c4c0d0da7ffa4373478fd9c8b9862890d025565e06be5802c383f82a161b3efa482975d4677333bbaa5c05edca512b059a8ca718bf244dfc2f573a1d448a9329ebb3eb9e278abd23842c320a944b8a86a1fa5b39ba08fec3de7b70b10c9be17406c09772b270f82348ec9b0ab262c7a314d37a15d68338806b48ecc3f0772c331a214b9cb61afe06072ffac2b86109369ddecb321f78c9a814b7bc8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a6598340569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e96e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e460f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d0361fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d29af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded60ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a207af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d79dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595eae225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d21bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef56df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18aa587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bffdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0aa4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df65550dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d82d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bdabae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836ecfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb7835366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757fa5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb14d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f34a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae273934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc7828717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b8182885d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f84e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552fe72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e97bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec3653450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945fa603cdbac2ebf385846864fd30",
-		};
-		int height = 6;
-		int layers = 2;
-		XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		for (int i = 0; i < (1 << height); i++) {
-			byte[] signature = xmssMT.sign(new byte[1024]);
-			assertEquals(signatures[i], Hex.toHexString(signature));
-		}
-		try {
-			xmssMT.sign(new byte[1024]);
-			fail();
-		} catch (Exception ex) { }
-	}
-	
-	public void testSignSHA256Complete2() {
-		final String[] signatures = {
-				"006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf8030347add4d942530446cc44c26dcd215193c168c0b255a0527f7ed94b7df5bfd28f26560243926420d87b78f3d7388af1e85204c7cb389a9d33db5e913d3a4e26044c3f4d8296c4e744bca233a84b42f0524ee3ccf68eca45b66de7dfc123cac45a2aa2369c746b540b4573813c6ebf40344f7cd5ac7222d55d812dc3fd6f66a81b19f27562e8c3ee26a04db098db46b4695e6be887a3db47cda06c80ac1989523f0f3241aedd852c7de1bd4d80091c443c25c0b5567a594584163ba01b609ccee1fc8c2e4ec4705957094fdd5a5d62ab4cd1149303522c06ac7d9c461cee06d36db642565a964f96ee40be0a75ab3ade6a952305655a8ba8b11ceca31bc37c5ec4650e67a2a8010cb348b81283aa265659ad68e4f45a219b5f104e84afcccbc75912064561f3d07fd243b6840fc3a9c02ebb9bb92c0077fe7084d7f81c64dc7ea1ce1efc000b20ea0f854f68f9ee1a03c414902bcdf648eb0a6b3512f710e2ccb6bd46d58c78322efe0c94ca8be6a35ccbb75fa14b913965ee02792b021c99d790367633318edd0c73a377db1f03c47071860b8a6c9764cf75e01a0d01921803286d635afe6977b30a653b2792aa8d8ce6597c87e046c603f44355201981a03827e7d1ad02d7754385e708bb697b5ac2c6d82848009911acef13095cf1eb1724e239c94dd6bd0123d13e44e5bdd2344a019c5ea46e947c81cf911a8f18eaf687a5a4d395072d53e6edcce71e849785e2d097dbdf82eaf62efaf51897de4b17997c72fee51d5d822d75acf9944db04e5d1cbaf50f83550c6fd4a360cf30621ff1b507d47a07065f091af9bd30d24a5ea7aafeef7537d05d896192e18422792b71d7d725c157d47ca2d9c33b8fe747f73cb344e51a19bdda74141a2ad62406a1dd41d4046a30994f50ee6bfb7b2450934db86c9cba3c4ce79e6348e18aaa693819d39d64aa6ee4e1ac5ad39cb71e35fe127533efb0344450aa55d61031f01ab160beba7667deae619ef4ed46074c899fc38dd1894896c314cb4adda6f7911f2ed6e1e498fdec6d365fb5858c171583a92fb73fb66dd4142a6aafacc5407aa52f78cc82a284f52d6b3816b708c04d32c63b3ef37a08a07926f521c4529e59f6cd74495c5ff49993a5ae898bec172babd64b398403e7207a5c703d9f04c88a97514c9440fcd90ca009421adc9fc22da94f35ad77f42031927c097a37a87d1a3ae35ff0774102aa52deb21275da6481905660f2b5149e275a3de718e211da518d0ec346007f9bb71800d128f6da23ecc6babed12bad95b3694f19921f7135a37ebead795ecfc7c9afa6893f76254d4667df608bde6e126109676ba1137eea67ea1d25a818c8af76eb64d338da019803d841efed57c6df63ad47da40f585121148de8c4a3c10a0197875b05063900b3101ac10f155d88101807032b66d9d9843e7960cc395658025c47e45a5ec746fc695c51a657622456fd09e38c2faf409cdd53f106ba7626cc2313805ac36a534d8b21af77bca84a5d7fbb750f6c93f06225e4452dee39b615b0f2feccb0fe2db1331633ddb90b31e4af49aec2de88dc59a68daba6a485396abf56a5de1bda251c096832ecf8127de7403025f127b5f3308dbf04033f928b2d0ec74349cc1c737ede88510e9ad62cfe1d5ca9c22b8a09b81a094924d3fb069998b21b401a0c1bc1ae2850f1e2a45cbf3c436b9e4723432d009cdf9fcb20926ad7c909688855b2c7af757e64e4b3603c3af4e67809f9abc5688f82f608ef06c531e95ffe92b0fcc0c5e10b03b29d5f3cf466b562ce441e4fac44df6036c07228a5196ed9b7b17e3f78dffb5d833309402b82977478da2227ce1857bd66c566e34bad421bcc51558e39dde6d086083a40e392ac0e1a6205b421598ebd717388dd7b65a3edada491ab6e9dc572caf564e4c3295c98216a005cf0e018c4ce5881819c3e6fe65e50ab7a71b629b5fac0ad19b556704d8da30ca1a66a4c1c037dfe7fad1e7f9087d4f5d4c7986aea352e862a06f49842247c296683c0c1cd4c4c84ed8efbb4ec60489a57d2bda9476f158ed7f3887447244bc1a8915efda8ba53301d080e8fb7fca806ffd7064c82adbaaa0e90ccbd808c08d07758866952e25d86cea977c431a8d88f56aa24d82b936343029070f2fe38d1ac3be1783de3bfe1db3d79ca9a986c8c6d31b53084a4b0f5b88c979baff4c991da692a45b583829ee442cb8d5fc143695d94d1fa4228f096f62e13143d4cfba7a9c929c5cd196a37d5cd3236594343d20b5d7880627995d5c809b74a6916965364e508ce32358169e7880fd1fb6d48c450de04642024f5268e7bc091d2fd3658ed7d2e924aed7e7a03fd9451d63459345112f3cda244cbc4a95c1d5e445b928f98195fab9c2b7160791bb2cd40f7462e6115bd4eab7b3bd711054a5dff71efbcb5e9f9e1fad83e33df5e46a182b843530472bc9a8f0eacf75bf8987e58813efe3c1633963202dac839b645d5253713b7e7c655664e642c1591794863bd65e69ec860be45ad6dc9b4da0ac90eb053be596690bf75e5876af16321389c929577f571124adc3aa125c3ef8146365d3d5b256dd13e4f615a64ef54c1b14800ab9e68d6d5e63088196dc626091be77018a00c23f48b53a282dcc5839b522507207f0a6b16b168f7fb4c6ce52bb03010a4e579c00b35415f1fcd4f47b08cf447d11140bc911787b61058ac55776bf3b989c79c735170c3932d617eece741ecded3211ed345e8bd9beb375380d8255b8b04df1e0a1795b3798a24a0182d9a969a4a554b4ecf9396b09bc2af3f79bf6a753da9f79a5294947c07a2caddf3d6a0d4a8a3289ac1e6fc5b86720590a50d2beb7f922c6b0d55c93a5af80169346014021caeed3cad3b2d83609c7a99189675c5e67460c3e20da7c2ccf55c8e447fcd46650ed01008502543594c29baa89a3b29abf1832de35d9186e6a6fd9034628127c2b1842d377fac6818689ac8eda4ad6c32ebf74e9e7e559ff0652bf58b3ba2ee9f5a5e5ea1bc1bf3e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"01994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c2b946cc55e90d1e91702a152efb6cf47c250f7034ed776f2f948a656ea362f4c9d50ceab45fa2ec26ae272a2a3797b580ac874bda3345cf56e069b6540ff8f603131f3aa0c35895e898165cdf763a6d5c88d4b2b88efcc10dde3281ab6653b0af9ea36dd569ca57b3ffead498f1081ffb38cfae75b368721f67ec1d9b5d7f8e29a2381c180584d9033536c5c77bd55d03577debb4ec171bf0728172858b21c6005d22e542f7867f2452e3ce5c1b3938eff1d79a74338478adb790a374934973c2b8597058dd84957d3d2685d8a7be23ab230b3d83b724799c0f235cdf3d0d2e0bc9d961759ea19ea0f6562e5f553b13a060b6f597c7f49c837a0456929ff01cc547d3a28f6a62cd9ca5b1965975d10a10667603c64900695dacb0ff43fecd4225577ab1c2867a6dd3b0b6ffb90d413bcaaabfc37f829aac0fca5e66647854deba07b0b93b3c48a147e02075456ace562178131bad047d9deb64f4c13205cee916cd0248378eaf715bac2b82f5540111150ef1cae360832ddac01ea6ad1d9019d3c66c55962241bcf85221586ee2a390e6220367c9cefccbd66d4c3ce2cf38bbca2d0ffaee10b04afe2088d835281d51b6e57894abf2ce971bd7372fe3e7521ce0a7163c431519be7717f43f0b4d85a277afd75b91fdd075c4d6d303110535305b9dd514ec9c46e0cd84f5a9e4c0a838036915b0fc3ace08b991ab44d44d567eb737148aa51f62e3909f9b6b1f690e5464f51ad4bc8e2fa38e85fecba6db7921d3cf0339ff2f3b01174abaa95e32fa01078e541db5479553eeffbd97d56ac15df3c5c074c0a0f087ed4bc0a47d9d24cb30e782138779862b15b5367534fd4bcb1cd92d5e6d4f612bd5ea7c03585052d8468c3644ebefeff03e62a3957f8adf4f2698362806594cc6600ef65838d6ffea9096e3ac3a5e421f550b584d74b2592c606aede90a315ae8d580abd5e7a9c595cabcaefd47e224a2d35607d601a8f9bbda0743d17ec0a9e74d4cb477d3103cb88d06f6dbd7c038b54a06c7bc6b48fa3929404bd0529d5abeeffa5c778c08702da452d9129654569e1fe12997143575c7a58b7de776c1d9efa06d93287b827d6f835d6ab2a4c965857967f0fa844bb3ae2657861f34288ace5f4f78b607efc8a41ef5ba1ee3d1446b8c4d9931ca5252c2b44aa4f655e80036b2395b26352658af213fce775b404712f85584cf0b7a9c017017501d868bf701490ebbc7c93d639067f98cf6bc9acd587350de7b6f783f75831dbea633f8dbaf804a019deb631c4a8b19bb36f583d5f6aa30bea7b002d48aee10ca4b9d747c61bf44cfecc13a75a39528a6c2677bbcd483140172078aa44992328f671b6e0b5d18edff0bc335ebd2ca9afdc73dab4955f250874ff7325655ba9b3b83ebb460193e323bd86c7a5799f863816b6cf12006a2d114c252807eac1e4dce16dda2f4dc1e9794ee2fae01bb35eb8d4d20b336d56a0fb5e128a635aaafa05ad631eb03cc74bbe5a2d50d76a79ada495260118f9abd4717a536e3965d917065c9920088846ea16a242cb1159ef67196b9c54b946385c3691b0cd8ff225f4b78fe0aa99f4c2faaa2ceb24203c2e9e18543f68989c7dae98facd6a610a023bc3a4316ddf99454cda95e6f311c5d6e47420034c7c267435b7f83faccc47b0889eaa759cb901b5687b7cd076d30543b930fe2a3128afc2546f75ccb77f626ec392ed0918724d9c9a054eae464081288292fd9e2a52e388a55badbcb352d7bea871151bd9062578d7cf26920fdc16ea40e8af876d6592cceaf266fa1b14b9c9e3c9dedce9d03796b8e32ea33b003ffb6dde709f853c88943eaf9a6a36fa821b920e28b3d48b8fa47b8b8a4013ee14ba01bc3721ac8186f46d00b61ec0d9c68b3919c0c6e47603cc88aec0379d23aa55664cfb15138aa739a08c820a2c87273bc6b7c7e2f75b95b2c12880a2002594b6862ca9f3700a1463b6a67ba9760ad8feb851f8d1855b4ae4eb716ed0fe73acbf35a96dc33020143e68a1b1b884cec92ac1e32829200c1acd4fad52e5d10734a16171fa8e4b711c99f993d185024f7539bf86038625df9e70ae98c606e3ff095f94b1eb3263cdafc9e401d29e7b801e2875d9e8646e68e85d4b9d87f1c2bdc4193c5e691542894ba42197cf1bbacea12906d5c578548a7ae5d27ff96c63e894526c3cb393e7fce900ae4f9dad940f0b64c61c44c65d9b0b7a498f612a78de3854c8948837d60765cc992a09d1dac33df336c5f3dc144f85f260195c82264cd88815362a1d2dcc432a9b54e670b0a7fb205352789d1a1159098d7bced399e731b80f83ccac1ad619d9bfcaedfdba9c6600b9a3e372741bfa9792eb719eb53f1b4b73b798c9601fe77342bd462fd700f69333598e493202e218b4c1bc619bcc53761d936b9b0a73a28a049be5d73bda80860dd1d7bed7f9d8e3872f331081aee35ecd1e81fae67643b38d97cb3a81b97a9d0ca68eeb72b1c6ff985bc1d0099d12d2dbf3488c48fb8d0ec26e737e51e553990395534e8961c6797e73233d418f1a2086ab06b17a47fb4965524d09ffd1c997cda75d8f242215eb7948e2fa9f72c3a92879fd2d17cf801e03acfb9bd0d87c18f13a89a494a629d05adf7c7a61109f811075011196aee7d6dc85817c7a307d28ee2162f3bf61329b847641e95d83ccbd42a482764d6a9a784245f5333027f53bef5f6b4300ab6a36a09c622832d8bcc49cd531af67a5cba36ccd9ea0a115964a00395c3e28cd168fbd6c6d4e146f5bff31c8876085bdc09663fb8387378293a8d188410ff4ed23bbf8e94e8ea3290c219c68180b8a59ea5f5e97cde51018ddbc835ef46658b0a5d79625599dfe624eb52e88934230d23a77c92c5408f62d87254ce43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307277b9a09c038aca00a99ff63060dad783e060e4cd9d59c5c9a5a013c7d555080fcfa56e6dfbbfbbc58df90cc9cc65fb10063623a3da00ba5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875ccc2bc23415d4faf87c4ce6e7525be296c65bb2798f19279dc59d02830695fac8bb044790c3dd1addef9b04a83f6e31ca470cb9d3cd02bfd45401100a35927c9577aa1434c71aa29cebf6bebbd5d209eeda8864a250215a168764a228979c671a5c5595946e83d31dc36a52777616c183714588db1760ef0c8e537c7504ea3400cc4a2845ed647066081db051fc3ed7a541b8f294597aa2fa5bfbfdc714fbe7493ffc401a28fa156cfbeee5f1163da5bc039297fcb5fc5363b48b2bce5e558a8fed0d7aacffad3f2d41d72e0e157faf66658dea45aefba44990912f1387af750ca53e94ec9dd72bc1e273c15024105fbaae774bfd0b84dc7314a3fae56777c8b960fb2450e360489900c5eeba5ad0a80325559431c9128d4d861b546b8f344417bc486940b5e8d27f98c90067a849af397a8798c3d37d24be57c1d73c5f47c67cfcaeb85354b334145e794b2454c5d9484eed5c292e3bc875d8ab56079e74b5a4c7acdda83521deea1931969bd6386934a394009f1a214e8ebcedc28c11f5c38c550dfa47d017dc4306026e12e98fa1489c6f0366c9e3f59cbba65fd3020d39048f84929a4bcb40d65fcea84e461c4b3fc2f8d4a1ba3e561be9805fdfd74741ab476adac59897ece299db3da7b4a250244c278e8c4ef0b31f1079980e2b9f5f2223c879568d89157ca0a22330de5aaee6501b8af26870bfc2f3fceece9c76c8ddc2529b48f96df991d4cdd7cd736ae84e09ae833f84c56811a851e9bd76c4eef4308430d16a056d1c50a2121f9720f408fce8ae8cf1465a3386f62ad2458e2e1f9c33c863ab07e4ece39d66402fc2443bed9dba5ccab665865bfe7519a19063cc487a11619c8f5d072fe5935fc424477bb38be9fb658eeac200fa0af18004deabded50080c47b7315150768eedd9bfe586fdf37d7a0c74807838fafc5a1e5e64fe1b3d70f5dffd395f5116e4920804137907aaceeef41e2a77e15ce6605d9d48e28f575d2f544bd5678ee67759d0f9078a33177b2daf479d53383e010f7cc378d2449dea19083128f32ddd41173123dcff9e06738b439f8e46b6670e22a5ce5ebb2dab417ff5cb5ca4ec6d441a80e3541bf4c588a74cb2c50ec9d75785abb54ad6c4a5260d45a795c9326cce6561a276e4bd11418ac3db09b40a29a6d5c81c0d68175a813623caaf0ff586330ddbc6dba9ee39e62da280ecc13ffc22f83a8cbfe7a98d9baf60e824b4b4cc54db0af44b23596422323d4bffe4ec4a8d02906403765e57e5d8d41820167acc8edac2c4615c8cfb730052e3c1f2222cc00b61691464c38bf725e61e303323b006809f4307c18f798523be6aceaaeb2675c871aa3bdb23d1435ff2a769c94e39ff51ab84e6ea0c4273fd8cd18aeaab4ab789f61e2a44ead5cdb47cfd9f6230a1f54fe0fa0041cac8a98cac7870072a28ab79574ff468a8c791c312ad81422316a86544faf93e4f54719a02960aed675d4c38b11daa308f122391f53218dddb6eeec42106980fc3a46a121f507d54dad5c5dea73bd7eeb12e1c204771511f4d2f07e70c3ebeb08b4d61d108de42d06a069a1ea270cf36cf7d22fc71465bb0279670206c9cbec762300bc336ae1b89f894b2cadc9a69f97f414e22799c4e5312a98a72a464ce11850772c4b6bff995be2567dba1364c9828041feaed5f3fa9d031a709279fac65bdcf63a36cef9feb399a711a744071df6783f5dc16ca0890934c00dadf04012cfea4213d7a66e468ac6e7a6ebb6618f0f8bb926335a31bc1684185e4c5c7363d7240ddd7bd1700dace25f48e548ce21d01a4b2084bd55b84b3de187dfeb8cef29d3572f39c2742ce2f8c2360d8aefdf3279d1dbe472332df35d0ffc1e87c2c80f66e2c72d1fd51a924bb3c61f1a81d97aece880dc9bc403cf212d8ca904098f24634ce77912b3436cac54077a9de2ab86395c31a4078dd24776650d0da94b7c499b68a58bc695e03b6e20c77684ba39b0680702147aba3ce04d13fadeeb02bb9f0b38693ed7d446213b377f609dd001f11c61a647ce183ea5b647ccb3fef9eb104ac9faa0a4366160a7887e65e0fdf4f5d9e818b67f83f7cde3a3c750c7acf38a038870cd1192fea9e23d62f0ff123ce2837a2d1c2a16227bb85118e77d7630ff0d6c7f64be35751259d2ce6150beea128c57064cf05381f0d690b88c943b3d790c03f1e3c6894c59b2dbf489f8b2f1c5035864b6d199943885e2f8532745ef3615a143ef8132ccfd21ea4dd0428d4270f939dbb2cfa4050abe2f03530da91fa11c7b898cb0878c5eadd53651f353c34eff767a5d42b8ad26f8d386dfdf8005da8afbb4f8ab0d94c416c5cdd07ec8fa329353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17ae460dd2c5e5bab23b278d47eaac4998231ee0ec13f620be038de02cae415c61d06c12f74e8e1175d1809755ca24a7eb94b52573588ead5fb4bf5a298af69979579d50e454e1c3cbe628c07cccb63071d0d6c98f921dbbc1eaa03e366efff3d4a50b270d419d6acc16d54c1ffbfa6db8aa3e76123903ff4b169b5d0b129eef63120386e239f55cfd9f4a6f6b88de7bfa273f322bf0a89a6bf0d3762077634b77634b0b5c03e4fad42268f05d84ec8a6480751d779a70b45996ee27cae86e78c12b8e7d327fa9fe367d2d26221217910c5dda292a1b9f2879badc8dbf5f15af72483f4e43390d1074a64c90b2ef4e670c7acee106eecc06a1c51451ea8e7531d84c72cb2abec68a5c80761a3c5088c653d48c04a9ec2dd29defd1ecd59b88abe0b4d4fed28508f5baca75c222a0c00eca957c7093f0c76800535ce2f4395b19a48587b2e54e22ad4f063765739722ed89ac07c9738803ffc999afb8f2527436c69003228c944f61f837d00baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d82a5b8a44a19816614b03e14656ad45203050629f14acc5d1e86828acfbe06140ddf140c79aea8b327d4ac843593590cd4749ac5e2263ffeb800e9f87c5dbad07a9be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"0386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647483b921c75c2c7bf7f9039f2ff3d1de086883313fef5dacdda84e19ff29306444a28d55e9a0f5218fe59b15437d77d2baebbc4123f6419b963c85ce97350cebd218111ad2d1dbb782a95d902d02157291b8511555b949cfdea77002d966f3f9c6e1ba486f60609701e7e202db4f623db28614f7152efabb995a79e5e2a8577effbca65919b91eff118c81038214df51fd883fb56e353334d0e3cdca46afefd63785d38a3f5e668bfee902b3d2b3c8ca2caf98a52fc418057f82ada73c81e10297955fa4e08154068f7dd9eb51a67d2e8eddd1913404b2df856a66ad468e28bb2d159c550c3894ad3749108cd04968e98e932094974e35a464a7280bf0de93b132074fbccaea7e25a01d304a1cf54710127d5cf3242d73b5d310780adaa30962bacdada1110b69b3e9aca6cc37e9c7f019e9cf19453ca13fa12ef3968412440b038f9566a34eb470ecbfc99bafa05987190c4775981a7a224cc0428971341ee8d76167e0743619b3011df2bb189b4cec9e2ed873660b8185e82efeb3bf00155ee2152238e7a8f7749c5337909185b5d6c294e5b827a7773d62443697f8cba298ec69e1b8b9d59e5051bd1291ffb52287807240a39763b15a90148ea98297d877057cc9f4ab6401f05a666212b554cedc2dee814cd975ecf107ca2de07db4bc75e6ce5015e49fe0c3a1018235b026fad4134257a1d49c37caa07d9b32055a7323d5a4f90310c04b44ee32537981f6d02a7baef1a8939378daf61d807a9e1003e737728deb4e00609c4f087e9c178a61ea4a4c65cde7acb55dfbc0217c41eeaeebce3ef5e90f65629d199a9077294cfff05d27e31c6de75797287d056eef0645c1073c3aed5b4c90c538bffe61bcbebfae8601db23622e41ddd6f2e6fcaadd2786cb6bf50b7e5cf92f5cc6df8ffec7584f88a9deea3b5ab4a8d8ba79c42b9bcb028ece158baad045b7cb248ac42d5ebe15c2476a16e25616c91036c6e3a4400de3619534109c949b2f8ac1625b128253b378c5caaa8766ebd7c0b1c60e76bfda0b067a9ddf4746fd90227b9b6876f771451f16a5c69bc6a92ac8388b218bab526f645fccaa1ad27539738778292ab1c9481c8105bef50552fcc206bdf26ac83d77d3d8ede4f9ffcd8624bc6a46996d9a5b78c5533249637996f1ea03c949b27e5434b6c9c7d2fe815a09c746e6e9fe1c437971055a1b0c44330cd52711790a0e284504e9cb6205c87adfb00fc917fd384b5a0a8fe1db836f2ca0a16e6734206d8ff07da4ed2987de4603b4dd67d1aac836897e03eb38242b3f135972d0550073d19bfbd1737da36ce12ffe3e512c3a7b6175aba3a26a865a478840a940a29f464ce201832ce5bd731854fd5e31d0ccb4421e4f73f121f4b79404d06786e3318fb337d3531f0ef8f56f81789286f5dc23c091ddbe35d859d78b71f07921aece0e853d35b369d82948a9e0dec94cb7d8a9b72332f0acf273976cc282661ef79a66f919e1309c1b49fd8ee56b9374210dda5818ff593be29ea5826a99b8b071e8c16e9a6a1df9ab3a1979248d89a1edda1bcfc6b23d7f08c0ec93c583d6226582a54c62849c2aedaaada4bce7e004e728ff0ddad26aca520c84e5b8383be2c405ccfa3e7fb5ca716fa451bcd967107b781321edfe3a72720ce026779b81313e641eb78276c008bbb378507d43d8a85544b1c7a496f9f8ba77531748a22e094bff7ead7e19734ac54d202bd6baa1e15d620ba90fbe8b357124c9625028f8eb651a03e7c3c9e9de78c2f767306321160208cb7ebaf99dd8b79e5eadc1b0c60f7292c95fcfad7dbaa69b7a483bc2806f4c35b4c49da1799cfbe893cd4c1f17d42136518480bfc026a534dd37740ed73eccc532262958e9d05ba25f29b7e91f7840e0a049e61700639ea6e1273e5115c537326d07f43f70969c23f60e21f9a4a331de1b56b3aa9dd884839e2015b6665ceb4678803291ca259b03fc16c99ae02bebdb65c579024d8fbfa55a785dc598c3358ca8867724e011dd6ebe2b04b3c57bc1f7bf43f8b389e33d1c2bf69790afd6eb03606db6aae9848e250f98dcf6654e4b2aac51b3ea38ef89f41337ec131ab3e3dc3959b78848f2dc51c9db0fd0fcc3d2bd670d1c29a501ab48d3368b185a6447652797a35c1e5b0135ae97936715dcb1be9975a37ae4306590a3a463b0970b298fd70c32f852872c4bfb6f7b6853110e9b7e56760094e5fd6e6849f9ffc7e4f7ee88cb8c2113ac09bb2eaa65fb5ca02376da2cf087fbe50283076a9e6ee68e92a303cdf6ccb2fd9a7e9a25100e15de3c3beb5562e21f1b93199e9b9b4327fb3e4d416252981d37accd370e294dea1bf20283adef6dab5210a224b9d7df6b603d12b0fcf9faf24a7530f2b17b7172891f69053840af6f9d11ce19e128e27283e30fd68c8906f7837c1e81feadefc3df9923de872358910869420eb0a78b4c5a8525b7dfcc00e919c2205d1daa5c65b7999806aa74e5c594b22aa0f9abaac67be10b73d1d2008ed8410ad0acbac08d3b35eea273bf9667061eaf2ecbaa8d1cfe44f62ad79eefc464b3734ce621121b70e0d26df8bd7003a5c45e2aa4d554fe8047f385a9f833932d4c0863bda946ce30cd81de19bb5fb8a6280611d1c3236c5be268463bea4a4289670e195ea6d3dabe7d4c1e58477fb9af5976eeb4d6824650286b59ea912e7dc726681eda65575cfe43e3b0d880e16b8b17c49e7ecd18d3a1bbfc3a5b5e6cbff2dfc01d5fdee57bc8ca6845af2dac6b6f30dca0ccb38eb97dba59be9c31c8daa7d941eb0a93dbe5be4f7443101aa017cb220bfc58f02be52081c8cfb9c220ec1b9306796c0f0c4b077dbb27ee127fa08dfb77c7c08260472aa8a158546cae77e304190cf5661f8f0087b5135248ee49d1b372fd3c38453e9bd59d04ff5409825280d38e100f29eccc045ecbb69cc28f191aee3239724a6a9986ae6fd0c0fa106eee4be3abe814db872b0cb8f3be0291b6b5016b073eec9c431dc83e31301243a51754974e2631a1cbb047d58a4600e09710e1c93982cf716daa1c350b4dbbaa17a95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf4235424678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af846833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d730738177f2ee514057cf2ddb528ba9fc2a40ef7ec8513f7538dd9c73d81646082cd9a7a7c1367853e15e647d3b64bb2979693f3d1b82f01e9eda03ee11d0c816a48584fb8435de581c0c0d9e7b7ad663df826fc12b0d059671021689776d6b5a32d8dde849f8e1700b63dbbf4beeb50e83f46af68830a51a6de1cea31dc3848c139c2b59229168c1790236cd07096f2e279c07598033818dcb4e3f63c854610654ec64a49c37975bb19ea6a0d0abc76564100c6c41bd4b66f09811f8ab846f147b1f0bce939c6e3ec2751c0f6acf03ac6e515673631528cdf8580294a7d135e0550cde1fca789eb3028b2a9fbfb4c0a2185fc1c5705ccef5a2d7e3d08b63b058162c1df1ce961b3953bb1e1b0667259bfae724fdac82c08f8ad6f840b75227af658a67b43cd6b5ab28016cd8fb6ead37492449d4758a259075b8bfded98d07dcbd188cafc53d999c29d010a10ba2e5bff32da123097d9de563699ffb5d457933bbc61cdd5bb90b270afd4453a5fe8bff0918f6242c989da1a2133786fb4ad726b2d6c6690f151e9a7c5ae49168d2483ae68325ab4dd718c00f67cdf89e5a9d8674a502dcc2d1bda3dfc954f03efc9c327ec0e8aac1e2bfe96c5c2f36a156241d6b329205cf8615904b6583db7d2bd2c743a23112b9e61455dfc938ccdb313a613bd990a329a746e09bf2860248e0908e536ce5963c8ef021ed1cad5491c9d14ac8bf30b51e42f627a637efa27bd1a03671d473dc3c647ebc1e532f264dff71d0d073cc9fd2b03aaec020e0757bab86167b0635061e7fa2d9ff6041b2c5816b404ebbfbaa276a766b5f6d6da07851b89f83d1c48e6fe5a9752a6d1aa640221c0132a7603d8bf451e0ade7d2c1a0c43a648463e1582ca4479b5478f69739b57fca00d69c42ae6ad9f05d4ff8fa1f23f55130cb3964492a4b03367901bd77361c5fd1746e7f5ef4a208d5940924b4a51260e57f572d96a36cd62a07bd5cdc8a4cf9869b8cdf2500eeb6494d968f62582a7ce964228d5fc151c663333e872a4977707df06d085db9ec0cff85acc8c674cae97b4990ebdd5bc66defd428c230e4bcb63303766317c14b8bb974718a972cc9c920e84b685c3c3c89818d0c25f95c477d9b3bdcf1c761c2d612c206794fadc606e5be91d2903c37037aaf4057b1179dacbede6e20e3472740ac41129be9af1ad12ac7a01901f840f3ee227b140cd6ef1470daaf0121cdec87542e391be845da9a5a821eaeb5d5739ae9e8deb992e9edfe9bd9dd814a6384b61cdeb465d150683a1650bc56e95122bb38f0071c0438b9142da457dde1b806c4542752a529b02bf3aa15ab4194741da7b17db72e6938d3fb0a8388b2478eaaa929cae0249585a72ad9c0d585f317c34a92fc96721abd73c0542585c8c7e5a07b8c7621312e7bfe05f6d8e47fed4ef33e255ca04f90576d4bc1cf99362f7be1190bc3e9338a995447fe72eb4cb125d017bf512b95e64de65a81b981f416bc289764b6c5d09d99e69642b5d929192a67690567a1b696f679af55d6c65d199d43342aee988be06ef592b6e231f6b65b7178dd4c7010dbcbde0b4299ce5e50fb07f6585207fdd524d79a089da519e302784e4deed52a9e69f268ac0d7e9d6ddde302f90ca6d5b8b3f609a8e52f16ac9e14a7577687e5ae2f18fc3566a308fa35501727bf39d48d80da6e2092534662d5ed1213ac73a2c981f346de33a39367eae655c74bf9f8e459e796883e931a7c7e7b422470c5244f7fbe480848eb144dac0b0a4a3647eb4451571fb82401f2010e40ab0751e80ed1564e13a20e0be4fbbca14b3a7e057870c5998824a38eb3c3f1e1a470f334705a9cc35d6a72d3f5ec38256e1570057cb01ea879fc17c62d1fbc418d97d8088c680ae66417880062ab93da14f40facf09015559caacad3fe165dd3be7cf9fb31fc5aba66add803f8b5e6d714d4c15af8128ddc7b69ca5784b1c6fd2034600fa98d578246583bda15f0083b14f68eece30619ac6469faeaa4d0126bd537af1b09cc5b9c17ea71348b78d5e20186b285a5765ecdc725b8ff36cc9dbcf6b5f22ed0a5d8cfc0a66eb25a7183d1e81342187bb4604a65afa544ff5a22cad71bae53c4cc514989fb27dd785c546abb154751b123a1bf6cbabf17b80eeaac0e6a50382e6485366393f8032603aa26d189a25f03948de6e9eecf7fa339b9b065bf80ccaccca888933f581e79aca1d6ea50fed6578604626fed8da660f51f83724e9c53da4b106456cc72fe9d1990d62c5a801f0e0b0c4a7816c3b9978cdc451fa894b69cf32966ea8de9a0c9b4c4a2190d1f03ffeb704ec7bdc51550c64d13f1faff6a0c29e59b4790c59dae2f838d34095cea02fb9cfb8bd8f826ff6338d78fff91a64b72650e7b90e8b8936d8a9d2b2352d3c198e7cb9167a09e20a3f4d21c241a22c32e5e58c78a140741d876286710060444655bb0a9012cb21c153c7d565782c14de425fba14872a3726f1ac6c83ede858ce6b760c7800bfef926dff27d58cae507568a7561b757de64b3b53fe5b521e3a6196b70e55b5fb91e4169eccaf19ea831c246f8426d722bc29879f2e925e87c7e55284b0d449a374916fa45427495dc4ed089a44ec4b50d0610173e19501adcceefee7f1981a84a2a81fbe9954fe0a9af886b6e76c45cce92bf036cdb962cf5329e8fa859f955c2739c760e92b3743c082407d7882a0229f46fe9045cd93e307d9eb0951b10400911deb4bc2d3087e77f4af13f0be51e156571691f3379a0125c9ff03f206f265a6a257cb03a3ac14ac50b9c43687039f13ef96459325034d3e1f616ab138691f95f09994e4a2c63992a4098938f759633c8f52862256a19e7f4e34f8c9d9ff13813e29f9484cbc88623960fb0f54ee2aebd8aee397c9509fdf6755091b4075164901de8d9c453d64f8189714aa0481a97c183d310b8bb6ab3b50f89effcd8d3fe5513ed488d19ae73e775c6362975e51559d98a5ed2cf98f1285cff295e28e4a9467bedac800b7a9264e7ec995bbe7e7e266c042ca507cfb1155fa4acf95331ec45a0ace6c12f9105d877028f4df37f8585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a8ff881ef6fb928c0d42eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e17202985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac90168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c32109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"05b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b5269d0f84c1e6411237d0b09c194e90e115a39561ddb20161f482842c8837ea1ad22a58320aaa0dc07f09b550096d5049c2f1673644528e3a27ecf7c78c552087c482d2b7215e988f404d2407ff8a3c537563a284e0a5de22a1c86e2b4fc7f63d256a51b46a7d586518df981426501af9d801152a2fac5aa26d75557c95b291ec2d3b7e5ac8583bef9a65f40d19a29068700b375f7f97dde44f7d58af559a13b783fdcb4429a200c9c44840a65a0a27bfcb63919cd2a0c58fb954c1bd1ce9a5da46d1fbe7a1999caf5aba92194e2671939aa8bca3615a609278ce28b133518ce84f644ee85c0e93083b2006eb08aee175a1cc19bca4832e789560377da4ce1600b16b5e262f6d17af05406de19d5161d5f48974740fe63a571223337a9cc1c2c842f6526db460826c7f1d4b0f593f942663255332e81aabd902eec4eb2ed204f78c11ce01ee62a818723fe215b6f26b494e3afe334e98527c66095bed2804fc1051e08ecf3a85eeed248dac2cd5986425d0ea1df64bf1a25df0bf37502c9c5fa13ba1f97ee6708941a79eccf228b10d39b3b3ac94453393ba455502c51a9608098fed9a4af70f938e45185d959135172268a13512edf97889ca6d0b3ed84aeab8780e187c9aa789de19ef4ed2b380553dbc56be377eaabceb70753652083d40ea84687feedeccb1daaf3ba49a1ec4a82d668dd64d51170e10c7e26ff17caf0f62e40e705b90445b0293e9f68945f7a4a167d8f1b33f3a382ba9d97e699c98b8f79f526e096bef68138e6b57772ffeb50ea7cc80cb7ab93ad24358dff0afc4d994244dab7404eb0a3dfefa2290987a292ee7c5acd4133f211dac3ef11c848d3f784776c97331ddfa141a36204a7add3dd2ed09876531a03cfe36c358c4beac03214ac152e061ba50c17bc58f8b403b427c2515ee6ef9b64af4ec86ca501edfa03ecbdcefb52eb2658cb9f25cbb3a7f07939969f8119356980c76e341e24d6f0c5684267f924ad88305f1edd68ce6bf9cc610dbb19235309faeadbbfc3853e1e2bb06ef0320d919fe83898bc03ef0a51bb82126c9d5ccbafc2b5825796cb716609e30b0c9082ca67beef530393b76f66862c69441b202831199373c302e94d2917d86a95bc27f29042661df832877b8bde2c91463c5c62d47675c089b091fc680d61aec35fe59547401b002648b960a1d7961782217f74bd9d82416eca47d301a3487c3557b08042a279addb23b3854cf7c404c84e3ca1afc8e15938a2f144955fb56d22c2ee0d4286ec82b9457453f7320250d197a52fa24f9ac6b4895ab497456a5bc105c9b658316cb0930fcb324070095c52862656ec4b9512b7f869ff4282921fc7c928ad6456c20e777bfb3e86e91c1c34791272a54c9626c1aa28a4fad591fee7d79325885123cc7085fdc5a0d66ad51bb57c24db9627d48ba5052b86983a6611e93b901abdcbe4e127dd9daefe38e3ecb25d2c9892359af26a7d13c4bc39bdd3a4c7dc14b5272de6aa92cca0b252f5727f7b43d6f1a5f12b4bb051435f670c0b0701ae9a98a23308f2ab1a33f7479aefbd7354c1e3f3e606d814fcc05847f0485546d07b6871c2781186f90d5040e970c3a45b92ce7082f97e4db4cd4efdd0e0d5e6f972827dd1bc47147d3df63d73f73c2bb9d058d407cfe53971e3e2b2ef9b122a959641b823b6b95def0ceb2735993265701ba1d1473889d287b3895f747ec0c1bec95b2408c6bf0db0dc8ac6ba1bd32157d2d6469decb33966f2051e0312bff4b176ce0442e1f2f920bfc79c22546ffaf429a80b8824ff87de7536d6c9c67a5db1822518bf5783f9c8463301c9fd07a0b56d1235eadfca962787c4436671bd48c8f45fc9e99090bd82879ea4265c250e839e52b3117b029f4f68b3c39e0ba5ec4282cf64589df2374ba7e417764adbd2ea7cf094397c2ca403d63bb9c2f28a4dd767d22ae89adfdc49a6f7fbf7be8b08a1cbf6f7c7715d7634e5f7bdaef05855bdf04c848f15046a854d6a908f44cb0ad1cbe303210fa34b0b832cf704dd5c699975b5616e5e6eba0fe579fd2ea1361ebe36fd3d3245a437dc68748c658aca35f85a726a955df1b2e3f047de21b150f0058ea9d3db4f32259dd2babbe8195b7b7a2a8d336326ce70812fea41949fd9ff9f8abf442f211311fe9dd1d134d16af258647ee32dc077fb1e1030c03060df079c6daf7f5aae8591f2ec06661af54276da41191fc2589e9850768e65adbdd095aed21407c632535ef0b09a3858533a4048ddc086ac88b4bf60d88671da2d54b1881fc58efccdb4601498781de66f0d5f1b6749163a1fe9afaa5c8390a43fbd28f930656fc97feb741ba9a7f12a2b612971bee5ad9186f1cbaaed73627dad5663d1ad2793eb146c009c0a83bf897c7cc2e3058d60bf67b2a432c7f551033b695eaf0301537f86c1e8d3e66335afbe0bcc8baf4fcdb70f1d502b8221abceacd30a2e17fc81f0f770ad5631f7ca50117de27c1b502008e8aebaf9ee7de7a3d72967f3cccf59f1cd790829a251bdf89be3b24e65c2b707350b7c1e9f34ad6f315233d8c7abafcc9ec6797aa1c70a63518d9c56f020a87721f8a5c0d3d48a83c09c0f369c80ed9beb6292726a06eee0d72dd753d15532793bb49a806c538d60d77c6490389d2746c80b0c730af73dfd40358274bf1fc1605a0c405134818794350de60acee25e70b00f871c2560335e3bfdd62f51b8489dd78eda9b647805e0ac6d5db0700ad07fb5f8ddb3ad03c8eabd85d9168b3c25e12916f0575b25f0131e47957b406fdba12e0d370dbf9abcac869a3a19a395ee00481630eae079303f7041db4a431c7e865fa7415772b91baa9932ed7daea77ac5f0d4d364d9678401c5aaa9b53fec7b2d56e77cbd2ed6a6f25a92759b1255933706c6c66bcb89e468c01f005225c62c22afd7573a069d293369be6881a6002dbad737d7a9139ed844e2c994d5f2b6494d77e79d008445920ca1e138868c018b118dc0ba69d72e02049978f50ec35cee1cc84f4f0a687b1617d512aeb134a08e0a84b79b62c2c1de14c982b71ffcc9965b7321dacfbb60368585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a8ff881ef6fb928c0d42eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e17202985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac90168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c32109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"06cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526eae9d434387610ce18b7e4b9e796b82666eedbe0bc694618de2a1af2f2ae9efaefc30a1db73ac242c475463d88708b7244abb2973d01c9e8bc326be9623431dbee054ddea13fedb967d1e7379d3c2c488a1f3517ea326c204fef88c79a656b7d772fbf29a774da262b8f39c739c3945900d2b55eae351dcfa0513cabb1e4c223e468637ce290f70af154246abd13e39f2bc277744576939f18d3d2e9d04efa69baf35ce1d06c97a3fb8618363f0f38fe4eee18ec7ea1f829ed4c548e980d575f83602e07bece5821253b2dff7181c84d1b659689912adea50898f6d807d6c45be7ad9e39e4a1404515d2c8650288c72da2f9199df58ff8d8b64757cdf0fe23e6607f370dcd3ddaeb9d51b38b55e8b3b50f63879fe3702be4d3197e9927cad54bc106f55b5f18ff705927796652ac0cc613738a21e9fe01c4515453f747295fc37c18725cd7080eb312cd8e76f2f2bb2f2b6fc0998a6b62c8a931b6aaaedbc5dc2a3adf020cd800b253986e7bc1d248af5024e8b0f2a51fe2cd88eaa8e3431ad725ed4016ccec15986fd1105f330b9d0b8fa163790e9ae62652ffbb42e5f57b1d468cdc50debb1ec8705f498fcac4ed894706e0ca73282559e244f15c98a879b06b377b3ae1c9638800a12b92da0f18237503793729731b0f5db191efc075ed2ded4e173e2340148054cb6eada9359680d7d3b82037a2d56e41831452f222a1ffe494736a0fcbbb910547f6097ee20b5b99fe879b7bcad95df7d78bb930079d9061b1724a7c882efff9842c01050a194bd0f0bf6f09cb8f40c1d7f9cd989f58934406be4d424f4646c261dcd57ddfcf892cd0b827651e21e8b6e111cb419da52b383c632ee600db0f230baff819635cae5d0ba01bab3ab39853a2132bc9b464c19848fd8bdcdbe2ef7d4a21811d3a0a03b1ad1db93638ec44734843e1703f6c3b74dfe1e9310058ae84179bf64a48db80dabbb604bb4a867ed247e7db97a8530eae6df367cebaf6791837ca7c0eee7580028a807f39c21415c0381d4f24fb632be74401e2329c43ddca7e1473f8e7edf08a5698dec2499faee23d11ad854f56311275b86af24db8428fa2e896dd264d1215f88bf3371048407e628f4ab27922a3f6b94e001cb245aece1f7f981ef6b9cba7da13a79752acdddc94ff22649c599fbb7e5337a6ab39767f3bdd254bf5869984b99b7bcf61cc0d8063a1879826a2ce10235796310ce2f26771f8f35f1626f6ea9415d096b9955414170d5352d4bd4dcb5e5364de604aac17569829254fe8fcf5de527a8da1509cac705eb3b044e7784a23892aba63c4ef42401881913a8248667a52d4fc21f244d2713cabbf2952d16e15ac9e1d747b7facb15616d5f93f352b13750c93b643ae82be55f67e6d94dd197038f174af9a28b87ada018cb5000e968ae6fcdbc42acf18e3807ad2bcbcbfffc1e19bf06aaa7d26d78700720c3171cdec7c23c9df38f302e0bd19ab99627e4f32810751e3c5cf1d2f8c141105d13b405eadfc2ec47cb1658ad9e0980e40617d63bb3faefdad93e695172e858f94a9b7c324ebff72ea0837dce9dbf0b88169727c332260008a7d9bedb3d9734cc34210c4a42959e62a639e36989836bafa003a3b0d6711fd0ac9b77b5d4d240a7b7eb728519d48bc3ccee75af4a704ada28495c11b1a16827b9e142a9c9a77558f92704fad282c98a664d938b74a0a0fc95e78e2ee3310be13f94c531920a77c7105b1eb925c0aa3c97bdf4ffa5573a80aab1b6c2da59d566b5d81364cfd2f5698e08a9d84cb5be62c2e3606d5cd3d3b2989e4131d17b2660185fdb1272bfe60b3e48bf343fd5a170bcf87f2a0e642ce59fefd1b8a0f1555f47445e8a2e7e52b0e21e7a2b5ddcb072d7ace5f6183f5e3e9afc909108867445bc996ea167df7da07b1fd17b51de178aface7480710a05183b8b16b2a90997be86038d060758874c6cb08d9c92f153c53b69c0f056d69a232fd8fd2d63df27c2fc18407945af783882f720cf687e61bba26f936a6fb2e28f123cae01c6fd1a88392dd9753451aece31be83d4e12f2146be5335e3e152e5b7f2d4f9026413980275829a5914d603d841400b0c527971a3c99ed12e91ad3f20cc9cdd92b8b0e4724eb7393880c5c0feae58fa89fd77158f6cf0767dc84a13155aae1add9b6d34f79a6902b3e43dcfdaf94c8abc470b79abc0011d83db29ea975d5f0b26cfc18ad0c0ba42302da459559abe52ecbc974d9c9a1401c4fa6c371ccbca1e191f9b936f6601d22a4cd4d73865dae420b28a3493b79ca13eca6f8ebd1751727cfe0309f436f49114b3093d65f30c69f2d2fb17521627b49a7ffab21c2462a6490ab3b8370c5f633df2ebb2dbd58e4d6e6ed6fe2defd44c429c9c89a0340fed6ce864af7f35d0415ebc479f219ea38f55fa6a6587ef6294330d0fc3deff1ff378d062d8513c28a669d467c0442476e9049341e935d5553f8bcf5cdd70e41778b9a6548547375e40cfc13838541e61f5920981951ab4756188ce16a627208229879859289e3c1872f76d9925e5ade2838a1d3f07665dfa28df4dd01aa6aee36af358a2df7d3329c797c1eb9e36e20b788c286995f5c8e91176e95f64cd1e88f6a1a7b6cd408a56fb234207ad74a94f9df875633e2909474fb6de3d4999929d296265f1ed03c5b1174466a4dd0f8550120cded11d4d15a3065f2656804c1e42d76fae12789e641f453a386f895b5340214ed4e9257121bd06572b4cdfffa03c0c5ba840bdcfed08935ec0dded6a8fca44b33a47d7248929dfed3ba484eb6f3f15e10926f1c292b244b24fb4196f2e81359381f6a5e1be82ec1124346d20373fe661cce2f2d1adcd08f19bb68e39ae4b3f9c0ce772f46406b354fc94f2e7f5f29845f92d64e6adcd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d8e064d5f7534f052d7a4eeedd94d706fb52e47e389354337dfcc66ed0122464523610d2a17306a5b43db9b8e1c95d5a9ea9836e698febd4c1bf00a2df217259e849896f6272430eead83bac308803c2b75eb8352e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e17202985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac90168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c32109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e6319d91eb21ef5cf4e6d0b57ee381ac0cd7d97db2b8258fecb4e7459887ff51b33577a23e17f980713a73b6c8b4f89fbe5e77211f58a31befaf154fd4d3851fbd1ed3cc60e353f02afcdb0400a99bc46bab79fd1f89a47f56cbba53061bec2d63157ac8ebbf7a0abe8e2c5280349165ae02786f28c7ef6da1ad16d7db0f133b67af61c7f3b3a229add5af4e895ee40f42950893b45fc982f0c83e644b6d9d65f905d8e227400fab2641af230283e1e77e384a8a4303e17223c1ecb768af6c548c9205770b25275c0754302438d153a6a26b43931af7eaa9d18c2fd22ba7118fc431bf8ba9f7d9b67249f2bbd29952d200154703bb17871f07d6a25506b59c6abfa8a5538bea2b56fc3eee2221396e20878fe8d7e73b3940073b0ce1af2e297d340daac8bfb53ffc04a68146e48045c05a1958e8cf40d0b9f0a4ba2507406f1324c1216027109e4eeee7573d71388dc18efa2fe1482c5ee1c040ffeaa1a47b6ef6f57a78f3a5c2b64cee629012f13ab12d4366c06d9a4b26dfd75f4ec519a4faa707a63bdd36ed7575093fcf25778b8fb3329414defc4edac2303dacfe87ae8a860db13149fc3d8172f9c7f6256ba9ed1e39e8c519dd50b19a411dcd666d62eb47d167c168603c0fa9548948244b32e9cabea3f2e4de6eb0001644a1bba6a7b04be81533607d85a94abce46a790cfacf61e9afa14bce763895c874b5aef5ebf0ca1aa7dca54458e2f139b87533ba10d16ed66d51a2d1a09e52a63263f0ed1a2009db907b4a6b5cbd9af9dfaae09b8ab51a6a64c09852d877eb2c0a5552c2c748d15c4d981ed5f3b1ce7439b69c809c5d121dae59a3aec834c7b2054c40cd583dc557fdb1d36bcc11950195973872de9a1dfde8dbb3f7e8d5d5dd5f46b08e51522d50ed0da1ccd76d2d11fb1979c65d209f247ad865bcfacdf64b05054aedb792fd6881c6ae51edf20a33226952adeea7754ebb8e920ca0f2d4861dc0ae32bc8c62506fc5aefa29bf0ac00a9da11159136caeff22abf3a1e6baddd1b91ad1d87d46776a2688f2fec19f0c3518b84816534dbba3537d052db18960b3f1f57b38cf3fa9c8117398e6a3be53ce5b945e22d1177d4aac19315635e9e527dccdf38d914eada5a89afd37df41e6fc74d1cf850b44abb6f5fac9d5a4f42eccc00f0f273d69c248cbd521168e75dca5120db40ad375042e54b4441f397dcf084a69e2787dd96752b69086ca13522b5cc089586779a2b6c9b1c5854328db9d10e59e01dcc5e28b15cb8a54de33fe8e2a4872666673198ab80336ed88a335394cbfaa89853364229da9dae2a01aef84a01ce209b80bc9d88327e473bca1925b5fedd4a7ab0a73d3ae6fd7ba4fe4528db89c74c5edd3f95140591ae5a746fe64e28315aec360feb49cabe55afb5f128a85f3f9741d19b1a81e0f89911a6b74abd9b4c592be6b4b1af2fec7fca754b9f5dd32d73c6ebda5566db4992b2ea767161e0534cd8e11d622a99a1ea43641aa7b7e63d9c85f4586246ccc6d44649bc81d48d97c50980f40fd6f0024c50db60b5a9dc2761e42c3225b5ce23da88c71e695e0790f1ac94bb56412690d6a6c3b95487401389f657a859d76c8663dfddf12afdaf409c7abaced638dcaf8aaee153e71690d62eda83726d0d1e428dbaa27d5c6c7c30edffdb7902b836c3b4a4bd0bae759e555766c2ea9fb14701e3c94ef895dc2729f3f1f2a2c3195b88450da08c87dbe05a929c2f489880f3d16a499f01ac393bef51c0b098a7d3fa36f529e87d0bbfeb73e048a7981193344bd473ef826a4319ec3c64083e91a0a44918f919e0a74dc185dd415d2ef903c2691b6d993aaf663a5f78626c122c7f52cb5ba42689451d879dea903fa137a03bbf853db9d955e10de01b2d8912a87684988e0da45873f78e363d7c03117012edbd4d1db36b457674f9bb9c773a2ddd1ec0d40c0ad87cdadda05af9d3f1527274551f875e3e8c0b04c010efd8068ac51a7be609eb371f0ddea574d0d32c48f0f5e1815e7d1dccda1d55ad613af776f4cb07829d92bee759febabd074e5e7be966b36f05fbaff1ab68e5302fabcda9c95e92cd5f19eef2fcd6a3ae99b5a0156c7ce94373394081237f27507e28c28f9dee15bf15f8823ab25fad23a1867bda19cf1b7341baf9e8b3c61838115aeb2175f425a9fe04c77c7a4d6308aa1b92b1b0e459562ef12b03e11ef026b10bfcb0d174ea3cec142a1340f272212dd89209d9c1b8fe274b229549d5d1c2919c627daaf8658b763c7f36be86eb644e5c5b0c3802ffc7680268f441832fbb006ec33d293e8c6b50ab8a675b79b1981003c6b465a7a45bbb44c9a52b26fda84d67104217be64b994404e2c7c31dcfacaebdb5b64e6fed85ff25fc5ffc31ec919ac37d2cb8d4e933bad652fdd4ba8116b1e5999f6bb67f0ce573d9a440e5ff0d1cef087f9cc09ba984384d6213b79ee1904edf75cc484827021d80c236d8d60f80beb836c23349fb1c6626ee38a808f97ff80818f39928540575d2c15f971f97f2c9666982a3bf3e4acfbe1969feb2fdb4d0188eeb456b4003e5f324b589f0b4cbd86fd5c70cd13e6c79e39c7ae4c083eef9f95d27ad452f2447bb4f8e3f5483e1a03a17df709c1081b99285284340158b25bd00fade9697b890273e5cf34f9ea9aa864c3318d4591024afef030b1247ae1e3f6cf25b19828abf23070d893853ec198f3d1cc0dc131cc18d88e5e15784ebec5ea56b897990f5f5c8f96662d1cda2006a53c03527490255b54451ff43ca0c46bd920705b3d4eec935eff65c3604d4e2eda011135b4fa61ca144dab298cc4a1cabb173acb6db5313b260c3f61dc8beff2c502fe28f40c35c0d3a9d4b9f117a808bf7ea11342deb3f7e9372b80c2e2a5648d2e18cbda268fc599146eba584f45a2b7acd9057c00417bda24cfbc798ff87dcca42e0ed4c56c5827eca22d09b9feedec97addad5ffc75e2a38c78ef9a7f7d47dcf547d3c146b24da566776d6a16bff1b2938a76ad84c2b1cedbd746a8f0a18396c0b9c06a40338ec77d33ffee72441c334e3b662e6663476b89ef26abb232542cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e17202985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac90168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c32109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
-				"081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f9437a517e998b835d7448f68cf70c16386179e8f4f8939c68414f54efeb951974c56680e8078ef9d974fc794a64c4731c912d3f5b72e098ba495abc4fc2b1a37cff5443cc3e5ca930cbf4f978441b39e4d8fa9ddf4639c543eeb4de961c29d8301b4239a3ea1a4b2c2935d4c7863b04e76ef0ac071d73eb1be76e478341e616ae95600945ba59825fae6c4638985fea0803d6c70114657352c68d616cb6873ad8eed6e7b608c4b33584e41431d3d846742b266d196e4757c3bf4e7e28e213fe6fae624ed2462f837af0f5a2012b9ceba54c28ec36fc197073ea1f5bb3c74a3d824ff37d8ae2a116a4d408d0eeb4992c844d2140b31157786ddef5da36d0faaa51058b08a89248960e7d19a72ab00b1b15b468d9007be79afc90b5cc5c06e67434e1f8c7a5f63083d236e0856379c57512e7e4342acbe391cdc14418e7e11711b26629eb237065db2c2e16d7b79f5a92b35e39d861992f9819dd03da549b695158ff68398c1a253c5760308c5c6e5ee9b8dfce6ebe0cbbdc2bbff03547c8e9d48cf307ecdae797b5d357340854c429dfb160ad8e7310473d2e9b200c4c70badb8294e3b1ffe8c950dc27e56a3cea47e61d1f144f63ffcfd666257115deba198d2616ff3c1ebf3e4d30d59182b259473b4e8fc5e2c055b4fd771bf19b68b30c6ef06476522b4283aaeb7f1798ccacdae29a1075403faba7e7e88c6dc5776baf1bfa9a9fc928c306a2bfee0aa814cc9431a56316b9eb95eb667ca9ca4f426efae5031dd45d80c7c17b6d7a4dbe895a2ce482374b41ccc954c68ad0654387b1ab457e4303371a63db5dace631894696e323997b388dede168f230ef0463e1b06f507efdb2972e7fd3781b5d08bd4cd4838e2bc1d8f1cff91171e48c3117fd43aaa0332e14e7921aa477ea13c6e6c30322a38081179115edae4680dfa074d8e69a3aa61aa2a4b0bbb142ce3027bc23143ac0d466c4f72bb9514512b98068ffc2b5b9d118f33a894fdccda725c0b74eba773e3d44101f88eb9c2ba0e8cf20032dadf6b35f60e6c3efd12980987a884f5ba393e7f4e718bab519d7202d206a545853962ce1ebbb7fabfcb52d8a2c2d8cb046fddf85c7aff7ea0e7f072adea76e8bee7838352a69d556a4fc2c4d0aa387b5fd563e317c08b753893bbc20cc8748e9e6e2ad35785d0746fc95d055146ea044d9a200af2ed9f3a99875bd2b5f4dc15294fa669d9ae8bf77a2014cd62a2191caa4bd4b3ba31b456d4b3ee5f5093f090bd9a06b0cd08a5904060f3330386b333963f5a5c3a6e20113eb6bdaf61189f9b0d9a95669310ed3cf07edf93b33814d7ef80bdfea2ecc874ec5f8cea3c88c8c56103798270c074281540cbcbf42e5bf859546ef18b255bc150285719ecb1bfbd89ac2e7d9d4bff84924dd679cf15007cc7589e4c93cf049d62d3fffbb647dcd78a109ef3951a4cf682083a6f2acbb7090a4073f3f867b2ac9d74b1ac3b6d19b93d1f462c4532574cedceb81ce843cb37de390dd96a5bda2e75de3b6e900f0fe70a29252ab9dffc2e2938aee1280c5d039d92f931532061db2c28b90cada5fb349f3206073366d6e535e998b644c60ba6b58cdcdea80b65d3a52baa0fc4416e388570a97151b98149c246e966a5d7b94c044a84067e386b4dc01e393e12d8450d1d5544ba5106f81876118117f304481a99c6a16b0207f363a8c12b0bd9674e9cf0927b8cad552d7ab94ad3acbbfb1d1561771c614b823e59790e558063fc6bf7ae14f199df0fcc4d9043e5b470c8e7088f505a28e3931b69e08248c07fe060c218c93a2fb51d01ad0fa581f40319313c44611622151d366c73991514fa99f1faf57e7760579a40f060de05276f04f25204f808a999cad53151b91c4374e8c81614379db93424a4825c58652fc8416a0baed3839a3110a6fbe451ca86ac485422ea466549e2bda3ad6e10359ba42ecc9c929aeab10d9a9d055842be9856a08583e75239f52972453f0ecdaab51bc73b8fac5f7365c5dfc0c3861ff627124388057cbbe2f00685705311193a62ec4f9fd25d8f55f35f336a4ad2368ef2947bd159a2ed3daec364e65b9450bb5b5e8e18364bee3dd3179cbe6579bc4e06a2ac6fa37329ae20b5605843cc8664c8f53891b859ddfb2a6004f197316c5d3275eb28c20e588eb1a8e8b8d2e732288613708d3ccd1b7d0fee41244dbdf94b0031fd9354db3588fe161ecd3b58f02936c83f421b324f4b816b61baec6c6c1419564e77df7766ebf29886da543e3ad1fa93aafa1c2b073c8714b3167a744e1558f595a385d6f9426ef1e87df55c273053a348cdc41ea71fd0d5294736403a3472e1640794febb9a0ff3bf633795aa03b9c71bd4aada57313cc35a596609f3586900ccaab8ad7ef872b8484beac99e260a78356387a4993180c12680aad89c4a90f8503483e887c518bc3d1a5935c9d329d2a97d12bf1fc23cf62d8c1064b717062944748d6fc4a485ef79076a641af1767dc55aae1b08224d23c0727b53babd49886a3efb23ab49277ed4bec40cc8f0b237eaa267801364ad865b6d6465215bc39b2018aa06f9a30598be039332d29f53f1dfb508b97aad5254e1ff06509f8e80fbced8a3d25fe6f9947cf2c2757c9abee05904a2b06dde79316ead2cd4a14f32d7d9f9348487c3017194255efaf1f2a39aea4ebb33b8a5a5d112ea29f80f9ba2b54ae2bbbf800e457d99b0019dab9145049f3080cbe33b98c0ba7aa194bebe64fcd1508ac6c567a6382099f807c4eb75ce002ed5f022742ebe48d835c975a4439db7eca5061a6870300726b55d89f6a91d4d8888d6a58d5f01f382864b95e310088acb781647152c21608a11394b4c2331a3531a796f51ffdf777a3c421c21dc7db47221af7bf777c3c380c075c242d71a05d3ea402a783a0cbc6cbf51c6a5606177f9408f56d2bef5c29bb2294a52f661c25c33906e6309e281c3202138082eb775c3b719a1444049d7a6956fcf697bc59492956557a4c41c961b3829a24bc17d9ccfb05890e574c151a622c2279e73ea1fabf9317cdce950fd50dfc18d95c18fdb071ce28907f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243134dfbdde74521a6e06c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b49efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab0500a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb778bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797bfc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723ce9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c61405935ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a431f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a26635e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449de0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d25d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba09181896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c538becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab316608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e562f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604abff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f720c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff54efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd8314074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d617120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee54095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09caf363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae1900468c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbfb619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608cfa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbacc62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"09cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c101571e99c5244a1776f85ee0d42a2d85e906ef1864eca38529bd1e6d2fed3be5c7510a269d1985d178bd6add4af93aaa6a44126531a764ad4cc05a1d1240cef0c22e44f79ddfae15623e4eff8f7fcb03dc9092693128057fa93dde124df023ede229f7a0a3cad41b23120e79dde6cc42289c4c19bb9a68806791c35a12bb350ace60164c24da5fa26ee29c0c4b36275d96d685be2cab2d64552d33d9f04cee58a2794579db6247dd6edbe5ba76b8573b01e8c14a70d1b3a528432246a1e8681806f384080b86f4a21894d613893bce6683d0bea2b739059d349972e696e866125415face8043a8018606a1e35574ef658c5b71c5082f9333ef924be448f99787b9e4b8747c633a8e5fcb23beb8d1c8e96e79ab714c2922a942060d2183abf51d905a7f2087d3f61d6dc0cc5dc479f4c278c01d53dd9c2a04e65ea35202a0849b3993f3168433e63617bdf8e997d24088784212f0c6e516772da382f771da6f601af7fc8aeabd56b01fe29f639c73fe3b67f444b0e48839035865c9326faea75109673865c6c7503229e87944357702a02d1fe4b9acadee9bbd24be79f3e93b125aa0d9b956b308e336aca774b9ecc9d4b2478b72321b3b4b0a6f562c3cb94177e521fcb74902cf343519268598c68f61570a90131b27190ffdc1c1f2cfffd0b508526ca0d69f8074e3e90c1aa949b717b661863a1ec5b26f6d77a275542d78b4b68b05f6b47fee1f1bd8260d41cd8578b3ed2d3091cf984a4cb9cad60460e6fbe4a94f807793a6306d9160728c53cd999d401ae3907854d36c11387fea4a900549c7c0162ebb139c461d9e33858d11bb828ffd1584e1dbc50c2bacccc622813f57ccf0afbfa9866f8ca3257b103458156a38a807f3620403d91680a74370c9b0dd6a558f95a58a7a026386460a940e5bf5655e42f7cf6b672e5aa4cef313aed544afba7cd8ee724293fa666007aeeffd0cab49595118ca0d005023646b0881a6e32ac338b99c3683310df63b505335a337df856bb67bedeffb029ccefcb8a5352de643f41f029beb961f61644f318cf9b19eab8c92cbf8d9f6237abca1e63749c26e9f5001c72d51097ff1512b673be0252d7bf8a355adfe8e24152732edd0efab6d1531732379c6d6134b6be11633d8898db0cf342281b4801eb0e1f5a0f0cb7193139a015bfa30494d16c6abd65c598937b427e889e4455a46c8e32b9d55e1af25ac9c57b203cb9151f5431fe2cebd41eeb7166b9911ec5ccf5dc7e94ce8a2051a2357894da7e23ff92a2b75356c81eab6591d28efede4e5f9f79e4193c1fbbce995de5c23ab3d32a005142897005bff3d92dd6c4e33876a722bc6f9ab4cee31b60bff041d017883b44d1eb8e1950dc6629e5adc10d419869d7511bb154f905dad9e9b674edbc88613d34a4406cb26211b79a938bd68f071765d9bf74c834e720315c1b38b347c92ad300705f4de0a960aeb537285641d546b7e7e3a867b4cbd85ffaa38c6306e49c5de2736fd7f115e555d0d0b065242db7e288c148225ff008a70f1f08ad17d1814bf05ba6acc3f09e503ce5262b7ddfa0e1f3e9c00bc77ac331c5043f5f3cb0840702587986225892763b5232459fc9223f11d5298d93cb7392d49b12d9dc521bca254e9df6812787bd6970bc9276a3a6fde1187314bd590a95df35eba03c5c4a1fde97186b5323edf04d68069460520bde60c55ae6d0707ece195b30bfd6503dd7baf6a0f099867be86f48b6871455dca6df56d8e6a374214cbb1798f54ba578f9565ce2bdf3456aa9b9dcc6c426a2a5b3ec91e8ac15ec85a163b4f2fe1e045a5a07ed495ff666d12751e5a63f5757cbbb2ce2c14471b8a6ae9f4e92098357f0af51e2e6bbbeec9ba4959d49133759982303482972660bbc9109a330353dd65e37644aae546f2ce93dee136729f93472f3c6954f643a9fc61e1c3aa8f375fe63afb962d2513fbc4a3a9d4baa54dc69c3ba4c5726514699289f90c502bb21119486a345a94ce23a4ef90eeae96c931a8eb109f96f81f6ca1331827e3b647b0e238deb774a3238f979790bea036cc6735c1b7a49f4f398319e79ec68cc4614a10ee2d11c6e32f39ad4959064b29421d20375b98965e6e5f0f394b1668fe75abded65b110f24f08a128b2d9a2d1b22b29e9aac33545c57017442faf896abffdab377d210f20f48ee14542db86a7d2f93c36a4a04b984c0a70c6896bdbe74e6d8b1bfd9389e290fded125be4dd465ef200c3864ecf8a593ac07bf4c88b6515cc77c4546ee615d229cc71e368a71a9aef2565622a9fcdd597cebfc276887ce8b22377ba3628658d294db6382c562541b9f2a4a7c40cc3efc85532233dc314750001a806f31dc9f6b4097abc80e8cdb88493ece98f3a33414ed6f2accd2e9ae6e618f862afc47f87e7d7bf7211b2105da888cfd26e600aa136b7f314407edb6f8bdb97080ce88cb2f41ab5a1d9ae27295fe3d180af1de5bd1c17f197969e8feede5b19b1d9695ee90a95ceda909904e82850a27a6d354862e45e79e209d9362fa601369cc2445525f0b75fac33b116bee801a1e0a7a93a8a79f68c78f115935f11192536e16c72409785cd513739e3d6ff022630b31be82154b8b5473a0b30613a34c601f5edebae6adf5d029eb08f6b4e8095151a36badb485f0ac7b0389a0dff3d1380799ea73fc347ee18231987170868f720a98d77c67f816d970e94918791f01ec4f55ca12fb293ff2172d8d862d8a91bde48eaaa91ddcaf2fd3a672eb7d1351a386d145af68d79ba23cb4d07e7742a0e9dcf30cfb70d693d855afd70ab43f756c6892038d050d77adceb8661a11b6cc160eaa22ccf1f6639a279a1adb010fce2d104afbaa2e8f2fc4169b06fc5f7da083288d920524e8ffffff35da20e4bc97a1c43eea60ff3bdf3677f890da0284b666968e769bb94dc6b3f871d34f345b2f042f512852d47e4174bb4d8a240f1feeded24f91a8e5175857573800c2c0153373d1bb324686cd75579b5e4562a01fc28e099885420374d379d90d77875e1ee92ed681543f2edff426ca83d072b3d74853c6bb354f3fb304f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243134dfbdde74521a6e06c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b49efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab0500a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb778bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797bfc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723ce9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c61405935ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a431f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a26635e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449de0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d25d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba09181896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c538becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab316608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e562f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604abff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f720c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff54efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd8314074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d617120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee54095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09caf363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae1900468c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbfb619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608cfa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbacc62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebd9ddf21bcb1e67d2cd01840ea4bd6c4f4cc94b23f656e9486bf4f6aa80adf8003b3005c4c78d59e9de695bd4c5dfdaed42c6019b5d33f73d7cb66429034fc6454e75d2d75650d910da551a81c1c3e996f6a128b820e9464387cacfcb5d52510ffcc978c270dd7a369378b336e86b0016b203dd0476d632b95578c6cc289806a23cee24cd48329bc87a196479435768de87403b414ef512a52d89c6691024613f52aa965147c0bd52824f7b4c4bbe228acb9c143a1436cceffc3841ad237a3c5285e59fa7f327406cae3b53be57ab26a8cf2c5103d5e62e3293c2ae42e071155443e123b13d19fa2603b6dc65dbb97d283fbb4a13966d61bec454b1693d961236fc106b33dd0fff2805f84b0cb8a838289fb76d353615a31d783eb51024d0e149426b62e2481f8d80f6580fae5de14dbc17e4bdc782b0fc39617d55098873a83f5988fe73329a332c17b8cbe3b2ce147a8458c3964b7ee2386c6ae454a884f3a7a1a9b54d6cddd17052e789ea7bef128068599fbb19449d3b304d5bb07d36ad72c214b81ab9d1cb68caf96730f668657f8fe6683de7684b965d0ba703eba93d23f3d6e2a97fc9c2ee6a8dfa1fc089463c5eb01a83df95f48ee11af72e1ac18e8edb13f98e1faa7b005932ea01d9766d52987de585444fa2ce35c037f799777766a6795b6c62071ad638ea585dedac6372114b644540a63312370d8301b0580b6c41287f28c302203a15739a50d1c700cb4a551f872cfe6bbe9715b742f50c7cf05b7585d234408b43cdd0878028d233463ee9c898ae89bf2bff4b1329168d787528c3d7aba200606a5acda4d6c50611938c7014697c5d1229c1e5035df5674ad3f0c60e518cf36326b5e2c2493993c8eaea63a6523192cec258f60c7c74e4762447a1839411a297ae42becad7b3b77fe299abed7a4070a839c0246401c5f395ce588de5799be451326734d33b08c1d7a7a5883adb71b8b5f15798d7930a6a8428c1efe8831e254ea87356a8d0b458284f09cad8cc6420ce404c74d737c8903af611b603a07237c3ca279d5a66182d204e03c1c29b2f7cc87afab28bf078f8c9fe5f8d865909e97758089f40d7c1f740ca0ddc133846b2bfc0eaf3547df8216a93dc41d8210632321faf8b67931e2ac5a923b55ef0e0150c87114749c4dd45e28c7513cd0e53782d8ff82bdea98b44b8b2f29c801d285bb2d7d7d62934ba8a86295608885b7946560319f041420646fd042b0dd2dbdcecc1c1dab180c6ac752c1244ffeefc93d51cf87789069056d9dfc7874e4115644b8a5615c1ceed9c9fff459ad811a6caddd1f2f1d5a4884c52c1f2f05dd3cfcfaec71bb792d38322c7b41d403e715f3374b1380f247282a1697ed35081c220d474084f1102ad1117ca4c4c5a2bcf5154561f8918a1ff7246b03c4167981db8600b4c4b3e770afc49c1271a0b9302c38be5db08230953af0b9e63ed1257718bb1e5b0f22bb29e49287acad0a0e8a27294347e09b598224d0eaa74269087c5355cb2e3e3769c845defd66f2ac84639e28e48a88823915812860fb4db342c520e4d1fd44dddbd0e4d5af623fe5bb2ff8f32b8544c8d7f490782d029d1cc5e30948da196f3d7cb4ef7774d682706bb42bee589759ad9a4896e4e93c1268bd01854d6042a43b9c362a16d2df6dd3fe07d7d0512fae0e8b8026ec61f345aa5afbdcb11f992086f20074669199619d8ae8453f355d3772a2df2f4b5a5a95a9dace2f217d190966a159aee2c60a9c51903f41b63cbb5d2037faf2fc081d3db0272d04947394e95f6381398ae5eefea421bc7d53927f855e1f0e741d38c62794e9fc7eacb29e8a4aae948c419adaf3a11ecb5f94716cb72912fea6bd7cc5065ce2e1c27673c7a9ee483df733e52a5b95fcd6ee9f9ec3676cb943d980c5b0abf01d9d55c6f71045fb1aa90924f42cace83cd45eaf7846488e17568b244d35eec1c1c1f5e82b3734051fad22fa92fc1db5192f9911328851f7ed3aa02d3eafccacd51bd3aa80ba4e941e1ac9b704ba08e5191d0e83427f12110c08736cfd99be8ab2489a2edaafa33a8643a01a2d143222c313f3dac5bfa66db2e3378d808bec5e338950ab72a94f6f28d685f13369a941407fd2d407706437d96832e49346e0580e15f9c1c661280f0c6dc32d84b4b6beed64c466de2bc7aa5cc602e75e3da5d6d9ea8ad3b85d1f6a15c7a6307260abceec94bbf7fd78dbe2c985c29ab32240212debd9dac89a90331bdbea61f3a4d0314c2200db0d356f2e02c9e0dde62f24c321948ab08b0206c8b29ce84bb719596294f554af5a22dfd4e860a9fc4113a92416d8c334afd3a8aa8b9516c5e6a29ea4b2393afb08e5003e27865f9c6a5ff15eb3edf61d02fcd5ada1c0ef2be107acaa0a06c306454fab17b7d3511cf4c1be8b2dd2a4a8435dbb1073e469bd2bcd322d42f3f1311a080b65f39610762e3f28b572b8ec25a18a4bbd1ea2a8633f0cec9f9d0cd24d8c8b63586e2d8c2ac5331b81828f220d24f875aa77e490ec30a16e040f3f6c375decd6125e38bdf1a003a35bcb9eee03c488507d164fd4c82ba483b3cdb198e4eef029c542816d601a11cc6ac92ccba109e8a90f4b881bed7979ddc1c9fa6ffdde4ed7a7fc07699901bb7b851df41914612bb3c8e5b07e010502f135f502091e9d64742e01aad28bc63f9ad89c7c616cdf417594f94cad9f16835800f0f641ae8b34cfccd010d98d712280713feb9a61229dfdf67badd765d6f3edb18e61a7be96f5f609384c8c9569fcd9e4d8dcde8a16a0b521567a53459f35769e782169548c0761d3ed37acaca8a7edc24459c3531b98aff5f323bf58946b04661cfdcc70c7b2ed4ba60e676d29e0460f17ac6c334e97c2c0876ad189ff439350cd8e105ba828715a51f34cdd3ea3ea3904097bf6a71b4a35cfd158bd9879bfc21a9df19beb1088f0a953843bdb29feed826a9b6be6a009e8aa1e13f2ea8702522753a1ad27200ca6c2e931708a7cd874410854e9a26fa4eec37332d53b475a4d37d1b74a1e32b7ffd97755b8b8bb77ff8e221361df97e56677f0950aec8aa57fad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280bf411e823ca0e4a693d6c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b49efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab0500a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb778bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797bfc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723ce9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c61405935ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a431f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a26635e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449de0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d25d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba09181896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c538becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab316608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e562f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604abff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f720c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff54efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd8314074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d617120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee54095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09caf363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae1900468c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbfb619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608cfa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbacc62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0c78848c9e053887ed436a6470bb99f7f34357811e280409fef6c81ecf07c477768358fe520c7e9b08084b682b1922567b70a1f4441573c39097f34c0098344581b22284e39c5a050080b3403fefa5d45878397d3fd19a8770e42aa61967e863735d16bfd7b1aeb0d4bd06ae0e581497a600805c6c56e214e01b18e023ece14ccda2703c62d54f3db1d46e2cfe6e96d873dc0a685f03b393d70685daa30a13e483d5c696e549b42c424b55ca351e6c8e0a1db1886122fd98538f4c219609e6956d48607fd14f1a9bc62d7cc04306606913d9d93fada3cbc1ca95e0df5512116708970daa627a5baecccb7c0728cce5554ba5ec8eb385a24efb1be2007078c1166f693523a9897e4fb92cbaf3f84a97f4386f105d78ee6101114557c5033695837f81ac985e4d6498876947270a1820a4b84f5eef991e08a7f58c6f82f2bd9741b4be455558e77f85d6a6c3af8d41815a2639563aee03d1af84e7a2f1df66d5bf327bb16c629fb4140051a27549da1cba648c4629cd87b88f285df12fce998100bef61269e0dc32f5b600008a6ee400ddc8ee58fd00585755b3c3d0ad5068110d37910bff98ba839adea2232fdfc85a91e4af5d808a322338c0e394628b57f60d8b48923bd98df01dc6f8411ef4a49becd8f069cabfd58b5e9ce213b7dfa1b691180b8e208360e2f984fd5943ce05b62be3f6da1cfe154bdbf4ca513a3793f30c91bc001b31a7caa17431431d08e24f9cd9f1dfede9cb7a1ce76a0c1b06dc567b4370a0e3ff46b2ad37998ddd841c8c2778199a3c1c490e05354a651442c3594133b44e012d8a5aeb450559c7cfa497844e3ddc719a26c79b11dfef604a559c011cdb24f3d09b42c1bfae665b5c090584880bd1123e0c28aea90f59318c8d391256c374693f490509d0dd8ec16bf271c604d5e1c0504003d044e16414b9c9ce34cd6027e4c60a0b58d90991c2e3aeb425f19ea8e9f6092f3ca1e5b4ef812db0339bf9ebecf80fdfed6a4da8a8f7954b92386e3e666df4260ffc6a40f07ddfcc528c0809f669bf145ca217844cc542125f9893c70d44813c9463ec1b8b6aa397784c69abd82f5b362870b1451d9752a163e630bce5edf6fe92884d772dcc50dc12dcb98ea7bc5f2a04499419ffb3690e005d5a713f82b2f1f237d723f2eb831826fbbadbe65c1485b8f2f2b2e0bf2716d4102b6e92d27d1ed853509d53cab2f45fdbda32d5c5c46bb74bccaeecb4b7aa38a8bfdfd5224ebf63e82e96911392500bc69c9987475f4ba115394b51cf5973a68a209aab4daf5d6471b0e24fb20d7d0d6b98b63f21f3976fea89cfeaa47c9b88ac1256cd103b6a5c77543f90b3482c3cffdbbd6b240c24e80ae1ec58c5ee7511d32ba58c7ebe8a0ec475dbd01a06841d70abfce93f213526665f98562478fa58fe807575bcd8cc46e7ae08641cb65ab01da44758d99c0665ef73312816cdb9f9c78947cb777cddca0e246c9116e9c3329dff0c457e719f0a935920f2e96c36cd183e0b0c83e7015752d01524f59ed6d4d3ec0119ce81ee33f28fadda0b0225e136b2a9beb59d5cff29560d93315a670b33829fcef52aad2f4bc2359a35ba2086758ccb7ab2fadd738eef37e67411ac16cefef31bccd42194a3750d0c03e953f83f9f3d635804fedc04fb8160e6f58f2dfd89dfdd194924a1341279b3110bea989a09e6c4add00a4c7144985e61212267a7a86d95ec8255fd184beda07f5314002af0e2ff51f90b7cb79058866213373f106084794d285a528c5107077bf2cb2858e0de27f5a80755e0a322bc91d9d7094dcff89fc6e490b4d10b5974fffaa9bd90a156de0465c22829fac8e0fb076dc3ed2590ce5ceb7ed501ab90b8dc2d4fe527b26ab1ef7e78d0db615c2e5acc02ac0ac97e659e9079696cd10fcee10fe4efdbf2467fd0aa8bc2aeb3f7e5473a4ac367cf367c114fbbecad16245beedbf55c332775fc355ba1602297ea09f5b352e26be577c0a3f63908cbf7b3280eb1f7f5eab36db6d31b1bd35a82d50b659a8d9d37094ed21cb19374f7a32e8d00985118ee41306a89dcdb1e6ad6bb8ab1189d9687e0a3c1bbd5f7bbffa34a40c2d1bd4954fd0c3b92fc684233c94ae243b76a5afdc8b0db7ac86eaf8130816669894029251eacfbd64136731e9d006d4855b1a2680125e9c9bbac4258f9bfdd258f6444dbee77f36173cb6b4d08328ae508db5ae0b6a4b90c5706a43e2be102c8c32df13b2a78a82aa13c55eb3f58ddbe8a24fbf516e37dca7e0a680e164df2863b86881ebc395e0b4aa336ed120d152bbd871be4b8e434295a5e6cb5d5bd042dc4f79a05ef542063bd8b197dae8f96c39866de9adc64a8db0b0931ba86bd9ff751b41d03d7e79b2f6f80ee8e8fe9fbe0758ad490f300b514ef76a1f8602570f26edee892f5f205aa871bb7c7067be9367497d5888f9ca15306307ee240b63009784615e1573b736d2def4a91c12c5c7316cfcd5686f0f7e20e5c5b7059ddf249b7559d2864725256306685af995e8ff21ef78bda2bd16c19609acda9eeb4c6e3bb9cd73e34ff92aaa40109bbc90775da570b6577c3679d1a430f386a452abef2f92694b31b77ddf232256367d0ad26d0d426696e076f4d67501438b696416c138240a0c57ce535a287f879e9b6ac67edd52860cdf832fc45606d2c9444c5af6172205b2b1da08959adccfdcacbcbd474cf229c2f7acf7f2da5aab63356f2556fbbf3ffae8547eb2c385c996bf068b16344d86ebca18ff672ac1703e310f1115829325406d14da7fe2fe5056dff725804b37172112ee85ab2d9a213ea3f71199de065c8022f3791d25efe5dbb90e68ba96068e67b572e670ddcf4dca637c6c1d154d9d70d7a0bddcef10ed93de3ddb41e9dac9f408a6e789723c37c92da2ae806c37e98c7fd203e7aec27669c818a06965ef0b227a157853bce6bd6e5cfcfdfdd1d0122946e7f354eb0ed7dce445cd7daaa5217ef572f3e8b599529873054dda889c4624ebfcef0020e14d5bc2de536efd4f58270cc87792ec9feedf1f241427b60015f4cbdf895d9577c3e6be45c5a72f25d342ad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280bf411e823ca0e4a693d6c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b49efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab0500a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb778bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797bfc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723ce9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c61405935ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a431f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a26635e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449de0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d25d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba09181896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c538becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab316608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e562f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604abff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f720c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff54efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd8314074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d617120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee54095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09caf363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae1900468c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbfb619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608cfa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbacc62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539bec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce78e582344b598dcff947725bd924a69e4e36a9698be91d950da538317972cd7c9ad6ae9a6a5c00670ced74d83b226aff0fc73cc25f3302ed178bd05cad5c7573cbcc427351ec5c32b1f65cb18b70361547f3fc2a347c934eaf9e53760d2a11bf4b96f9716a8a5582f057e70faa453f439cabdd77adb5733733f577168199a3eb7bc0ee3cd9cba4bd93cdf7ceb1c626ecbfcc7d2012d1801486c45f2ae09a60055dac74dc2ba38e62b82a4f0037ee8af97dd9d24622c57039c5553735277c716fe5aca875bf9a4c7af222eb2c69bc94f8d43890ea542b4c8fcbb714de7625ee3a25bd90fb445feff1ccd30239d79bdc0ed554c4e04190ebdea7707014f9315065507ed045647bd00e21654cc3d96efa5d3e3d1850b50b6aeba7e69dddd66743dcea51731f69248ffa97e266f807caab52760a648bdd25390cfa8027290e6b629e3aad1a5d429a6e123dbe8e40ce116ecfa044fd064c6cfb6d75b8eda2f9fd45c547300eba311510461d5a35b641b6643955631fd07aee48248d7969eac0ef904cf72ac0b997afa8a5eb9652c73707e9c6998a899e4a559d0b8f77013b4a05d357965c42034bfbb926cd40d210bf83253d01e2b6454811579ec21c1d9b4204fbaa6fc0dbbe3f5a8dc361bbe4cb080610459506564cd734f019c21ef14239a8a7f33736422dd9389153ab21dfe11b1fcad12c206bd7b2d1aec6cf97a0642ec9985fc53580ddf2fd01e743ebb16c38e821b2c179767fbbaea1eb16ed783e51a91ff1bbeb0c94bce53fc2793dbc8c587951468e5404495851472ea5528ada4a87931dfbb84ea57f9a0ffb658689dc74006ba080b7e8470b5262f2e58c5cdc378341bff5a5734ba1c3adea089d3d74561ff757aa1587cfba5ce7257602ec99713949e15c8f4b4a620f3aafd2887cfa88d486353b3179d5ecb1dcc57986479c3a25aeca56a6ecd4f94057b2724476bb3afb447b9294d4171b52a4fe4decfe393df5faf401713fe2f12e5ac3f2abeb9a4ee829801eb65e26c5c126cef16dd59fd670691db271a21c8b1207ce41ef26bf58c66517435ade6980ff6eac73d1c5a25669afa21e30ecab43cacfd4a1fe7af058527d0722ac33b10216c96087bd2842446efe4055447dc3fdd1ea217ded44ba1ba81babc9783014bc95ac934344bbacb9639bc6cc5dc791a81ad9c1f8dee4ff1c108cd6796f64db6db160cf9fb5c5dba2de9f1f996e82c2d26c4c51ddc6c935a901415e2914f77ca352b877da29488abd2f9ad2ac50db50af5048a2135b359f285647e334e1dfea06884be93418e3e983b3e8b3f3b04247294f71ba7222577fc1f37c9bba01e1cc203f04a34ab0863c92b5c8070796babe791b027f14b5a1f0a9fae39380862a7c16856b2b5df2cab4c3de2107dd40d8db697a5b4bc123e9df20222befa9fc8c591c33c8256b15cfdd54eebda108bee0eeecc7a0d6a20c620e250d5a74e007d5b40ac0519731bb319af0b22921c32c53bcae5c876d34d739e91c7be6b92b2affda082a9283c65173e1ee202853899bdde9830f660064792ded789a4b3c3781d695c5a286dc2d53ae86f8c33bf153f75d87b472acbe51426f5973dcf80a5562ea8e04fd795e98856aee72e165fbde097236134acb53b300d7351536dd99c39000ef48ed79cd5993b36beaae946e32f719c7cc125cc6ddbc4198ef51a9d5d4c611aa60c5f62cfb3e819459bac21d62e1b683c0e3e2503b4b4465274bac9856a3534826fbf5d4b308c36a5093c0cbe8b54140765cd0337eb809611a09a4600a945a64eef5d2803b9944ed45c4f339741ed23e6ef4993ceef813a7e6ad7d151d95d1cdcd78bcb7cd5dc5f678a7c534df8cedcaf2d77857c84c9af63e7d539d0dc968582e45bc4c29252d1a33940dd73625b27b11997c872b07b5e8eccdb9f5db7d810f7dc34be8036f2a6b5c3a0f10632f4aaa2bb3e6e609f0e4ab9101237783a5d2138d9a0545c6ab847627d2f8ebd9d33e498e9f74c01877f93ccaa591436a61ea804ed464e6acecd2667edd3452ea0141c0142080ccd4d76700a7e46f20277166421549293c404e7c17707d27ba36fb964eb9863a98316ccf9811c5239ecb65f3b213a3762e1c2f8d52532ea506cd3673ee9188fe844ebd8ca9e53da1eff8e8a6db01bb528cc44aa42850928b33ea463071c2782ac9798d1f5835249ee8f0b8d30c7394bd65fb59de8dff5e03da92d8ec4a41621789a8a01b9494e74eaa4313a68e67218e056678c73041579e3a981e2145bc27ce5ff529cdc5cd39b5740c5978b58507d8ab23ea40af4d4ac338cfe5f8b3bda61711d5502dce06f78acf52d062bd1cd59492e5f8a8b07a84dae1d81c0e0616a1009a5d42c32f99060a5728d901e505e19901422cb4b1f6e4626986f88d0bc2e3b1650139680adf068896b8f552a3d6c1a59070700702adaae1c62d94ffa5a67c6705cbbcc41eacd0cd4dc1d15042ab82fae19d89e6d67bf3421245ce81ef38b0f92db8116a8aa8612493e6d93e9fae4ebfadbef5332daadc80c9a46d58ed73ccdcad756d77cc73c32b1a0977fed2c264c09cac0efb454ef840a9a31a0606aa7db40f555c6631f6b01019c274a5e035817fc6a46002c0d1be9ef692c67279e16fd07c780b37d3046e433b1edab22c35e80af9293912c8f5dab5c017f8494f6493b287e9bca60734cc7ce14cfe6b9594c192506f6e7f055cd590c30a672e1337fdaf884bdfb081c3259c7f6571795a830bb567f66920fc18cea16b2e5c2fb655c6716b1dde9d1e23e49b60ae52ec7ad9ff805024f4fde50cfb35a652cff1285efbf4d83e0bcbe4addbeb89edae7aff1643477e5f722db76badadeaef4c68be7b5527f9ad89faf0415164fa8b93d62ac00bd082ffa96a88764cd7dc077beae8a909fcde9436a66acb8c3d0eaffb1d94396373fcc47c162fc7ddaf3e42c26258a0a771839a6c18bafef9f2759f7db0475568759a9de77af9cdb05bc86ed4952bd96b0ca2219d3588490aefa11cf1d13ec91ef65c2756c7cab038755ef7571a6c396596b3676e62fc35d9d336dfb10de120b730f5e668ea7fa9ab309702f0030d0436282667f9f2b4fb849088b139e035728c3bbba3e613731974cb831aa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c39466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b6348ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca865725db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c80313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43fa773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d23b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b8974156964b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35bac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b95375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf941e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5ad677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc895e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017bbf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb5834069f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6af95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a86c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa9233ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a41da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b2165ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a8723a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb569a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677cb58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b081b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725dc2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f49a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f47902d63013c6536ae47023e86bcde904f9aaa901c57dba3621a06357d5e6a3c65f88a1d681aa8bc75416a9be5d1e478f6c8268172110c1dae6c6821f4bc3b36e907aab3fbc16e01ef6671dff82e84e76985a0ea7e25ec890edbb1813ca87bfa61a002548741d792691957e43e774b8141ef9602caf775cc5be87c9abe6aa6fd8a7b83d7a13b3635076b13099b822c4b4b7caef53a800ad740ea93f1f8d06842d8c0b7158714d4369ebdba995ce1c7d45fca8bdf8b6e7a6cb0efd228a5689c99c01f90a9edecad9409975594b65c0404da45c18d18be04a61a87cfb1ae1c0b6d4be10024a0f6d5c75ca06f0b9b15a582abb52ab8b347dc8d7b2222f93bef0f07608905cf8c25080f613a8e20c5fe9daace2d06b7db380d5cf6b94663960743d5c88cb039b71278fd08795da93cb474e8a9a20406ff1792ae569395e409647cbc5629fc88f2b1ba15cf4fe0e1864b2bc095868b92960b3496d903c5add506278d85138d6b11a27c19c7506c74b1c8fa6796867413c37c90386fa91e50fbc73e7f6bf4b22248d679d6187607fd4ab227c95c783091f4165e35a425e8e076cdcc8986f5c6756f9d96bd9a17b3f89256069d6dcd9943ca28ffa6bc60f96190437b7e2b39977ab8ce858cb138bb4ea763edf76281d0614ceba6faccd4f5a5301d2edfb9074b28d73d929aef9bd6cbed546cb06e9ed31e4f998329378a80a771f6c0aad2fe68ca974c2f09e89d837f1062454b957f02ca15ffaec5eb0b45194ec03dcb92a9b75f26a9ff0dc99d98c3d5107c2b07fb858c4c9ef3207c63bfc73da979182fef927ed234d04de03716ff60743d00b16cc1255b35e46224ec0ae6e2a78ac72165247d26c1afe2959a741d9023e8dfe738be95c0f9d1bf0cf80f9a2d1e4b2756ca44d5d751b522450b22bca9b5a6b1d14a58e69b16d5a2e074bb0e5787bd7a9d21532b3c8c8256c9cba44d4ab159d7756d1e49b184f2ac032df5350013cc37e931dec91ecafad2a202a8aa7850b306b1177e94f30b65c8f0cac8fbf05d9389b07ffa222328922ad9e9318fd1dfe03ec37d57da7ade4bf43007534e9d3616eef5e1393a6c7a42108eb019c0e706a2839b86f013b72ccc833ed93777d0f1bf70fa2a02d2b52e11391226836e15c62302b7582b3702111375ccb7b775407f1e53df3c877ea97668c07f415d024092cf3ad7d59dbcab565165d3e2027cdf180b8ee006449f4bae1397b2e7565e6de14bd79a75ee69d1d4b814d92e8eb3e17bca30ce2e7a04f1b1d15d7b3d8beb79fdd2bc97637910c3f8052a68339bc01a1507cf6a29585d86605ba57ddf1d56fb0764a68c6f8b6a88198aa6a9064a826bcfd0fa61e3c9721b19c68ae1ef17bc2f0d7c4e39d176de97bcdaeead1ef5f1fd74e70df110c7d845ce225ec148e60a1f2e54a7e5b2aaa2e0d9048f993ebbc8b1d8720e9e24826b58fd073b11d28f8bde2f883fed8ce609738fa68d68cee4c8ba94c2460dce24aeb32b373ff875f6a34745d65d8fbe9e843af826c0b7015bed00ed6bc45f97f2f511f96d664f7d852f26bd7a4cf9436bd723a5db23191c8c70ece261cb26b73d4087b8b51dd3b6e97370a009f89924b0c369ce69a57456c7aadac0d4a4053f97b1829a902be22a9ae7dbfd8347220b34b1d1b4625a3eb5af1d6cbd69068293c236fb28c65939afbd755b937ce0b732e49c02872710c037508b097cdf6ea90ce9c1d8626232ad444374519534db2b333fc8aa107f0b269bee8f7a7f78a60ee95c97398e7e3abd13eed9a9c3b6c2eeb2a5a29c53f911fb1c67b7c57b33da087ca760aea1325bef7fa2f20f7bcfea67b8ffcef60f7de082497a0a52b3e56814d666162eb2df2771e47d8adb1b6d376d7b6dfd47f84fc3803808a9eb048aba65f461275ce23ca5aa3550e366d4d12826ceeaf72edc549c2959f611029b95eda6b5af1c3d8f6158170f238082423f72513ed672d78831470aa681626615d0ae6da78251ea96e03206889f5d8f74bb71491d357117704e71b2111fb51992c3832176736d8c5b31dce9fc452d7c6d78b407b7c6b2b145b273c805365099447cceca2be4cda3b67e9ae3926f636a9b356e945b653e02c86d64c40246e635e033926724d0d709558a2e1cff8887b5a3382493658aadf7ccb2a09143bc320670836e08125b44780d5c90220201f5bf7042d68c4affa00ea8a6db3314b0db60d25ca5cf69e23fa890c0bc770a9320fc2288c775cc6ff259b8ce45fdea8aa1e4e1d57cc1396035fad1f3159209133dfbebf47207ab25a8f33655b09178596e30235947dcccbff2f6a56a1fe7179d7ae76aebebfc9c7880e602ed92e166ea5a1fe623cfb5351cc8a911cefe676a638457ee40380941e82a44c0fc93b124e366425ab315c7fdcd056c335cedc1003571d8dab54ccd2d5a83c4c3afd9c4a0e04ffd021f236fbc42fbb1b229b2ceed70db9c47f1e9c01973bb37e068a46a58538eddfad948dc189cc203e1afd671b1c024841f24c9c78a78c3a2635b8289cfb7f46500bec7927e59db76751ab53a845a5e9e0309ac376158cec3a9cd5c9856d30006b17bd58924eb287eea2e74d5e4a6465412f906bf70e0b0e37aa6351ce9d9894070417b554d188ad7072efde03ff7102d31f6cd3ba7215f006f87c346144e3ef2283a1377eb60d648c1fdeaf5726a6f0d93ab7129b1f56c7c4fae65a281aa4452907b08fdf2fae131a34d215dbdaf60e1ee5be13af4c194a1eff4f17ce1aab6f6f9494a050302c30734efa6744c3c92e53da53e414befccd66997eb20e34c6e561beeac9788adf6d3565d69df1ec7294067ce85962ea4f5a21d7c298354fc723b90fe4737635ceef13a2cf985c7ce2bbc2a737ca426bdd24533b9fca54274928c312ac79982267e02f3a11e53487934fcca2d6d1222037087d11082e5e2f86da3929235bee95fc6a19364784164bc1487bea1dec2b34a2a9fffa842983690c53eb11ec2ee330111af072625f92532f50a8c3bcab9f05173e059b01658030d76f285299b9acb7bf48452d347dfbb6c60a28d0f34a475c24780ed4ee656ba8ac4c3947667c12f0030d0436282667f9f2b4fb849088b139e035728c3bbba3e613731974cb831aa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c39466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b6348ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca865725db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c80313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43fa773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d23b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b8974156964b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35bac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b95375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf941e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5ad677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc895e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017bbf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb5834069f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6af95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a86c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa9233ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a41da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b2165ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a8723a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb569a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677cb58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b081b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725dc2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f49a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b8279dbef079b1a2414a978211d00ddb8cdd0335dc54090f23da05a4f0ae84bd0631f4394a22ff85d1381493676890f4afd8ab2c36ac63d55319b6dfd9ba54e58180be85e2ec5165524d8079fca088968db3a34e608dff56467bd9f720bc44cf2871247c0ed88b45a8ffe895cf00c1a5c397d6b1c7acccd1419cbbec3545b2215507e29228c3621657abfa32984f3bd1edb451e2fb41147cc7737eca098a8bbe748a625cd334dc99d56c2d69a2f0d9d1573f6753408d16b88dc8a84a1e68107aabfd8fc994bca9585811a1d5e8b9c83727fd7efecf4c643436d29324db9e29802fb67e6597ae76459bf7ad7bc30179c9694a57ef693e1a01864929e69070e11f5d32a81f12a58fd24097bab1f32b308b63779e44c180654da74fecdeee57a66777d5c3619bcb1f01c8e87f6d46ccadaa159bc8074e33dd1c4467febc6ca84de82e94eb4a2a9f5c6ec739084be66ba08ac8606eccbb238489c5ec4403f3c18fb53ba63d55a976decfb5a68c6bba89488b100ded7fefbc1a022498e1d5a00808dd13ea4562bc17c3337103e1528db0efe480d56d10423d5f1f484b9ce1579a77a6194d9c4174d7d04bf54853f1d0ba101fc5ddf6de4f5143c02348e61be28f27e492f9ed00c58fa66d437b2ac651cbcb23d6692cd0c0afa0cfb2e7837ee5fdb2ef1956a3b79ed80c603e565e38a77686dd9430ff9024d1c63b07239835a87a6038e51eb9a394ea48d9529e66008034eab069e62fe6e54cd06bc0c0132c1d99e9cb7f1399181f777a60f749310ce4795e54039ae0f393cbe7bfe37e41cb4de14d9def7847ffc1e24b565384c508aa9311f61b6b4aff4d5d64bb3856026998dffc286a799ff146381db72d5f240c9e7c26da94b015b331b8a0a039082b46cb9adc30ce29ca68ef3687d00057740af97169febdb45f45f77e2566c915f9543db3f6456e632371231c9d6948404837e58d3b9f6919d12153286fd9dd26587c0963c7a712547a1f326c8cdba3108e3887877a6c39db2bb3a20e27052a9481efb0b3bb4f5296be74e942a7ce6ecd83fcd0fddecbd641344af647e209fb4745d600607a21fb42ed9941fc49b4d321f478e96a0b4fa2246a0815ab6ffabe6b7408358a874ec66cc7def2125529fbb53fb23cc78b6c021d9c3ae3e32816b6377b4c5a72285910ea4dfa041886bdc2cd3151a53bfa23701f1dbccf3a4690a2e4f921387793fed895f42bfcf958748abdb87f27cea9b211589ae04ef19e173493be44a49178a19bf1d86737eecb8450ceb10130c25e84dffe681b66c6f3f5bde01873f5236d4d4abe16368d0a4753847a25e5d48e0d3ba4ce3d6ea05f70e8afda8e4ea4cd107fedfdb4332c116b58f7d69ec8c08a0a10d01bf49b1b60f0190382261daeb53a035282658e61988e94fd5cbbe15503294ba04f947665edba644f6485a89d0f3863b1b2db482c95142a4dc58a14dd1d77c2a24c9f60e3a721371580be29ce7e4816d496a1a4da2cf18ba66d372c714a7230038bf05cbc77a9815ed63b3dc4263e01246ab6898cd451ecf143d32d3a546ff905ea5d80e54ced07ef2b267ba5cb536216c5fcf2d4aca39bdb0c94733392dfe080eb87796d0ba8b4e690c452e794dab735829f14e9ad1520357bca717ec4b15d40b317fdde40fe28fc543761419e10c4b665987f232d1b9c78cd84cbcb95bcb7731639ad9e847df26abfd921bb320121a5f31c683a9e6466a9cffdf7661f3b0c788dac5c4823d20ea9c80494f2cf2de198dea0caa1b3bd2b3cf85052a6892f8dd55329cf075df23f139112520ce6555a489b2a273cd7ad40ee31d344007df6888bcf87f966f04b50bb6a32c6b521b2163bedc55d42b12c9b9bd9a3b7882bb78fc9681f788ff5bb6c7abac783f154a42a3bc87afe43947407e4f9303529cebea2cb87ec78898966dbe665951edbbbb2edb2db9237eba9b028e9fbc6146d1d38bc9ba5731647ce67317e5c205c8de3375fced28932066ed6203c6d37e5db2d352be54950f71b5a569c3358dbd864e50d89f676fe6650897df51633b65d93840aa71e568cceaaa815f94475bd80b74c9c3426a033b6582b4268bcb247bf22a6b76faa60e2d0593fbbeff4d6917982c847bb209922aab9a04b20d329c60a26cf2d09d6a83ffab185a58152eff499bbe60a1c617193f2cae87fcd30cf8804e84b14f9699ad7f160b96c3a9e59898317feab31524009d6ba03e2f4834b1aba841fed06455e4bdd397de41b4164207a50dc00096116ccd43318a40362b602007a840c69c33f7ca989fb82311b8b40e363a59b01857d3dcc30f5df29633c9bc5dbc0b379a20857c3f53ce51205bd3a4e662b8ebcf280ebb40747e7925b6f75a477dd9c8005f65324dfdcafc94b0e898f40ebe347120ff447a0f1b60becc621120b3db00c172bf39b55cbd477b010580a155d623f2e31a55d61aab9e355b65917198a7167c4240f78241c5f5dee52d27f04ae236052ad1eb26a254527226aa671b0144c804166f70f9a0cffde31503555ac5164ed9157b16552fd175e9dbf0752801a4fc4566e6f798dd3a0644c64b54bc299fbec19f2e7309a4d95a7abf3bee247e34ce64154e67a032eea406108790a72468480711ab4ca74d264ae7057bb3136eac69ad8c631362fdd2bb9665d0d05959a0bee5716ffa8254182e95fa6402397d54dfbdbbe6ad0e477419ac96d3a02e92e8ae368410948fad6886915e68c144ab004c314abde7925a972a518e53e6a331aa8e8e0b686b6606a3bf68ba818095a8c528178f9d550916ba283c577ec36654f03a989015b4df73865d1a8a89c6403d8b4e88e39bdde7a36d0ff80be19bf0ed431a93796d281180fd8851524e39ac512f00846955922a17665f5f6c713d3862a5a65d843fb412c0800508e23a92d49abfebe2c8a4f593bb4373047b46105430dbcd329b294e258eacb4bb453ae1ea937f17da097000cfa38461bcf363c60cac09f7ed374d427909e5d3e699fc1a20705bfb65c82414b0c624e1fa3e24a288e53b7fa31e842ac7ed1525144747ca737fd05256c39c2a349d6f6e45c0feca1cea47d9c51aee2513e2dd53597593a7cb7483192177d7854778d249b181ecd72cfccc0dddaa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c39466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b6348ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca865725db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c80313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43fa773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d23b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b8974156964b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35bac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b95375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf941e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5ad677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc895e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017bbf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb5834069f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6af95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a86c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa9233ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a41da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b2165ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a8723a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb569a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677cb58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b081b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725dc2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f49a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2",
-				"0fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a0983810316e28dd113d90b6c648c1d77a0d01f950f3e318b7db7e9021de9ff06ef4cacbc6e3c411807afeca3e7b0984c1bc41573b41c374afa07825a322aadbd66ce4a87e408bc3d51408d1e1c6cca9bd368b2d4d918a07229b955868fc688ac447bd829a42673d263a05e53e8d171c67f5390037ccef9e7ecbf56b8c9ccf7a59efd65c8c5daabc94701720b52dfeb34126485d61caa522c37a68e149e58c60add76e381f4e8e79805e56a5361fd7b584b1e0eb3cda8b3078854c32b7c2a28f433dd0d2fb6cacd17dbe8dae7cca9128680f0d29215b3248af347deb24abac86ed9f5246ff7a1f92a37162f7eeadb9c8f2c842a93557d02ce51401545b1581554dbf494e978c6f44ea2a8666cc0f6bccc9532de42ae349d97b4aecbfdfdb02f36825a4d194ca03c9518f912c833a858486dc4c0dc9ebdafa681cdd8255a0f66b7c2fc01e04ea5fc737ddc9cf638cf68f32a532177fa1e0d1738a3c9c1e275c82465725980c4a8428b2325b82f509072ef6b5a2e13cad3ea25638c04c542d970e0f89143923ceb51a1e3a20abd9f71a277a2786b6c4dea616fb5fbb739c8aabf8421c43fd77bd7c58765c72e564519919161293061c2aa5890b1a2e43dc24ea5847f8a12b278688176d01a22e9526c035b9e4c4ec58be5e3ea375d033316f31cce55f476d19346b0cc4b8cfb6c661dab3f23db42d82e3130a62727ca9eb20ea894e22ae51b9c08a1aaca0f842fadc71219b202101eda2fd1ad3ee39604b4dfa8b6b6b00c5999ee26c67e2611d718a8f8826585d762e76c106d257aeeb5168c9c666eb5b2063ce2538b60e660ccfd430462a802fcb1597dead1b176951fd09f56dc56cbdc0c2a66f1d8a10a7c2062d89062fef49c095f7466bcb1be2490d8349f50145693bd6a87c1007e3b8c8eaca21f5f7ef839cd663bd7fbc7d9a11920e2fd667e9409d965001ad7bab843bc4c935e86f063a56b59c3f159b543733799fecf6d369b9a0ec0cb6607f0d708f430e9190b18495bc809cde6f1cc041d6abdcccb4ebc379d78c618660336d83bce7b1140700020b18a24adea40727668bd288247d5b1d983560b47000060a0584dea28e6ae8d28b8a8466f88a5dc47760534d3dfb4ac27053f60dbaa6e81d5a93dbaff02078dcc27c2c43c198d5ccc7f32f734b48e7f8b45b5310810596f79f0931b08cb4af7a228ea8ce40a39d0ee4e76e5c083dfd10118ac130b76ecec80edce962e12a7d86beb61a75b32ca602a029618e2cdf09604a34330646c3f4e269166b1f180d5b97d9202befcd00a1da60d4232b45e4c336e29fb9ce4e4c5389dc5e5ac2beadc28bdb6d61fe3575517c80f589b599ed34edeb29e43d7960566f8896deb5f35ba1092734cfd6520d22704aef659f4e62c4e17977b1048940b57ca65985264013c611f357e8b9aaa57868e89403b62a86c7a9cb5319b496e8a92795684056e786d4af5a60456c1dcac8e9d774a2657b9812934d813bf202660bfd1c4b7e753b1c8190edb2c040bc0991183c0cacb059ab1e641c50da895e4c0ea0fc5ced262d509db22e7844044b006bc0fa961127f58f157a8e42e7d4d8308829ae7d95d1851e3c78f2e7058a726230510318789d62cf1a0c33d3c1f9031e0c8f17bc1a478c9d336a312c0e99f45e52451d9b93546907f354fc25b0ed5f72e9144f06069577ca677377017b614326da2f4c7f1a7df04f61d67002facac2a60fec492996bab08ce9bedcb7d40812714d05f4514e6bf6fed3e2a050f3144eda1daf5e2b98e99b21264ada12a7ac812ba904620345e32f64954bfd6eae0686c2080a2ec62b51c348505712c888226f15a8d72044e8e8180bb145ba617c681ab08f0d6f67f1a46148735c165c34c8f64155ffb695d030b4f4f25a403d8d87c8bb2986427e5d8037a2ccf52d3fc58cf4e87262a280c327c58470b597b655baba835da4a51be561704aaec675e55fbff770d694802c5f217cde7de5ec1ba3b3b03e6f2914ab2f2a98ab6d5801a05134ef6cacfeff7dc90f1d768012dd0ca67a4b1be06df19e0032e2fa8d99630ff935834519b823b0c91c6541b9396fe1ffb3152243249cae0483e1ca5718ebc61f19aafeb8419fe8f75e267a2e3541ac00abaf05140c10cfdefb9ffbf8a045f0ec3494ad47e9dec2eb0031d299f07cf3d164fef3a2a9bd6d468ce9bd343bedf7d09a9bd7ed7aee8ffec01f4ce35d743f61833922c0fc71b1ade92cd4253bfddd04442f86032aff76bde1936ff387fd5809141faa0c63ba71a7fc1aa7150ef55e645bce21a939a581b62618dcda367ff9a0778277371ff592e19c4da4d4728ef726342b6209c81d7bbecbcccef477f1b3169b2b5323d544b9649fb60cd6cb8380a7ad20562d2d7f0ee59919dc05f848fba4e81b8b48b468896ebce44531612d993da9f42696ecda2a7171b8b6dbf39125517c8fc1f8dd9d04b313024fd9fe8de34d930d8119afbb47613c1d74ddc56ba57a75e85ab0a46189f974ecb3bf09cf5f40be99824b37c37f449ffab05ea2b27e0b1519b0e165c522d50806dfcff884ba3042542874ef0428095b571081fb180719d9e2ff590380a15b9d8576650d2b546cdd67b00324fa5dfa58d49168e2cc1939a491513d2735dac6252b18e6cf69db9f730dc02d7864d5a7e7a0b8934dae6cbf0f1e170f7bb7a9ea5f140c0310caf41d1925058aa86aa01d100a3326545bae9f6335d583fdc12ccbe378063893735ee585b50f238caa4dbb9eda37d9eae5fe2a6dd62069dd13e6b2f68e0817648f8d728c53296c14d4d276b0ad7881e4eda43bd8c9b1aac952224c5164fd95225ff5e330cf722d4b457acd968c80e9dfec8d35f16715d851d91674f3ea4c4ce02028975fd6a349b8345544409b3cfc676f44dd35d4179ec3e25222b91716c09aefacf65787c0c23dcc4da4ae5e1c4a4da82d0e2fa467f8bd75328256d6a256c0bc76d98fbd30fb27d3f423b2f006cdb35f64fb9f4edd76127c860c97ec250d2c4a872e21b1f7b3286a8dc9282fbcbca45c877d13364742e3c49d378fde024816d4f2cf36f6550821d8f68fa8cf602a2ccaeef1aee2513e2dd53597593a7cb7483192177d7854778d249b181ecd72cfccc0dddaa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c39466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b6348ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca865725db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c80313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43fa773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d23b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b8974156964b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35bac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b95375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf941e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5ad677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc895e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017bbf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb5834069f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6af95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a86c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa9233ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a41da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b2165ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a8723a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6aaa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb569a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677cb58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b081b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725dc2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f49a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b4355ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9a089fac89496294a8542198d67836112a2"
-		};
-		int height = 4;
-		int layers = 2;
-		XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		for (int i = 0; i < (1 << height); i++) {
-			byte[] signature = xmssMT.sign(new byte[1024]);
-			assertEquals(signatures[i], Hex.toHexString(signature));
-		}
-		try {
-			xmssMT.sign(new byte[1024]);
-			fail();
-		} catch (Exception ex) { }
-	}
-	
-	public void testSignSHA256Complete3() {
-		final String[] signatures = {
-				"0006cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e127f6d28c19102daafd5132d989b6c61bef63c5d3c1c163861be3aaed7cd10e9fc30a1db73ac242c475463d88708b7244abb2973d01c9e8bc326be9623431dbe546fbf9ff58adf2005efc9ec3e8fdc978d12d6d82226b373fa4907e409b9152526f96e4cd1e74727dc48798061a3c79d399da7110f4026e78d5333969073fe47ae3f6b12ece2b4202ff407f210b2d052ff09ea3e71157f052af45242ad6a1ca0e51b62e1e78e5e764b75b4637e98476bf7f4a3555b8a8e63973e6b23f979816123c361f4e2b0b1f30870a54f387d9c60b17a3c5f448e7c149a4f1a2d7b0b16a7700b7b9ab9893ba914ce3226a82b9cd190e977d2dcb6b9b9aa7d4fc647c60a75514bb7784752a21d3e12a9e77acbbe4c23bf10774c9a9f6cc77fcd214fd48bcc2fa4d6e2ee562a3aa1d07577cdbc4ca1465f4eeabae7f0272fd2df9ae0d14144dc8ed8e66a9ebc3176e607608dcf3cd533f007d17408608a33f2cfb183b4bf038ba68674f13fecf8b1903f5cc2b81e891d47876bb9b2b9289129463fb0522a500f7e6688a342a2aa4ebfe18ed13a81a3441bae2a200414ffdeea5b1ea39d8581f7e53b562ddbc8f99d4926840b01b00b6663ec58a55bc5cee700889a5b45535cc65181c6c126573d886cc618646adf9f3c192b05959e4ad1a5b8fc62d8a9dac0464e86a483f888e4fc2becfe85c1c5d8bb9bbc946d2f2919a604b7ab6aab1945cc32f3d7bc75d697c5f56ac2847261d2fe2419a4c3b722a67b66de61529e820d89f46dbf7bc8d09d9f0c580744cfd1c64fe06ebb73264c9fd5c943a730d905a8c0165b0dea4cc921f000e5108c52595c46cbc4fbdb8a5ccd8a9e36d62615c43c3acac4883f4512a4ac4348057d90813786f5056fc6fb907b24664f0c1a944d780908646643dd6be88ae67f36789191d05763ed0906524576bc3b2f02520ad09b6dfc4fcc3350eb42f55015f227d1bd3ff0fbf1bc08887c552a7f933036d2ec94c00ed83b8e53e348c4bd6da3bdf50ef533c33eeae8d95b1e61acf76ac196e1e9a7d6a7c629aaa3b60be502ebfc1f2c76a030125b3d081a4c8535b40662083b83275b86af24db8428fa2e896dd264d1215f88bf3371048407e628f4ab27922a3f47e71182a4ab4e346d972d89bc4f4dd2cb9b5a7dbb543e04bb411d48d18e7628be0d8f0f8b0dce180b5d23cd023c044ae3b1614424a13a29f432886fc3d7f18726f569411f7ba31db4f998249b0f90eb6333fac94d86284bdddfc90003fa29d8e1d4b210e981aee9045451bc71dbe18e6d71d3f66083434540de6fc8095982010937821879de16fc13cb7669c4777919ca8ce149c3f1f17dc4536a1a44768614286ba56ab61a94ddbc9834ad73de154a4561c6fa22f08c65eda9f2078afce06cae94ecd6e604df3350345749780aa310358025ebaf11f0b78f08334d0ea7d9c3d8b06f1b37fb562cd23167a87b10fb4b2f3a76c13e045c9e8585bce79f9c152c9ffa0d54a993a7133fad225d3fe5dc0f15a5c40112237b330c2cac94116cadf29c7d7b5fa75901f151994d1956eacadee3845850a482900b7421ab712c64f7dfaebb68046008e459ef0b75bf7068703c58a9f8f8a0438d83683a2bc7e54861769b60b81f1b83f6ff2f2c92ee68a9cead253e3b38a99e064d05e6d89f5db101626e79872d9f72086502549fc9e290412ef3bfaab45db982f405c74c50269942935b02d4c2903876233294d2c49107db8516e6075ed21f6745a9724e0f87bcc68bb6c2da59d566b5d81364cfd2f5698e08a9d84cb5be62c2e3606d5cd3d3b2989e3102608f7247a73691e57031719a19e13d6baa8a1614099f9609d37ff0e408053782cd0022cc200cd8f885e0fcf6e15dff56126f00c342a0b6e00a82f3b3025d4a93523ef509bcb759ce64c5c95a90386c44f5ea34cfb1f98a48ea081f4c490916b2a90997be86038d060758874c6cb08d9c92f153c53b69c0f056d69a232fd8e99d57b537483acd1268429fa18a17368937109508f783e6ceb302b973a2ce41caf0714b3feb11a0b900963a0707f950e7734dfd72c458a2bcaaf39ff6cb61b2ce44f76cbe8d2dc5f4ba6bd48eaa05479215cfd7315daf830b249e89821c73a93d3a1942466f1118fa55c91f682c0ab498bbeb0161be5cf5912bdade63a3ca1bc1774ff595fe76668bd5045fd7bbc8c2e8e10cea030226fae68c5c4e665a1fc72983942060f14dfd0cef924b08102518e677d93dd1184db84459e86dbab1a1e5da423c7a07afb2c65abdae44cd81aa0ac34c836cbdeefdae91e9ebd0577005fd31ddac5d79e50743df035754634930d233bdcab7e66c7ccbd9b832388d02970b2c604618e04055df780e3ce0470e0783822e808cacf6be550e37a56a175d9872c7af8d2d79f614740fdcffe77bc8ccca1d1a521769d342110afd8680b2850e3e19843c760965dd702af43fa6916f0b281d833497c4ce1569b85cc4c89a972721afaf111c6c45babd3876c61be5852c3874d6a1f36fca5a1ab95e180469b30a67e2a0ff832abc12a3cc6e91f6a459fbf54f0f06916addaa1ae07ea48205e98bac1fc0a58858c7f63ad04ff04e3c45c4c21d9f32b0e3eec7b0ca715ffeb9d6bc2e99c15bb8240dd6c3e2fefbe3ac285e8ebb445363d0962768b4a7578bcf08cf2584f46d45202cfbc38c131ca7fb6c9528a21fc14463349a7f71bdfe534aa2bf7d85fd2aa9531229791fd4e82ae43cbcab09997388e22906c47e0dc5c0b0f0f9478ce5d2f2e3938b76f4a3723c14a137257a89da6f00c7c0dd22155363e293a985a62bfc7e128340afe31e80dba1653ef1607b7a9e50b35070d354bf1cc507592efbe00a42cb7f3f0ec5579e06e5f6aadf7f20ab2a6e65d60786cd0a20afb3d6909845f92d64e6adcd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d876344bddd416aacfc32a31c9bd688d8e2712f2e8e78cb38b163db158ea4eb9c9f45a29e7e7483fc6a23613dd2ee32559863d45eb5954905ae83f57094eaff4a349896f6272430eead83bac308803c2b75eb8352e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e17202985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac90168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c32109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711cfebc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672fb34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f8936c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b724da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662bf90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de061b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f127adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a63176d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a5734d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb3509394735f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d662417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c22882d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2eff62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612dc283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191bd96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf235daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933fc5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce020744615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e406a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e485c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96dae4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018ef4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e83914933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d909996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708aba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c18434653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c438976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37defd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b4989994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaecad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc672455405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb1731523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
-				"0023a2267c7d68930fcd162fb7b743e94c520197ce16e57ffcb0af6790245d85cb05619764567c8c9935c52d3e16f832ae63ca4e28dff7a13a41c76b791ad61b38479bd43d4b0325678ba1241a244a0db58b15ea8ac715ef3e0f6dbfc0b595c53b7178f2a899af8f68d04f9f8a07b4c5107c4470a3cda8cd3e265268e92217fe52d41956a1c4aad1c0982b8c38020ae84eccd1b9d121462b33bbcd5230d64836e3666f369f253dfac6c906013e005754222870921ea38c60dcb096de3c5c4aff406689cd945488073df096c6f01edcff13ca3a06ed959393c71fb7e6358364ab75c6204ad6fc46a736c30cc52a1ad78a74b767f8595c473c423dbe2f027e2cd9436b59c7a48807fe1a4d9059353baebb6c93ef5f773f53b8d54b5e96955ac98f19e9039b3b66c2d0bb2715f649e30abf9334b2eae21ab6f30c85323e1cea583e81178de649e14b6a8f6f7b08228f9de1182d4f228afc85eab0f522a0e48d97c7d2ddb7dba82d9163517653652bdbf7db2c53d3f063dabdaf9e6fc6d25f38b221738570c65785c2c721862a732b098ce725483e00319764ebb5d9dadea279c742a6b9d4a5d2366f1af726bfd38a9dccc837e161a8874dd6bd8faa9faea68c048d86ec7f52ab14953218ac2d3f390e1c414c1fc31a72b80965469273d39a43c5d4512fbbc6d5e56b3c50646198f3a91c4d7300915c8342f2d0108bf21ea6a7c5c7fba5d6da11bb4508257a7441b78bcad9256b0be8bd0f68cecb7dfa1d57bb22f765db60c43486d4b32f753abfe9ae1cdbfbddb8a5b53477364324148e8e53664cb499cb9d9006505c3dab44539398b4e517bbd03181588d89ef68167ef376ee8a88b10938946135543b65f81d1f2e9603d47f7aae994a7dd10abbd5de1131dfda96f2997422755395e5d0cc605de27c37a9f243a7118521f3e18cb37301ff545f7550765b3af69ebd00cefef71b54ce7942ad89a7f11ea835b7258c1da2d5eb1bdded96dd46a9271410afcc82abe375216bfaed58db4974cf1103c02036577e72ba33d9c2f9f9e8e99e85b93dfbf832acaf75f31a8a99ab98e899103e9121ce94993bcc5f83ebee7051a168a0450a02283cc717106d8d861180bc8ab078950e913aa8f0697e33e4a0508f4f52b5c1f3937ab9af87ac86937f3720b4c1cf8f2f17830ad03c0c509ef410a1ed0ccb89a39a57622015e19dc0a8e8cc6f0626d5163c63d5594abb21ecdc3925b1734c006f35d59f2a636d6da6d96552d18e8167a8dad361cd8a2106757248b4b2ddaad4b6326d89d8274b07840060269bdceaab449090a263adfa834c4478e129d3a44fca4b62191764cea33cb7c451284c60117630ba43a79bdd30021c809d2ff617c88410b25fb4c77b244ca4b92fde91116945d0f89b2bd5c74c4227e10aaca2fe5bc3030495e66e628f6679f826bcf1f7ab67ff167c687427a65809dfc53699a11d8212756133694fefcb0e18451e35c17287c20bda162321e2db4a1d6f97346da22fa44bd2f251a978c963264306fae780abe6e391cfe512ed7fe4cc410505ec4d5b1770d6519e4c8038c7f7d32d2c6e8cc654bc9a92a6ea459c0bd0f1195272d43093bca508dfaa891f4e65c62c6b2957a0d9ac592c8f70c51d2bc657d837cc7c786e8c3f171bafb2e1460d881637c87286706a545afdb2f14bed61e2d27365da891ebdddf9f0af9b221ed8e146cf672673e3b3cdeeb7f7a1655a1673af55d06a22379e16319bb15a0dcfa4d970c0cc9a56db378b44e7302667fbf87f8a1c9b9f17189aad966f44a9ba609c58eb41659b3900bbceb406c1845fe73b29373b03049e4adc79aa2afced837171edb16e3ed234cf00ab960a3fdd70e3e5ab89a986ab25e6de93251a6d61f0a4f9cd1524c3b2cffba2d773ecf367af2ee3d1f27eaab963a65d644fc76ce1835e36f5a6a501bba82831c4895a44546dbf7b7a0d4c5c40c094ec416185a4df54a86f37479b025b923f0a64f5ca3dea1627452f2a08a3d426ae9a8704c66ff53005536a8150b3b5302c65f49ebcc5e968ec7c54fe3ab5d1ef8c7b4e4e47302edd619530bf6cc42543eef17db409d92f50f5e6f5bce845acd9f4ec5b20d042ca48309f1079d2e2b01cd52fe9d340852daba400d3fda682162b108cca7ab4b5356f444fb4c87f48541c9b5d1e58fab94df1e0bd21299dc7bb7144cf9da351c3baba8e920a4c14b59fa84da1d1e2428818df608398662ea0500932a92f16b29b06e681830f6afc25ae6efd2dcf7134cfd3bbdb74d35bf2ea0b59f3faff32985a2c63ee1c206de9bf6275a4539e6afaf986b9b86535fc81653d87c029517cf1b599ccdc591bb63cc13254ee867e3432397ccbb877bc6fb7a89a6890e1b568ad5a9b810cd0d7760723f13f5468656fdd1c207f6a2b50632bd6a7b2b7e8478c2d2a82f429b6bae63907846632b43b00d3716b83524f3f92b764c35cd2eccf8f167aee4d08514914abbcf39c2fa2d6ba77500bba727a77cba850988b46e9c0240b11ef4299073aff9f68e0f51ff9490078ef26cd19106a1bde8d626659862d3414720ba6f3269c70dee4e86d307d29937a792653138aed7ff241c71849c97d57cb2fabd161083f992350131d3846f3dc2aefb4cbac1148f532680425e3258243ce83c1569934f7e678382dd78eaeedf67864c0527fbff2d86d735cf18b99b55e5e853868e6d9a05db58e3b2a17dbfaeefe807e25070a3c177fbb7b2feeaf0de13569e4a24da7d801fc8e95a153d48c457b6d59b8d183393e7aba668063e1db92980a8f428d1965a0d577d8ab169fad00497f12405caed3b2a76c0b46bfd7392f581db48393280e16ef4fcdd2ca49be4f4d964ec9d74cbf0881eae7657396996055196991a28facd2e025681d05d066215f986c44a6da0ca71120f7c2032f7802c9125355e5cb9450560f53ad002b01642f9356f4873f0c9735f939ed450af4a2b78a60d38ed468eea4f616394fd5b1413c8cf72217fcb03b3f3d62598d2cc0daea758697eb4c9c365379b7a17fe2fd3145dd0d456c0e9c54a9ded677e5817d8dc19fa0e28215908b274ce11e6b094e18294c7ee8066a533cde38cc172508fee96e00673a45140b99963fee4078c6781c2240822950ac763b2dccbbd2f9c555f02385efdf7366b7b87c7c2e9a0c0e385fdef6f2877590ca88e637f0dbb875b74d8be4bd2ea75e068fed6e21c9ba7051d0dad832d69d94e609b50f12c804da07a08173e5635adfde10b57405c5443749856722e9bbb5e0b9717088d4717556d6c35e314731cef73eb6d27050adece44314b4acfe16947db8fd71161dcfa7b896b660090f29cebedc8557befc0dde717ccb565cb56ea5c200bb09f598ad88814e28cce3467d2179b83814b5efb87a4468ce4d2dfc3d998895fe2952e5109dbd32a9793e5ce6cd9a7df9f164f2063dad85dabd972ad48b9c803f41a31a210b3c0e2d6ce32d408c96a158dc86432a20fe1b7817a52e046108a27afd13c6a2e6fd2b3d0ed36cae56ae26cba95173ad7d2c1d8ba46d721f01202e62f10e067b61360c0704689ed8114671f467c84bfb7383f6847f2abdb076f5b8bfca9b8972946fedde492872e5b1113588acdcc11b0786ae5b7d02ea27fcbb9806947f481fe684ad95e82b5da216bd9f89351714110dc2acfab187a7c788c40267434050e775f93bf7242ccc32da47f065149480aee64cf03446599a60b9030fb5428ce27005588fc3159b05c0718201103bbd569906b747671ee21c3aad7632fe88273ac591c381671599b229c734fff0903a166522179962c6b460cedf1ea42de6617a4f7b58cfca76137fb5ebea25b3de1336c2e0673f4df60f3f95eff5e1af75fb432358e3be311818dd3d8f6706fe0d671030d5b0b564cf51ec1cac9e91c9bb3643eaa6c4c3f2a59d9fdb7fe44eec5f8158c5f8acec22f2e43574fb9f408fa993e8f9c471906ea4398a5092d182a488b2539747ee0d36efa43ea7dc2d316bab38e3a750b86b075fad35c330f2d5d9e4b9488dab41e9355a681d0a2d230cb54251e333d14ff21ba55e31c582e6d4e387a4f3db77473bde3acc689404ab6ae939299a2cdc46be14b7a6d615f9ad22d7c67ca73f81c3d0342dbc83b94fe9caf536b8e9db70b0c48bf7a7169fb0d65b792e5970cc333db03bae8c20204823eb5ffd3db08b4a3725040cce81b09aa986f11c39ee526d9cc1a628aef0da68c90755121e53b70dbd2ac0019152003cb5fdf6f764752424e6d036ca162d1915ba6a78db1f1e18a51b555287ac3fc806a4b6297b8faed1c23682b91d79a2d7db4a37822b402c4247108393a8fa26ffafcccefb73fe7bb50ba8d4240cadb8627c72c8f6399c2f44386d9b32e477bf6249931d3f2ff9db055fcddd7e381122bcf466f0117645365d72ed9bab6460e2e0a02c3a96bfd91e491326cd483f72569d403e1ae6772f9925eeac01125537b5df37d162c24ef331b5eed9a5871e12873ea44443b12296c696688d6182b7368c6479bf9e1dc68bae084e8cd01d60aeec317c4d4db922c21a9d57d1185ecba62cb91b1ef420889a174df735c1be89d7cc859c0ceeb61b6c4965060ecb1569b540c9db44f6e38399e0da56ab959e5c184cb1c5e013694e6beb83df866f2fcaab85bb8e9c30d8177a37e57a6dfcc36635402d680e9da7d690a8438debb2d02d50e796b00999ee857c2de3a650561226f1a6107f09e2ff57dbbe879c6da998ef4cd2e70132c6ca38ff651c0a30bc3a0862197b3005322eb854c71f12cc1de17907b0c2dc6dc26cabcea975160e993859f75d0b8072d83877ac6e67cb9ea5a629616361846bd541a565ac9accfc499492fcf68bb50602b2b9caeca133aa1ddbad9f200cf46c60625c73fd8cea2c28da3afdce5cec497b42bf3e1eee0754e1f0489ee3e3b8fe22bdfaa4cbc7feab401e91ae0ebfbe89e8b3330a7e417ff99121f68ded9345cc02fc38a89d8c8f8419187b0cd565b319d14dca076d17d99bac9a73854c707ddc9f4b0247b791951234151c9c2f6e7869efd83e3ab6c6803e4a0a3339e572893bc53631dcf0ff247b355a8552807073ecdc067227c66fcab0fa3be117162f8104ba884a086e70e0c757dc53c45941e262bc82d6ba33a94d68b7a66bddff6ca159ed7bdcf3ba3d4082469b1bfb860f4d1e1e9efb672c69d99cf89412fe481bbe8ca1f6592c686e2acb996bafa887073dd17f08eafec9f196423becd348fe81045034689f915976133c678b8f0412366f31cb88d0fdccf984ff6578951f0688af37c7f560ae540858e438bbf252d61cd603cae8e5b734b7a4cae4cdbbc7b6a97e5de5eaae07981f2c4bc262bf6220a49be02742bb26b32def0c0f43f25efd5e0e6cdbf5bdd11b26833af51baca8bcf6e90575d764b9cdf4379cb1740bcc53d3fbec9c8022fab9b43f7d975ec8c3a758e03fc89fc8645bb067cf6d88204b4e73cd630844b519f2efd32ddfcd8e8f73cb294ac10fdaef716c0b313f89d799871093a11fe0a29862ef12623f7df601ceda2aa3544adadcec0d99852db137fb3cf000761a5f55f8f09c390627b14256daf0702d4ad0ad9e207e44c7a40c09a35e4078fa88d08ededa735ec54083fde98dddc296aa2be4b2b41b814f4614ec9002c42cf15890988b03380a9256e30434f243e826083acb9e71528eb6c41f45f55585c6eee12b8b7a4da15c6e2833660a284f74b4aa6c20280e21bff1b1ea5f6677e9760681a6c3596cebb4f597f246f10a9ed875c9506ee2992aabe34feafbac4c0d8f44ccd2e65e3eede4808ff4fbe98c9937efa76077dfdcc59380d5bcd11b00373462b8be8bde74ea4f0ff873874ba86a16462da7d59daa6e77c78ade093342795e38629a81200949aad16c5dc6111f8c3c8154d18fdad070ba67b8c18005f91d60dcaaf252eb5e981be537580415464f31076e5ae6101249151a9f14b09f12dea801d2c5dada02c29cc5be3002f578801ff5450e6a2127301befd5013db31c6dbebd92466605578d820541693677c48ad27e8070940f33a14255f43f57290b286ba0da1cf2250240cb85b8794182f95f065d60288bf77df39583ee863d064a7be1e27a75db75e0ae29f880a4466142b03ccae446821cdfc901e214807cf13a6dbfef33e20192e74e10641f4b1b600dd21ed6d816417039e81494b879f9f2beb31da8d9aa493aa121e6f22733f1ee06c0f6b96dd5d5689b866dba9c7c735503c38408ce3424cdba773abef65212e9ff3770364ddb0260fe3094ffee9b89ce69a34f9159485d4bd4c4372774e20617eadc0c6950f612dd2ee05e1ca2f1ca4e21730e472fc8bdd6e2d7140bc362f6334a354ba250341994408ae7469b996eea196520ae1569ee2a681bd2a175187f3a57d3716842c65c82d8e4aa9d6e910f8c3d4a60a7fd9c2d6820535e0fb7abcab820750dac95b9d166b8a8b506d75fd55d1aeb1ce40dbc2c9f2de3de227392faa0b79f2ea0864aba2e4936f20c379fd6426f85184b615a4807e86893e920794c886dceb0d766daf9f7e484d55b177fe89451db5b053afa946f238412c96c8d88da889190de7826ee1a2c712a046a5676a8e92d9ecf2ec923608e52319751f0442f583412c83789bdc5fdb17cd3940f6e091cbc69b3fec8a0a125262d63c6e2bedb5a415cba00f4bf16e07a06250201def099da3bc02d3343091eb1ad334533ecb8fbf244f2c1800ae1ced35ffcb5543b09ae6e1d45a11d7402db5ee432c5cf90a41ac9aeddbeebaaee26d905fd265c77efa0ace6688b5cec7657485facfa881c29b3e2e7409f89396bf534fd9dd348ff7c623f5885eab6c385154ab42ad11d3a015970034b7a84616c4e62e6bb174405264c4aa98836e4a74597c842aef4d267c8f1118d1056ec2afd3ac790f07d8bb387060abc16fb12a2fcc50d647a8a8f36b693545d983466b27c8f982f5721b16f3160d77d7c391cda673dd320784ba4ffcbe527b44c2ced6fb7079eedba182ae0d43bd92735192f5715e7eb58cce42d29d69bffbe0696b573ba7e634ae42bc662b3704099cb63a23d7a195fcd90536bbe6ad4ec23fb88f25a9dc398a54c7239b08bd470aa37d91f5300f0f49983262efe29d15ae548078122d599bb6c67e064ee780097ab491b2f0ff25cefa3cee82b947fec16fabcabb6f2410894e53d7ca82c2eacbf6a0bcabb999a6be56d3562e6ac18536caacd8520c9371899e57aca2259a8467a783052be0debedca1a3d947ae175bec985e1346a36234f8448651a200d99402bc47b7623f6fc0e15a866a361dd35f5d220f5302e48ebae027c8e71e53c7273f5c0a8c70de15b7c141da8ee6e11b7596c340366c5aa0621ff1e39b05c6f56317b146464415a6e1511c24a2d02b91a8dd2397767c4a1f0fef5fb3f211f729755a27f2fdeaea572ea5b46f63595393c112aa56356085b7d19ce4bb6adefe32de2f08e3bbb431047629647239d40e0917664de38b739a78bcee2d0af9270662f600b0fb38d26e9b43390b73b80c0cf0325e77a8e0c14a6dd9eb50ddcd81bdbc060c9f0c5634711adbeaed2dc40e086a455f07c19b016a95c03c6a4ceccfefddff7aefee523332bf5894a4b9dabbaa26ea310d8e5dd12204d4277fb8b1451ce98f26376094601a944a41ca297e2e9a66f8b72731bcdabffe454e389f250ae73b8da40e268b70dd24ab28b329f87242319d6ae5b7777f8b5292522d93dc6c9ba99fcf7dbce171155dfa143cb71035af6c5406af6ceee2cf67b1a72ce2fca03aaf471fb5d8e4b5c529f67d90e9e89fc1ddb3178b4f7939c6cccf5ce1639344f15c8cdd1a6a2ff6809429de678b796cc30e8a76fa4afb6251f6525f2a536d67f608be21a1477ece3e04ea67add8549adc0b59254c4f2698d4c271125c0dd70674346cfe9163e46eb593e144577139611fab7ab513bd8745fb610e585809058e5d21c3443c0cf2fcad51efda63c3f1e24b60d7aaa53eee8267ea9e06cfc2b181e1f2a372c1ad7172372aa3cb5525a4fc1b6d15d4d61ecdefa9f59668ac7f961785ae02b452149327df8500769dc5ff032b805da4d772c22177c4a968707ac849c590209ce0b6e2ac69167f03b7488c8dd497754031228ce5b9c0e65472ebccb070afeff49087ccfeda42c692395e81121483125bc718c817f1b39498966a40dc3409a1a620b4692c2c76b277067989d7514aad2718e850f4f2f4be71eaab33e3f7634735ae1cb65b892dfcaac2ab3dc8c9a33efee6eef2b7a95bb1450ffdc5093eb25122c0d76279e95d6038da4e0a8c960f9f934b28b112923a74900c633b490717e104110893474379581e8cec192f941b6f78b0fc9e45aa509e8a71986e8e89087d815ac461c6c97f42a50799b231b1a46f172cbe41d216caf32e658a4dd5f7f81aafe7eb4a62b27470dd28f6c736cd47688a9fbac279cfdefc0eb3c168ba920463bc28382031e59f936b019857fc3807b35c492c9318642fbb976fd5eb0f2a3f044e3f05d569f51cef65d23be91f4c2112c039f7776b156bac1847f1571853c4af9b0890f3fe1b35bccf692534d444d58a42db0750a194e1a567c0a00ff5b443f4c6f6d6d2cb65d1713f326139c48bf6fcb762bada7707ec291fe74aa879be3c4ee4d3700f13cf13d1d8b7dea8620b805c780a30e4254404b02da6b957fa278cbf748fa3277d39f187aec7255a49aa7e3a9ffe62adfd6e62469fd106a022222e407bdccf0adb0cb7b16138a63b3c273a5b7676b5b72f128a96bd19c8ebfedab3609822d02139873542921b87de821fa65e38d5c2e6ab662e53f20d294814ad5273ee8ca7d66f59d2f76a2e97d0690bf7eaf4ff29ddba74040ada31dee6e5d80ade45111c8e6b146b1310ce2f109a45b97836fc23e453b7103819506b88b3829ee09f22157e783f279e06f1d5fcb22e9962cb58827dca7a037350b0c7c4ed32f1caceab7b945387d5738fab345640f20e64cae09dbb99c0435542084b5cde398313df48538ca91c794de544e4b6faa019708213c6dc773292f29f7a0e8e3f8fcc507b9d4fd96db13111bc8ecd38d712377aa2fe84567e9fab06b35fe6051fc13b07cd6d1bbb933439f5efa40d73beb52b29a0386fb1fcb48a5ab60f9352f8efe8795ebcda35b26d6f87c19dbf2fed86f90814f68d2c986041e779c634ffc9532dffd0d2e217c3b4fc7f7659fa6dceae771975a7ccf5f07ec5c1ef43b23309f372baa57957508f43afe8e921ee789efaa3eb7c47e077b62a537ad35b51f04e837ca446e84739c7508d900b0cf66a4af38da2398b9573cafe2e72cd0316cd53556b4425533140cfb45a87b7b9ea65f3a0267c809cd1a8e299d7794702de7eba060d9740b839e8c4633db1f7127b787f8229317371f6c9a2440f7b67faf6c653569757f84862b7c473656223161a223ee596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c18434653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c438976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37defd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b4989994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaecad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc672455405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb1731523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
-				"014c8c263f11d661b874b01e6b3e13d75497978b7ee0ea10bf494646c61cc5353e57d89eef5804d798637519d83795b5d2e9c38c61dda8ff2de50d4f5838223d20d9bcfd0fd0e881082ffe225b044bcb020a116b7e8545b63d0c956306098e52967b26cb7e0384053519331fa0abf134235b5fb3f8f540b6520f75dab9525c0d4bc046a46546820d91571522123fbcf8f4ef762e2fe8cddca417bf7e191aee05e31752981a811de03e8faff60dbcc0f98768baa5282586c412a7c1de05291a198c22e7a49649c3be95352a68167a7ef3a2aa6624ad3c4b70c136324ff38275d422d960d4a47a54e02912caeca1a30b9ca9c480d38bba65acb3fdd03e09f40c65dc6d13783eb2148fc50cf3ae694c42f097e06d9df1ba629118af03a321b972a6af9ace9c0bf902d8ada593c9e2642af4c5568c03643e6847a55f7272bc0ed198a90194e8436a0b58da32dc64ee3408034d9cbe66525f2c49be26a356d981a3272cd278513803a38bdd6e880300f6ad1de500ba63ad0ead86597ad6eb79c941f3644f7c736a941fe9c3798c97145973cf9aff08dda23d291de425e458105077c090a6c436d4c7826ae19778661814cc03e51836deecc44636035f8a8d1f353f94bff5a3bc01a9b917210ce6d8ee6ac27243668c30aa63ea2b18e3ade79f3ad6e90d5e5f216bdc9567f0bb8fd4eb4510c6078c67a4a720faab10e278da76542577edeb34575aef42025dac2aadee0a918f17f8f2f2c241a3cab3882dd72c74bc615d2e94beb7d55854046aa8377e4be48a0c8f24b113537d31a7e83b22dcfd35ae8d948c1bb162fb5ba47cc9744ed5c9472dfe396285d72cadaff73c1e582e5cfeb7413244f221f123d6f5b5c5dcbee603d9f7312444171d48499f282de4b3b929d6d0b697f71ad4b4f0f7f098b926221daea713a070f74e79056b0dacf04e1761b57151b6e07ff8b070df5867e3b590bf905e89f3c7d362492c9663487a7133cbf659b4fda61c2bac98e8064ae2196d67dc08c3cda352126d8b88b8ca89ff449d32f47d0da2aaa1c639f50189b6cd9b598cee46449d654ee79277e24ff091ce0b0a312d35775c9b64da043ffce21e6721d86fdd073349c8a12ceb9353984b2547af8645798f6304300c050fb7ffa3bcd82e3932d4bb4fca19455ec44fc6d8cdc2a14f8bc10ea62cb6a4e616fe2ca57bc2a8be2ec3045cebf7f800c8a29030c88b862c6f4ea85a986e3474fe114543d4d0db8e398c581867d5eea316f0928cc740c5b02f53a29fac34d1269507836a9f85c3609be0867dd738559a739afdc2d07cc2d7c7d234e8a37dd40742fc3b91a27281d76f0c887b692613857859e94038a11fca4d704da467c81bb1ea0f872ec659be203dae5002f84399d2bfadc797031e2e09ce04807d234d8c0d68d65562ac3d90e3f679cd9ddb1625da1f60023f31ca1f62f570f98c5886b0600a2a6ae829cab6ba61a447e7954c5640cee83c5a8404933eb1a95f6621fd20e0d67b690c765da1722fa77a07549e16750f30042d5a35464c327a979f2fd099c6d718c5924ea778dea1e844db0803f2a714f1535bc24b3984b58c914980805148cb62e9796de50d3b709cb4201ed211a45f094d35b1156c6e3cf8be705d6f4290e5b8ce17f105576ce8801f36ac6e92986fcd49582fd0f882788e4f6919fff1145d42dda525f57c93a0298263408898faf872e654cf4ae6babd97d69389df817cddec10008050f4d87fbfef4dbc842a0e1e200aa9d2fa2e0459ba0a3aca44af1efa7d7def9fe3eb7b92db20acb165361dc4b22750d34a2ae1ac5a74ebe23cd7029ddc1c452724309726b5bdfaa87b6338701bf29d99dabbecd3bde338b1e3790f3844537da268c357a90069549ff94557d9d633be69581b2ae9f3078351a3e32762d494e32d16dc173a68fb7aa3a3d7e0d0e80c34ee7b5ab8b173c82ce8ab9390738f11c6be502865f438487dfb7c9b1832f6e6df3687c42c59e7d8e1b33a89546ff7b633a5c541026cbf98ba2e56738e34f2882f45f0f5bf2ec8d2c404bf6284424ba33d788fd2d9f4a7d237aad788417d8f417a415466ab7e526264b7214834fb84226ccb1cf7d2aca573d6a2cec56ddfe9924a85cfd1b35e217abf6695cf467a65d3a3eed94c29488ee5a54da11783f3092ed4f4a86d640180c9b898b6dbad8f95d1156bdca574a33a922b98e06d2ceb8a97a78caa88cfd18242bccb97d3da15b0e3dfe0ca29efb2ce4443ff6262ad08d6dc9c9a10b912b848ce61f0c71959fc451c0580314e0613ea8d383f1cf4a266568f67f28fb51704bb82b3230173337c97cc9a63679630dbc08946f208150d683f36baba8e2ad0fa40a7db18029841f2a02a88c994134cdec08ddd902c2ec1113564f578103af43fa5423069b72bd7f59b013ae75e4310672700c25f7a653ea9a814484b9010ddee16d7412141178bd32ead662963803412da9b000eb98ba89760d5b0c372b00fd8de1b5706f189d44fd59cbf7f26c60898c156a9aaf3d47e20dd9e374222a4b052a90ba0ef250ca8cfb56fc04cacd9f7341bcd400323ce2e37dbc1091f6c8cd42eea6a9bcda68b4e96433f26c6ebdaea50d73c6cd5e1cc9d89f66ade892f45bb078d2bb4d19c28b6d9dda2af3e89d0ab472c8234cfd05a8625991c1fa10fddb7f12dfc6a882a6285ba93fb0a6f8479db50f2e9aa25081e167cac8f8ad8e025cc74c2c219938a6a6b381b0b5282188061c2c1c46912a368110439ac0d0a41981bc6762c8a4e526c38fa23eef95ea7883b6590e392a86c8d78d7151701d12d3b71789e35a2e61c0f27019048f9f53de608e94a1ba95d61907f7f17e9c87b2b88aaf9f738f95e68325371fd536e171de890654dd33cebf98e246b9e0e69e03cc02286891a38aa7d3af6056c07b48822525d36360502b99cc0760cb92b0ca84d259b3f876680e6244fe2aeacec78fe6ea104197ff6f9261b8bcbda34b33c85f9fb71bbbd9cc5438bf9c914308d623868aa4dd2507c22e4e01cee8a5cc402c385303b19dadef511f0fd843d1eae34023b43cc09d393a5b8d11215a335f41a4d95457bc2ae37981e5792ddd20476fc6e1ba59c008b6f0da15e67b8b1191744094ae57b9c568c68a61439dc5c62ef08dca117902e513b5305955beedc5e5969f13ddb0f733ba1342f53ea259fefcd8ec2044be1d6cc1b83b6e63df35eb2dd4430588d6c2d02668189d7cd7b9bed2540c92b22298ec1e41860caee32006a2624dcd0b0a03348360041efa35f013f257961ae911765ef7359602ace8e1908dcdea92cfb715599c773935411dc6576bc58621de95e308ff151e975bfa09894e03b5649a69f46d24a03550455c1c1666214177c0d2c3d8c20e99c980eafbc50e01acc76b62a28bd5e8df32040b5f10673400d26dc5c35069af6481fdd991eb8a041b90f6185f0ea375c0ca814c29652b68d4f9af97c8a838bec2871357c199d7337b7bf237fc8bd036c7d0c1060c646d10cc3113a31e07eec184ca29a0e0c6ada346d73c6f528ba529507823c989be3c7d08e9d4aa3a6976e6ecf0bef2f65fc185a7a1d1050c9a2688c34c932d9eebfac896dd3278363819496704792557943afff482e2be44e404ec2ed633fcafa7b776597a64d97bc56b73b8978088d851218964ef36b93af1cb23454e8515132d58f3a2f7daa8994f771822d239066dcda30763f98ef18b79937a30afa2844dbff9247302da9edd54fe619cd7fe5cbb29d5bfdac786f8e0bce195dce8b3942cd312ab4bfcfed1af8d4fc054e30c811fe03ca2b2784e56892aeace51c8ba886b4ac302f55fe126fa2e6e4b8e52716245b5d529cb1d3056ce7f4fc93abe6b58fe3c84ba38715300013959c93f717fc93d9a7a38c58f9dafa3f25108469d15ddfbc077e73a257d0f309c0fd3ab1b650e389e7d00f20e37299338673d52c2b0f8dee72ce67884e77acec0edd8dbe7f981bcdae0fe643b615341ac3aa09767553d77f0bc7741ed4087e5a03041e5b724aa93b4b6cfd28cc7e82d0ffacfad667f589073df06bd135cd00d55d68fd8950fe997b09bdc7dc4d90bea36b945f709d167fe4f40e8fbd6be9eeeee262d8b8fbb50052503403840737530b4eb4f9a96abf6b1372ffff794822043eff81d4ce61fdd7be47ad9edb1899a5c6526684fc68561a13b637db7af1443a2bd2872ac27afb2de89f8c3319bfb3f342411f1631f95b867a245628f6c38aeebc3d2f6bc49e96595c844a40afe65bbe1b0531fd33a6e2468b6348f988f0b70846e07d78f32a668d693ea3dff016da2d9540f3251a8b89368affc57d342d1032ef7841915b16c4768d95b77670d91a035023c05b93f080681835604ab93dbdb5c5a8c86cc856749d102e02fbafc494c28b79b899c17be7e6bd5cdcab2f23d2893c9469ee8c23378c9f4f695acbb43b105936591a7b7dcd0a436cd72f461c23970571fdbcb6384be7ac14bff951d70e0d627e0d5682a0077954a5c081492f814982dcec00a9c9f2ac87c88345bda6079218c194d95233853b322aa24116573d9566996af0db91ed7797baebce70126b26b8682e2da7646d0a66a375d8168b8b550d0c5618fad7f2cdfc50dd706b0e9ffeb94faf76523a3734f3d566792f5f501a5d29d93c42a7299a851139b8a50e13d5852c191b250295da2ed07bd4c2d53a2fd53cc84f8ea352e668d0de8f830f61b41b9ca8e905766e816eb2581b76960a4360955f59a04bf96ee5b47bba41eb7cd73414cb154dabf1f007790af951dc79add700d897005bd13e151c46a3a4715a523fa8bb10dc16fd13a474dadad37e239345f11e1609a485a8eda3385655ea6c29802184c30b391e183de48016f27753758ac69ced125b5dc390ece5695bcc8140437acc115f10e6df6aa8d7fb0ee34edf8a4b6e26d14e9dd91c836fbea08bf50f978c47c190ee1b3d4e164df00032898238363610a38c2619303a1c2a00ee84dace21c9e4eccd082694f96c8b8f55bc417d4a3a040ac1d349d5e790967e36e18a8ae51bfd7b47f68382cd0512a9e3754534414173396112e4de51a62cdd3a19a7504555affdb35252ecda828a282f9743ed2e2f086c9ba5bb3876a4318717a6cfa34b210b817b3492769b1b85a9351076ed4bd94d688d945cb195b5ed37b77a5747c5c078dc65d15198188a6c1df4dea677063fa8b0a10218d0f86ad17c436bff768988a5527b4f0505dfa295a0016a0e843d0eb135a31a9241cd9f686fac777dd00f6a29225ceb4afb9231c887179930129e0d78a6b491e8fb85df0c65f1b20b45dac097a93340e0cd7e12735d0dc68889cffe0c70e823d021824de50339689a8ebf0cf193684a51cc4a1b89d091edc39ccbed1972602e465cb27e65cea207bcd5445f5afef4a157135b24835b3fc50d2da7307bb82080f81b2f2ee568e02781c902065ee52cdc33c6bb7e97befc02c65434a2d76e09e486af7f777fc8466ce646123d96c4c70a702dcaf1325388724aec2effa05e020fe152548585b08b90780419a6c037fb9efb5c0b3d26e64cee05b53aa3cd9de907a24b7b69c10001832595fa464b3a3c5546d576e7853f869fbf343b27884e35a63ba63c67025fd0b26defbd3f99afe8bc17555f70d306de4d5a293b5bdd09d63d94358d0daf237886d3479b8bb54c3c64bc3f70ee22b071971f1e293561b60e7928c3a081169d15a83cebcfe6cc90f139265926cc4608522cc73ca8f1e0c8c5dcf15b8ce284614d9292472df1c6e6c55644124fadc1eb3ffd640abdf8d1ea21997f8a5b8913f323dd6597d61977d48f583b355166d9b32215ecc5e532cc616ab6c7d4cccdef69877d45c479924f9fe01e0a405ebd84b943f21720088b93bbda4bc10df69a31419862fc635006d70b8834be694797dd60a2d0a7d569f3e0a34b8c4a76e89a0d968b58355c64eb2c8da5884cc53bdb42008c649bb0a903bb9805a74a87d50ded5118996907e9c4eac1f08798c48758cb10376fae499f4203f82ca98ba8c3d8556908db478940ab30c44b4ecf0c0444667bb88d6411d01b9edb699896cafefa342c6dc4d65ebf08e20251d37b001c27d222cd5007144742ce12092f618dd3677324765a049484b403513e27b6e9cda013d174310fca12de45ab9542aa150152700ae5b860566658f2445a876f3a733ed32bec5becc32b1a4ae5c31b6260a72e642264f13d7e28273358b3d8e822c88044ad674a8169a62237175155109858388cfa897dc4c8770407c4cccca1bf7bc7506cc0e374dbd85f1c47031a47c6ac78131240f249973ff5571256ea813a3d20e18e81cd1a309b91a07110b3847076886ffdeb181df8dfd07851a173e381aefd3bbb297fb636e34b64620d0a85f3f5bdea2f44c7d759975a82d66352f224b357beb9f57172aed019432cb2d4aae03b0c8085a64a0ed4f6fbd1345a9698d0b65ffdecfcf39e8fa4707f55b0af4d6af9707dda0bc86144522fe8b2024dde6f86e580f67141ee95614f6ae96577bb8afa06ad8ed470327d01d5e106500ac314494dd6c32380a4179983fb5f93cd0e9565ddd2afb8b541011355fffca391939bfd4e154ebc75834f85d9035fc23c33240c628b97fda26fae787aa585894e9fc5bb00dcc41f802944b08a407b57e28cbd792c7b23e3ef791f713fa9f827bedb1552dae90b5ffa3285d0be4a814532369f2a96b789915ade54b9d0585550b60cca7539d1681f7a1b0a23d0525e65e1dfb59bd4058e6727b4c97350d2a107b615536d9849e7ab3129bddb8ca50cab26d1e1ecff1502643a939292a3725f2b556f5063251f258a01eec7524146f6577ed592b301f7dc545f17dc1e72140d77a7d0b2b3555b25c5adbbc874419fe1d2bd40e31bbfcf78d5ea9961c6d69c6c9de28c2af118cf73649f031da80d674138f63f598da4bd183b32c0a40f4c5b461985ef074c5882d2beaf823976e489f6bef8d0500f45853ee395e5ec1e44623c698d2e5880c8711a7ee85161cbca4952704dea3805c90ab7c467836630693df4166d000d5c4fd0d7d5da47b3419d8356777d74e55abe6da56196a0ed5c8e54a9e83394cc9724bc52d499b8c898483f6a09f26dd08c5639eca69f541fd5122f2b9d1880b522885406672761be011bc5ac9a8d7c5b083ba910916e20b2ed3bc8ff630b55d3642764fe613297a2ed3f76ca70d6e72392e4a018a2a54f87e9624d9eccd5e2acac1b782c1de2ed14fc2953c2c878372bb068205359975ee1cb7f454491fc5d25be953153a2b14b6d0202af7cb6b7acd17739619f526c0525ff22d4d98566f4b1717f7888ac4c1960cd3e0e792c1e4f768117bef387bcff5ac0d407c296a9f177a290d2b397a4c252f6d442820172de7646eea8d3976bad6923f9040c11bc24aac3aea650da423f30f5a2f1bcdfbe49f588614c452ebc18fb20fb81eacf0dbeb719da57d02708b7294412a9d813420f0d082b88b10b6f0f6a61a339066d367883bf805dbd6276bdb7a6b97230cb51f30aa250f3d8dcf761e1b3fb8ca57eb9d7c0f1d32d1068f2b0d2f7401b87c47a46ae9e2f711ad2d360d00873b92de96e8e18672aeb9ff6c938c7bc711c55701211efe931be163fdf9b4a9c3ecc9c091137b3f785902ee8c1cd657c4b66e04c9ebcd1e433a52c765afad6f2c87967f796fde54347e145ed4061e747543c9b305a345dd01cd42e5086ee97f8f73faffdba2a36ebb28b726dfef906de490021e9629ba847d31556b7ae3ccecba3cdce5214787c969c09d797baefca31806570320f14b4c1c561b2a9ada51aa32f802e793bacec077d8de7e06d1e3da6603383f239ab4fe9301eeaeee67025bc6de6159492d2a7b91126ce1be61b0637db9e2934fe3debd2ed04c789e04f46a4584c8d722f0d9d61e813a7a5ff20e3585d9902fa51d20945ac45a022b2a19c4680035b950abce7728fbcf5cabbd2ed50f783bd54ccf0f65aed06da48044bace53050b857c407b1945eb366231d85a7c50eac4910cc865ba96361921fb44a626b49644cab0b06a7974e622f85af83d59918578b019655863221cfb96e65691a374b84f4df69ce62b2c90e8df9e0aa560af32f75436a4556817967af2d244a521c4f92ab1396e28bac827e6e55178f8ce1c0b19a8ba28bb8c5bd3a4f42570389af775f6ecda0bdad6f74eea2963849ced86d7d06a9aa4e0f8734d30c1b9b5d4b24e42f2a95dc860aff5ef37fc276abbe7a43414a21e91461531fa078df9c41215356ae4ee8472affc16b696edec6841935458328d666b79e7d7175b4f5c17d658f506348e0b6f8af44024992278f47cf08f446ed4ba545094fc45a17dae3d96c5bf54d4d3c056ec4a730c244959f9660877c7e8a78df2b9db876e5d04db93ea7a3c814f9b2d7ab7bba472d35451322b4b88a90ac6bf2c4b8d9306866f928e64466cb1130ec6bd26872facbe7c86fbc224f8dd5c177930962e6e4bcbce221c822f20601b886ee7ac39b89df238c7ec2fea1a2fe0d22397537c04d96bd92e00011b4c317ae786f16d4697cdb11b92fc0fb77c0424fc25ce76b0a916e36ac587bd35dbad8904cae4265a551b6308ab83f10eba6dec5bafce68704ef617e10f48319beccc17e6846fdbf77f413e41ad918d33aea32c2017f0193aee6b9bc1cc9a417d8a1ef3335fff07c23cbde8ee7e4952964f70c032f9d0391a35259de6d6aad65f9525db971cb2cbcef334dbec2d2c95732f75558fb2ef04bb99ae9d3c4a674b6f4b29f7c80d1c88d1f02cadd3fc2a03acbd3dd9784a3d25a8d5d1bd0087044b624e5f585a9a4b2ba153352316f261aae4209d91ebccbc86b2ad0942a30dd44768177d9d1541a420fc47c66e84a4a76660d72e2a52fc143d0052bdc9d5b6488ec556aaedaac2613c7f31b7e0d1d5936f560533a0f9cfb7bdff18ec3ddd0b6a6f4e3fa2179859b834e5af7e0450bb776614a1bb03c502051c2598a32cf68c604e354f386f322549aa73f4b8a03d58bbff4ce2dfe9454d9d4389eb8fa8e0ba12540d3b144aa82f0dc1d906507180b49899d1d007a4f8e6501cd1a65fbdeda9b007c551a59063b88788b1384e114994c1f68cf3948899df99c0401a4c0ddb4a794ccb5ebaea877fcc53dfdd78b8bc41faf05a2adea2aeec5b11e060938e9ed772fd1919d64f6bf089df59f4ad926068df8dfb8e13863ca82631f75a825b2dff182d127dab53d1f0cb69a968b2cba950c02c7be53890bfffcd7443e01f2bb74e7d119fb646093090ef903f71c47eeba464185edf40e53206de25bc08e084f90ade8c2485bd562b1969372d2039c814f2be4bb222f215e98cd0a7c567c5735bcec9cb72077fa94516bd493c6ae887cfe74fc0c6eb1fa75f3e150172d181bc6bea02ed54e7fb100f4fccd2fe4ae1894d2833ad3f9e6fb3a5bfe703667e09053374bd45b71fa46b611d6cea440728766a4e3e176c17917958a4bc1144a03f0acd0e010e3272a135c539a8d94239a7917a96721acdd6a7041d7f709a7abf6ce3d9d2d5a20da4df857712381ee88c3d5dfbfc2cc5ab6e5924dd81750fb2005810fd200a9d6048f954abd44c9dd37b05b423500bf3794c8677714058fabb75066838198a557fd8ac9c333c8e6a41febd7d9bdce3e7b5b5f2337da32da64a805f5b96edc0183dbbdc46072559614891c29dfe6502993160c0cb90773158377f072e771300885221f3c94d7c5fabb6e0a4a59ac567d14bac9d5aa2f1f50b29e053f36bdf35652366483729f4a133f0980b21fb4b7a69956ff7376b0f1e71991f587402d0be549a237b699ed3e4b03d33099f893ee96b49ae983ed021cc3a977fc2a2236c8ea3cec6c3c9557c80577ee784d9fc082e6b21b306bfefb91b5e0edb3ef7b1536c4d5ac80b1d437869af4903c0788ba778ba1fcf7062cf8d9acb2af934b38a88e532f6474b72242e01a118e118e4ce3163614531416dcb5ca7373164012f594d25db938e4c12435d6313887efe3a41778526d642ce595b3b97a834ff0fe8988d1176d0d272fc82d962c61c8fef74499ee795786d4983f6731a1624dadef88c7697f629033798013b9a02af4a3775e12d5591ca3d49e10c32e8c8739cecf0391767da35de9ff6d54d0e203c0090bf39b78e97353b20268439dbd6990b136a57f43e0beaf5c17f8c0b66e3471c908dfcca3f180f2cf26341706f0ddc24027703ca799c3d0274d6ed279022c01d029ff3197093a82d81a6597960520fad548278acfa45de74e5629b05fbd2002f5daa0dc43524140ecff4aeda89537cb507161d83eadc2c52a6baa22c01874b057d26bdb83797793f41304a0769b7fdd30201a1309821d334317e0bddfa987c3f6a0aef90a539220572005dbf2dd73122977102776d4f93909fd73e734b41762900d14a038cb448bfe91c53b0b5df2e94da9b49e41dc7d9af8d69d0cea4581379106c7a399a81467b5775cbe7a92db95ad1bad091cd95640b014846a7b2c0bc7b97a672a21df791c111147bedd8397b225fce285ae27474e5890dcdd568afb7f781f239189b04200747e7952e01170ee926035317daaf9ce0cf2620f078f53a8b7890ab420112e16ceb7108d3e6cc5a43757524d2e1e125eeb903348398806c85946c0f20c47e2773992d88ce7eba0a7dc17e6b8520f16b023ec6a94150c0eb9dc78d9253f61faa3a55b6c596d5d6950f84df554809956d142decc745c7a072196745acc0dc617c0f55cbeef5bf98d0155580e8f7723f2dd17a0403ee866fd8a11ef727ef92e9b88125dc5f265dbc720c82891697a7469b10c00e0b2eaa57fd259fb1905e21e86fd1e32440dceca047f21771cf70ca2e39aeec323840dd4a6688085981472397beecace7a2ef1762a8cc48fd913af638d361269e22081be5f948b040d8c1b136f9ffa8662b02a7d10b951d2030a733b0210d52d343542e9cfb41b1af66fb43d8b02db01f7439776af4cbfebb1dff6e5ce963fd6f70dec299fc0f67ca32501127d16b79afe38de60ad90040d2a0ee4b4c552ba382ad29f3b3191c260d2063aa1e44db6338aa2fd6a7f24aeea376944b4b9a9e0b800d2b8fba5bcbd1264fff9f6b07065a3becbeed633f3d28d3aaede5163adad84b2c68294ed843bbf0d6e0f2d80637092334c09fb907ade1ed65e6f15920047f32774224b48e8fe4d8b626ef2d559add44a8e8ccbad667c37575510bd7b63bc2d23e1aa433c7d2542c710d1e5cabcd573e20e838b2573c99cb2f535dd51e6ef46cb2436732610df002ebe089be2a6dc9834308dd67542fca2d01d15d6e5319ba547f317e37515de90d8bc91380d6c1387839c4339bfe83fcd325f81d3c11c9726e2007d45ab00aaabdb0bf056ec837a26266c8c016a6875d786795ea5833c0b1e209eafdc079f62b44940002a24c3706361bc0f1c84c97d66a030cd6abef01dd04b1b09ba246242a36339d562ad062c57363f7f53cf09eee2819f85f3a4bd498c93e0d10bbaf781aea9cef820aac022185ab5c5fc39c0dedf381ecc58a6d758f3d3389c11f20a7e850ca17db92b86594615d7ecf7b9facbd21dfafe15dccafca77109bc28f40d81e8911852890960d1023ede7b4d5dae80f275100bf87e261170d302610c5c0bb538e3f0808b9e577b5b1e75fb48ba3605447c98ac9c7342dcd8619a9cb1410e18a57c2ab2bd2a908538a9caededf64d1c9929b2dfc02d4c8c4a336e469a5d0b3626e956efd73812d932268ae5097799414800d7b8b02134eac4ca66b9a2b8e39ec304897c7f566c8e23b12f2ed55635de35e4e1ed1ffedf8088c55cac9df2dc233c5c11c6e5fa39f07b6632fac9e87dfda54bf433c82899ca6c39b8508da380faefc73c39b3996660b59deb154acf9fe90b78f6b244d09927f383f4892daf59c7bf43cac3b4c21b64efaa0037ca42c7e65780816efc1b8124b30f8e481532a8a6300969dc1032bb76f7f02e6e33dd329aec8bf1a122b8d99763b971f935a74c966ee50d3bb2bc257c9729fbba1aa2841531802eea879e454d81bb1c968ce3e9de355bb20ef0653856ac01890059da9f367539143e5ac5742e08bec6e6f7c1a8871495a07e56571efd24b0b1cdb1a5e35e00d916c2421d566fb4b4ecbdc19c7697111737131f1c8788f731dae0069e2d3b608dc2ecc7422816630156b89cd83141524011b50dc513cc7864c728d3ade5dee1471219209cd6b2e17ca824fbde97973e3a5f26d4140e729bccccb761fb9ac2b207f7177f145c5d0765f2ed43476f969aa1b2be9362cffa465b228fae06e0ed23c8c0168c93950d6b800a68022675f916d5ded69fe457c9e7d56b7ebf95cad7d97966415a6fe6cc3ce2b967beb62d2aa34ca7d9dda52828fa9256c668263eac6704d9e5e6b62c0cc5394126717f98b12e64676173576be5a83fcc748b589ecf3e1c492e84a9a1bfe8e3fdfa41ef2820f4cc9e743d109e147e29e415975ea0bd6d087343b39067dc4ada9e7d61a2ea4c1df3b83686397abb9e8fe1093e5f08d283a00e46d01814346d41ae61e6001cabedbb3d4c5cdc5e4c38e8fdd6336710cc8b36ec10653b7665f3ce6d1b5e34b835dda0cb2b8e6799b07c1e5a3764bddb1d075309d4907e61e52e049fa57d365bc52a14b252e54d90ed79eaf83e6736f66787767b8fa6f7e583931581bf5dd115e7c47fbf9a625d9f0ce7bb817dafa9cce669056f2c11e0032624908c93811570f2c245ba61f6abcc15793420869c62a448ccc372ff0c0e98e528e71fd791e37464395e071ce23f6699fcf4a79f4c7ffbc341b8e950da3675094a7386693384ec6aeb71faaeea14e973888acde8f46be1b77184ce546570c9b6e07612c7c6e846c48038800b433c6bfd1ea8d250958d2c78daf0ad8b4c4efd85cab7600fca914a22fc2735ba3b7768825e54f20f06a500c31d8ca2a557ca209c2324deca382b27cdf9b4b7d0ea21ed164cd9003971a7de731726d7db35b3ed7daeedf8ab151e484504158cb2eb090644de8309f837bd8ef5a633253a75fed6399b880e911513ee8b3bf56251ea548150eb0ca77fcbefba3b41bbf868a97f0a5097466bb4963b24580640ec08fa10f666ee3591d9232606ec2e2e06a48fa6d1484eaaef5e7d64b8df9aa4aee033d5353de9e9053e6ceb9e1e59a4786169eaba14ad6be497f62053e6b15bbf749c3fd5289efecf4cad5e451ac16e1e082cc9114cfe31835c25b764a5f0b6c66a6e3d59603e3a503bdaff870ce3b8fb4a014d6e8dc7eb066a7f8ff0c077680fcccc09e03069c3ae99b05aeebba06b54590810e11c7109ee420a7b4da2e3cfc18aad0d3cca9faff93a1006d022a4da3f0799a85dcbc79d4cdbaf8d07996e2189a4a37768791a2b15a0d77afa56d0d12b1addfd49952cb7035832925b46859b3a726cd5b6da895ddf6b7977992eb9abd78746c6c4d034de1857408db162b0eaa521a4d8bc56323d67ce85735571fbfb830a76ac93154764596cfdeaf6a9eb37df0394e324c7896b328a349251f415dae7dab73df1c4b4c443db8afe5b8e56706982fa5843bb77460d8a229808aa085b07b0aeb4301bb213013db1a8ae76ee41edf189a9410c4de5fccca29d85e6fd6037d9a430837fff1c80c03f6b3b455a83c983b6289dd10ba05825f0b99b688c8abacd5eaa40bb104daecb06ed65ba25996fcbf0d545cd1c1f24361920fd8dedfa893a45fc7c7ab3a0ff3d29857c77d4af963033bf6ed5b3a14938654acf74743b597d01f25c31aa32c271945a46149fbdd819be1142ed959bb2f1f26c84219fa07e128b8aebd029efbab1a8962415928fce48f53cee74e8b0fc871ee49d52fffceec0a3182809332ca125a59893f540b693226816d9df294bbf8b470cc4e25f9ece111da040524329318bed4bb09ba07f77334f23e4334e23e2de9f3f34813ee2ea68b983eaaaccafa0e3d92e8ae4d507793d13a548cf6a25ed7524dca9c702c6fb68e97b3880861dac03a9ca1b63d5a7decb15e91958c7eaa8406291f39b4eae652d3ee22bb8ed9d2a9be9c97f393c5cf335d5b7775733df6f9adedff0e882a52f04079418fe0869157576e4f18cc7abcfa7fbc84629801b4b54af4dc3c2a9e475b5b4b2ab305ee0da237073df4e59ad3f11656912f3ae92574c07d41853244e91da13c98fd574c83fdde2b62a3e5a84d8904df8a21347ffcf9e1d3bf96c5d25c12087ef94977b91fbe6486d6efe219b5939a587643ebba464258d2ff91e34225182a3f8281d43244860d740693ef88013982f41b4f8d4c971fda2b2386a84afaa37c5357f6555aaaf03b5e85878d917961eda027dc339e621e7852b143f0616f91fc7cf60cc178a1abd95b5303f4dc6bccfc32302f5c6c8b1ae6e4a532f2647394b720a233d5eb863bbd4eb9e74be2f802f7daaa9f76cfc1d9d0ddd675d3bb790673105b4acc2c9a6f2e95f2ad01a42eb63480f6a37b716cc76a24627e56b3d48ae79d858b03e1d31cecdaa95aaf96822bf355dad141d51b5e65d8d93e4623e1b0e459d005e4516539ddbb80bfad45306f7079f1d52aec4535af26ffdb3c1f49e4b436b3e98dbee3d5eeb89b97d9eea318f903d291d286cf33eb88af7b7deecea0a0ae844142aad00a5cb6dd020cd5fa1adf890d21936a6c7add54005778e720e8002fce5ff01e891b97f00459d6e5c7995ba785b3300ec6dc65e90c5e0bb3f76abaad636e0e89fd8818a4dc647dd146b3f6b0a1c953c4cd9a8c98dfc502a733b0d8baacd9faab6dfcbfee2c82aba9bd0fa3922a8d8bf56979a460e63733c4834929d2c9d452e8b4e2e362a67ffacd43136a1086f3b1b1a91ef67b6f317b6b78a3d1f6d3c635444064faeb2183d591d0d042ab38ae354ef382bb44f8f97cdc3e02292d0e02ffd5111b39467570dcd5d7529ee20dbc33ad51cc9bee34767d9ccd642e846a896883cb79a9073b57f7bd72a3d23c2b9b98de0834d9a6b650ae902c1511276880a6265c5dca59423bb3079b593df17ee4b04cec8aaa44251ddbe150eb9e54a193d3164054c74674f037a7448d51bbd693cff4c42d160fcdfa9400ef8d6bd6647eb586306aa19e3d94979ed8a539f6a75cb5bf6942c9a22db8f752b6a743602ea946373eb0f6cf22c12851a56a6977b35a1334a1350cb4822c6b12de1d785a2319146e479d71fdde15958faae8a73f8cb2650818becc765d79250ff1f07a3c87005d035bdcc959a62aedd59d26a29f23a580a98af17e8c80ded13d12acb47ef78ca7b4d36e426afffa7c3a75724330e1d41be5a9e829ec1c284c50197210b3053a3241dbfbe737a906c4f426366c6417693ea6c118bf699297bfeec84738acc3bbc26da53f6e102c1c1d3d688c732882b4f9a363e5b01d21913d2d7cd6dfe8ae667a4b4b6c9e5c78bc2ccceed96697c8d48434e8fb6b44006eac75fd65ac755386c6263c77a27f12002bb829e611c14ace578c2c563bb5df4c3dea110a8684c617a0533ce3539b8da44f5faf6a43d14e7ce71a31537817590aea1aedbf74e1cb6b2b3caf6b9297437d9c0974ab78d286e1bf6e4d63553c0df68bc11c81c8edccf989e4440f3de37cfabb2e84605954835f89512055281aa2cc60bafcea2de151aad2045b96c2fa77a2a2f1b95e13d088da1566567b7e5f24539988a27337b37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
-				"01dd2f9b3a9e6b46a98da3f7dbeb4e6bc7fd9629403c30577e876f915fecb6b33450635c5e6fb308eaf1c7890e4761cabb79a373a4f7b67284a04330696a51015cd7cee070559cfc966dd65c9244382b4ef40da2f7a4d82d82583d95deeddb950014b74ed8618350f8303c32a0264b526e679570707c15d9f989773f62957e5aa1be13806c592b08807eb7781bd8ccab5766e56a2fb3458b67595d36f22e25adc0f0724fd0577af295ed997e1afb9013b0f940740c1a2567e9cd78fcfa8743e8a5ccf170a3e4b4e56cc551a3a69762bd11cc981b65c65d7908086f8e07d0ec8ab44a58106f0d064fcdd0f98fae905f79629b2a76e502cee978c0eed94facaf2cdc3eb06d350363287ba2771ce0dccce6bf7607238152301bf37567c8665b50dab15c62ce50a8a32743ef67d68ac95a9cc48e9d8b5e1651ce02d94ddfbe735a9820fa3570181fe11dd350151beecbf49fed2be0c3d3a83455a38440e71570e2385bf1dedf4e0d14b3f4139f78aa65b6e8f32a3bd5afdaec4037a819b29c57cf5a4ec4db5d5db9f9895fd5de1564b089d4de5a0553b0ad357d41ade477b42025d1833e38703803085965cc8b108ede6704d85d0aab7a332b6f2670e4e63a8b524e4f03ea1f813e26774d00b87237edc6b454a416da4e1b55436493ef16a9e8a80acdd218b48e7a7de565fae8c3eaa25b50c0a3e7bc4ebd28a4ef3c5ead159c1cbe08b028b11149a0e9e655bed09002f6bbc2a33dcc61406d452985c10e3f516c562978033872e0aaa31874d435d2181d7b256a9485d785f67275bc482ca7964a9f5e98bef74e3380e193129b6ab7276844397b8ecbf47225d0b8067cc1e51f1fee76fcf3dcf5144f70de7651bf7e7ddf0eaf2d4b0bc01b9b661af8559b6e4e2b56dfe1094f415ab3594ec171d9f23de1177598cb7f1b3934f84456b45b41622329d7ff0c8dc9582bd88253376f0675a702ef6897278647f39af7e936e9c887dd476c3b62ba4cbf5066e4a4bf49384910180e948646b79059d52884c8a80450ed3c45cd785a041eef5722d04c8a675f22d06ea05627902e598048e013a4cec723874239ade00b0bd884c7bf7bfc5df2cc45ee859ccc4ca6456506b7d347b60ee1153e90e74a005310a303c7990d8e268816bdabacc26632b353419afe7053f0bf6de2dc900ab5b3e4d0d11a459a395b6ec9b02d634ace8df35257a7ff24f011ad39094864755a016cddfd6391d267dd7761015b055add9a3e7d20e415367666f5130a9df4ff0ed9b2e1da5a2ac922c54e18e56da2478576d9a36f6dd37c0b90729d5082033cd9bcabe67aa5d0751af9686baf80b537e699b53f88a95f98d787fa1e46a0d71d048b31afb0255e7f39872bf42dc8a331df5557899bdce1d0f12e1ee5b80355fdc738438a56bd68a26617db53a0980d67469b4068e7a1727409ea8a50983a3394890f4891a9ac4e126bfecb8be5dc285a86f1f7715bc214e716c5094c5cbd79ccc01eafb11d51bd83069f2479a20c5c939f839976b0395bc939e161cc24feaa44df0ddb23ef22f1c2fd7312bd39bf87839ea59e794883245fa2b1ce8a811de6313ff9e060cc389d98d0fbfd926192eba49e92b4f5be7c5292f37e2ad0476f7645367bf99dbea078f1c88d39e11cbea2a581d74df841ba456f8132bff3e6e69cebac4308b4ced30a33b3156a105b4c340e7ede5e33bea00fc2aa37ad48f5581cd17e74781c2d746dd33a49abc9ed52b2ff020381eee04fd9babb0af875ed80944880456d0bc700a717543a068639f464f2558febb47ab52d1329e08ca4e7dbd8ae67cabb8088d6d669542c0eb031287b6403dd20dd1e1b88aa0dd6709bd2c8bb51d026270e3da08df9bbd376615b403adf25b8cbf1144e15a02b6fedc043846ebc31f759b272699a867883391b33cbdeed53c04c7eb1298729f25f91482d09fc05fafa69b208b1625739ae3864adbb1e5953866876ba1e10cbf350420717bc22263706f2e33a7f8ab22e004e45ac10aa4a9c983e931f8d201985e7fd12a40235cda42e4f0e417f610bdab1871fc6624c2d527fb1f5eaebe75c576c4eb3ddbf71844f81f28afb7c91eaff940f469f8c30fe89d3ae8c23c377defc0f98dceac6a4e6975cd03b95861fd1355ac8fa4318f3e2cdf5606a8dd04f38c27b7d8794faee39346b6376c43a2d2c62cf01c11650878cca2001c2ec1857338b7333c102d1f2a74fba8eb432c8065dd219a92f5f222857813eb4cc82058fb38d7baf00c45cfbfbaae0328cf9cd7ece7be2699d15971683edf4f21475b2feb215e4676bb1cadfe48054c7961eb016132d9538926db6138dc73ed3ebb6a4d797a8f267609c9448b199136a61deae6a114ac71221e04052c9d840a84a59db18bd2fe91492a923ec442452b571838c05f8a09649762450d9cabe8b273464b5ca95e8bc95456a9bc9557e30613dc8d14368c98333cd77c0a33c816e93d79ed5a5e95bcb40f619db273333210ce8723507f0daa465ab0e01a12ec46a0df57178796e10af04a00c251e96f84ed137e81af57b647b7712127f6e03aefeec8bac9995ac1f9bc737ce92ee12d97333d1b083503cbe6d9783c708b0ed5b02312c1bfbbf4e32fde1d5cec9d8b2fecd8f4478874b8a7fb6147b3f74aa6dce7fe07040c1e2b7d3dfae37209f727969ce700cfbd2a9de4d85a3c79c641ab47581a80d6d10fbf4180e9ba656f885a49e7ec2058af7797474ca244269036bfb51c148a21bf1b4ba1464a69595cad7444b9b0bceb1181e1f9d543ce126cb707ea9991c98ae203daaa07d6f7bafae26db8c71ddcae1fbf0d00d52f22a7a11797fe24e62f6e43e8d9ef46e1be869cfff5542053b12c3ba183cd8785880e32cf99e48309ffcdc3b792a9e031c18a5ef137ff378e36313c1dc239bdc8eb978cc724ba513ac40a0b27684138265bc6f37412c46f188b8fd2698a9626fbeb2bd7517e3c2fc52bce52e76ca73e02e2364d2b00136180514aeb81363ccf51e4ee1a17d2c5f8e57ed1f510764565edb06b53a6332d67ff0250703a7d199a2c12a44d06cc27d1fc9b6d11bc3aebb153a032764f1a0bd664600e595e2c274b86b4247dbd182700f65910ad1b07df8e7097887269c1a8731f8489433ea83df61a800dc48d0eea8a00a12e3f1f31c155a7ac75bf39f7b4ab92bf3697a53062c9d8ee5a3510da10d8704858589a26bc64b2af69234a595e3725e838cac046cbc06fb80372f7e47afbb2468cf6dbd44ce2b3304b7b815d7ca8222966094d23ea394c1ee58e0fe88bb1a8d75f4de0575df43a2a9489d095cba97a138e7b7f811e325cbeeacef1c75cb97d6141ed825b3963334635a1ea04cac48faa76b016ee95227f090dde64f3d26ece554fc17bb20f212e7035ad243fcb1ae2d08ecb47fa3eafa000a30985688838c2d2bbaa46111db8bc0a4811af01a07e3b6596304e21a09bd399786b38790c1a62c4cd1be5808e1869a794b899c9cec46a3503a06f5d3cd9a17788c7147d1b9ab562bf6d947c8458a215792e9b8fb0fc407b08bb176ad3383720cc9be5fead4956cc83304f8f168183d5b15c56101011d716d92ed230a4b4f74e9f1f2193587bd8f99a8dc6ca95d95c568bfadca8207e4aa0b59bb95dadf3992c26690959cad69928a4ed827339dd266143f85cff2559e8b14c8db0f71b61416184277227cb45bb0d4f7af04556c64b08751103c1ff5d1256454349579df44fdee0b6c1af59cc73632528d11c7a3103292901cc248bf73c1a7eb623a5ebc6036673aaf95a7c8589df6402d81993ec97fdd2086999d8dc15e3d99113b27a8422d9084c77060261395ec1dad38f3f18f6bc688bc6fb4b3b442932ce7727be7130921f9adf5d177358686bcfa854e90683d0ad37c413bb52cf70375eda43a3cee589a3e84c65cbf4aec088d4d585c3665e12b37b5be0c0afcd0bc52faf10a7c853e93fe1d9bfba68dd4cf98fe0d5d8dcac15797ed172d8692343e8bf79195e0e5879ce468c604ce9ff765ba6c86c1653cdf3dec8d06442665bdacf55835f9c832d4ed06135f3d41d7ee8cd770dda6646a7a7ef5189a237600d3964e5a728824417588405e63283f97ebeb17fe49f64b253c18743d67946ee18ef5ff96f1e3c55a165dd8f373464449464c447318b6bc95f2efcb15d1a627cca17c68cb0e3d7efaa38cf66fd562cecd3c63cbee3fbd462e281e1aa2bcd775931e88e991844785af12d7d327a7383ff6bdde4f91ad31c9af84e153b9a0d0028f01c8a8d478a8d0bab5431e4c535b5251bcd9bd2f3d2fe6e2263e433979e867ad2f35c836d813e083487de56e9522d201c6c6570a94113b760b9bbcbc95bfe3de92344076ca89ce80b0f32fd26ebed3f47a5027e6b8aeb40c82c4affeceaec24b6f7993ab89dcbf7cfb83fecc211dff0cc0115b2af6a30049e7b97d211a271def8f6925d146c43cc21db3e6942274c2bbff786be3ce85f292b8bea2830f9d052ce67c4decb39ca3c051b1bb004579aa4497b582e76b81168222918c9ae0dfecdbeda50d9674783cb50df55df4729d436697932f51f878db57e62b612f0bf631674096e0c7988695f8f6ffad459cc305263fd38604290d57127f8f0533305423a793c992230dda9d122a97c974c3d60a96b039189633bbcaecf3db47bf6c39742ddaa37e0ab91a281d211957b3c0f9738ec90e2785202a9023b494f42480be4e81299cc75e2d560415378646cbb2d3641ed73a8b886c8909b13819aedd540b46ac97abea7e87dc122a1da614eaad8163f16dbee37bb881ef1fd4cd0287316f130b5cfd65028cf2f7ef9f4ba7d15afc3192444afea93c35542309892d98208f61998684768a11fc771a3c7dfd544fc81a5c33352f01f763d920ba287d7f3e3c57ec6b7ef78467e2efea39fc7dfe0a1b3d706751e157b0e183fdbc1c6684793c64d9bc8b5bb74ba345aa4b003a230923b648a21bf046df84e0075659f6b2d08f4cdbfa2e3b4088d00d49c6a3dde69fbebb909ff55f80bbf0f22846678481c013e0e851ef6323b176dea50e629d622b5c3f88c9fb161a34fb0a4da1358b0fbcad268f4f71ad6e294dd468fcc7f574f2087a748d34b416ba2002ccf3ebddfc45942fb8577def9624ddf80e880ace925d4b2c24e4bc64d34fec4b6fcbb093e3325b25360369a97efe4d76eb919f14466b2d421aab0c6bf49813c7a16633750439ec5fe14f39f4c86fdc47b0edc5480ab32e81e3f289d16206c6c8d496736792c6aeb3b5e5cd8c02454542300a6c03ff56cb0905b98834e082095b0728e9ff9519e5158759a329155bfb19760c1ad8c2d6260100e58cee0f1ac78e3ee3ff5bd104dd96ec6a690ea34785f0005c5637c3c3d0ccff229d8d1c444394f876a56a6385e03e502e3b5a0351d718fb4771f878020bec35c17860aa8566a7e7dffd63e1b25df90e0994721a41e040c89f80c4bf131222e6d3d0371f67115d0fd446da933b31fde347d4d85b82bfc87f1e742af9652a453f01d8849fee7705c9da3d5570aa4286a87f17ca27e748f8c114d1601661766a8b9c5c712102f747c66a8e1876406a26d9c3a9650684c8865de060dead429ec278e302cd2de2f103faa7ad31acdfd4ae3d3083bcc94f611247fcf6b53b6d5c4b4c0a1b527e84d0171315c1ea8247575fe6b0774b7e006f4bf3cecb09600e2cdc59b0d33b2445065d5778f648077ce17a736993a15c2fb3c1c6d1e7f697081858ce2e8df63a6be0d0923ddc9daebed22990477e09d57c18566d7f7bd43779c3bd14345121cd0be58c32774dc85a75d42686111c438908b982071424719999fb5088e05ff90035dcd69140a1289a1fd3bdd58d95a026ae16de49cb35c379fad7d38e730f2555ed790bd6a8e6e93ccaf74c5be15e95e0c352279ce9f7c814afecaf2560d93e169b3aa58ba4bf3d9ffce11c72b8ca98b38ee3c06d1ab4c633de5522f94efffc43e06cb090e119b76eb568cf15471c4701b68f426b19ae24a5917edbf1a4a1b0c583673b0ff7f98be901f17001f8aebb609850051799674f079d4397627fc65e5d30590592d5d337c91f713607d47324f95d32955861b5aa1c6a9789ef75c412f27173891e26a4d418bdcfa3b6fc9adfd53db14d74885f3c4ca234f48fee094fe220d795d693ff44f2c2d056a19d489951214bbf6b68604edc2bb3e7f8e1e871f5dcead73a9e4d452b8861b93db30ee372e11afd64fc40426ad84c439ad3eb66313d14917434857a4e14d6d51127d8180126cdc2a35c1ba658b5e4a953ef304eaa3500386c07678eed85a934458d469ba8b48ef4ccdfb865181fc087686fd2949374e2eb5dbc1518437b2f60c2c0061619afe3db0b4e6bdee6ba3966b67160180baaf2ba58fde0d1f0631375f028daa91a63d87b37513c9173d74440de4ea96dbdf332d67cf462cc532dba85f9bea52816f53c47d0aaeba206b32f21bb157d571f17ecbbdf5ddd0499d34816fda112852963433524631620df12c6b264fb53003ace63fa23880eafdbb7cd71c6b31a0979f683982bcd570314ebafc646c32f715c033e7c628b91fa4ae24406df0bfb4cf48f98fc5fdfdcc65b2db6ebb2e19d6b5b26144beb035ba5198c8223306f5310da1ddaf5a469209e3f11d6250f5e66d1b408d2515ccda5c68b691c16c2bb78483806fb4c1714ae5ed569202414b2297816ef1cde61478b75e82328acc76687fe6453f97611f609ae23f1956dfc83d44eb397de1c62ee7e3e222e87a3aec0881f479c87266ebd64b0b7c0e2a50b8650599f031f546b84c3160a522dae2c8b2b49d0aca76eac937b58cc4d0e3e935961d072c94c63a3e11b39c3409a49f8e9f7d3edf83dcc538352a38a348d2cd01a78412be64c46a12f67e1d41d50b60227c37c8c0043f798c87800afc85892710336172c40aa01c69baa4a20305751de64f4c77c0e4cfe2a360c61aba9fb0cd40339b7db00d3dbce9b5838c4b435fee5f30531d0eecd695aedd7156365fe3af959e36f2aa040504503560019a0c16361c233cb8de21449fc851b59752a1e32537402c38550204144028feb4078c967557877b0ad6e6adedbdc3b366cf1082e18d83f6b5615ceffd5359cb793c04b51afce3510e55333bf3c583328a2485a7a0ad65f64fc88cb40902e02ef437e112e3d0d54a27d2d08390bcbc62abfbf1dab8ffd98f954de815218ea92c9cd9c93bf82d50cd1dbcf2d5cc07c21c126e93d67c9567431bec659e54cf95c2ce95a5a692f746a3778bf30986970697beb52a250179dffe83809ba2278fdb1ddca4b57b0a316d9982d62a85e3d33ad38ce7f47f9f397266c064fcc2ee39623e67faf48877821922e2676caa147a364d1c22d5643c38876ab1753b53ad37d25893bd20e294971ffa528e16cf742abc3702780dcc01f949583f3437ba10bedd1a98be015bb17673eec5d40469c9ad1558e688fe5439f3943040ed37d8d1e21cb32a7e56edc06b83cd2d4a563fc142ccefd9590905240bacb9a552d6b777f8295737fe91f55c90540195d8f89777b9e07935e248fa7fcb7c2066bf748934b4bfc12f4d7d95a628984d27215bd5a477175347d0c90378e01d8497dad86558d45f8a458e96db26e35454e7e9002e47354d859ec45d5643969d94dfa8e0a74bb9457576c096135fae2b81efb054b7a75f856b87a43c6806d76fe2327a0abe2d42ce4855c2cf6b2b3fdd43cffb28d41a719d24e72162752139bb1ab00fcb6d28038c2994e7b8e8ac6730399b7acdc47bded773bd2cc15e60ce06601b21455bb4c0e8ecd02116c6d8403f43ebc2017fa421cc2863391be4d2a8fb545fdaafe6f80d7743157f3bd68ffb3dd3a82c7a08d29f5fe19d657d7495f3f7aed07a67444f2fd863c1af0d399de5bc3a71c1e6a211df69be3088fcd73998435a63611618ac4445ebb99f5df5daf02b45ee585c62ade53de945f3fd73d26c094aa1c3f0d0b19b0273ab3f0eb3d8a21639e4e7d6e99f63039639804c0bb014b1e6a83a522b274eaaf5f8d1921eb88720809d04a8fe91e241e2c030185b151e4fa56cd52aa2490b3891410f503646e362162fe619e942dee9d8fb11b601dae644bfa894c809cf205a58fa4ac7ce5e472a05668f696240cf81acc7be6a1ed1934dbeed1e6fa50f41685e413a6e22768169644be1e2419e78ab0922d502934c5aef5a9212252c4c0bd46ebddade402dffe970d8aecc535a102b5e8a01b1689f96e5cff5b9adb9104d88a7d55c8a694eccef50dd58a662686a4e76e8724c133363a46e0cdbce85cde10adc415c1329fe366139fbb6e47fb3b37dc6bd030067d0eaca26d16ab22a8b2592f08bcc0451f3b848fd58576091e914881b82efeb0c57f85541f26e38f8e611f733e3b4bfbb02fde7fc43ddc05144be25e32108bbf90631f3b4e88c5df91fca2ab63db007eae705cb2e20b6039758f5478dadaa955d985c67444f8cadefee4d7929ee5109a6ba149ae48f166631848a2f1617bb1bc0e88da0308f8b2d6abb6c8a0c6f4dec77558477a85f207516b01d12ef9890506211e6d116d8ebb17c85b9e8023f3e56659d046f410fc4091a5a37214f551cc04f4f093c5f4284e441dbb8e7f96aa8be39c40fc9332c3e7be5f9cad90320d018028d1df0554b92ee92268307f905d43362ba5702b074e6b213e604656cd7d7d57ab6a9ac0c4a9c9fbf0df4355ca75a0bd314933ffcd04ce8a9fa812a78ddfffbbd8240610a55f9a59222ed6a00d159c388783c07f1fee79a77abbbb02649128acf21abed0ad0fd8f3ede2c52b5316082429ba473b331a27c6db7e34c0bb81385c10e623d7e5865a8d3565547b3619621edf06d2b58b8b3d56c2dda975fedd0fbbb76f2dd6da507b4a04b13c8bdcbe00e987c2355711a47113bf7ff9de8fcc75f852b1939f858e9158ddace967bbadf9ad04ace7d93cefe42473143c13a71c31ad0e95dbcd096e27699c9a969cf5d5856ac16b1dba553b68a8a13e243c62709cf9d14350ee92ff10a62be843dc894f8a901b38882066a0acf23338da38eba33012e287e2cc0010d4f8d07364bc75815ae49b0e2094ae26f2af67b0502ca11041b051de6f631aeb9c7105074db5562560ba830a2070d4a596fbb30632f221f59af16fbb017bc87427ceb24c5e1f6696ea423b98dd632db93f00c16d0b80211bd9a49edb23b79f039296aa6e0a8db61d9dadb06644e335670b0e74f51f786ab889dfbc2e329f214efd9b26a97b45987bffc81ba39084aaefdede24903ac6a77cab13438b8c895e27213f0b183de71b558ce6e05a269bbdbff6934a11898501e803ce1f0286133cec3d09482fba75c0b26d73af5275139d460e3657ba019ad9f565ed1324ad62a8c1cdfe6f925318096a9708f2b17ddc3503f86baadfa6d75725b4f54cfa34c714a4311ee5f1928505724665715ecb8836b4a5314192f490844658f7352568e83cd02125e7c589807f01aecbc8e33c38a19587f3c03db1daca9690ab6f806a096956b132b4f2d0074723563c7fd789dae10314b14cd9de8a0a78d196651cd9bcf97cb840e6893a8cabe4aa5b0eb2c74b885ab7a64bec0b4e18219143fc1a59529b954adcc6082a199fe2e48395e21c5ce5535c8f9bf3ef72d2f0f89b6e338e338b8856757019ca35ca017761020b98b61b8a513f2cbf0f2e29aa04502de69a7d2eb78fb828599a2c9d11bff59d539ee43316e68227fc66fa6dbb8cde0066b44ad77aaa9486a733e79b6de689e71c7a5f7497743a267cb0e5bcefa0b359e44e9f2ff0d9b447871fd26983f188395355030f2d176f420a94caa5ba283e53cb40ab787a6160d4157e38f2904599f7429deb9006584c9b929eb99711c5087ba7443d36a9e2bd8d2415f495bc5194fa59f16773883e144f84ce8483c85b1a3120a15c82b7b83558c5ad244fa48bdd7790a6da5c11765891b5da14391575a9f533acd8e3bc5b72ec3ab18fa4b775802150652d0d710f0c8b03e63881b00037d26163f38d4fd2e49154ea7c8cef79c51432ee0a9bb561689aa252d7f49bfcced83101bda4cbaf702b4d6dc6fc390b8657dfe2b8f36db4adfef7cef9b3760fd522f154f68889d6cb04dc399223df40ebba5e40167ec60e6ebf0b75bebb0df2368c7c7003deb3c1841796ebc178aa8ad6a34601edf623054680465e9bbebea2b5d80e473f40459fee240a73ee58fde1883f2436951dbf83f24ef567582fbcec21e5b7d02471fa9cab4a7868862f746781df633fe91a6a2d31bba32667c9e2b92d7f806c4de8ccb1b565aa3b0db9d94fc0d9408ba435c4a151d6a3eb8f7e30bc5489ee34dac617f1d446caf2e13e662aee6e16ae7354773d1f5d5aedafa5ef517186840e5ab906e9f564a479f45c75867e1d7fda7ffaf0fa29fd3e784f471b6ebc1a0f16208fc0c96ee886955acf40d8335def23cf48b9be02b14db0c8d515e95984173f11137d096ba01c58c0d6520dd553e1f6a46855980dccbf868b44cfdae37fabe9c2b1e0ca078ca2bbc6a1ea37615c1f141aca739a16665ef359a786a9c9c2533c763a441a1c77f4e58ef2b528a5a6ab21c2c88ce7dd85de0e6ce5ee3e71656ebf5a5b2cea07f35ccf872a037af6013576a814561353a666f11ba298b0206247dfa5488212268e846a66d6e277be652c013134338f064ab008f6b943b9e95183f6d84c545e4c5c2340cc4513c28bc40a3bcc41eea5a779f8d5585fe65377283dd152710b80f363499668c2283f26b840ca8265e2adf07416003a46d80e769c4dd8f786a5703d7efbf2549e61dbf71a9d722e7af8ed20e8f2446fe5879f2571758df53e66778991fb1f5fae534edd006eaab3548f70c7eb87573ddd9348235f3289cd452a6f3ffada397f1e787db77094e5c13f8be924de471863260dc54f8ca784000fa5f88dacf2ee8e0d237a6dbfbc4d2eb67293e2c26dd07114f4dd99a64cdd3c91f0b998b52087409ca442e3e0784eab4f0f2f256aaed526579a97c3ff87c5724475463ca117108879796b7b7ab903edd5ff82c1f14f371649c9bbcac5e32497cf9703032d5559045124880d9c246905c312b92993ac12f6f030bc90e6d309286509b06c3800c78157b708c0603e2be48015dbe6956f3d83e940eb97e5939c0e3e2e2e7d2e657eeaee23d54f4d849fb8227cea5a3301355b7867790c4f54ed36a3e143e466d18e3f3e4ca8b092a5c8c8f9f226c4d636bfc7728e0c1d41d206669fb3dcb1aa19db6b56de01befff4c6227764a0e9aa1949469fea46ab56e5acf340af4ab62fb05cfd87c3893177b77a1296a94e7a7069a690d6a48fa6940a8b3c405564d0dfdd4b8ab56e29acaae09e2cefcb4276226f4a2fc664a61b5d4aa76d0e37626c93edf16ed86b474779b9b33beb49db1b211d17244d23408adecdbf8aabf2152bd3b51f2150ca5542205965c4610d2571d5f566635529e755fd4b1a4aa033007a5f9676694fed2e6dc71ced63be0c39e3beb237da3b7f1463691942ede74158e2c03bb8c13580a609769bf082ad9256f385b131e2eec5cbde0ce761ba1db2d45bcf7b9ceffb2a488073b10c7ae01717f76efbab5e3a57cdfc45e582e2a61fa23e889a9b80ea31951ab9d0cd185f98404324f55a04295050bb6859778fdd9ee5ec1306d8bd3e97a4b5249e2add51836da186b6f43c7bd00da34d3032dad4dd6983f40adb7433b9b738b37ae3b1797273ccc8553a0429216fb803059b21a73173e87438b90f01450cb7066a630ba34f5a4fac6f565e95d1627285a38a3f4a2958020f636e5e1c2c9dae1c8bd2dfc5f94606fc8c197719ecaa5c809215dcf64d02fa0e544f575666b9ad3072d93c076cc60bbdab9990c49732241bfc2c2655a2ba4eca53985a89a995497e2c9a63ddbc7c292ac1d766e6dcd5d6b22ff967113a279ab42694bf7a01ea8df30eab7511edb677452a844a90608cc99445db0c1d0a3e32da45245dc7e51eba883fbbde7e7a3317567995d7a079d2773ae79f3d65d6e15aa7b317481bd16ec25e8a903dbc643504532b06240206557be7629f6cc793a7278d1bb209a2222368e33c3315532dc2e3418ac5b660249620e31e8ebd7d2404ffd9111ee4b8f7fbf58b048b3bd68f2c5cf5e2cf759a69dce3dc4162cc1a722d94dba16cb81c511f686220f92e3d78f2018c85f35b21b0057c9dde2cafee32977f707719c42902aeec54ca6a909477958c2740083f1c84f923b1a539c7b6232c5898b248af67a5763c07dd934574af80e2e67043b7f03877d65a864182439644fc6d08a5ab7679283f72d0941033ace6e57b7088ec3e74f1d20c23f030879782b10e26ed41816be1bafee4089c3ecb1e363033f9e5e374967b94bd9d069844286164cb7c2e437f412c52456bb17446e07cd74fbf4d1dc1122c84a5a8204567b341313ea565957bf7741036ac7f77f4fcb51a070fbd1a72d27215f00b53a3eb228c8af66bf172d9ddd251a181d4e7427e231a3e0a655da22f252baf974f79cf913f11c7f81e9b8b8e35c4b2485212d43c8b5828e8eaa2ec6c5f9af29c95876b360bb0f900bfdcb03b7665f3ce6d1b5e34b835dda0cb2b8e6799b07c1e5a3764bddb1d075309d4907e61e52e049fa57d365bc52a14b252e54d90ed79eaf83e6736f66787767b8fa6f7e583931581bf5dd115e7c47fbf9a625d9f0ce7bb817dafa9cce669056f2c11e0032624908c93811570f2c245ba61f6abcc15793420869c62a448ccc372ff0c0e98e528e71fd791e37464395e071ce23f6699fcf4a79f4c7ffbc341b8e950da3675094a7386693384ec6aeb71faaeea14e973888acde8f46be1b77184ce546570c9b6e07612c7c6e846c48038800b433c6bfd1ea8d250958d2c78daf0ad8b4c4efd85cab7600fca914a22fc2735ba3b7768825e54f20f06a500c31d8ca2a557ca209c2324deca382b27cdf9b4b7d0ea21ed164cd9003971a7de731726d7db35b3ed7daeedf8ab151e484504158cb2eb090644de8309f837bd8ef5a633253a75fed6399b880e911513ee8b3bf56251ea548150eb0ca77fcbefba3b41bbf868a97f0a5097466bb4963b24580640ec08fa10f666ee3591d9232606ec2e2e06a48fa6d1484eaaef5e7d64b8df9aa4aee033d5353de9e9053e6ceb9e1e59a4786169eaba14ad6be497f62053e6b15bbf749c3fd5289efecf4cad5e451ac16e1e082cc9114cfe31835c25b764a5f0b6c66a6e3d59603e3a503bdaff870ce3b8fb4a014d6e8dc7eb066a7f8ff0c077680fcccc09e03069c3ae99b05aeebba06b54590810e11c7109ee420a7b4da2e3cfc18aad0d3cca9faff93a1006d022a4da3f0799a85dcbc79d4cdbaf8d07996e2189a4a37768791a2b15a0d77afa56d0d12b1addfd49952cb7035832925b46859b3a726cd5b6da895ddf6b7977992eb9abd78746c6c4d034de1857408db162b0eaa521a4d8bc56323d67ce85735571fbfb830a76ac93154764596cfdeaf6a9eb37df0394e324c7896b328a349251f415dae7dab73df1c4b4c443db8afe5b8e56706982fa5843bb77460d8a229808aa085b07b0aeb4301bb213013db1a8ae76ee41edf189a9410c4de5fccca29d85e6fd6037d9a430837fff1c80c03f6b3b455a83c983b6289dd10ba05825f0b99b688c8abacd5eaa40bb104daecb06ed65ba25996fcbf0d545cd1c1f24361920fd8dedfa893a45fc7c7ab3a0ff3d29857c77d4af963033bf6ed5b3a14938654acf74743b597d01f25c31aa32c271945a46149fbdd819be1142ed959bb2f1f26c84219fa07e128b8aebd029efbab1a8962415928fce48f53cee74e8b0fc871ee49d52fffceec0a3182809332ca125a59893f540b693226816d9df294bbf8b470cc4e25f9ece111da040524329318bed4bb09ba07f77334f23e4334e23e2de9f3f34813ee2ea68b983eaaaccafa0e3d92e8ae4d507793d13a548cf6a25ed7524dca9c702c6fb68e97b3880861dac03a9ca1b63d5a7decb15e91958c7eaa8406291f39b4eae652d3ee22bb8ed9d2a9be9c97f393c5cf335d5b7775733df6f9adedff0e882a52f04079418fe0869157576e4f18cc7abcfa7fbc84629801b4b54af4dc3c2a9e475b5b4b2ab305ee0da237073df4e59ad3f11656912f3ae92574c07d41853244e91da13c98fd574c83fdde2b62a3e5a84d8904df8a21347ffcf9e1d3bf96c5d25c12087ef94977b91fbe6486d6efe219b5939a587643ebba464258d2ff91e34225182a3f8281d43244860d740693ef88013982f41b4f8d4c971fda2b2386a84afaa37c5357f6555aaaf03b5e85878d917961eda027dc339e621e7852b143f0616f91fc7cf60cc178a1abd95b5303f4dc6bccfc32302f5c6c8b1ae6e4a532f2647394b720a233d5eb863bbd4eb9e74be2f802f7daaa9f76cfc1d9d0ddd675d3bb790673105b4acc2c9a6f2e95f2ad01a42eb63480f6a37b716cc76a24627e56b3d48ae79d858b03e1d31cecdaa95aaf96822bf355dad141d51b5e65d8d93e4623e1b0e459d005e4516539ddbb80bfad45306f7079f1d52aec4535af26ffdb3c1f49e4b436b3e98dbee3d5eeb89b97d9eea318f903d291d286cf33eb88af7b7deecea0a0ae844142aad00a5cb6dd020cd5fa1adf890d21936a6c7add54005778e720e8002fce5ff01e891b97f00459d6e5c7995ba785b3300ec6dc65e90c5e0bb3f76abaad636e0e89fd8818a4dc647dd146b3f6b0a1c953c4cd9a8c98dfc502a733b0d8baacd9faab6dfcbfee2c82aba9bd0fa3922a8d8bf56979a460e63733c4834929d2c9d452e8b4e2e362a67ffacd43136a1086f3b1b1a91ef67b6f317b6b78a3d1f6d3c635444064faeb2183d591d0d042ab38ae354ef382bb44f8f97cdc3e02292d0e02ffd5111b39467570dcd5d7529ee20dbc33ad51cc9bee34767d9ccd642e846a896883cb79a9073b57f7bd72a3d23c2b9b98de0834d9a6b650ae902c1511276880a6265c5dca59423bb3079b593df17ee4b04cec8aaa44251ddbe150eb9e54a193d3164054c74674f037a7448d51bbd693cff4c42d160fcdfa9400ef8d6bd6647eb586306aa19e3d94979ed8a539f6a75cb5bf6942c9a22db8f752b6a743602ea946373eb0f6cf22c12851a56a6977b35a1334a1350cb4822c6b12de1d785a2319146e479d71fdde15958faae8a73f8cb2650818becc765d79250ff1f07a3c87005d035bdcc959a62aedd59d26a29f23a580a98af17e8c80ded13d12acb47ef78ca7b4d36e426afffa7c3a75724330e1d41be5a9e829ec1c284c50197210b3053a3241dbfbe737a906c4f426366c6417693ea6c118bf699297bfeec84738acc3bbc26da53f6e102c1c1d3d688c732882b4f9a363e5b01d21913d2d7cd6dfe8ae667a4b4b6c9e5c78bc2ccceed96697c8d48434e8fb6b44006eac75fd65ac755386c6263c77a27f12002bb829e611c14ace578c2c563bb5df4c3dea110a8684c617a0533ce3539b8da44f5faf6a43d14e7ce71a31537817590aea1aedbf74e1cb6b2b3caf6b9297437d9c0974ab78d286e1bf6e4d63553c0df68bc11c81c8edccf989e4440f3de37cfabb2e84605954835f89512055281aa2cc60bafcea2de151aad2045b96c2fa77a2a2f1b95e13d088da1566567b7e5f24539988a27337b37c767b702970f4214f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
-				"028de6552b83e136db31735a58f0ea3f8c39292ef79f24f3992401fc3e6d318a8c5a592ae5eaf0afb562a40ae39a2bbc29e76e194b841ca535a2585b1246cddcc715a7147112a72a530de4bf95b38910e2ba305b225614dbe868930e65b6d27639754ec9f5a97a37844bd5c907779b32df0ef7dc5a42bf3b282f3cc3c13d73a9fbf1cd982c692df8d8a11fdd893ab923e0353360fc62ea5241baa68d686df794b3676a93380d963cb939aff9498cd9a60d7c431044a3c49cc232114c8f23c38df8f1a876d39e2ca168589257315946edf9cf5b59f5cad21f99092c4a601836c70bd845276c7d5d08b8237c3a9180fd1053f60cdb8c0eb595e087e0b9e9a229917c74598967c48e665ede8053a978d96fbf3965fe597c08dc1f780b32ecb482e38e0550a4d964fe2197abc9473075a8a83b2a901ba07b08c0f4964fe7f391d4717f7eb02628ef408033ed34ccae77f976fd00ab83d3b2561325b6b60e270f2c284a0080d29bdab05fe411d66aeaeb22cbebf9ee12a74a22f59448ce49b82de84b391277e61483a82e8a164ce4684360ac641f24e057c54ea65a19bc42b6477e871ff2cee37e42a6e2781da60387babc1aa1acdf99f51e0eae0004c9577d7b16e9a58426c2c73ec199509ff85740023538d1fbced211c3f48aee7e0d5150a173c24dfbdf3cfaf4c16ef5f76b05b0fae7372cc0ed64a9d4b3de1199c9061f8c968a2fefa7e6eb04669d6ef88316c511617e8de4350a507dc337a725c3c7c605f1b829235ab9599b4a8aaa96408e3a76ea29988047fa25bdb25da193bdf019f16fbf6c6370bcb01e74127060b826335e7068879f636b359ed3de1c10676991c1cc9862893866139f0acbabe69b362bd4277962d567d19ceb42180c112c129771c46ed3ad810e1f73589aa9205e9898d82c9f26b3535301917cd0e8afa6a2b33763d3d5cde2b5ae2023b5bf2575857951398809dfc1b21d99aa492c223cb3b95991ddad59848318b4aa93685158ebab5529c4d2adcb55a4230e4d4b1206cc06f88f307d49a7e06590cd98b9cbf09ac1808ccbfb7d84bc0e779363df9b6e76ba60f5df8346f037669d4d59b19784223ec42db03dd76ea79aac36e251a20a486fb629aa7576d5f34327564f3162e38691087b9465fb1dea92829375ccfd63db377825da9545d43101d85cc8cc5f559e82d57702c8c454eca11f172e657935ce269f4b2a86cfe028be25a8bea615a1f8768c7d770eb8d7813cacaf4258d3baec0f83c257249bd214f8572e69a3dbe92503b41d2cc873eea3e68eb649bbb609f5d22cf0450311efd92cd1d533dd27b92e79d4cb958a58a5792ddc710b094c80ce6cdb5f6c53dbd1ee4db9a14cd8a9d7d95b213611fdb5706135c1c1adace4ffefc61a471abc08cd97f87b0a5609b3a41a788afba253c5e75807be357efef9f39992cc1c978c38d8904b028b85d98371abff01037076f401ce63014354b11d0992abf65b46f525a14980d31e50f539289243e48a0e20725e81bbcc668322d40c617c4ddece75dd571ea47cb13fdfe71ab55431ae55a0553b4db069427e5385352ded2d43551f0dc6b62194c66f9a9350bab90a9155786e962f9f1783156e43cd52a049a7af6febd130e355867ab96060eabec3e9fd54b0409177c0c9de6d9f08fc52439f8b3e63f90178ddceb96976efbf56964536c87477f395db5cb0dc259170c01a8acdc80eedfedb0c13384d2fb6b095ed528ef1fccf8f9cf2d040220d502828b677053c40a7b5138275c85aeaacc4e4ed3434c72acdb4076af2006dbe5557ba3a08c0a3a0270a0046a616565aa3cb69912b2fc1808369f45b3a9e5a1d90651a02c8c8072c40565dd294f86e9b53c301acc1ce5a19d49dcaa6b5feb0ee046e06442431deed4dcd1eb184884d4d09bc1aee883e476f48cca3897297f3aecbab4bb2b98e9b542deca28deee98d0cefe17e313b5ae362e05ba4dd2110022525ac1f6bc3631bf2626662313ff9c698da06b036f987c943b3c0629a9de9b029221fd3982aa82455fda07a2efc5871716d5bff85943d9f61a29c705afb729c9976b8f5a165267ce5783eb600ca1e1ed19a70ad47a98812048cda0bacda753a55339b9b411a44e2694b026e33035aa90232d9dae1ce06e8996785666bc4b6a82b9fe4fef1c1930ebedea590f8e1c1d82d25f1718b21a1e40fcdc931a2e911f1a0bc4895ed0bcc5987c8e58f12243bfaff873a11004d064013c3e6c9306f06d78a0a260d7d0f008a7a3cb24fb14e9fb8f699f80a76d846662dcd124a4cfe6ba2b39d08e6005119d50b82975e4041ce82448ed43bcb4263721280a07e4461921fb3f85d89f64db6630087f39d49eefea0671bedac17f2cd299dfeb9e7b307ede46c64fc6449c6e100cae977c34c0b79ab3f6fb89de4c9f4d12d6ac38fb7fb5b0150aa17138c22717a099c8eb9246ecd6f9864c5932576560bdb9d436ab4f85befd00c42eb33f8083edeaaede0d7626fa21191f17b2d29ffbf4cff4fdf83cc6361c4c59d6e23731f8684c608e0c2833dc2bcc3f1c2e335e9998b5fb85301bfcbf6a9d5bb4d7521735ef93afdc89ce4ce64f8f2365656d07969846d81c32b18c52655477c0d9fd996ca0c38f1173e1c776497722e74900ed2a0bbee050d14a6aacbbfeb8244f62eb3a438ddeff474ad6157b9b87af9730835aa3d39de834ae35a90846ed2aded25a98595b1319a5a7d68d632ded46cd390f21ccf9f47ad308ed26e719703584d1cad159b017ba03b7a437b61c50019084a9bbbf546752fd6f14f28320603f6f1e2d8adfcbcbcd99d49419e9d6d862ca87b897d18cd53c3aa3ef0612f49edf3f5a2e64d828b5ea9547274c5c4b61e09903e7942fe8f2bac74df96fe1072089bcb651ccd06a478438dc911f3a9f4d20c212cee27005767cf85eba9c790f484e03a177bed24a8db6c394f90222313535847518bebd9a27a22097d1f7d86732437cd5e6b7ef4f9745a01accad6e6809f94c95ae10f28da707c81ce9206b2e61e5f6fb6a7a199e042b2de99c259eaabc35f9c9f39ccb36684084ad105eaf74930a2439090b3e8c80e8c5ff631c9b36d32498d4946bedde9ce59dd929b48519ca6cc6eb36b489bbb39a649fb0f9504f68bb54096b3f8514ac1fb81b51c5bbcfa290b221a4ca065098eed104adde8e6f7a7c91fe6abb51f715e115bf121f25635bfe695a898de21c9d4f7d5604104ecd7fdbff317f7e769033f4dbe7663923b4a6c8c909c807d2a1320bffa3314f40686b582dd82088681503fe91687d3d6e27d47cff720a29380176c1659b65559ef0afc37a914daf56c083671ef8668e06241369656656f916cb4e74459513ba7c0cdaf099ad6cb8ae5cb765419036886cbd7be1b394a845126e3a58f6c25063bd06198772ac2a536cbf8aaabe69b906836dfcdd5427e18189379dfb235a57d27be2a2de2190469b9df11a0be6452fc7187ec1ee5faf70a9b8ccf921255040f53c6b1c5fc31bf48aed2675cce010d58c8b9067ece86c6a218a982d89411de3b4aab867c42b616d994ee50c3644b222a3e89c4f0d265de338e1c59c07be186139858c33146f95091ce41a88d79ebcf54ff6f4e983ca2146b8f5dd62807b697f767bd3289795dedeb74f9bb361ac904a35c131a3e038307c03802f93e9e7cb6fb5fc84571608af4939304e02fa3a884350a4d211cc3f62f3cdb1eb36d3e1e974110fac593153cff215a3b25c4f75c56ad7c92b1e4b14a0db4b27d780ee4eafe153818775e208789c92706bb1994ded148e1858f20b97916edd21af5fc370a4238b971043c527d61527bc12fd86bdf99d393378cafd62ac57c3e8702990126e609928867dfb0e150609be9f3cbbe22d15f77b6a72b05fdeda94af4bf531d726b0f90e652f9839f87576064282b3b44cbc9316d8ee5862c430feb936d89ef95b6ba3d29a9636e8508b130e88a563007517ef335fb6dbd3f0c382b011107fb4e59b673be6e3a36947e6940a4602b06d2161b12b94b7f8f0098fec17580ba843b0eda44a67b429f37ef19b056135c58f7630a140de52f70485957ddaebf4b9c73b535c7fd40243dfc620bddfb9f47b7b4f12cfb22c5d0028ba6a04c1c2754797737f4be2617de02fc92c283680740630fbcde2a77b1dd23ceb3b6b0d8473d8e03c222ef97c6008fb56a0661a6d1d2b040e54896b06fb5c3675869bda6a09718eee0ac77e0519b0461e9a1ea7b754869af7dfedc584d10449a3bdf3b95d4cb0e03f9eb48e3b1dca628fdc0a3c2cb3859d4ee4866450252604184ca08569375371725d6d14629a9df79670539a625f4ef39c2efdb54f8555926e18651a10b5280e25a94b5b93bbabdb3fbca8d697b74ecc8d15f5224af8ce1331d4c77552d9316ab05ff191fb2bc8323c7f1a5a49e8a16b9aa9413604a7941712e2a908f2adb4ccfdd0327584dd6c723a88e497da890489c4c2c36860f999cfd95f5a2f6ae99ec4174def7f36f3f877eac6fe6c74087372c8f936d161a2927199aa3918d3a2fc3f639139eeecc9cf58b61984f11b3e35a42de10eb88cd42843077962a1d50368613f1c4fc98b8ad20b7ed423feeb445aa54ea3b919a2afbf19f115c19c676ff33c8323419f4c29281a44066587e53045a94d2bf93248bd0a9b87d4eaaf3ac9eda29416b947696e25486f2b70922fb34234aafed17d75ee3d61c70215e69662d1033010cbcd791cf5576fbb3a155f4bf6477632f7163f306c5e19c2b181d47e04a53d29f62c0087a23f9bdff588ef598dedca29badc4f9cc7b17294129e217584303bc70a08ad9a5e5786a370dfec05b707c75ceedf2d035c677436a091466a3faf8699bba63f333e5398f8fb0a6a15630b2ab3ec084155d33a46b7951167d0ddd5350fd89fbae466ff9cb280192d015f956ca8b27907c7823c1f7e42ca321b97c5b2e9e346ab008acdf8a5035232fbc6b0849ac620ea0f673630e1ad5929e237363eb14be422345e392cc5dc2948e4582bdf1e707970b021a026a40ddec7abd0e1bfe4d91f4b1d7b4987864d6a19182128c5864b234e22019eba178df05508840a724e08bb8ce83d4fd5516ef77ef2e5c82f8ba50e53bf4ede0400bdf4f7ea0a71409057a98f07084926778ef6d44edbe31fe2abdf0c18497e936e7aef40c62bb00389cadc533d138b792b978b2194d0538d0bc81f4b53db8d30fac0d7200187bc6ff06fa30da7848b1ff504dfc44ddee1fbdf1405a51f9515ea832ef7b20d3c28cc0ee0b1281f44798c8e0f2582056c925efd7ade4201a45f040875eb5f1394109304805a3351e49b431a8e36b563d93d81db8f2658a1850bba23e01e3926b4762d92eeb11e31e6c0a716500cd285b2d5de1f96e3ea5b9e7d7534ce2c5e2ded8f43ea3641a1c2e2624fbf7cead517168a9e3b17bf7c797f0aa5e379e74e714d0f17b9d42f525391b205755c841189e2aa1522714784064a0b8dae587993f904681bc832fcd99cef9acdb7bdd09015000a584a39dbe73878b9f0cc0d4d468b3d37cb5326c0492e54faccd96d5c22ca7d88fe6ad2a1b751ee7248d45fe79df967e8297a0f41fc278fa6f68ecf3b83b0756c7b50ae6265599997449095f6cc9916ffe6a2896e50e06dc4b0b3bd30617019b4fbc72b8af9394166c275a554abbfea02b8d75f50bbda22b5e5440cd8e2b94dc74418ce0481b8a2a9ef7e36d4da37f2a1b4546e081dd1fe870f16172a7e1ff8af70ff31ed86b555ccaf6fdacfe28bdbf03e3b65134b7f855f9b82738e9506a46b5acc67c03fc0d10a8719e3db057b0eb08e05a33de1b1348c90296dce4ef155a325a99ffe6748d31bb44ce010cc585b618b23267cf637d128fb154d5184891f1a3564e9098a4335192dc472185c43b8a6019fef385618a8701f554cc4bb5b3148b72ca8cf855c0b43bcce4a4caded4ef349b7e9d6c8122b622a4bb1ba924d9dfc956e8020600e81561770212e8ae88379e617dea3186133b8cfc5d0700f83eeb49ede771b7abafa0bc521d3deaf8d0ae4f39e818eb2f81f5200e7e5284301fc0074429eedb98490168321d35ec66573de1cfe6262efd081adbd38a15de3cb6630f3f7573fd5a6abbf713e6f4661a18f2f24b5d2623c21443b7df1578c216de1563ab6b9f336cb2ddca8ad25abd209296f6d0cb5f6a31103acbaaa03def743152b307148cf3512c7f9dd73553eef9c3ebe8afe914588b4f8758c002af50de21532ccd5334cc54f667ab219ce2f33e9e74c19fd4c6bf3400f6243c160989acf899001f25be8c4fe9f07d00cbe12c1106d9208ce35203f69c480ada378bfc9cb1d594b08072d1e7513f378bd5f9e63452fa1fc24383a54b43501238bc5bd18e26d05a85743ff819277f2db2ac2ef334b380dd02caf93bde7bd0b21c7c6d5993613bf96a98b900799ad7af1bc35697a577716882f5eebc401519643391259962b8736a1a2ebf7211e56a5f64d55652f5e6d3ec6c935dae35f8e1aeec35e107f03fc5b6954ffb7bfed94ecb7c3461743436f8149c9e236a3c99ac05fc9d9539f3b3dd01b837c0880e75aa81ebcad5e1547bf9238bd733fea88af5a4a512fe68348c111f1beb08d634c604244d5fdfbc41c5dee448e8d2d0b1d636dc36404080627c45b8bb99f1440bf59e78234a0925b868787787aab0c0264e257747f7b08dc5bea6527adcdbae9d4296241b459c84a9bba22322f65bef658331ef0290bbf6c94890499f094d2f8abc23b201a50a81584c36d819a5fc8de9a3a1069f3e324ea3da97282157192cf42e99ad6af8d22a55d9fb13458195178e90fc9d180c7eefec1c7e78b00751c95f74073ec83b3155521ae6663ac3c9fb0ac646e35e3e10933c8a044796fdb75656aab3ab4a5fa4a0d30eacc31da672f4c9f08a1dd34ffe51e6c954e9203dde766d0301db175796ee6fb835708c6d836113e6e4ff40357f1a4186062c127124de0c89d5f726849a0d7f2ec7a397053febe5d19b38df1558b7fdc3b30a817016869b92b02ef81826908e4f313b499b1af66ad485b91f6bc1c50150327908fb7e2591152a3e18d17622a026012a616e33b544312442797fdb42a54eeb88b61b17ad0572f02138470bfe18907dea0b30c41a65f1840cf3401ce41bf0e356dd4477d1b675f944cd17c823404270b76015191ae91f2eadee2fd0d05a072d0d40b16931b9bbbd4d245f3c78000e3074a97f6b78c4477fb1bb40e8cd7b3fca65dbf074704eba70b3090a2c292d8d5470ee355272a7203b047ca686fc4be6c65993ee11bb72c4672d94f40477a4d1dd36b771a4fe8803aa5961a2c7a9f77e9a4cdada37ff724e7992226b52de7495e0a505e84b638632be13bfab4421e11df0e7eb22e4c1512f972cd9982f52b508d7fa56bd4894ca67cafbb305caf686fe046be936ebdb99772ee68e8a612a4ce8d4d96f1e209c1ae03ea84153435e250de928503b7ff478aae74818665d7c64f96fbac8996c8369c6553f5c91340f98db47f41025c32336ceac70f5b713ee78a993d0eb15da54e55fa7b19649fdf115e20f770268751e0accb8de5af351c708d6ede259810d6586ef9c4d5ef04aa29685a3499d01ad532a781ca773af5e7499c6c409df418e3be271507ef63c46bc30673e14e50ed0f0d65a31fac3cbd5c73c0d66fa40bd4e55df54e4410a93caec29c78c5da94dc98628cbe26579269d76badaf5012a4c3737c8e0adc4575fb01dfeb0f1620b07232c03906125a0c80718d9e9489e5aa8d459d9d284d49995f4c2b54a5c1db2cdf32bff2278ad1f247701aab4ec2cc244a88ab985443094081edc97a4275c2b67b764e1ddbc8a5094a5feb7f8c36dfa77bd661406689f60a7ca51ebe107b65a3d23170e6b705718d452ee69de2bed8b731b757253ebbbc465f1d928a6a74501dd81650e64c5fa1eecf2812856d7535624acce076250d230800b1286183e91adeb41ef18d7b8bd9e43997676ba87ef61c14fb8cda6289eb1ffb158687d8dc869dc7f0c17cad34e6a4c3e4ddbd52d94b281830e405b609cfc47b69cc32bec309546dca47191fa1fc447607f2367d87ab33d5c0d88952ecc501e038236412c4f2bfa141805766e6378bf1a40dc5db95fa300ac455e39ddb4938d28f22243c83ba5b05d1821126623bc7bda81df89f70bee28372e78a2b4f5e8bc2bd033327796ab6f25757de1f90bcd852cc1789cf4195463d83fb8a221baa258a4d8d884e557da0f37db6be8edaff4f585097d740564a400a64d060ffe2e94eca3f2c3484f5c5d649092672d7593e4402d2b42662cea65de6d291f66629fdbb9256bdf7537510080e17186135f2609605955fb3573d3fd1899b7358b91e5e70ed27f72dd779e153df6942f2eff0b57b71b9badbf75c585bc45970e3cc5e22c5da83615cae553d59a066127bbbd490ccf52d1938d201ae12d8b7b97c754af65227dc518c1f0143ea96f7b8db769170ff65fa15f102095664f6cee14e4b6886c1ac0dd03fdbde355b845183fdb3186b4824a74a657faa4ab7c86969825316b5e8044fc6e3e16898095b92b598b7bd20aa66bdb70eb9e0c520fd89af2fca7ad65347e4cd480c609d167236c7347306e4e8636efd5756683a7a5fa7deec6b6c2cf0784e07898d22abc0c89357f315108cce33d8f0003e05c53bdb0fe673c55a25a8966e3843fb40dd5ed9169d519a4d83542e0a12a41fb9c0ec090df940711a44cf4de84510fb66efbb5e7886f726d5b9539032a08d12d730528c786fa01d43490b84acd99ba4c7ce8a1a5d9eba86125b297deb681ea7bd11a08c113087f7549b7e999ff799184464f45c76caacf978a069c400884851bf734d97ac7a4381cb6be627575cd2135daf67a385bef686c342cad601a4354190aa3ae3caff33c764522b39a4eccbf7ffc2e67dff6459c8a10c235167b4ad4a70d9fe2815cbcfdc4dd303d0b37c6af62c9c0ffaae74dda5dd349959b816f23efe5b993d30b6922885614ea33d34e15ee5254a852a15ea6c63f050075ad7f8b70cfef19d8b799a6536e3bb2d3ac2b9e67bf6b335c026a1bbf22c8383a7624c5dc97dd944a3d3295fa3545739cb41496d880e8889e611025c03dd38c3f5779d4a9aa4e4b299231a6e2ce9983e18b30f4fcd4c84f5de461041acaa8fec0750dec1215231e63d520eb9092f1210180a3f97c2bf20219637cdb34feb5ea2ece17e0a16855e602ee494956a1965d0ce2cb44ee5ca7e2b4bae46ee9f4521140bd1d90c3a3510861b392d3ef902d1dd6b8698930356c88b9c9ea2209e5dacc48c409c5f1a4858544fe2ab35d9279a68627e965150d74b13cb220ec134a3acccc47925b9bc62dca979207d489e18582233f4d18febcec1cfaf69d703506f85aa5230accdd04080e700b78950e1d08a60709eb150a991adb9792be2ffc5b6b173a4b9086eef861f7d808120409d0ff76302ea42d9841eb3ac93b90159306e74bfd6372d2e0ad1092e42ba5256733da428d20eb9e42688d87013fd24eaac1f812b9f812fa591642b879182ae54bdbb4c3fabd4cd2c7476ab3aada0d7c1867aab5b191d4632c67b3764688a4d8bb05aa494b3eff086b759926404e5dc5cb96c688b87f66adda7169066f157b6a2331134bd551b1287d84c42b40173a0e81c50afb0815dbd5cf416f0090523b00194aad5acaaf06de0b00373638eac4efc7e45bda4a03dae71c8cd2eb9a4cdf7562181cd7308813e217ffd427c30115a28604ebc1d05ac09678de01231f4c2051ae04b22c721415ff181af5dc5832aae4ecf72116fa2a144b08a47162ff178213165bf494f988f9f476d38d33c7a5881abb2094546796e4110892f9b36e60870f54c607eb8622e17a0da7c6513ba16254b41f90286506fd15248d149fee19dbd8143b7a5c1b6979b749b32c537a3602463e1895b8b4bd71c845dbc4ee37fae4825e7d085b82b7769c2943761bf30541aa9d49493de74389c17704062ccb72ebdb1bf5afa0cabcffae8d9d5e33abe3619a948e60de163084723c881a315efca301eee2596015ecc1a2ddf6f9500e899228a177c61df945321860c48e03fa4654487b1e9a0fd76631ccf6ba77f74fbcd4c82d1229683644ecee743f323ab34fe43f757aca147f06e64f51b0af623bfe92e1c7a518be1c371dcc14702301b4b9a1ae11336ec700d678422d670629ed13ded8ea4f55639d1ed183110169107644b4118cefcb3000085a56f69b375eda4097f4498e9a56e97484b8900a5ee81d37e8270e01ad4149b0924f37140eacee17330a12d5355e43684d1c315edc766295bea0fafa982ff6949d73f60b8bdd8631f9c51ed1fd689c9cc816e0a97f9b5ce8215ebcfe053e394755d0fde1c36fdaffc6cb734a67c95c67a09a78840de30f6beb0a64baa3351ea9cea12c1a95f1c7fa931fde769873e383ccbd254598051b766497f6ce8a3d7e7fc32ead2aaf1f07ca55e1b1c64fd83730308c9924f8128f96c100a60b0f96649d2448f5c09e7fb1757530d2fda883fe329477049340c2178a7f8edcf57103a291b0468ae363800bc0209fd2ab5ceac81aaf9efee7c06af19567ad6b277e368e2576f04e329734f6fc5fba3111dc747025f245ccdb541d8630194092475421de4fa0300702e2df210818f0aea39c36b0e0d65414cd514609561b26a4e7579d7d2096433ab549cc8c8438835f9f47118552e08f1a6b414d51e38c9d0348ad0f5fd38b834c0f26d835d4df70cf9500da3f78bb42377918296ef2ec814c38f273e0609f0ba1f0d6cb6573e6620009a467626c7b7b41d12454bfa396bf7af88711b1ae1484df3df7c8a654697cf39b92c73b4a963fe941fa77b169f71fd4361f847ff951da10b7886061b0b5cc29fc93498ae668b7122b92fc6a64a8b36605251aa947bfbd4eb11a10d0e14f3bc62b8b4d4d6ea96fb0c33b6075e71a89fe390c537e63ff15ec9ea6d3bf4ecef658cc0b793e3c4c2e0b07f6e7d984231eb27ab7ba77767ea9393d827262fd862c62fc3be420739a9e48a73e1d21aa4f2d04fb0a2df2272d388911a8a6c457bd2ef86cad97e91203f73d32334e2a25b1039f229a74faf02e51207aed3f4a3424b7538c3ca4f359a312243a6ff840543dddd730d13a79eb2ce187d8d7e4e0df02a5721914520404457acc529a8b32946332da5d1dd943b32608293cad0312289fa603ea13c90b07425393be66816f0068f46eeec699125e446b521ad429b4314d4fb48d54896b3fc54e5a8682426fe09d0f371f56edf7737fe1844c2ea56ecb4e1560215f67da20b57a352bfe61408cafefd4327260ee27c8cc179315368325d4ecbbd7077a69f9a5a793dbbd43e99d3ba6ede439ad2699230a5b7247166c8f23f465b0831075ea0cf3319843f294a68c3eb5428d18edb9db480261550fcd602abc82e8155817df3bcc7a27da23f396f75fee8f04c925471381e2af11e0994ae2f37255f04df087a38a63f8fc6d254c64c4b8c2c361a19c3e7cf55850746e81814f8be6abb1882e37876b3a2dd65f1be873ee5a4286a87e5674ada8a6835001777e0ec8cb4a6cc5397b3fdf304ca7b44d94acfa9db204460f80df0fe29cdcefc3a291e0b2b3bfb82a508ca774f7a0186b35798843c76fb78bedb9629ed95fe342d68c80a6a404a53e040af129ba9251557f00596d3ea41fc2352b5e6f876bce685fd986eadab794b1f7053ce5a365c198da730cd694287bf0a087539c9ff1190f0ca7db74ccf8e7779f21b244efd7068dd6ad2b74553e8c4f49f16f65ef686348cc1bc17442cb3541931b7193b7715e87f07f955ba2e5ffb42e0ef8804e9296a3522dbc1c11db9eafe2a09e7430a00c09038e6bdfe7338da64de6bd508a9d4443e417e7d72db42bba1c29c791c979c0c3b7bc7ec11f840d382932f1098644a5fee47132d178622d17a34a593a25a319a4741f47b421f584e77942d18a7fcfd13fc66e90b190b1aed180c1301d8f126067e6cbdfb84d6ac9ff301c9e27ba27aa8dee8b74a6d23de64043f1e4eedc6d4ad1329b5e46479591c83820e79b313c9dcb74fa78f2efc46554bac072e8fe4f28bb7dd04857a9e8a0517533d8ecc9b3887602918a8c565de2cd71fb7efa24e7a52847bcadd9be9141088c17cbad418395679d946a76ff6b8af1f279c1a150d8a77f04b8e96750d1df6372b21152ec6d38ca8d5717dd4aed631503b6317852c4e9e1d8315142844632448a5300854b18f011b579978e2b4d255e6dadca6589289b142e76aee830c1818abb5bd977e065349a4a97290d0cd36f342775e5604d08b1b58bf05595cbda5cc8d92453b0bf01f37c235a3b4f79ff30102ae8233d9dc60dbe67be424e183aed1595ac004d488035964d84f1deea00afe83ce4c959aa7d5e0052ebdd5a8d6d9503bbf5ada1dd38ee93308797f68dfac50ecaca81e5c8fad9fc488d9398bbf0031e63da258897c8e5375128fec5f91550a17d1c912f79ed0a9af60527fe2347ebf6f55277e495e82d777c4baa19108adae8ba851395b8e2b4670721eb81d42315ae0f83a05ef16c7a127d809be67de91c269b5e9008c37028a4e1c25224ba91f9552926dd180ae36367e654b98d2019561dad9d3f03e7dd844793ec6f88ebf1257766da1906dd22d8a6160140fefa392610cf0869f132ab263c6b3c4f486fc5da399026d1852191c66d9fdadbfb997567dff5d52a8a28c3fa71b06186e8e2a55f764949165bdd5f8f4ff5195781c63ee737dce59cc64376e822f7b79a81d6779657090796f92347a3610b369ef281ada03e9fdf193a4dabc07299fbf5a2b0f6546843a6b5a7160cd991a1b40ccfab81f866e7d0ac937fa892d887f861558144d1a0f79412f9a3953e637becb2e8ce7c5cfb22ae079e67fcf31638a139c0544fb279f1f554708123227c9eec14c933fa99c9b9302d7c60d2c3213e43964742dba721fcfa58c506f5c8df01dbed51405a9f1e93ff449fa6ef8f23ac9f89ffaa6d37eb09ee1701324799b656b713838142970777214871d5c763e4f0a3a5b1168a6d616f6e5479856fb3a5c798105f525b532fb2858eef040c5426360e6dd5493d9e803a3fbc7cc24ef180725cc36267139812bb8e18d741493fde485a9b3db3f245927e836bb2c6fb95cbe549e787fff9bca888d8db51b543b33999f49d7dc2e8b20cee59291eb5c5091a403aef17abe5358700d4368bc135b35ab9d7a5077ba571067e775b8be222ae968124174a86adbb357f1623e288c114e77a87c1af4c8e23344b10f879d48a7383eba4d363d2697321fc9eef3f9889b8f252b05764904841c837e400f6ba4cdea406090d56e085c486455dcb69cb7b2770808c2207bb50fe5cce6e9a9b50ab698f1ab77fc2cb7378d05460a649750536a0527dcc399b359fcdd2a9b93993ec1af024ec734d54f1827d705f754bcc5a4b8a0f1df86e0df57f29cfd332967a1a202a8964686e1617429c7b4eb6c5bbeefcb68059028d78fe283669a412dff2d651cebfcd6046a4350813f201fe000bc988bdf6ac33c866111c70a010c432b5f54ea48a712ce244eec83d0ead83304f0c6b0162af07c44d4ea0769bbd5cf2b6857c38aa692a300290784d0582f3f739c581ae2554c590dd87660403fcce4df0f0a6622bfc3dd3fdce255380cc5e960c468335a69711da18d707c84e94b3dc77f1ecb55cf7f41da3fa009851319d9dcd7e83663f6bebff140a97bf7a2ecb6533257e1b6a1263708caa68200c4229c3ecb03616ae6606536fc26748c3b68909204a837a0d7bc3371f6dabffdc0f238d63d929e46e6173beaa730051b6c12dfaa146a439461e7a990e8dfbdfe08f79c81ea468234a43fa4d89784da1f1b87675f4a9c9ab5ea6b2985f3b8466fddc3a3bc26de8725080a26c85f420ed6ce46c303ba3dbd86805b7e2e007d1244c6c4f6a29549b053cfba0e0d00b0d6f18c8b5f5f9145b412469c84d7bbe28de69d009e11c66a7851cb46235a61972c42dedb064b93de58a2644627a650dc16c30704e780502742b816661629fd19a898621bd2267cfd0caca65e291cf580f0afe8bc3f236c040db175940bf8c8d7483ce748f036871c087df3b2f982a28cd4b4d5f3f6b7da0dcc405770588ab09abf0b987a699545c0d9ea51c51e2c4eb81d1fec6f98f38a4c41719c4bec0a681f90e3367ea7e8bfcc523155c98e3af0a855faba28b158f011fb2c99e5966e4652361e5370e2f0745202ecef16ad544d27061d29bf3e51f0ddb4da2da8d00570855bb889e24002dbe9d29f93e6d93487226636892a1c6503020d7c56e21c1d904b0f70043ea401f05384db81b5155955b7c9044af10410bb29f0bf90e108b3cdd5bc00a1674474ab5870810c59711692f579b66d9a7463d9153edd9f2a1810253855ae75639520588f65c24bedb8c5bbf478fd176c7deb9d3683defe5d636bdbc1806a1f083ea71d64b1cd39c4f45c97bdf0a945f71df3b875196712650ad24e86f6387cc872cda22ffa218a99aa329ca170b44c7ffb3dc6b51a4fe192c501eb9cfe931690b14ff9e593ab9d81d67091c21a549323bf719c501c5d3471beb1dc0d31959baf91abce216b213bacc3614dcf247ee76b3fde5c347dc3c3e40fa9fa31431d14d9d0b7533d9a1557ef7698b47a5fe5936431848417e90bc28108095de735bb165791c12b33868e2bb7cecad95c770ab90c92b4ca84c76c59eb5837186c379570a4054b0404f0df61e61312b2d0aab3be939e8b581178b000a794735f2a39dcb0f232bff7baffd427823a6341af81d835990056218fb628f72bd53cb83e2580ca4eeedaa0f904e902f0e60ab3fffac10cfe1fc09971f7b18ab467bdf0be77f7f314421a9918647ecc2eb45927562ab8c66cfd3b24b27558b2e71cf3cf521d81e178e07a355928e9f943f98e1b319cff9bd6aea16b94fb9695227dd46c92742173ebb2bd63ed1231c57313a188a1ca5a1318aeaf925a3117d58ebf9654c0eb24d8f4b50b92f00bf8f29b3559c1b6f233af6e61cc3fe3888ececd535299b2b5ff975925a253b2910190656e0e5866ed51646291a5182bab9878e1d620316bd664bd95205b3aa360d7dddb10cd3d360e15dccb3986258ceca8d5181c1862b92931a5682d527ea47364974c5a357877acdd29f1589a30a0b4132cd1986fbc01d987784cc1d22ed1143bc02e84aabc569ee8f63a875b1a0f5a3412665ea2e01745e8c5ffb275e8374c88883e21ac05c02d2489e9362b771de1ebb14cc6b5f7574cdcb4b1b8676296837cf8aa7b1afbc294d416a6b931430e9457358394bfb56e6726211e008150321fd6d8a3f6b2a9626e9e0bd92a6665f66c1176e2cabb0fd672de165d0d01c18515b78db38cfb171e3cc8cc6c8467b772c85fd26d283744d0094bb6745a920d972d33162baaa8906ab6e90bffbf0174ba3a557629da19fb71bece63cef94a71de2014a3fa2035c2893382229e725802a6e798d7862142caca8a2e177c676fcfa20b627d3ea4f81bafbc12f88bda9e89a19e7f9d235e1b05e182b437a19e9772afa959aa257ebf9399b1fbec88ae8168f11fa65b2a98f4e1b7c6df6d1e27519f5e7b23e7537579b5eb9f052113659dc602cb013a3b7a7c9e60bfda8dba5a8488f300bed3bbe9a4cb7efbf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
-				"03208790dd4a7fa1b1d28a71622db4d9c619bb2a9a7464176cdf74fd4e2cbdf23a5c8ee5c49b4c2f7b9cd6ce9f41018f60ca1751d8c3678d0804075999340edd9f386d06c1cfffa990e81fd3f6d9c464b238a3fac707c740a3648f60754d7cf404e4e2a334ffbddf9db6acd43a14013472648ce61c6fd37dedff025b725ccc78521c9aff2642a17358bd628186eed03662f9bff62a413e9fff7bdd4b269aeecec34441dfcb44855c444b7ff8d59524af0b59ee25538518b224933503bf86849288ff387b1fb6c5c7f9391bf163e159b4979ade1e419fc7c19e05be265ab6e9531dcfe9518b3e1220dfca9e5559ce05cdea4a70cad14eeab42ff111641dd82a21e840707409047f4d1c904e9f22b28c8972f2106b366c45917b3b3538a0b1e9252ab2d5d3f7b4fa408d3e8ad510539055805a9cca3352eb8256ecb5cfbc05d8771fc55e8c8fbec7d242a2296c90dfa1d7e484be4fdb26cc27b0fdf2d3295cf9c259eec0db756e2e98d83d96d1913942cadcf2bd9f367ae14bdd5153ff84aee00f9617b20ee584f6d962d97b60439805fc1b5c4ee93b5e3ad422e8529a7462fe40709de559115e1a09feecc82d0bada613be56a3671f7ae9b84a2d468718f77c6bf1c3c20e7c9bae4a4fbaa37c919ffd40bbfb4026ddd40dd7bb9e536b23e7eae56d56a27a42214fb2819e82e8a16ca451885a55395f6eae637c62d73e53cfc7c6a4ae12928134501f422edea0e2af4cf3fd9de014dfce198b34b4d6fab7e4eb90acc2e40ab6030086e09fade8b53c4b15fe80e327e6c54070fee9cb4f774134630e0c1b03d4b03083885788cff249c17597ce56ee74c5e03b5d7ecc2bf100f480b68c21230d4f1932acc8635e001ea351319ce2f3f428bb42159cfcaaa65a9207b5367c00e2a8751d993349868e1765658f0e12e92cb79dc93f10ebcf30fb7057c7314f928e8494f8ab49f4ee25e158d8a0292ce3e06b939668e11cebcd460e6d0c90f45c6a8486fda1ddd7dec3af2d9c4a3d84a8c0c9e8d57f177b6982e645c903bc4a3d3a858ab298de4444cdcd7b6a8ee3c504a40b1113e7c9fe75a9877000e34b8f448e69871462622d67832d9e8f062a6e9d4b39441c06af8402bc1060f4ec15d73a6bd2f0c797813a5ab6406778323a5b248f0044031170d8fec4ac9621c03a75410939025d42308db6898e82caac170a15a5c8a19af27f338f750df2c8cf822dd5f1f103be19cb58e61645f4fd754ff6dacff67b045980937b2b332f3b957414a79d70df39e0811788c7a18e6830f4e5c5f05d79cbe4603c3929c505e226fa0fd03134a03836fbc7c2decbc41f5a9ebe4c3c33de7b4288fb364b3e4ebe72b79ddfde42658784bba26aa9b6e0108c1319a6e0bc87c2821e8cfdaffcf7a3fb350b7bbbf79e48993227c8b9bec8ff9dc2b7d7a349f20f8058e63de8374c4ac670951a9ee7ff15f4cbb7d6ad6c8e296b1e3830090dd48805ea3cc50dceff8080edc778b45f6d07f056d386ca9bc0f7e5b875d0b577f2b97f8c5466d1f92d501dae49e3cf20c313c3a9e0e8d383a0046b0c35fa768ced57838be0fb289312f6db13f3be2965ec4cd4e1c3562bdbfd21a81cffd353ded065cf07c07b1feb9fea26156b77148638eb4960e1436ebdb4b9b00a3986ea86d243853e8a78b590d70d37f1c37f721687a26d99f6633a3177484a6701a0d62756e2000627dddf87a581fcf44fd21b1601a99352ee4e414d3085512022bf695919eab97845889d232b4dfbc1d6e79d7cbaa67042653363fc47d202e380f24a5fb19124dab43b8225f6814994bf9effe76b823c28c33874873d104f83e60778baa7ca5317879127677a3cd9045b1ab95a52bbebb63a20b1c132823561c1e6b71b15239349e15807888551a21996d5a2857d28cea48bb36bbe6c7a2577e8eefbc89f983afc3f41a7dd29a060f24d80585ddeca6fe1cd24df4af0a0ea7fabd83747b5244ed05f90c5e5c8334c2a0b238e85550ddbc0168fa49e7e616e4d21157810b7f409e2fff79ebc1a0a07c105b2c75f2358a1240430e67c70078eb63b8df8cb572530e306c909c064232e23594f1c74e4e1104ce515de8b374002f8fecee12653132b134eb1584c36d2e4110c51bfe70bf07e24ae024d17e29077f86790f1a16e465b5e07a3fbc8740b66a061293fbc681c0a255ad1deab590116a0b654dcc91f3f5ce172502463ec7ec9b89e9d525d442279d3fe71d63c4781fc39f1fcc0847ec47376eb565e9d1f4074ad8af5dfb55267022fef6555ef77a3decaa8060f94cb5202733c0f4fe0b7d16e2f68ca96eee1af31a8143d1671c008ab8150980acad5e229abd8ead94bcaf949bee243e475d844c56c2a773fb0b8940b2473bae2295a1e280beba1b2018af12c172391768c65272caffe7b7264327d2cc58e058a2c4b058043f3cddcf5f1cf18bce4db04891175df0a4e4dd4598f705532c4e307cb9568a7fa86626fb8f0c1a47f8a2ab901419ae127d2cc3b82e46ddb174099dc9bf234eb49d0f7c054c2ac7051f163b4d0bf9d99c619e04f6a0f19715a41647661a74588a26ea78cd0d12ca1ecd335cf6ca84c51951c476663179543af3b7b3d0329a87fe2c07d467ee11562e226f9096f62c40fe23803a850ed70e0dce12527c10876850bbd41b11048accf5bc02102add5136f6308353358d54d1fc2f5d3c8da06d0730c9f9e019b4cd70afa4fe964b0801f42b96a9049d200d0e0abcec0415af07f400dadbe7d11b2ea68bef4b33d4c8ece9c4913651384365d4072394d5c380f6665cbba4494a5bbbf77bffba41ac451a52364917b54267dd2859ef0df061023e91bfa7c41ac580456bb75d48f4b85c5bbbdeb75e3b6319cc3eefcd043d3d406a530bf4706add0d991a2850f85f91f5faae0fc64c4e165d626643136711f5ab8c302b9d34dd6ae14819dc7680e7d31519963ee5d5fe34220d2e34c012e27aa831b27d6cdf62e82eabd5347d2002694802a1d4568e12a1d299279cd3d0dead1a71726eef58a6554129c3efdcdba4ece03c2564185d6acbd98cd1aeb3044a4b62a8c667ca50427cac84c48c201fb6b27117b9501896b62e63e660622a6949a8c7f879fea90b01db334625eb3bd709c4245bfb0b9214a885c4b63c88f1f61af7da1f81c5f6f714e1b68abe2aca2c5c7bdc84e366f7928665c3b428350c8a1a3e272c8fe59b7ac1fe507f717df821ddfabdb0f8ddb4cb127480c2e92c1c8ffc360b7601665c901fafff9448495e69a8c265177ca0442c9ae7eb59a74ae190dd846ed49038eaadba282a32c8a06199b1bd03e007851a8c273f825d1c8adc9dded7d6cad75ea52cf6e3d4f68860ae3fd89971270e8eb2ba6f5bb5c6b68659aa3c8fdd0df5d9d2e279e6a1c4bfd4ba3baef599ef372287187589441f6b9ee870d716d240cee816abc659c1dc62f707479a32e802fdb9c9ef222a24c92cea9ef261aae42e6796b26836b99f7acf53c566f04c4505e919d2e827eff8592729f9fbbd02ceb5d865ee3a2a0e1a0a8b7edd601dd5cab09d15ab55332af6a593dc3f9cebf931239ff0525874c723c1006eac826c1d5f707a9013f433570b9c09c2d085dc06d287585f3467873f192206ac3aad489b10e453211ef9eb42a943f7686df1c83c645c9303a21b7ef9bdfcfcd7aa831b06d08426c0ea9a16577368b009a2c2871994fbfdb1a9ba66db667fb9ce84ba62ef5969b2c6cc07a6803ed5952b8bce82edae2e5ca85e2eb98e1699c501f8e241a82b1888d7529d1adb6bc84f2f1fad9588f432fef2172632c08335d7bcfe34ee5d0eabb8750aa7c510064f389cbf6d56c149cdb3eced412d22ef17cee7dc043d4cd424d706fdd50176a180373aa5582a80bfc3f5bbda8c1a72e0d82b0a295ffc3d4f9f2c01f3404a0155c1743b69d607c30f0bcf32bcc1a45810d88d7b87d380a331126a6b1f56e27ad09fef4aed79c30f9e9be6ac8cd1e4ea69c8e01f35315a0f2b6d957034303733ecf59b2a60cb5cbb875d83fd7a84e40fdb04c647101ad1a7808d79ebad1c6a4000e9b45b2b92584b9dac9827d8fd6cba39dcb8a248e81c52b8ab9f062b2664df9b665fb9f324cc0341813989d830f5b04d09c409b808bbd29004b3c92bf4c31951937dfe0e3b53fc55446f3aed937c2c09e9b0550fad0c353d9b9425be9dbcd2f5b45f9486c1603887f4cf5481e1b16656d5fba59bac29ffcee578e574d0db062289bc21ccbe558793522aae371beec11571787ce0b6993d1fd8ce2cf97b91488a82bcfcc79ca776745315db1971ad913aaf11c89846c790539af6e89936fdc6096e9cce57c3128acd30f1e6bfd18533e5b6e499f85d015f940bc77d4e4f26c3fdf6985dc8a8dd28a34279b920487feba903f903101bbad2715cef2a377f1a674fad5eb65a06cc762a959601cd2b29fdaf240fa9b8764a07cce995a447afcc956d1ad8eed4b72073ae5c5cd61a8e76a5214f7ee60c577227c609251d9475f21ab244546bc1e63e167d8f47c9fe13a20a0cfa5e17c572cc5535932223cf0768a1ff4d8ea80cd4b43cf11abcf9039d6a7afb15516206cd96cf1f42502985d027d42eaa560801dcf1495f124370640a1b2235b260a89235107714265fb0ace062812d48096e6378cac9abe673b3cce4dab696f435010ffe857c5bcccadd9683cd82151fa011ee7a6311529496b188c930493640d27e2000527cc451550359eb28aedbb9108a8040364c16c65788ba22f3a33ca37009734edc7d3d9a6b5aba33fc389f459116326dde1277a1ddb81eaedded2db8886ae2c6fe166ff4cb3d7860c59834f9fcd4321bc2345a136045b699f7f8463ae9eaa1bd3d443ff92ec8e2fe311066303218ab7b1768bc304d8d0ef0b5e7f42aa7fc6aa182338923f5bdc9b679b882029d8bba536ad3c9b6d1cabc112ddfa27c9c10f5022d4b8cb9b5166ac2ffd67bbc47cf093f4af9719ead9fa28a4bbb958f7a0c884358029bff1d52ba249099739211ceae1a00c9be48fb12fce6f6a6de170646774e67c4bb8c2a490140917136519aab7c96040545821cc1ab6ffab210830ff9b5e4eae8a96a8c042a427ef7b9a41e39108a8fb680197c252696c3ed6843b3fc596bdcd2ddbe4e80d03ffa4607ce5b77fe7cb0464472ee14b311a06bfaadb0dfbd6cb8605852c491acec561b381e32d4b5191b69aa8b31bcc849e31927a806eceb18d0c3ad463927e196f097b829afc2d1c4d35e700174a3ca3e4c8e308df6ca519ef74bcb712f08657d79bcd0e86ac37809a28f368a58117aa10d96895dcce0711d70bd0fddedd74a728b85e6123533fc52d077121cd7b78026e4fbb289c0f21123ae505e578c7423512e56629bc69e8f06b5addb1f72a2ee2a8a9224de18f509f3968730ac96a7dbddf5868e51b1fcfbfb1bb8d8cb038663d5fb2df7085d6d671f654cc9733087b32057b7c5b37e7855cd6e9c9dd15ed0ecf9f6619f36ecfd1415bf1154cfb37188ffa9922b64f5a457c8236d7ade468f178a23009a1a0798ec11d572984000cf20c4f62b3628f9a086a8aaa345d62a5c84e5028db67ce280afae26e77e0663d5c414e4d4a2bd68e66f5db03003510abd45f51d5775a100415842a597631f779188f4be15b1f74f0cc96520f97dff78237304985e11f0730bb92e9c46a4b767f42a44dfc1279da95ddad0abb0412bb9e623246dd7f26ac6a0c47d95badcf38f29679e5ed86611a6b7b015adf20c9635990560ee4aa2dd04d3dc6ac77855a9c973fa927fb2f0f8dec5bce26599ba7b1ed6d3968b7a618cbbb62b46d103813b6a297c710d8e5dde8aff9f1464f596f947bbcb0e674391bfceca767c9e782509f1bcd7d413e86620688c55dc5bf25a63c7ef45f53b48adbd2f3280771c861291e77f9eeb196808cbe7355c7e9272613523b230253d23c68319d8f5f366ba51a64876a93939b9c11384cf9be766b332e974df21bd205bbcceaebd6892d390753182ac0bac0fc28ac8b7025f915467cac566ba1dfcfb1d816de813b69057f666949e24ad77e5726755a7ecf9c88ef691ab1d0caf1a2b6a6abe508c8b18b5f6083cbd5f0197a2f6f32c110693b741f1a26bf2b855ad737e36147309fbe0d705b83de6883ce9aba460b43a04b24290ba90ad03e0bb22cb7367b4258d7392c7f01c4035dcb0ed22c0e93c7ed6c6cecd21449b349438df5fb2138d71704cc5a7c29576c7bf6f90967e7c9039386752d2319d6319279213d7f2625aa40835126fa6030edd820b2ecc54bbe1091e5a807bf3089cc92bbc5fa58adfc18b768716d6d68335140e083453ba5ce6f2a4b6d84a605d74f226da3aac4009a540e5130629313665e7bdd3b72087c864571951f6bae85f01f46498f366aced9fdd40b570e70a04d1d4ab6f1e9f80f20a3ed5aab8b9c4c4b94fe4ea5223d7247ba44b389132d3b44b2be83d0305c3c4904722ceb094baeddb60aef21258ca61452c6cc6a06aa47f61c9916ffd51a8ad46516afeb6e55496cec4ce7d96f7052914d2014e54095b18c4fdc3045abd6be098169cc95a9566466eec78dcb605facd120bbe865bfcecd14a7fa496e2c2877d5c185b15c62c9c3c93c8b191e32410bc350d2b40a2f868a86af01e0d665bafd8cd7484d4e0ffc41a67e46a05a4f62af7e658557d8d25d8c489c7fd6bec2ca2cd7e1487039fecc136ebb2e8f9434468cbbcd2b1e3e90ea190f88f363e3ad573823209f8b440c459fb2cc6474605ea4881608f5715a8be11292369cf764c75f2fb71690685331533347f435d55d2dac7bed0bba2e4189e255a75c1769d175514f65aa230139b052f5429749075c04f144dfb2aeeb369137f29b07c61334449ff6046ecfe5467d201e7d98a4095476cb4397018c3e3bb9f4f0bed0d1645e4cf4b4b74df589d9c76e03ac2dd1fa0ba08bed51585f1e5a65de19d1a2f14b6bd7b0dc6679c2832dd4c93b1b613cefadf232d1199b5e83424d4b48ee73bc00b3026cea2f75240d8de829114beaf85ccd520695bebba66666022fae1ca4501e1c1f5916fff287b13d65eb8ccbdb80e3dea217235680d16051784dcd8965e35c754efcbce50486d9930b1d53f7a67ce7215bc9b3989528c54831a7f2f05d407fca5ce9db47dede1f1aa13d575971af42b23d56df381c1fd3d82c7a150c972dd779429fb1a9d6613198a2b4df2269597fc4dcb483a66841bbc679d7ecdb1fcdd0b53a1a6e1781199e974eb18ae05a4c0e146af19a60bbf9a05c78850abd7c03a4ebd50b7f4395ab086a6613da80bfaa7416b5276c2573e105752daeb44e933934abcab1e1c0f9ade80edcaf10f635c9577b53e358f49458743d5cf50e117ef8f98525a0f889cbfd94f432c54524b14861e3cdb71829657b0e01bb9110f4d6b700193a042ccefb75af33309a64d5fe9ab1a2aff76c77886fc8f0caefd52cf91d759d26e687f59c3865d5d05ce397a1ddcaf8e159abca91c2d3232d812dcc7fe4a33c36abd65aab04102577ed312d5ebd9c951f41369100d6879aac685d365844ebb159e9967c6aebff20a554bfe4197de19b108dc7abe79bdaa54153e51eeb1ff1176e1a33328453f94157d0da210bd5f5fe3f2fe5ba83ccc74a75ac75525daa38d98a73a8c2981c16c78459212dc88a8e46328b5deab19bcae7464551597109ad84717b76e155955e93d6f57d9236f507f8c712eaf0e0709f931ef4c8b6cf3eb96296bf8532d2213b27836f89bc98cd7b0b8d4af82af37f3825bbe559fb99818c3407b7be60c1ba4402c1c0b2dcebd1597891aa7210ceb0df81fd65ef10f66b491a220437f3e3c20bb6418d7fae5a72d3e1a9e98c9663d40ffc3b48ba549f49d2414e8363dded41d780c98ccbb58e9adf1287bdf92204e0a7a5d763f28876249f5b72468dce06ef490839956ba6d9f175201268cb54768702f58ad0dccab6e7209ef1d133d60cb5c73dacbaf7fd73ee4e18ccdb9a02cc5687170718dff35be84b4f350d5c3625ca389e2f2741dc409816754e551cc3b8d598ae5c776ae6075c5ed6510d4f5538fe24ed8ed1ce052c0b62d61e83b1ecd11a89e6668b9ac98f18294d1fd6740edd7cccdb83545f2c15f4198216bd2a4e3606c2ae731ff9074bae103fb74f4931fd079140a4fb23e0d326bdca3a8fd1499d6fae594bad25ce17e01766cf0668eecdcd62c72955ede5401f4085e1ae4ed7c95c54d18ce6e07a1477c50c8b1d227cd6535537fafc6f7887f0e672faa46d629947489005bdc36434c363f8991db0ede78673ffe52036f4e687a8d96cb4ffcc14505fd43fe3a747b45f973dd9d7a0733ec4a86be65ec2d16f2c9282630c29eccf467119d6cf01cf78b010e8c5cfc38035038d9d6ed1925ac15be70592ed6be4f0ae178f9be23df3f17c7c1de2e285c6d57e19af4fd7e34cbf956a737800b6eb80272b1d58d10773fc0a45a74e45a9fb33950955d20f119152203e3e09d5ceda392deea24bbe84bd08adede976f39c95beb33bd4716506d779dd2ec0bf66d08d48e40920e86314f4441c5a1eaa56c2e21fe0c10ee5d88bbbb77f2f9faf03d131861069a3d4bd0a188df337e8430b20be94e94a130ca6a4a708b22da811dbc52cb886a7ef52909409eb2542ee0262105abda6995374e9eb6f8224c2409dc78a256fad06ec4676c716ae160c150fe88ef57e5e47966660f8dc83689acb820f86df125bc3390b005109102ec8d7f25c6e7ff8b59c5082ddf297c875224287ce613f3ec64c08131c9ae19169ba16bfeee55269a1b14beb874d0f46f8713bf8a0ea34f50ca38b4f6da0981eaf928f129ad551d97cb2d41bcc3699e3969529f33ac27029be91a9693af0cc3a201d192c66d7b86caa7e4d7ce16d769590f9e4060f8194bec8d220e835480c90745227ac00bcd583df1d32dc18a29b6a0a89128d988216972f90da16602714be090c585b469536f202dbb8a66b03a81cfd19abb0ebdc5cf57047361e65ae34be1ae98fa6e9ebf8969b93ee4dd251169a08aa991baeca0b107a3f2611dcb03d6b13e2b744dc4152d9769421d040529f270c537ce220a1af2113715fad7b5f5cb75f783bffaab75e05efbff0c3b20925ab251e53bf3f79f8de691edf79d277e1eda7a58403d4fed819523561efdce90ecc57d23c925a4eba6083cabe1c9481f10666317586472f5bb2d760dc2d6c7018b9d1259efbaf1b2909c4e58f1975a10c59f8ee64022ca5cddf8273b4fd0533e44b1acdadaf30b5e17293c444205db4b858f6929c1109623f09938c8d7458a5a0578779f7cb24e974c0fdb9a181d47d407a4e92621be668d3c860de808b0d1662394c039df12384ccb8a292e2105c283de4b263b24e045e50b54817f69906436463b7560ec1d312359166cedeaf6d2461c8e48e186a76239ffa3e7141b3e901384b2c37bbf55080edb7f155ed07f73bb4575c5d657c040f90658336d5b47ac25d808e9c15b2ee589efbe8700eacd36ebdce4e05fde768de50dd2274ae41c60d480e44921fa9281a65cd462198758865d1c82d2d5c8b13ec635b18e56a62d2db9d2416f0c7b0ec9934d06dc61670344c502dfd13a1f24ef1c3f8b1640fbb9da42868a5cee4512ad3726260bbe0c80d42b542059e0e007b1064f6e2409b3b99fa6134db2995fa89fc89daec591f49e56363911c79eadfe51499f54cf72bbde3360289e10284d23ce2e028f23560dfa4cfbe58cceffa234f25308fd1d28967d93e30a343691fc4d4046fea1147c9acbf0b7dd11a1469437a61a3eee9932dca81a2fb50a35672ba7f5b8b2577182e604ff5a75d279af4233684c2c58bfd99de5a28a62fa8f07f5d765097a79641c7854c834cc499db3da7d317117ea70afb50026ad4a6f0e8d1f270e5de83ed8f086f10929643e114bde2344b9baabb379b86156622ebd35080462cb493d19dde8a04b455a8a6c67c9466d4aacc642d808598ac362b4888267b46d1c7d91d7d17c3b5b09b3040f566be91e1675a9ed6a7f59e7e54594de27775a8906761c6cf52661c8b6235d45b117233a1dcdb8ccec28eb9fb969d785a4e829ee6be6299e070f51a7fb73f6242a6c3d5e41c6c3dec176ce66ffb640350b4bf8bc489298092e9d27e44e181fe10d50f86593df008f8ba9e521b549b884933e124d6d3f265be66e9986ac9e86c6c903f60ecb2c62acbc1cb6cc478c54d9d5f5734625bfb89e8610c86a8fe5ab45a688fd1af9bfcb56928faa722f82a85e233462c0072f488f44e2d91994fe54d509d444f5bd1a8c0d3bc1d88d9711b7c447e30059b4438b83a048787d6bc919c6299bcd663e6088b27073399486927ab85bd7b8f60cee514ac276086c90ee2cb375d37ab823b9a425d217eb3a8d2ba2a0059101df8dcc9e4d3a82c58d2b3f96a3e0c4dc00b706bcb8eb2ececb209f759c9b6f674eeb8a752ff8e66c16410cae20a6b02dc505717ef5ffef5761510da67367e53ba0274c1379010c4d18db1bfce4d6b681bf1a66b8e695a419f6d134d5ec59826665e10daddbeb4a80f83c78fe92c117c9466c7c8837f66f4c57c379eaa407ca9b4e43fa477466067d5eef8049d72eb7c2b493ad0b7ce0c494ff1f53c5690aa65c75b3fb3d027d1790a89142a79f8c39cd9f015a52bbda5fc07935f2328c46dae67ce5d5c65a10e91980a71ce2910519ecf8c018f5b17029f79f735c8bf0d9c044f2455d0bb40a132029186b989291608e21badd68160f48dd8fec5b95b5b26d975456421502cf91c56fb5ac4cdd489c643e72272dde4a35d6f062a10f384ebd9a71a487ec5959cde04630678eb54294bdb835d7ac0ebe404a1e8edd28c5a41b4622188511bc530fbc81d26661f594d825b701085905d925eb976af765ea89f93079af888d49593f913d78365fdc290698f69a3d5fd1ad2d2e5c2ae59af407863faacd4536beac7f8515350810940e10a92c8c9c97153e9ce088d11ec43952af719caebd15e02fdafee286e7a7e9e0dd027ec77cb7404bb562d5c5320c9bba867ce3c974192b1301f6afb6e03dc9c5be7de3e5673b1147db2eb2301f91b48995d145df254698f0c60498547c55f7ef62de081ddeb42505b62d6b18d37269814db597c75fb3c6d8c2a062b391995c7d332a9b6f6f66ba502bdbf69b73e0ff54e8337db6f65a14d58ff2d223703cc68b3c04bc10ba48c32f29ba763167de9cad171eb5cc65b7247178c99c35774adbcd241b102914ce2895a220b50b5d7a9776280f3d4b898d3ddcb6b1e14f2702ae71c20ff29c849d874323aaf6bea30ae2d31b151d96f9d7bd786cbdb5db7d766afcf31cce6c03240d4db2edb7dc3810c364e203d233b65fcd16d4236d0048b4e6ad9994f83fc294cf4a8594cf0f696f4ac6c7b378a89527119c2937a942e001d523d2d08f301cf9f060531dde931fafad8aa348ce6cae1b39ecbdb1d6ea0d111ee75e98e307f60be639b8dc3f9b71a1a21ef4063b4fcdfb9ebc4ba59b109b0254e383d814adf65456e93f59bf4570bd4adc97242229dbe96e5464b63ddc192c265d47b6f96134be0180e659c897127579d9fc90d6d34a3b720c8d340e29a9aa250984fdf17e9fd71d863f88e11b2262c31e913a6aa60b8445ad6ed0ef654474886a4bab7d08bfef21cc19acaf41550b6b5b3202ce3eb8116ccf5992ccd19497e76dfe35fa5f6f87c48d404ab2a08960a65003496126cd22aeb2baa4d6ea6f2846e90533538f6697df745cc491b87ac5c18ac24cd8724d6b7b0aa6ef6720e9d8e937c24d096b493c28606327769f865c44426cb478d993d1044b6a7ab991dd58eabb02a0de80c6b2e29520ef4d31e3a46a7ed0e5887e1db022fc4fca42e90e99775ce915ce26bc518cc2b25a121501ac0694ba81d34f7991d4a96f50009139f77d5a114e10d35a11cc7f7a41d80ab56e366c7d6cab81c6e7281afad5232734a0ad7c1621136879ac9288b72baf86a55488c77f0bc557cb76b3ceb64d042192f29aa5d92767cdb820a4523f768a3ed4ce8593360f454f9a471159e2b4f89018153131216a81a2bc85e96992e0a437e66ec25c331127e6cb578223d7efdcc84f6be2c2e93a1f93713b58c5e23722c727021b56d0d3af80b312e82f10d16cb068e3825d380bb0347856e7a2357177b9c3def6177904c498d3a519583e32985bd521a6fda487bac15edfd74a1ace1ef8af95730b014d9bb9c9547490895321ec794cab19e87d2af92ef157a2d6a1b9a76f4a11cd63902fb16ee9329a624808218d5215d11da4e95abf6ffb6ed68055b94a0babcf43701d5537c67e9a0ee5a42a53046bdaacdfc7980196d570ca8df8cf00a727c37b3f83203660c13716d24da6b367614019195982c4970bc5c3889bb3cc9b7ba6ed788ced4c4b132b3617ac8038902ce0fd566c52435e8793578b93594c8aa08882875e8c1909d48ab967fa1edec3ea22322ceb0088a3662138bc23a4fbb682d49e949b38dc6bc08ac75054c268e667eabb1d93ac57a951c89ddf21a2bd66bb3296dc012d17256e3a013d651430b7172d4d92d047e13c11284aad7086b3a709a0e4a95e5a2d7640a1aca146aac6b2c57bb6cbc933635312776b292beccdf275326652e3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d01346112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd98d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd36699e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebedeeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26adf774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f94a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a301bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df48612344be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed786ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab573d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d503f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a94f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1eee5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e11e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a89f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca88742d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e10938b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e4982242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13fb108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e61316259942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787bee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d245460267be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f3925518165cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735ecb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c566dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf67a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e4544be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
-				"03383f9085f61df71557736df9d4e3d54184f8bec63f85442a8d7a1381d8305b3993f0a9514cdab55458973e3f98af9a1be1375a8b52c2dfa0cd5a9bcfb40650861eb7428d34c39f633e8afc60088a9ccb93bb2116084799162e5471ec390a84178cdd09e51b5aeddcb445165142e4b33be4c1c5395cda0fc61befd0e42411f0998c42ebcf55be0a8c94e7c2c5d6e8b6038adfae681a32c6b977cefa022610a738d9b4bcd7f0a330a5cb7bf34b2dd1c7feaf6a6f278384a174c9e5df1fba86878292d5d1f43f4049fcd04c796a2ade88b35e3dcddf64eaf11968cf12b95c6b1a329678eb05f4bedf014d9b6f5d7180cfdd221cd44aadc58eaa04c9461a3f0dc03df985b9e10c6860c80f28b3fecc46cd0ce20d0c2cec18f8107c4a96fee6f2bbade46b94aa0103177ffe5b45be5a94164728b8ee9b5643cf9665ef973279f10c706888bf319d5e8db370a42085a09b7bd5c067492b1576d67e3a587a1982be2fbbf9bfe7abe3f48d2a50d4e102cafa67d95f7d9239bccd78df918628668163f7b933fe13754f056c8952f3b1381a5dd276d1a72773a565437a3247e36731bdbfdaf4f3167550454c59e963817a5dd0f6a3f438f564b23ea6576c9b6debc9b61c0538387566448d2d186d11efd51c9c51cfcbc89cbc90229a018902da5e370b8752f4044b5836df062f74c26a562328f39eee8789cd4d52b7e66a507fb6f90737cb278dc8414eca01c620d8bf9dfba8cc5fbc6db10eac9aaa47b0bfd02baa02524fdb36ec42da68029941aacf82d29f5b1323a0cf6a5746766736d02ad0c2034898e9fa73159188f2c352372226db02308225d1a427204df705576fc8c499b7e3cecbc8f307a7fb019d4b405f9492deee4ac3ccd51862217923ac0acc0bdd3f7ad86093471eb796cc35bd92dbdb1dcf70b44ea3a4b0e9cce2314717ab181108ee8a63276ee84f4df21fb715f3a4cc73f196cee7b756ec996b14e0113d23c677839df5e5843599f86c29a43a6a0ecb73bb020151a98da2352079357d4acd66442f699ff7de56ec214e4f0846bb4555c29b76d9d8c8ffc83831b038bb232875a1354fe99058aa2f8d03f7e86bf510e48030234194917c1ec69187c5fa65d17d96ce3580ab0ac863a7ab6ef207497b3f63ce2ef08d7e1a9d0f6fc5b53eea20c789677f930245131f952eae43ac98345ef8c0dc2a491b08e194a06593893ab1e4f18051e0fe498fd882f5ceb30a3ddaa234bc2d54bc3f121f510dfb72bff123241408500a65e7c4b3957cb94d5135d01a95c95e644858ce9f4891764c0baf68eac8dede086e5911f21c74297c35ed8c7fb734858690446be0ece986e35c5ec690370a07ec582e2d00f9569298bac4a295fe8fb3652b83207fdfb513df2c24e29e1d8051d4b7080327a57ab21a15c877d594f9b93369f732d5d205717a2dc4329a262724086a051b9992168a99dc556bc8e4af70e3ccd4c2e3fc171100698e80e05cc2627697661ddda7f90a9a50df7432850a47fa5c28088b8fb53ea8258b2cbc0aafe2af9e567f43225f91f0f830dc5a3a861052654501aabf4a9d584b0f77f93abb215e680fd5f27902f1ea10ff880a39bb8002a95160b1f53db9f7a7386e22a6de47b259844841710a656c8fcb67a95bc326852027b5779b492390ae7e8f2c75468942f2b5ef8606e5aa6415057781054410d639b866e2a820ab14081b936e21bec7c1118752762e22e0f188c268ea6ab625763946cdbc2e3ea8b37e207d58b2276f716af10169cfbb5bf653a73fe6de0f568ffa1aabcc8fead9f3c1c38d4ee0dd13725ae3e10ac40d1a52ed1d8986f35e36370bb7141e4ffb8a81f56bab0429b887550fd86f0e537d308098a1866d82461063ea2cd1a6236d942000f44624d2c0b1ccc08d30b547a04cc6e55fbf860f0bf4b638e213a16e175a952efce2e7c614418d24a36c46f7ce35437791790e278fadfcac4e0c4bbd332a0daf17c72368fc61fb046662275d7aa87ade5a7c7fc8384c90b9889c0f04724d07c3d94eb47b1910144b9328d770fdf14c75046360fe17fe1a4d07625389ec7d7a2b9e97c592961ff84afc662885172ac5443464fc23c968faf39d8cee0d24292fd261b1f1e5a79e3df1b5b52ed6d3f7ab0db8822b9e44ac72d21fe242050b3d72d15f31bbf88d3570e1162cb595d079914cfe2933ffd8f6a01fe0ac7dfa4993b281d2c998cd80ac679f508f4b6073f4f1608f9d67d26c1fc2e021506490e973c54d07a54f5bc204c01151e09ad0f1ae30aefc95821083f235eab7e2a9450c5602ce71c5e197e742fae10baf56c161096c2f1ce2fa5a793334cd97932929fce2b040cdc1027484a74f901189d4b4f5deb5ddf86c9cf4d722b1888673e8150d8cfc597860796430be61852d37df213623b179f8667576f690770db4343c201cd2e3ed8d1602a27096d2ab6d5b65f8b0900407b787fbeb51176e3dc84ed2bd0d4fbe02051c422aa346f351db5d1e02a2fa2e50a6993ef8c71b3e4c72c2d283b53c423b4609c91f4d0bc432d81756f5b548a6de883f82a4278a7d1be74cc7e46cf524a78c50ff0b507aad48985a8eca93173c257b56030a8a9e07f8bef29a991ea0ee677f0b45cbac244c7a937a78872e4f4e4892b7f7e2613d36a668bbd96490662c9b153953c588650e6ef83dd829aa759f10683dc7997215172e01e135c66752e447b4a68d5ad06183047e2468abfe74aeb24f2eab6be72dc61b8b901673d226d4162e1ce738f20043e3346d9861a16f6f9cd25af0d0726fcdc642da7cec294880adb3aa4d8cd56aa8a9e9a31feda62d931fc8a2f8a18d39ce71cceb39a0a2022046b5e5701100b94c8e82c4112d3eefd01292002f1ec038328834f10672ced0aa7b3f94a093050890d8cfab9e2b3f9349fe61dfc90d0c1419bfc63235bb060c6ca9907162c9f2c0c1f090cf0fa25824dd159beed051e667f7c9471a8203abc635fe0ba97039a3216e9e044f02b32a0b0b0f73a5b94467a04e012172d0158d0980dbf4eeb5747235738953e4f8d3e4fa3634164b5ae6cda6f1784a7fac24d37e9fa1890b26a770d150ad72822159ef1942087a7a227dee5b01634924eb86b7f7d610eca5ed16138ab1cade501585409d6b30bd1c18d4b097af6571d113473618ed8784151df96d9031cc1f570ca36bd85a5953da2a34056a88fd7321bf89f7b939ac8f4a27bb0d6946f1ac24d3a4614193c8bc7ac172acee5788312f3e8848759dad00556949b96e79f09945392f208eb5c4bcaae26d77b03070331b420e09d726ce9fcba4062c7986ede74aef2f7692106dc4178945e33f10c42a3edcb28ea2fa1c36de05245f7de7bd8eacbf5667cc23d7fb5456f23862404c784a5a6612f7ac98065d9a66448b01cf8cb5e2a2ad2ab0e79fc6e2db723dc1a738319e7cef78c4d7ac37d5da1763aae117cc4c0afa928ab6a56ae61f5cf38a9cc2b203a9d50fcce22671a441ec0ea8e8dc0aac0d429f61e6fe1f2f66e7603c05ebc81b19d5d7c95c0c85fd61567f40e9b075105c914ddd930cca4288ce9628aace17b5f94531688f5dd61222c21b4bb0b486bfed796d841b056d2e931175f81574ad34aba3cd1d6d7e494c1e1cb78995039bd592edcdf2a23888b635857ae6211a1bce16d899a74cc365ef5a0c015e99308c1137ab97a3b1ad0eeee68a56d4317ed9439003901ce8c83dd30e7a5449814f50fc8905b7a79e1dde18e403b36b6b2ab18a4423cc740a2d24d8322d15500efe7b0c6f76c0d81a1bdc2a7127b327a080e00171c357ca4a2a44241d998e9fe7a9b1071d231c83596f0cec9d1281c8d093cf2a090dc3578bf6e5fc13c1208ce68cda1d9674cc02e582d1e894ce2140c69bf5fcc5523afcafb5aec4133d2fe80cb5360ee73e3b791dce7e07ba731caf7b505bdd01d207ab2635ca25004002cde4b581aae39cf5b8d2469185d78c36e2b8948d82fbc342fd7b654fc52cb35bce4ad269f434fe24d3f9dd54983a6c4c06f0db338b73850eef29e99f2f5e1feb70de9bd2f73fb705e2f154e818e6a56096e36901307dd22011796be9f1d05ed5d5751aff6a1bc2ed731d5505b5d6305f5509192bcc17659d10de8d22d272082b17544db30d833f149c4a0b3544d5a478ce56bae38e22b9044af4f9c997e6b9d004edb43cf9dbe6b49a20755d58f575f70431f083b151f1bcbcb5713c572e2bf7e0c8b611ae6d2051816c8500c751a29352d4eb0b077754c8879e4968774aa20442bc12359e6ef1209a23d25df343641e857c7c4031c025309867d91041034b9a9ceb620768bee81e2809f04c49baf0d6179e952d257fbd5a053938de5756b82c552a4a95aae87ed2f31579219598b7635942c724886f0f7dad53c218ec32e36170cef879d571d843ebd0e14e92272649cb0a647660667f18fb415155c5ac92ab432d376fae7ef7073d8df8000202fb80b1ba261b9275eaa5289242809b4990038bf9d837f903030a932c17e1bb4d95f93df6f349575e061f1ee8b367d5825a55c5e43237054739f20fc3638734333ece03cda62dc5aa239deaecd18ae4a52a9f24d2dfe7c50fa0e14146537a4c155c7036dd180ca72a82927a47406e2b0684505e220e4e0f08374f1b0d3426fc31398c2717c5f4b6f004705187f5e1dbe0c477a84347679cc4919b560a5317e55c2e649481e2d3b96146758448b255e0efa9cc16e8d2f674a9267a6ef91960e995335db3933e4e8e42e6f6eafbeebc9fc1d4846855e2f74f1e0d97b74c66edcc0c3a0c67a412875e8d79bf76cfd018f10e3751aa7916275af1f96500d2d6857be7c0860916525997a4cd8daf2f44e44046dca7cdc9d8f0ab579cec870a1bea5ef5a3502fc99271d8b2a826475be3ea0ec3e4fb79b4ea0b35efeb8dd4a25fdd490356fd9ae74b377e51789ac77c2c1d180db2c24687ebbd9451efcf60b16dcf79e4f29be606f4c7e373eb1a240011d8c3567a1a2b7cde05e4b69b9487a5af41fc510da0130bd556011c06d9b4e1595c69ff9e6d822cb6a3de3c782bba90f02d6cbf338b6e6636892d5cb4b10e0f29f357714c0e444c370a8402ad2615d430b0ba04ee857c7266ed0bbfcab0ff79bbfa1811190e03c07df9343b9009a74d711a9129df639f4a8983ad793933c612eecaba19939f0ad02e54f20362c92b95e9b28ae914c731dab52995335dfa8d5c659d8c2ffb51bba4a63fdc008a965ee2f5d2358c75799b09086882c9f0380c3228c8dab6be4021ae1f92599f33afe9372900298ce637c58860e3b08069fecf2aee8b03854b7c903625cf2e9c1268229c511fb2e932bb3952ab024085bad064a6886f13cbc265ea9453de9e23bba112482d512c3247b98be799ddc28ee6eef80af7d869e790f635bcaa43b9e9d7d53d208c97b8dcfd2a6f32ae397d73fe3d36f83dadc47d682737c851625de5bb33370e7b073753415a77af0b547dfe869606988deda3bef30d58b1c2cb4c9cf39a198af96be9e1e05b572fcd55226bdda65b3b360561eb2a1425603dd467370c75124a162e609e8a847b1711a14cf1ff36b8fb7ddbbe346b096612e77c5c3a042b0bc51aae1b25e479e13e1f76d2c6c83f7d383600c4f5514214c04da4884f3a767cd8945ad3434624fc120a076f3e806d95a5b411272e86ddd8fcfca95b3748fc7550cbdf9dcd38f51d2efa576324c22cec800e81ed55bd7364d1dc88398e4cab0c6e7a2f4aba779455efb1a00c6366c151695495691c658f713cd3b37046d48137a6c3f92dc1e4839f4d53c0fa8addfc23fe13c6170edd2a5560b8e85768b543089be7febc19ddc56847d4cfd5c7122cf5e3fb5d5f2cabc18121017077199b7680940ea3a309946aecec3c3d922c2186f2c6fe7fc3e520e7a3714efcd86a435dc224f01cad9bd5eef5c5ba1bae06de04aaa78a4629c8263427dafb02c71ac6230d45aa88751db537dd574603c87f263cf297bafbaba7ed07dea7b01395128f837f17660bc95eb9715493e7ca756012429f8aec9758fc9e6fa8a08fbf4f2c98a5efafe0584c9fcc9f69f81981af0a301831b72bf8e4efacfe4e7a8578a8ac1cdcfdce4c4cb046b7c42c648ce9fbdea35ec1d3c20c710488276d2556d42a96c86cc5c5c3b49b3421184c94e4f03e6dd711b86a32a73770501033ff50d0e67490adccd4c72b186a97a4314d4e93d1f90b8cde6c4fe80d1e4cc1c48a200e41705afc20c78fffab6ed403fac59266fbf51090caf65e4cb4c12c51e0d7b49af1bd1f77bb5bb27d9befe1b3dd5abff9d3e82ed765166aeca221287980ff92ecde3243f44807faeb4d8229fa64d5f58ad699a604197503fe640823c151dc0b7cab58800c3230f8db81115963b7acad68faef46e0328912c86ffa5053fa878cf93345a226c149765a9d2e261e13347ba9d545d7c2bfde966293f37c59e3363015742938bbb756dac645215bc13419d26a12c07ab818817338666d246a8276a113caffca7f24fed0a09408b69693643b8d0a4d35fb555da096f8f1f293cd127c5f3cb6aa405f611eee1aada3db7102c783dc1e1a13f90edaa1cce7a9365d10e66115e84b7f6386f0e57384e5cd941836c2b5e670f648a043c359cf70d3d02aa7c073e71c1881b4f2095754848c5274f7d0b2c7ae35c3faeb40de8337829158ed6cf22c2b9d51240bf57f5c3a81092aa01fb31d297b6b1ab4a6c54ab303ea3c07db6eefd05e3677cad9dd0fe0d23d2aba63c232bf3b6b0731e7898da46291e7ee8ccd05b56f948f03bf1170eeec714d2d4850c228c96fb48a969ae64cc7b324e69eda6df389728c1eaf673eb512b59f7442ad217731b346da582a8b25c97494700cd4b7f326dd6bb5b8de03e0afe68c12ccd924cc5ac1d14dae95eb5c276b47325bcd1627da150f9e9a1cd31c15eb42c674782cc3d4e189908173cd5e267cacab7f476999f47929384b6f5f87d0e02debf42b4ff57f531555b4ad81649237e027070f38c0b849db7107f05046a0a30900bdf3cf626112afb6f540cee9da095f4e4d7f690ed0c615b233e17f650ec9141a6721ae9415947fb2e444277061c1fe9f3f796b13e88a510b51ad0b5548e73e7d29ecd198f20cf4f6d3c250ce1678ecdce90911ceab85ecd992f98ad8ae7de1514d710817984b20e896fb2ee5613e91892f233843242054edffba1f958c0a16d08556be15546f1e3e4f6434101ab0b067bf95f94e0d50c0cf25bea487467ac4f10f8b980c08ef0ac11c8263239bf8a79a0bc6e2908fa4b682fe5e462e9d5cbaeaa3a8de6b232a646df34bb6830cb1329622d243d76cf471add577fa7d0ae041a43f9598a0e179af57768ee38612d92b1ae8655772db2a05a6825f11e6ba874de5b326410f232e17bddd09290819ed932dda2d1260292296c4cc9bbf8d3db26754e515b0ec8088f0438d0b58fb7e18d7443d7d9760f6d739e7ff13d868ac27d165c962de749b8db879bac47b33d50dd721ceff662b8de95a70697b3f754ce0d0d3a45c1dd65fb2d7ca0187559244fed5de0676c5646a6623f4a1e875acd6f5742358edc71dc64cc9c028931298239648d83fd9a801646539927ecaf506be54a72b9d4477696dd65e11205c69f61b82c55a2f9ba37235e8169cddf7db311ae41ec9f1f32f2d46ee21a032e0ae1218761045959523c10aa2e3091a3de6b517033deb3c7f3ed98f59416908db14d847cc5c5184e5d3174d60b18faf07b7c2f4c803d323fc5166038cb1bb8715f4170e609e37a1cfd9678a64957c5878660a7d67fa88b6db1516d04292fa61130172788c759e150262c694dffd0e4a54004d3f7fa2863d5269ceb10d53bd04147d04dceb830578a46d5a617b9db3363401c5545a9ef1b969720069d5b710cd8d2dae0f16e3a41a4931062ee8d43b0513305a175f85ce429f5ea2f36416f8769877d996b39d8145f7abe2cc062cd1254fb8b5434ad5e89b42b48e60c8bdecac12b73d36f6884b1b91dcbddaaad07ba63299a1580b9cefd8099aacc652d632f8d08b88375d96d61644adb4652202c862b75b17b61f7510e3bdf074616312435b6b54e93c48c5788d74544ba195b4636eaca38a9a13456fcbd9690613c57cfdb97adaf95fd30234893776e559b6830adf385064e2a5631006981e1ac848e95769114289473df1a87bbd4d501e3a882a7b483ed518b5e5317efc31e90e2b595d4852a0c2f843c872d3d526792cd6e3fa4bfc9f873bed7687c463a165469e706380e537383b5ed534157a127c77239d0b2470e333dbd129fd2194629d707f5954e444b6a2b7d07c7b7a2c168c71c47121b3b38b41ce76b18781ce37bee76eea812afe9f35e29e2b3b1c40e8a15ff1a6a67574238408e7d1a8bc3728937c9cb0da73b57fb3be153818c73a41db6971872024ae94acb00e68cc9a85469b1944e7e4599e12cc0cf8dedfb6b2d8916f2101323769ef7f1d0e0eed52acea1c2809ea3050782e5a52a768413606ed07212afe6ee16cac04ffbffbc11536811c81187a2ad350be5eb06d82ef17a75df9a3d746f37fcb3bddb7401ed05e3fca749e0def202df033251a2db806e7367525891a4f2a5a7d6c5de122723691893fa264a6a61783c3aafa70c09f71bd72176799530c65ae2225ab6334e422eb52a01ad7ac028abff2d53fc7f9604e5860232660a0580fbe75288eebb6ae15f78e70d1f06e56eb438b79a3acbf714b994f3900d8886fe4dd92a2d6087bfbe7b3a16f001ca2cf4d519fcc48720d667610a3d7598b586760e0f51193dad94c5bfc24d686fd02bfef706d0b3329c113196612989f0da22339cc0262cb6a464337a1765e9b33949b94a1feab43f3bf7083a89a2f09936206cd7bf7c6f4a32f96a75e57fbfc3be9fa3da3b316aec6c50c54966c8ebb848224bfa0bbc68e171a592dc0acc7ce2e6753b54eefd0b11701d881a987ad4e51209118678eaa376fc1e47767b0af8e159e07bfd823586ec508db573c855110d3fa75df0d70b9a524220186230aa84ac2927e65b0326b54f2d7e13342113ba82ce070bf7fb322e58659f0447024d3e2d8e72232f8f7ca8ea99b19e92814e5c81bd2c8e3f64eb89d425bc79e09e110b0af1e644e767e30bc7cc6a4231570a085460b8699933b9a4caae915a29be50638f16e6969faee3e4ec6b2d6b53f84af3c6be55049c9b2ac412462b3886a696eeb2c249c9265bdda8d323adb1e40134f58ae1f64defc20facbe0ae8f2924fe564236fb1004a9604e90293418c74c2cde15af0c01512c77368233d1759f4dd02f166e2dcf6d4ad04cd91161a775abf279f775cf419fe140cd4a01c00598f47eb761af1a333199a476f5fb26fcc6c0489d88a0eb357e031b6c1f8fba99f449e3d33cbe61a16cb6630c504d14deaf6d2461c8e48e186a76239ffa3e7141b3e901384b2c37bbf55080edb7f155ed07f73bb4575c5d657c040f90658336d5b47ac25d808e9c15b2ee589efbe8700eacd36ebdce4e05fde768de50dd2274ae41c60d480e44921fa9281a65cd462198758865d1c82d2d5c8b13ec635b18e56a62d2db9d2416f0c7b0ec9934d06dc61670344c502dfd13a1f24ef1c3f8b1640fbb9da42868a5cee4512ad3726260bbe0c80d42b542059e0e007b1064f6e2409b3b99fa6134db2995fa89fc89daec591f49e56363911c79eadfe51499f54cf72bbde3360289e10284d23ce2e028f23560dfa4cfbe58cceffa234f25308fd1d28967d93e30a343691fc4d4046fea1147c9acbf0b7dd11a1469437a61a3eee9932dca81a2fb50a35672ba7f5b8b2577182e604ff5a75d279af4233684c2c58bfd99de5a28a62fa8f07f5d765097a79641c7854c834cc499db3da7d317117ea70afb50026ad4a6f0e8d1f270e5de83ed8f086f10929643e114bde2344b9baabb379b86156622ebd35080462cb493d19dde8a04b455a8a6c67c9466d4aacc642d808598ac362b4888267b46d1c7d91d7d17c3b5b09b3040f566be91e1675a9ed6a7f59e7e54594de27775a8906761c6cf52661c8b6235d45b117233a1dcdb8ccec28eb9fb969d785a4e829ee6be6299e070f51a7fb73f6242a6c3d5e41c6c3dec176ce66ffb640350b4bf8bc489298092e9d27e44e181fe10d50f86593df008f8ba9e521b549b884933e124d6d3f265be66e9986ac9e86c6c903f60ecb2c62acbc1cb6cc478c54d9d5f5734625bfb89e8610c86a8fe5ab45a688fd1af9bfcb56928faa722f82a85e233462c0072f488f44e2d91994fe54d509d444f5bd1a8c0d3bc1d88d9711b7c447e30059b4438b83a048787d6bc919c6299bcd663e6088b27073399486927ab85bd7b8f60cee514ac276086c90ee2cb375d37ab823b9a425d217eb3a8d2ba2a0059101df8dcc9e4d3a82c58d2b3f96a3e0c4dc00b706bcb8eb2ececb209f759c9b6f674eeb8a752ff8e66c16410cae20a6b02dc505717ef5ffef5761510da67367e53ba0274c1379010c4d18db1bfce4d6b681bf1a66b8e695a419f6d134d5ec59826665e10daddbeb4a80f83c78fe92c117c9466c7c8837f66f4c57c379eaa407ca9b4e43fa477466067d5eef8049d72eb7c2b493ad0b7ce0c494ff1f53c5690aa65c75b3fb3d027d1790a89142a79f8c39cd9f015a52bbda5fc07935f2328c46dae67ce5d5c65a10e91980a71ce2910519ecf8c018f5b17029f79f735c8bf0d9c044f2455d0bb40a132029186b989291608e21badd68160f48dd8fec5b95b5b26d975456421502cf91c56fb5ac4cdd489c643e72272dde4a35d6f062a10f384ebd9a71a487ec5959cde04630678eb54294bdb835d7ac0ebe404a1e8edd28c5a41b4622188511bc530fbc81d26661f594d825b701085905d925eb976af765ea89f93079af888d49593f913d78365fdc290698f69a3d5fd1ad2d2e5c2ae59af407863faacd4536beac7f8515350810940e10a92c8c9c97153e9ce088d11ec43952af719caebd15e02fdafee286e7a7e9e0dd027ec77cb7404bb562d5c5320c9bba867ce3c974192b1301f6afb6e03dc9c5be7de3e5673b1147db2eb2301f91b48995d145df254698f0c60498547c55f7ef62de081ddeb42505b62d6b18d37269814db597c75fb3c6d8c2a062b391995c7d332a9b6f6f66ba502bdbf69b73e0ff54e8337db6f65a14d58ff2d223703cc68b3c04bc10ba48c32f29ba763167de9cad171eb5cc65b7247178c99c35774adbcd241b102914ce2895a220b50b5d7a9776280f3d4b898d3ddcb6b1e14f2702ae71c20ff29c849d874323aaf6bea30ae2d31b151d96f9d7bd786cbdb5db7d766afcf31cce6c03240d4db2edb7dc3810c364e203d233b65fcd16d4236d0048b4e6ad9994f83fc294cf4a8594cf0f696f4ac6c7b378a89527119c2937a942e001d523d2d08f301cf9f060531dde931fafad8aa348ce6cae1b39ecbdb1d6ea0d111ee75e98e307f60be639b8dc3f9b71a1a21ef4063b4fcdfb9ebc4ba59b109b0254e383d814adf65456e93f59bf4570bd4adc97242229dbe96e5464b63ddc192c265d47b6f96134be0180e659c897127579d9fc90d6d34a3b720c8d340e29a9aa250984fdf17e9fd71d863f88e11b2262c31e913a6aa60b8445ad6ed0ef654474886a4bab7d08bfef21cc19acaf41550b6b5b3202ce3eb8116ccf5992ccd19497e76dfe35fa5f6f87c48d404ab2a08960a65003496126cd22aeb2baa4d6ea6f2846e90533538f6697df745cc491b87ac5c18ac24cd8724d6b7b0aa6ef6720e9d8e937c24d096b493c28606327769f865c44426cb478d993d1044b6a7ab991dd58eabb02a0de80c6b2e29520ef4d31e3a46a7ed0e5887e1db022fc4fca42e90e99775ce915ce26bc518cc2b25a121501ac0694ba81d34f7991d4a96f50009139f77d5a114e10d35a11cc7f7a41d80ab56e366c7d6cab81c6e7281afad5232734a0ad7c1621136879ac9288b72baf86a55488c77f0bc557cb76b3ceb64d042192f29aa5d92767cdb820a4523f768a3ed4ce8593360f454f9a471159e2b4f89018153131216a81a2bc85e96992e0a437e66ec25c331127e6cb578223d7efdcc84f6be2c2e93a1f93713b58c5e23722c727021b56d0d3af80b312e82f10d16cb068e3825d380bb0347856e7a2357177b9c3def6177904c498d3a519583e32985bd521a6fda487bac15edfd74a1ace1ef8af95730b014d9bb9c9547490895321ec794cab19e87d2af92ef157a2d6a1b9a76f4a11cd63902fb16ee9329a624808218d5215d11da4e95abf6ffb6ed68055b94a0babcf43701d5537c67e9a0ee5a42a53046bdaacdfc7980196d570ca8df8cf00a727c37b3f83203660c13716d24da6b367614019195982c4970bc5c3889bb3cc9b7ba6ed788ced4c4b132b3617ac8038902ce0fd566c52435e8793578b93594c8aa08882875e8c1909d48ab967fa1edec3ea22322ceb0088a3662138bc23a4fbb682d49e949b38dc6bc08ac75054c268e667eabb1d93ac57a951c89ddf21a2bd66bb3296dc012d17256e3a013d651430b7172d4d92d047e13c11284aad7086b3a709a0e4a95e5a2d7640a1aca146aac6b2c57bb6cbc933635312776b292beccdf275326652e3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d01346112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd98d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd36699e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebedeeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26adf774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f94a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a301bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df48612344be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed786ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab573d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d503f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a94f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1eee5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e11e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a89f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca88742d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e10938b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e4982242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13fb108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e61316259942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787bee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d245460267be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f3925518165cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735ecb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c566dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf67a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e4544be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
-				"03e9f01ffde735a486ffaf4a881d32ba494cc7ae5e55b4756a6f6f640981835e55634459f96d9b0216eea50e56d1558c0c53be1b3e108589eb39c0136594742a0a7802b20c349173434a863e645eac92dcddccb3db0bd69370dc9a2253afa72d97df378f7a66f882aa695d4b4b561e185bd527910e710cfc62f52fdf53d3dc881d3f6815ddb5c4b49a327d2f69198e6274d19a61a666d9aa2f0c2b035b91f584910f33f9a538115c17ed80d4870e16f14e5520e27abafc4f37fa1c872d0b5a9b47037a5fb7fe6e754ca05f33232c69403b71f62d97ffa39ff2657e829a7b9d3bf095ad1a363d50f0d37a72c6e26036a68492288b9bdced8ffdacaecfb46475876ec3a28b8fdd34f2e1df486b321c26d2b3775b284cd937ae1648cb5def50872ae97ce40c499d4687c226a7db5697260d34a2fdfe2c652ca4fcd4ae40e0aaf7bec33e44a96b65f50457b332f8d5c5948bfa352eded0cfd439bb6c25fc03d10eaa281cb1dfcf771fce536d0ac7712bf53f84a656073716b610eba64562e51d55efef7c297a56e149b2fb2eb428011a6039104fb169cfbb5798d536d715c6e106f72bb88aa9428e69a2b0b6a7bd1fba54c9757af27a139a275b3550fa4b06f1e8e95b380be1c4db016f93896b9753b0eb660a05c9876c031516e1e0274c370f9ec349680a8050f783ce9852164267de5ec5ca1b4c2aa49749a392abe83051897675317d5ad6bbe7a2ed5470f312b40310aff5639daf1e44b0d6add11d1551c1ff76f345b4d890b1ad56aad8da87a25997ba07d1c6ce6f3e4070e679310ab122a2465bef28685686da89612d56b0324484ae64f641cdc3ff4ce0d2c1090365dd874b6b865977a03d7772ff592da05f94cc7b19257d5904196daa986f764f04f46640c11d2a00f6009cb89077270caf3a699e386e2a679f3f8c44ee9a62e0a5eadeb7c3fbb032440b63af69623bf4be092192470a8b42fcea8e3e0de7bfa257fc4c95b8ca4e2f4d3c1367a7de83a428a36de7328b3dd6d8c84802cc6966bb8802a12a16c1dc877713ebfb1ab653aeccbd875bbe8652b647345087097d04d8577f790e599c4e091c764097036a4eebde3eb370adf2f4458e192deffec3555a2c11c5a568e988cbf5d2b02bb9f1898ea283013cb42fbac2f501feffa738c08d3fff3d3eaead37eb688e097b5c0c0018bf9f032d25ec4641fb831874be16fdac57338ac1822a9340753ad0e536213a87ee6384c49a14c11cbe83280db9aa37ae07a9dca0a590cea399c4e23beecc6e1e1ff5ccc4d4fc05bb79f8409a1d2331bc5a31b859a41532c413132e9f388c8933562bcc61d27c9ab9689ce8cc821f019842bb546d651f2a68cea349f36dc630b29a68f43fa127c80262314738fbdfc043ba517cc2c015be56794a8b7862325226b077b612a4b3b75e144c85b1d123f41634fed56b6792bb7c5c5efb0f466ca4e2b05b2a07b170548f7324ccf9382ca8db4aff755e6cae95e4d1c748a028a6611168169e49ce988742ef552230635b49c815fea87595770173c8f4a442276f070786a8f5df6c9da04645b084da2247281e30ad7402fa54c8d0d9283b98f04c6ff097ccf5381dd9efcba98bedaf58b0c311d136401bedf7ace4ce27614028a1268fcdbd4d9045bd17cf799c92896bb487e7ef79c227e3302f339f1d7b6152d9257c43f2d14c37842d001fce1d8952e04cb84f7e2888d14158fdcc7f1488abdd392cd76bd0d9298118d8a8e6bf492bfc63e2b52265d0fa7178f4830fe56c0446f37343f91d89489f1394a62a5fd0d732c07905d32084912a1ed2e0ef9ff79115cee167c040f39fd238090dbad4e64af6639452ab5bbeef89af933d08cff92a03d2b8a9e2baf58228857755e3553f8bac70b738946d11ea8a40a6820d8b9d9e0a534dea54f4338bb304438ff465dbf8d105ed28cbd65af70e98e75cef4548128effa5f75a123051274f26663d78ba29ead801d8647ba13a1a3f95af2eacc559df2d220f923781b8a00695bd9a4ea4b471ce417799b840d713e1a34edfcdb2846136cf14a5cd98ab098fc27191857e95ac395f79b797edd9aa1ad5515f2ecf28bd4e4a888b4cb0912beddd1f40ce7f2ec22622710f3daf16a812381610a2b7595abde77cd480ec457accd8eafbf6aa1477e1f99f3498b161ce54d9d1991c08ec473e46c86628103399cd414e2cb2fa8d78f0c56dd0b6b242cb806232a5232853b11defa98643375dfc149db9622c46d95c428b53243e4a35ec53afa845dc791fb04e6e96faf66da30b3e92f7425f4dcfb8eb78546c9e1149b8e2527d57c3bc0e481593e1f439d5457e2433317b0cdb2fbc80dded260d4b0613e88fa82a176912552c0ad6945f4d26c2e29d2aaf9879a359cfcb6f88412e0d2595f1158a750bcb899ecfedb07f42d59cfd4f36a3242860f017bd211dd796e8b455bc19b87ecb0fe9dd52c3cfc342b6a9e86365a939179df9778a4b50b2dda4227d49f773e60e1d81a52d5b9940903318869e5e916bd3d69a52f6e895e58b69ce6748094884fdf10bf6437e6ce5e4813ff37d1d8a127c4cfce3818b549a40180e9e0010d9ee5fed140c676bb0c1a2c618c484864e900cf5e3e46a44da97fa60b2f7e17c33c1d9d95bfd8ee2454036734d16dcb731bba606354758d3e87b3cfe8c3a105e86888a90b766e7a3daea80b360c256e31a10f2bcf420187eeaa6b033bdcf7bca460b0f446956eb8499f3e516a26e378d40b927589f9d5532b88b90d79168f7e9dfc81b7cb9d0c3bc83688a43e4438866a3709332216993f0a6ab88864f26538151f91c8ee274685af3f4f858d83f7057a12d301a7d2d771c87f5e9ab889380d1916f63972e130baca2b364c5b4a680753faeab7af876234e475c56171a8218789d93202fbf7e05a9b72801a3bd22e55c6309f1f02c24dbd49691b95d38c7e5df3dde50b56010b65caf9380d05460735f2b91e18d8bcd5c086a2fe54b6bf4f97cc812490d7d0b092f470bdfec096168ba1cadda094876caab9aff1541e447bd9c3323416e1606345f839d3bc866f8e633fdecb0af6be9bd5e2adf32eb0f50b948b68df87305ba0579183410d98da4e83e6df5f88ece5703426d22c44454d304d99b451f8024631b0ddd5d51616b2c72f5a56980c060596192e2463a1e0288b0e6ad089a46f1a962c1bdf8052950381987957336fd1f5017ff37475f0f71ad39885428e617aaaffa0e61600c0bc40b6167f13ea2334f18f73e4e322b38b555c4dba014182e8319b80a38ec2d48f1b48b1bec2fe334d8dd44d6b053ee2a2768e0cda48c707a4ba95967fe32922063eed5285a538b83d5daea206a412eb97fc732446094d8242657631751b67a54bd7c88a1ecfbf8ecf42b6e38e56a6f03aaf3dd65b2ea405d18bce8b94ba3f196d60552bf525d61abf4cb1371af226415aaa7834080dbfa7b93e7a206895f839cf3ea0c0ea64cd8b9811136bf226fa4003b3be34e16e28d8b11c9386f53743b3b715625b1a6a5549edec026c600c096e06a853dca5a5d44247812b6a0d3a8dfc59b2336cbb0ff3ab7edb7d72d93de31dc7c28ded2a0a5a483552d85f35b9aa48c0db2c36aca2fe4f8ab362e7f4602740f3062d5911c921b3b258a435bf6be26565d3793f46bb6f4c6dee2213f19036d510367fca6d699134709de72811af07b04c00b55d55d6670b5b6ddaa6ce44a56d5da3d6cc5c0b8d7b95581cb9f0d7dae5e6f6da29da60ade836d35160554593d7855713c8e7cafc738dd4e803aeb45440ab66740461ddbc606cd8059d4374a99ffd59a0c5e27d61756b1174ec7ee1a0dc106edc04e30ce8a7a026a6445359502d112350ff9ce10f2b5e3fadc4abaaca3eeaec544f7d9794107d5107db86b8a87345507a3589fe1d8ffd6bac075b2823cf86a529b8c0f3f0a31edeb8c1c946ba9fb211d22a76ed52c25b816630b9aa01ce1d6897db26807f57b82b8faacb9330b5e00c0e4ad00645306f3d299efffd2676b831db296911fd1bc506cc1dbf536093446253a48b52f3eec3c821fef533b23d4dea3c99e08e278236cca2bea10449188bbbe98c8ac1d8eee5d5e6db7a366974b3908699ff21a2b3c219db06e4d74b8e8ddae47904bedc3b524b5786601f9a880fd898a171681515224f3d968fc67aaae50e961c10b54a8e7e1fea0b0a357032477c8cb4ed556d267937ac47fe410cd341f6020b415027238961d384d703f8b54576185c5d70296431f30383292ec14b3942aa880706988092b87c2c617e053558b1cf2bd6c7610f4728acf5d622858b85e8ed385689cb11595033bd2f511bba01254a2d4855dc988143d2bbdad24e115a6de729199d889fa3e61b3b64541d45957e7ca9e0c320f9572f7dbb901fc5f94b2d82555f3138c96bc3754edfc8d5e8f664ddd16af9d87baccd8756fda09eab4f5cc29fda1f55900cc1af62bf09e9de43ad2bec74746509637e82fd1ebf38e80344ad229644490f8a1b9c0b9abc3761da42e164b955b59c9210415425e25ea174a0d92c1796a7abdc62f7cabb5627ff533912c771840c578d44aaefc84214d5f42a9f00d3537f2d302006a116d7fed10bf8745759b4b1cb75afc76dec9dcabf1634abec8ab0c0333e65eb9937afc72e9a709c97f0fe7464344aca7fffc480c1ed1a676072c5248e29f203e0db8f5c503fbcc19c223f448902a0b6cd7f49d61ba9597388c1e9ea88ff06ec5b7cea2d295825fa94b5d8c2ab17e057477184d009149620bd8f4eac656a31633dfb444417fafb6b38c9d3823d30620eca4eb5809ee3cc6937f456ce7cf8af8781246af65c32badcbd1fdfd1673bebdaae56e817816d5642b86ed8b0a7cda7cb6a5e576be60a2e2e211f3eac7a84c723869e9167aeefbeb720c25d6832b07a9b359f8436bdf482cc33957a427e005bed32b0d7dae5f7d0827f5581bcc14a22c73b0dbcf8e98d546052554a95f80bac9e21c177f2c70f3abb1378664e25a7e206e8d24a59c6d8e0f2093d6f73baa92fe75eba36f975aa420e70384c298df7d38453a35b884cd3ea34d4f60fa9f60ee11d48ce3a6e1292162f67d9030a049f27c4c0f69d773717d66c51bf37d8fb56f7c810cb5c1667cf3c13a7a72bba24cf3482e0fef98e1969c6af58de6c16fcc5be2a8908e57f87b3bdb93fd692ff4b823dd5ca77f9a4798061f80eb392bf88d12cde02706598f6d986a84c28c275424c0fc5baa7e9848b738a6658ef77402b94e5bdab4cfa069ee6d83bcf790967ea84a8bb2832f4e56185338713837bf486014e3d30f199c9601600259cab114bfbcec350347ed05a4d2ad960680cf0d2ba7a9c5367769395ada176a45e1efdee836a9a1a5d6440924a274dac36ec7a7389de2be0c69de33d0ea2aabc9f996aa9532250d141d41b0c9273b2dbcfe5417bbd1bd7937661a46e5ae3483e5adb6080443a1f0867036b176d4b4fdcdbe2cc410f4e591c6458b7c7d9a6a0a67c351ed8705a2e9708725aa67a5d58bee734cbe03fab9979f9590f3b2964d0b843eb65da51bfa3ee876042a1a61cfeb7a8249854179426b2d8940baa85f5fc9d77ad1ca70d95ee31843732b3a4b1912658ba3b78f89e944e8940b310eac6dd71f232ada67b99ad61956b6541af0a27c1368383f0a00abbb14e84afda650119661aff626470fde7975175d1e74d81e6adf9cfbe08821c94e438d6e7e4c8aa45bbecdf47cd7a623068c0d18d0b92100ad72815995264ce8c5deff5f3e970fb6fa0c45fe0f403d8debdb87df83ff86e121e230998f4f6358721ee6c6138573eb386df2a43dbfa64f9048ac470bea6294893904d42a2e4ce3cdd53b29f685b0e2bd88942d08fae980e78221bb1bbbcbb4f81edf4cd44ec8ac306c94901520c4b0d9b2688d3d5185e591163ead98ad9a8d3f721f0254fbabfd863d290ece83c1bd38084440e973a178935a4cae3effab1f79f5749661cccb515660cc8b3b9c30c5b7a69aa6e248322b9c6ae4d19152760267afce57703f090ab2ae4f37722cb520a7b67d935ae6bd8d5860fdeff97f56f14a33980bca09896a53ceb8c89660037ec8009fc7c5a6f2621d0c0fc4c54f307757a80d5445b3632d657055b8fff0eb1945e910d9559a5376c08b680b0bc1f9ac81cfd00faab560a45717d4c81137abaea91b244a2aa150deb507b9b2b1afeec0b14577a5653b4b51748ba07d262aef675fe091073bde9ec47424f55c44147f2f682b071f73c354bf47e9fc52cbf33c5d87dc6f21a4ff4cabe2281b3aea26659380296906e0187804f601a4065268f7ca4beb50199f1e93d6c239a42711772f3f10e7395e929d1c5e3eea50781a3f58e8c49fafb519cfc352ea3ac5c43893fd36ac70b83ff0446c0ff2cc49eb59d833217828b3b1b2741b68a484960479b3789357b04dd36a42d2d8aad9a1b0c4113ef70208a36fe4e55f1c06e6ccfefb243c768bc004c1454edb67adfb9615ee05aa933145e69fa96b3a0aa770ee045001e03ae8393a158e69683946337c5b16331581701136168235dc380e8653c6c61e060c18f70cb71ca3fa461817b5e36c0eab8389f811f4bec0f65649b9074676c74343bf1584e869841497fb4a3726775c2a17f649c1f413b1f4ce6b897e0e1f89460a7089afe015211a16e2126b5a2189997b06effc58306e399ac62db822b7724ab09711fb2fd1e348f4dbd6a544ae6a9297039da254c081eb351206187b1699d2adf4b240d8e415ff5500f3b6fc9f86d98fb5c1f3392beffbfe9686b78ec162f1ba5c7523bc95b8e35eee142d001c8d4a0bd0365dc4645c15310328dbd20330a64005e3ba9e3396bd80673191374558d1070edaff3238e68c9ae615e2f7b1ff32c5cbb6b70a3eb4c39f2dee2ded2f90359213cd99e048e87a9799f418551e5de54e764ba39e971925ba17309f25e9ce8bdc6926f0f26939ef35b274db38344f29ab57c21baaa0bf91f0b410b8410f0f03b5525d309358abe004e2e41d49140f8be0c12f945f9202fa77c9e2b5d98b535870295f99380f4e103954d119d561180db88d04d40cfdfc1fdda3d8546ebf828b21dd3664369fb5d46c645e43c7d4c144862ba370b5d5e51970db6a28488d5e3c629c788fe634fb5668baa6a6ddca5b3852ba0009012a562d36cec07d09b8db71ecaafa0493234edfc68e3d54ecad2821efd3b00026ab1924bb943fa845340ea45f754a595998e4d22cc2057711f175929779cc58555862548c4a39f79440a1aee4301bfbc9cb9a0b7ab7c2f1dbd31233157b270441249a11e599a207abb9e79ba35d076b2186a96497c65a12ac3453cc3cd50470525fe6c62ebbca59434878672edd76f4042d8e9164afef105d838966201cafbfbca21b8bc1781fa29e56029a2d3a30d74c32237e57b618cab48cd4f319afbdacdc074b57eedf3875e47c95c2833933b21616ede97b2f48befeea2fb87b2b3ec4e29fb24c3e29b43a2047402ae1d194abc978b2ec118f7aea1122f4f1d52b7fc3c942481ff3e7eada5bab8c0d0ab9599cf43105262e3b4a692efd9174b50725d1332475c9c7cf1342062402e7d43ebc633e8174b92df853799423cfdf189ff445fb908fc16b2d7349282ea58bcc0bf28ef6fe35fcd264c139a2d174768bea8e471153a563a4861c1b303c75b7ff66a646f0d2f0766949236af140985b45a18ae336c51d3f671fa8f56895c6aebb03d7f5ae0cb53b7fb80fe0e0aca45f2a362a3f9fb36f77e3acc715618b84a0fd8d42aec0dcabb4a20870b233501971dcb6e8b7930ac66489498aee4f72f462503c2957c43f7c01a65ff15ef36ba65167d61ae2d8a9fc9a45a3dafacd1b3b3071b78524b6cd185dad49c5c27b2edde567c5fc4e085b1c68d969b0492d6cd2688154ea35f82aa1c39d1fb09d097b5369c5d39973902ee47e5b267302f9923b90fad9e128cfe8613350d744a2731a2849d9bbba4458443ee2891016f8a4fc8e871ece944eb164c0c76263ae8080e5a04eebbc395bba456939e5b82f44cef4fac69054e29d04320228371947e3156ddd0e3f7029887003751e3283c4d72b5fa8e0d32d3efc02928b25321070f14d9a15e2f16c2a828e3107941c5ea81a3d7022def7408e55e4f3b85a2c453e2913792681d9fb3edc778014c7fca681ba60b1b3716f7c1e5066c9994dce7468980780ae924aacce0896fa81c571d1a69f9f822d9ccd9f9c1230b3a3f50933de67bbc79f1e4c5dd62996f2cd301d147e38e8279625d52e96815bec697fbb12c1613dc5485ecf6dec4bcdb3b8a92e6e1736b1b72f5404fbfe065b88eba775ca464036062b83c306df767b367f863725c3676b9841bc29a88e503f00cd1326d2137b9839b27ba258b6923acf3b01b9eab50f76542c610136fe3d144dc9c5a035948e01bdde41eaeb0de454dd699be116468c049815e492e6a2788439208ba190ac482ae033c10f44aecfabae9b96acd1bccbc174c2bc23483acbeb74f5fe5e57104054cb71b29ea94c1b77414f817409abdda8b443e2326f26b26f4e07748d15e37ee46220227df3d290d29ad0932b5bdbfcaa9e53a19f6e2af9c000f9bd523ad7f770d139f023738432d14d9add2d8ad498a3cc5b6141e1a04f5bda3243f7edad8b06c7298f0bd15378a1dfc514917319c0bd019beece3bf4dda245abb829dfccfe6e210a20fe621fe609b32013c211dc44aea9f7cfc127c95f17e043a1b94a34426d1b8e2716675c0f19cc85cd0113229c86e9a7e18bc258f90517da68d0cf1625489237ebf9d2c46818b408fabf3dc48df77de7957e27bfbce31b4dff5385dac72c1158fe7dc2cd3afd9b9d8db11159942e6cb6ba68f712545e4a8c50bcd8de1650576f41b1952bfedf3a76a5cc931f2c42780e32062083d6a2147ad4b8f4440ff58bf1aef47daebc99f42dc497fb3edd04dba05ee6a7a5327d2a1f6ef43a57dfe1655baeed25b9dcf7ae9527604cbebb753daef99b12f5b1d527f79b8d9f0c4089db8dde23867ff37bd8fa776cbe85d7b5798af7229daa117b096de137cde04dc0a37ebb78165539569c7f2a3ecd39d14c5dab57691d73542f9e5353af42a115fee8e16955ead438332429874b983ca4ef5a03c7cd0f1a874cb0db95eb73a631e5723859fe1a1a27bf01f198a1432b28bb0eda40034d2e5c21cb13d9d7b1588563f53db2054fe32a54d4573beaeba77a62bbec0a5153702c47a2a9422b375230b52847390340ec05dbe18e4108ce7f57b370fe4c2519994383efed53b0ba254a80182589fa58d5201c11f274f05d34cc77d3bea22359c47f1677cf12a21e72de3c53f028f4daddb5fdf7c1497ad4412f1c2f1dcbad6d84956d336e0922f4fdb36f7b8be9e48c19d5c4b57ca5bb85060772aca262e6d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c83799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463bfa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b21bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb85e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f7297605d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d13cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fadb2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d6793421429697809fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f057704b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6ef44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a84639e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b4679dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f927993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684bbf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea62f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9eb96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35afd8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c456eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5ab8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792af6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d75727ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590ef40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c0723db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f412fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd56e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f9244bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d01346112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd98d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd36699e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebedeeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26adf774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f94a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a301bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df48612344be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed786ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab573d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d503f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a94f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1eee5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e11e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a89f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca88742d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e10938b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e4982242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13fb108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e61316259942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787bee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d245460267be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f3925518165cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735ecb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c566dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf67a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e4544be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
-				"03fe19e89df20c2fd4b7227948357829765b81d88c2212de4197d1700a69b3abdcb5b02cadf7e2df91958c5994a9a68b5b47158b77dce08b3419a746561e3eaef652b628c20a173ef1a21560c8384725bc7f3ec9abf6a6a433ea54451f15a7fd5ed08c2a53d95e4b51eaf54cac4260e11b98cee8cb185af54122f699321acf0b9535edcbc757c71bd5c74f4fd754614ccb4a948fcdcce6e0aa8d590baad1f812b2f794c84ab36039430d42203d0a423866ba1d06c3e48ac789c4162574529e5282b10616181571cbf8c7904b3d34f449c6ba934bc1d9de8e29afd383a32ec5e6198c2405aa4c9370d7907ad29301d0a5decae9055a2f374afd48bdd66d642889a553ae29bdc7fce2497af7c9dbe20b453e21efb07fe08a4f9f7019fab8ea8433ae68953a9810234313b9cba7f87c13df3b3c7cd80d0078d9943734cd8ed1dae65beef615295d4cf35723485293a551c98917a5a923d5049e803e9b2c403c1db00853c320bb5f0bc00de1a41567d9ed94549a4130c05cf159b0d48b9d81dd1091df8c25c3797d485a688accc217a33c216480ab323f43fcb9d6df4476ce5bb875ab9c870842518566833c847d47106d775155c79edb6636be4b35020cab53cce6fb026a5b38f4ed3d427c4244d8acae573f1e01aab8a406f6ffe361bad6a028c7c0229766e8ffbd45e83e4e880580c03aefcb40f0fe7d20e05d4df51b0a8cfba618f94fe297b6d0a8a7a1e19cf8ef19f06a96273186fcb72ea0049ad7febce5585e20ac75e008d75d91bcd2cec85489989dce68dd3114a0ec3f9d67a9e6157c28f39d072c5a0f9be55bfbec17f1427e15a9ea71f0b707ca6a28512072216b463d6e144752eb6a25a2a74ea86231f41af41b8dd9890173229f1a0f5687223848139f385568bbb71a04266552ffcfdfe5b8f4871e2ae905054e0ba08e0b1de71044ee96ec46b8ce9bd1dc567077821322f7b11172484ec498eba2e9016cb20c2418deaf5c3d72216a976cc591b19ee0c5020b650fddbabe931c1965ee53e2f8fd8e0e849085460ede1c5d180861cfe61c4b1e708d0590e0ef7dbb25434a619d036124044a6c4ffcfff3c5cb1d02b9b47acc518bba187b8f604aa91dfd0424446a2a693f995128ea9bf003d83fc034cb951a697ad596b5f9e1d84dd74a37ba043d6a2f623fb1689a0f714b89cd6cab5d715fddc97b1338afd6536306debecf0235859417213af2bc7db2a91cce582f2f9d1a1fcfc9031425d111c35da1f3deecdee8b3f7aa0d9bb32459cbc93513d964b33035348ab816e82e9ab5990ce4b2db8eee3e65ee355aa5a8c9e0499589b226c5f93621ed189b395b5b5f639d4c74218a73992f763c6ceefd4636fec088929479e154b6d5db7fc236061d0240c743176c0accbfe7bdc44eba4cf65ce0e07015ed109b2146fe37bfdaacad4fdce3e01ca41fe25037e8e5a10e88da6b8fc56742cda818526b47ce3240c2bfcef3699e2af5a01ae6f42265b83aa76015e6b5e3c657afea16c25000895081f43a2e7b6a2e0de9c6c70993ae3ce31d9a7474260ec37ed5ac3f54aeaec54d635e7724b7d1c894f2e631d0df9d3da5937731ae29b056f2b84a9833fd2e04217f56862ead24eb80ce72a4f127fcbd0087cce69ae0b84cf1897629cc9b41fb1643e0642ba0eba008f783ca91ec82af80d533c3f1c0b73957ea77511610ee70e0708762791c1c3ad7aa2d8033fa54be97fae1559e3e0271b036129d62c3e694790a5a6120ed2ac3f99ecbd76f4a76fec84219620dc0025825924ee4d24fa590f6fc29a16d3546f3e71080b5847a6ff2bde3cda414463e7cb847897853e9bcbd33ed46bd01535245dd584e1a17e75265973bf25b2b8e098f5c0a42215d68b65fb357de4e76ab795a39c3b8350fbbe7bed8967a615076b1b36b96c0cb791479fa3a798664370795ab793d69aaa654346c7b914b6572b34b6f4a053c655c41e02b626d4736e2aed36f0878935f46578600009e2d5e0fccff5d46d551806cc1b802184513234325188a3a6786b2519a6013aa2d8ac9c776118b9000d1502fc058eb7ebcc86091815f4724589050bdd82cc249211545d2228b38503cc486a2c4ba224752063b315f49c9974245e6870ae369da4e572ca850be421c8fe0ebe7da05eb453f0f258b295b7d8e8fbdac0caefcfbe84134a4ac9f4d8fdd596df2e18081e9fe5128a71eb8ccc79df036c2380941d0cfe6907c354d579a2855b45724992f18d5ee277fd2dd9e48251130640e175817135f895e4e752193554bcccdd82e08fcfc4f33269c2a33621471ac102766932b8f78fdf3a21c318eb2d9ee59ab7fa204d7258219ec7e92617f9ceedefdfcff4c9c5692c498ba0549f578402405f651ed4dccaa786cba95366e41ab2708a05c39339493518671e2411fa1c641eac239a4ccca80ecafa879930902a6f221dc77977b6621914988930beb5f69d2c031208b13f0bf44d0a82deb0520ef738b172a5f663998837439e595985e782e69e41371bc5d6600f14f8b0a180bdcef74b56149dfb8d2224357c46e486fc52492ae7718514a26d5d9acdf15dfa990d265900afb3d3f0ccb548efe5a9bb39208cbbdd172ee5a2330aec8574b894f2d8d4a473ef4a3b21aec96ea9669d23fc72773fa9d7c2bb9052131d056e9841f3215fd66422107e2c3fe0ddea1dab6fc76bcf5ee8e90305020a8439a9b867c7a09475b3fb4515e685b506fe498c547acb659dc862e27e27041c9ab69c055d483fd7f88a5c715df32b1e7258e7007c744e0356bacccf93ebc3cdbfbeaf9af0d6bf224995bb4ea8640a1f7f7472de5aad1645746b37b227dd84b806331c9c91aa44d6724f6cea9f59b3489b136fbd7fb9fdc7bd3919f40738f0215896f0e57fc3c6cc4b07f3eb18ef7121a3a51506a57fc37578c7c982dd031cbdabbba993c4bfbe5f95634544f5f08b46f3c7fd5c3585e66363da541b95fed20f316f746319fb7b6a0a4f2fa70ddf3943b56522a244856cede10aaa813d924dc0984df05260b2eeb33fc14d82ac096bd3a84564c129883595210a196bd3312e8d38ec808d5009992decea8ad2165597cdb27430ab3ab595e2d51f268bb406e2e013ab1b7fb48fb47c753a7a01a257523bb59d378365713e189956f37f035d3b0865cf0d28ec65685d4e700f585fd2a9679c22f4028e892a05e374b768077da7a0508027d37ea278fd43e8a6c40218e00b47a8bfcf14d1d3db7df57b5c6cf69259d87773a3345c45cb8f6a7e91f23f5a6566bc9a3125598ee90c3d3f5d814597e0d87a54c6c3e883a70e5c249efbd57cc8494e9c56e088c355a49b512f09908df78a900169fd3ea8de67b521b8268264c7597c2900e8bb986e648f8b69990956e8ac069d811fd9755235260aa1eccaf25b6ce4846d33f7ff6f0291d6cca010d3be72ff3f885b9252138a801788f6661ab2040866cdd776e1616ae57ab588231c748656e955746986766494a6a460c4352f8a843bfbbd6199822ef8534ac1c436389c7227f4b0626670c8aa2e40c650bc5d27e6668b8bc7794c852e66f98ea3db47bf906a2dc47fbc17468a1a06b4c559db922fb0649ad3fff2dd9302eb79b9b242750eeb95e0985296bdba72aca0bd2c33d48b68eb89c8f4c4ddb9c36387f8cc521698eddbbb60ebbf356145c811cc136579d0246eb9c6fa224438bdffbfd74c138302ade0307003c04b88132fa21c6119e1c4fe48c4133dab830acde6d9d050b74e1e7a05466e314208a4a148a7a6223530f18aeb1a6e65f0680402c879bfb914525132f99c185c96c26b394d3b69e3d96ce167ba9edde0cf6b689d065f853b49839d868b4fc0e879f0e2aba5053c5746630ae06a80b5f4cf424c1ec3812dcd16b2ad79bf342a7e84535940b254941e942a1da02394dc19978307ea5122e9607f598c5c6f9c64d9100f9971a3e7b8e4da6b789b41aafe6fd66d69b4ec7af946b5abb71c8213367b45eb67200b6f3b293f5d5fbe9c37b84b641c6418eea1cd07b0c63d51505249a0c253e19c4c6071fe6aaaff1de33a96e6c7e40a8bc647bec54b4798a0076fe2d11507334c12896123de599d0712488f056d5aeadaf66dae5f46009b9d2621e399e3eb08a9e2cfbecb82396fa3610c35e1f6464e326b0f969609fea857a5b59913d4a82f08dca0fbb1821751a1a9c4993429f715f30e6a2bdb102b1d8264ef83eae3009431f228f1c2b5c3e2923f564bcf2ebc2862b5733b5ec22bf9f7d0dcfbe418fcb989b3adbb2ab1af9a161daa4c2e2920cec9ccebafb9d3a241d8e7c659d7caddcc20dd787b9707c32dc5a34261e2d18e85a9a1e7a709ea161e1d12869a490c8a451443ba53347d3ec0b9df4b644634f12dbe36dd950a11b633f0d1bb3c4e6caeb735b6a393a9676a7ec9eae6cb916c5636c9a57a84b919e9cff9451e0695dfa7a8b46e36a72d40397decb7a8405537cd8fc42cba1f11f34d5e10fc02929c8830d941798a52d79be307a535b04dcd73da1734e696e2b075de7d43b211983fba7d3eb6758aa23ab87dd338ae5b7d4943755ec9999558ed1ff4d59e7e0fc57304b4e0cdd98bc8f6dc528ac55c6951045a04a6ad2ff813ecfc1c08bb11c27f8cbd93ee4d99b07e56f61fb895bd9724c052ec7ef148b496e8093b6fb9ba3c236e43f575e19d728bfd2405f252014cd79984d14cc2a12f0434a826712f299a85a6c80edd30dda8f0df1639afdc1217d7536ba6aea69059bcd1b909e01ba516206b24b37ef8ee329ee88a4539f9d336209abfc823c9f22323127b10a135dcd9cee68d62f0a43510408e4f1676072beacc6fcd646093aa736284c67264d387fa3ff0f76e14e86ebb12c518940fae86d4749bbcbc0da42e2334bb0fa284efdf970f69eaf55c58be039269e69c6e857f824b0a4aef81cc4b0cd39cbe821e2b9325745c642648731f5edc1e35a2b41bd5f07b8c7b48207264426d8203ba45d6191ccf280e475112d80b66a467419596da008c13ff2c7a59d20e07c45444b8366e37d21a2ea0c435c74cee05cc9360c3750a512ba03dbd9ee1342da85c50a10d6668215c48f1395ab4a88af64810137cd1a94f15a080c321af1b269203416caaaa53d3314a867ad16d72f7b6e3b2cd9ca1deff124eaa103f033a76f9dfae79543adbf24bf5d0bcaaea2b468d11a779d53564c7f131363af96bc2003ccb0636fc219a0f59cc0eaf48ba3750801bb916b78220dcf99fe5fde841d4084e47519a176dce6a1bbd65c2162f923336b13b045a4bbc19337c71afd351b1af0ccd5642f5ca62df6374d5c1c3f636d8e5169b2e9582607efd5b3925d584bbe8b19a29a06d8f0ee958011f9844d24d6652ea8f21de11ebce37e3c64ff24bc986a5ffd3c2ea59d17dc8d3cc7e5ee820bf0fce9755249b520bded1eef46d08204b39fcb6bf5a9667f9f07b2d630e5cb5fd56136faf85a2efe82efd22914a0f32c3ac226d793a0b323c71c8e91b27e45650b61de65e13a0179faaf98bdff8031c1fb1775c824be05bcfce16a10fa21d5e2e3fe06b4b17b02fa19a76df8d9c3d5417c1eea28aa8de67e8d6d29eced26c07c9361c9896372fd451754d8204f44bd903ce7ff5d0dae69b5bea66692e0e8160452095b509f85ded03e0d195929849923cd28e5ef4a0b969b25a34e80873ab0e49eef5d0f532d05a6acc13d92b4ffcb6fcf80212a5a744f11f3bfc93eae1f19c2e66a8a799868f47d4bb3873b88ed7bd73e01ab27738657fe7ded82d5d847d3b05851c55a2ec2dee3587b96a07f6bc8bcdda75e1acd6af8b85c1ffa6bece522773d30e0fcebcd09da2d6d9130961571db1546cdb405566b3b80eff0139e1c5f5fcc1cdc1646da134b24bfd6cf0f3cf790214f505a349911c15f6356eac315eeed27981b5d780ae493f3fccc17f93fe13466abbbd2096f170cfc69748e6fbc8c243e61da0aee285ca2f795bb5c03762b0fcabdfd8c422dc8625d18feaad2b7ad31bdf6641401d509393701642eed48f3787880ec1e355760ad6d095139c5af12da8a1af32f7078ceaddd5db21c8561d18098ab29d95e1337bc2a1f6b7372e298dd7f7dd52613b2e057a103179977ad5e4718e0fde0ec6e21b4af9fa01b110280ca29adbc776cd275a46115f42b566c9cf530203e221ab39dc3484a96e06c65821edadbfc50ddbd5702ab563eb4fbafae513735c93178aef68486e78776c8be66b14135d8d04f2af39442e0f3c264495530c554b85b06d59e722ce1542ca428d6bc724bc97b974c10e69f6db0a1e854c6cee309a109374fa3d39f5c780cac40295702217833867220751dee6fa2e5badd335c788a267dd9cc6a2ddcda372a2b25097eedd139ffaae7147ee593da74de201f959fe86f9166dd59a2dc36b04625c804cbe8e96023f2eeacdc8140d22d0fe9e16fa807021891f772505dab861088b492154acde10a1539c3466c0f40111a902bb15d37fddc4f1b9c53e651f8657360c636ca65407df9a537c7f2a3cf8cc69310bf6f2b00bbd6e04a172bac7de33c77554ae4255bf71f437b2fb976b8295346c7d8746536db31a0edcdb2e0743f4c4cdf1152df1c65d11b4f98b35da958fcf6a41a813878c4d3625f3856f8b7d85a428a29526452ad1374ae59cb3f70329960644aeed4604c54fdb9c89b507a46c3471d70ea84d4b9ae08bf43d206571f8d85a2594f7b1f23bc705822461f46d0683313f98ad4de25fdf96e4e612ac233db75fe8f7d55b7d7c1ebaa5a19308c4d35a9f7841bbc2d78aeaca3dbfe51eab8d38b60d1f4a1afdf5bd5144c332efc29cebfffe3d0a8b3ca8e065f13c143d2489e8f26cbd806eec413d904b96ed1f7b44e48a540921a9542c451389f7a7fc15b540d30e6d42758082a680e308789f99cb9b4fe0405880bc6a1fdc2ea25895b109f7b406fa69d0971bd056a61ba1d0672289aca060073740b985f0883805f75b54c492465ca237bda765dac097f069a8ec735779eb71db82465dc257b9e501fa2fb9b7ef69b64e59fd763e8baa8fa4037af84c979f9464cb70a80772da62d293e70a39c572d3a2c3108e6e188294f360c8e3c319ddd3017b5b0fec885f2ad5d412cea1c73635e9b141c3360e41a40b715db5780323c17b10864d1b45b61781fe487592858ecb11c99ccf7300bf4d0e3fbae7939f922a989d464fdeccbd3b001494998cacf4e38229b7ba49b851d09c8adf900a3d0abed61442919fe1973ab44a40e3cc2b6ed8b5c501dc9ffb37b68d3f58ff4811bdadc6464ac2b37a3bdb39de4e18a6a9b15e9f3189b8010df6250da8f18ba514ed50eb979dde95836f0505c58c90028849573562dd78e24a56a0c42f4d533023910097d8e73f2ee5bb4ec173f3b8a4b467bcaafe2896454979e753bb3dfc2f4092d889e6e7a123994e2f7fd835188f8bb668afdee1bddb8cb3076c6a72a013c50cce90e922370bb484e419b22d86a0abad1ce1af95f26aa14af36542ee25274f90ab2284567db373f1c395f0d90f09924613ff8f20b46947baa8a5e88f48b63f52251e9889aa149f33efc6cc653a4f6c7888cc2c493c0637f7c46664b1a41e82be785c080e5e81999e63d0b02f71cfb93afae9f1d64fa723a515dbbbf0e84f48876c902bf0fd5a24e21c59344547782959f72e855933087e3142d7407b0e9224ba2b5976d43b2d228f9368374512d5f59eb2d1394c879dd392a1401085f6443231fb8f208b9e3143bbe5488f185fc1eaa96cf3562cbc5bb5608f7bad8250fb1fc28f72e9fa04a4bb5819c00c00ff702d0be366bdc1f2f91d65a605a887bde087bb0d5f4a608e1d2418b33e49e04dd706357389e94dbc6ba7d7c1aa1ec0ce2ceafd5ea409c7b3a435b2a4f5bc3209c3d5bc9e0bf60fc96f75038aedd82ee23a6b6aad794f30c6ddd0fc73224f531ebc0334401741d6c009bce65dfe56432e34d5b24c0acce727982be1c01b4d02d96ad4297c8e17bd550b4dd586a3f4f2a2783790927d220dc254f020efce565d0fedf01125edf2098808bd8e42db499a8bf603f70e45620f7b86df3e5cd27e8bf6ff8b3f4d5a5403d0dd6304579447bb8aee092629efdd5b8229946cda41104fe063f2e5ef5599c9f409e8332939f48abdff0bf90fbc13b34b462e7725f8656197b97b97a437c3fa82ee97c217c0b4e4d63681f6fb9d9746823231f27b19fcc6f15a65e8453546f9594d76546febf8bf7e189fa36676d04a3c4f13277f20a58df88a285321624d0ac173fb8376af9cb224091ed86269a7d6911e7d031141c118725a9cec27523ccae6aaa92293b247b6e6d68c52a873032d69f09e32510b3a3a2d9112f09fb35b54ef7bdaad0e71781faf246283127ceba1f13767830eeaa766e5487ae2d90a07d6f3e1164da65c1459e85e5ac935d15a62951a33d08b1741ae03515e38f29b945e4a5d4bb65c54a735ccaceb626bdd373c330f74844a6ba0e781cdf8cebe829d1ad02bab96405afc68253265a88187d920415414fa1af0bdd01cc395353f8eab21e5d30660c329ec6b83a77714739f6ea24c0ad78fd538b06139318c9aa84e3d6e52b7db6d08372ad91695de5ec6f1d738a46b8672b12af6c63427eb7593be449c52be0963bee1282b615673863494c5102aa3c3370022af20ab9e9ec9a7025f14850709d08a175844d9e837a611b3a70fc03a6c1fc191b4e3536c844722596ed8a697afef863d3ead8f35337cfbb1e1b3fb23034699777bb2a21cecd31120d4dbc6326b5404766ed53f275122ba02d2ec7035c584b79387aeb772f233d524e329f7f9031e6c9f5a18cf76873fd6a875d131bea71fc1fab6b1e869bd0dc5fcbc6a6cf1fd1a1ed204938ba3b496a68fac70a948af119141bdc0d92d1faf7d1f46d08062f87b8b2b12b522a7afb73426462677b915ed1dd2e096c174fa23eb7fc710e2123998105a177fc55a90b2cb9f3f7f13c86eb4f169a63f0ca454a54f90dd16a8245bc7b9034b2a6b6374d8ccdbe1862feeecef9be224b8448b3e80c76c57b63465d386ffff26e3f14d0e7c6f2a31f37a9b94293bab1b5d5d2ab46b7279b99dc466eb4b04918a84d1385fdc74fce86698f9dcab3e3ccae7e94d8cec8bca18fac444db03f37853f416cb5037cb0ce5b75a24e7d2e52b5beefaba941c907c03f000fdd667096cfe68357b8173b62095e9c312d98a5cbc3ad6cbafdcce98f4a52634623a010814344b6de5e7c9347f8dfe0d1cf52ae1d7443ecd12805f877012abd49cae1453b4c689e47e6877850beb657efcaef5623fc99215ef89ac33b9cf32126865299829478e23a19f1321dbb05814bb263ed59b01ca72949afca6abe116c449598dddeb71c51f5cf1098bb24db154a29a0aca63cb89719b2ec7ae2097b41d9734fbf89d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c83799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463bfa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b21bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb85e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f7297605d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d13cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fadb2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d6793421429697809fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f057704b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6ef44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a84639e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b4679dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f927993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684bbf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea62f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9eb96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35afd8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c456eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5ab8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792af6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d75727ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590ef40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c0723db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f412fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd56e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f9244bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d01346112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd98d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd36699e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebedeeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26adf774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f94a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a301bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df48612344be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed786ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab573d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d503f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a94f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1eee5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e11e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a89f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca88742d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e10938b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e4982242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13fb108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e61316259942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787bee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d245460267be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f3925518165cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735ecb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c566dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf67a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e4544be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
-				"03ff3c180f191eb9214326a3df038f5b168f96539b2af6bac89088a2c521534b4ac1c555e3a754af50c012e38bf5e5127c9f5204fe4976ad8b2b6b2a8936cd3a6506ae1095ea348f0aa6686646b5a0dfed9ed5d91a9d5d6b74e8c5164656d5ad426301efe4d19682970cbd170ebcb2f829911cffe487f8838600a277585805e2dd575bd385d2f18c9f15877bb5601f9ef672f5523ac369daebd31ea15cbdfebae36e8b3dfe2f09237855202c28bfd059c1a2d700c2ba2c5ca97d59693c73ba073f852dcb41b4e19a6d9f352367a252039afdc7c76ddd0ffca9b0e77d97a87722e9f8f1b63f63aae3ae1957482f1dc753dad460af43e078acb9497512a97ba6a858b3986d608d1eff4302133c0ef86fe2b123bb39cbebb0954a94dedf81f5f9a212549cbb3d09a42b2323bc1ce86241ae30596e5cbbcabacad1d594a7d6bc3bae235a040ad7955323b18f2357993846f2f2a90d3cdfda2271fbaa94269c564d99023780cd6097aba4d2f76610b4350345f340e5d43beb12ef5d09c27268b50a4d7ec7428d3e008ff6c4f0de5021fb85c8861444ebb42cb49d2e34af19452c6685ffb5a7c6e36af83e1d1b1c770a05b9b0e5cdbb4127db85f88f95c0e197bb1fdf2cdaac23c6525aa5880341123f1ed83c10b8c77a1a0717fbc5c42ae6d65b5bdbd986e58538c7abff89f26be07e612f6036ce3b196105bf6222795f109e3bbb14ab9e4b6dcba8fddc6c273b8004454a75fac6819b13fa9ab3f3508e659a2a413590057cf8d577493ecc055c2d88f403f03d2785deeed1a99481d91777777a0a80c49822f0fc0dcff9a4f384bf0eaf35cf1f0c8726abedb956166c07dd167918acdb6b0f8653989c9566284028ed49875c443b4ca9be2d101dac23a248ee96973a76219194a1626097897877ac151bb67741d49ddb08dc1054c822e9b63dc4cf71ae49a8e6c3cd1b5bdd48ecce768e6d9630cdf86c7aada5c9485f5b026ed55fb29925cffa23b81654ca9662efc811b6ea4aff0cf2da79943078198f133353daaa1beba45e8f4ba2a8d82dab553504c3615b6c4b22a92e7cbd0b5ddc3ab88a29cf555c5635351be42881c685463ccf09977514ace0603e467d62ece7f65e25b2425dfe5cce51a7b836b45292be6f9de3c760a62126881ff7c910caea8fc7701be59a47031821a87267e6d3eab4589db9220b17d636a23e7d685d98e5d49ae86b3318ae3709af2814a5800c2962f373a71e249e9c26de27f1a1a08fb3be26a6384102fa71b84e94c0ac8952df997682840cf8cea302f14ff872de49bf975f1b281354eed33571c51dbfa70ae3497eedf53a23ca84f53976f0db4bea237c96bcca420dedac8ef6397a14def1a18b063f48a802a159b0715cd241a1a844a988e299467ce2847e7e1d3d9b1d5bf8951852403df4b74a01fbb123b4a3544cfbf2daf3c6a4eb9c08e300735d230c5657f29efd1edf56215fc6fd1de693027096bcb708e3cbe8083450bdd76cf1a031d4a49f0c565f2e91442ccc4f2d8b24c535084ec23e4bb9ea21821c77b615f964dd7f28bae609fb4d263978dc5c81df2415be8c9ae320d3a2c366743d6a108f4a8cdcafc1592a3312e6051bedc031e93e5df4c129c1379bbfacea92e24e99e1bed384bd5f33f2438305bffe54c54ccc78586d3ae92fcfc53ff7e357285367b6503a972ac951508cad8d7ac52296631317fa418e8fd42d8c15d69fc424972efb9d39f7231b3a32ecfd0036f09ad7c23ed98eb83e43705f1e73b69c36ca46603478ead74a375744217f0db23e67f801ff606622547cc964022fcf7ee81dc122742b1ade2fcc3d0013dfc961e365a21c396877e04e65f3128a496f00965df4ebeca73e32e88946fa5141e2425e0484ca8c23fbc29e115209a8a7750c2b73e107c89eb914d7a4b54911a44e64604f53368f4c65a228cc9bc0ba8d67d45d84972cd4c9a7f9594b3659d60cac3e3630d1e8cf8e625f321b492a6eff590da2efb0ab7ed84b9cd9483f427b75bd0195245e9f686bf5add057c6aecf38690662dcf5704c76cc5c5ae275a403ed42e740e7fdb8fd054435c6b34643d1136c6ecfd06aa375a886574e30b13260f01ec4fb79313617ecb2c8a5a39e8ed92652796424b1b118910ffc50d28046d68705737f5b4a8a4011c773d335b228413f3fab3690230c4ecd7c7622da87f3d0d6b51c018a2deaf8a4302d4788e9b634d62248193b45771833082ca986ab376db3bb612f530d5cdfe26138a1fec23f9267651099adec9c8f1ca9dc38f579fa631408ccfe1dd998dbb16a8691c7b2e62fad3426d9a1260e4b4b87e3703fd13b8f348da313a4895bdc2470007fb7d455469dccfd88ffbd54543cfe176143d088a3694702e0e0fea21171ca0573385689b42f8005ea79bb8550fc796dbb96904d80299f5d5d65dd81ec4bf21f5fd0eaec09c5850c6af5d043e9c968f696324957a2a2869bc65499a834e3abc232a3b3737885b4a580b0ee5e1438a126bffe9e16130b9c57a3b42818a3f92bf6f83bd77d54fe21af108621d6f33114d8a7e3ed9c55b500301d646822cfe22bd6f1dc2e71594d9c8942a8ee8948ac83b40b0a2d18d2b2e3c7040d85592bb2bebf70a50a0a42d71c6a40a18e3332519288a05127b599b258f97f840c7d3646e1fdde9e9121852ab2d4744f9aca249c45881f62d41792518fd51b6dc85a41e31ff700dac42da2bbafa44df6d0abe12defb39b066b62c88cfec1aec5d57b5648fe7c4df944b63a39be63b05d8219e65a506cedb414c3d2c0ed3d0122b8a799eccaab77a93c27a68c4d74868ba1f1125dfe18c871552560e69e05d5dc48254f41dee7c413ad03ea54e12fca064865a8e328894100136eef64c74e18a957b13e0d6761c0953bac3c38d62fdcee2ad5d39b01deb3d3b8a4921baa7efbf699b008934cccbdb396a9db0027ba312524eb193df16a8e345cf58bb95c889978e38aea27c88601d658f8bc9aaabd342ee9d4b26a83db62d9dac58f4499e9c4c473e73f69206e20d8e6987ac1297c331537e0a525956fede028baa478daa0e35106f74d0100ed6a293dd40ccd56e7ff33a6d0b8ae9ced954fe6ff837b2a64f3da224e9a81779664870c209953a7a01a257523bb59d378365713e189956f37f035d3b0865cf0d28ec65685d4e700f585fd2a9679c22f4028e892a05e374b768077da7a0508027d37ea278fd43e8a6c40218e00b47a8bfcf14d1d3db7df57b5c6cf69259d87773a3345c45cb8f6a7e91f23f5a6566bc9a3125598ee90c3d3f5d814597e0d87a54c6c3e883a70e5c249efbd57cc8494e9c56e088c355a49b512f09908df78a900169fd3ea8de67b521b8268264c7597c2900e8bb986e648f8b69990956e8ac069d811fd9755235260aa1eccaf25b6ce4846d33f7ff6f0291d6cca010d3be72ff3f885b9252138a801788f6661ab2040866cdd776e1616ae57ab588231c748656e955746986766494a6a460c4352f8a843bfbbd6199822ef8534ac1c436389c7227f4b0626670c8aa2e40c650bc5d27e6668b8bc7794c852e66f98ea3db47bf906a2dc47fbc17468a1a06b4c559db922fb0649ad3fff2dd9302eb79b9b242750eeb95e0985296bdba72aca0bd2c33d48b68eb89c8f4c4ddb9c36387f8cc521698eddbbb60ebbf356145c811cc136579d0246eb9c6fa224438bdffbfd74c138302ade0307003c04b88132fa21c6119e1c4fe48c4133dab830acde6d9d050b74e1e7a05466e314208a4a148a7a6223530f18aeb1a6e65f0680402c879bfb914525132f99c185c96c26b394d3b69e3d96ce167ba9edde0cf6b689d065f853b49839d868b4fc0e879f0e2aba5053c5746630ae06a80b5f4cf424c1ec3812dcd16b2ad79bf342a7e84535940b254941e942a1da02394dc19978307ea5122e9607f598c5c6f9c64d9100f9971a3e7b8e4da6b789b41aafe6fd66d69b4ec7af946b5abb71c8213367b45eb67200b6f3b293f5d5fbe9c37b84b641c6418eea1cd07b0c63d51505249a0c253e19c4c6071fe6aaaff1de33a96e6c7e40a8bc647bec54b4798a0076fe2d11507334c12896123de599d0712488f056d5aeadaf66dae5f46009b9d2621e399e3eb08a9e2cfbecb82396fa3610c35e1f6464e326b0f969609fea857a5b59913d4a82f08dca0fbb1821751a1a9c4993429f715f30e6a2bdb102b1d8264ef83eae3009431f228f1c2b5c3e2923f564bcf2ebc2862b5733b5ec22bf9f7d0dcfbe418fcb989b3adbb2ab1af9a161daa4c2e2920cec9ccebafb9d3a241d8e7c659d7caddcc20dd787b9707c32dc5a34261e2d18e85a9a1e7a709ea161e1d12869a490c8a451443ba53347d3ec0b9df4b644634f12dbe36dd950a11b633f0d1bb3c4e6caeb735b6a393a9676a7ec9eae6cb916c5636c9a57a84b919e9cff9451e0695dfa7a8b46e36a72d40397decb7a8405537cd8fc42cba1f11f34d5e10fc02929c8830d941798a52d79be307a535b04dcd73da1734e696e2b075de7d43b211983fba7d3eb6758aa23ab87dd338ae5b7d4943755ec9999558ed1ff4d59e7e0fc57304b4e0cdd98bc8f6dc528ac55c6951045a04a6ad2ff813ecfc1c08bb11c27f8cbd93ee4d99b07e56f61fb895bd9724c052ec7ef148b496e8093b6fb9ba3c236e43f575e19d728bfd2405f252014cd79984d14cc2a12f0434a826712f299a85a6c80edd30dda8f0df1639afdc1217d7536ba6aea69059bcd1b909e01ba516206b24b37ef8ee329ee88a4539f9d336209abfc823c9f22323127b10a135dcd9cee68d62f0a43510408e4f1676072beacc6fcd646093aa736284c67264d387fa3ff0f76e14e86ebb12c518940fae86d4749bbcbc0da42e2334bb0fa284efdf970f69eaf55c58be039269e69c6e857f824b0a4aef81cc4b0cd39cbe821e2b9325745c642648731f5edc1e35a2b41bd5f07b8c7b48207264426d8203ba45d6191ccf280e475112d80b66a467419596da008c13ff2c7a59d20e07c45444b8366e37d21a2ea0c435c74cee05cc9360c3750a512ba03dbd9ee1342da85c50a10d6668215c48f1395ab4a88af64810137cd1a94f15a080c321af1b269203416caaaa53d3314a867ad16d72f7b6e3b2cd9ca1deff124eaa103f033a76f9dfae79543adbf24bf5d0bcaaea2b468d11a779d53564c7f131363af96bc2003ccb0636fc219a0f59cc0eaf48ba3750801bb916b78220dcf99fe5fde841d4084e47519a176dce6a1bbd65c2162f923336b13b045a4bbc19337c71afd351b1af0ccd5642f5ca62df6374d5c1c3f636d8e5169b2e9582607efd5b3925d584bbe8b19a29a06d8f0ee958011f9844d24d6652ea8f21de11ebce37e3c64ff24bc986a5ffd3c2ea59d17dc8d3cc7e5ee820bf0fce9755249b520bded1eef46d08204b39fcb6bf5a9667f9f07b2d630e5cb5fd56136faf85a2efe82efd22914a0f32c3ac226d793a0b323c71c8e91b27e45650b61de65e13a0179faaf98bdff8031c1fb1775c824be05bcfce16a10fa21d5e2e3fe06b4b17b02fa19a76df8d9c3d5417c1eea28aa8de67e8d6d29eced26c07c9361c9896372fd451754d8204f44bd903ce7ff5d0dae69b5bea66692e0e8160452095b509f85ded03e0d195929849923cd28e5ef4a0b969b25a34e80873ab0e49eef5d0f532d05a6acc13d92b4ffcb6fcf80212a5a744f11f3bfc93eae1f19c2e66a8a799868f47d4bb3873b88ed7bd73e01ab27738657fe7ded82d5d847d3b05851c55a2ec2dee3587b96a07f6bc8bcdda75e1acd6af8b85c1ffa6bece522773d30e0fcebcd09da2d6d9130961571db1546cdb405566b3b80eff0139e1c5f5fcc1cdc1646da134b24bfd6cf0f3cf790214f505a349911c15f6356eac315eeed27981b5d780ae493f3fccc17f93fe13466abbbd2096f170cfc69748e6fbc8c243e61da0aee285ca2f795bb5c03762b0fcabdfd8c422dc8625d18feaad2b7ad31bdf6641401d509393701642eed48f3787880ec1e355760ad6d095139c5af12da8a1af32f7078ceaddd5db21c8561d18098ab29d95e1337bc2a1f6b7372e298dd7f7dd52613b2e057a103179977ad5e4718e0fde0ec6e21b4af9fa01b110280ca29adbc776cd275a46115f42b566c9cf530203e221ab39dc3484a96e06c65821edadbfc50ddbd5702ab563eb4fbafae513735c93178aef68486e78776c8be66b14135d8d04f2af39442e0f3c264495530c554b85b06d59e722ce1542ca428d6bc724bc97b974c10e69f6db0a1e854c6cee309a109374fa3d39f5c780cac40295702217833867220751dee6fa2e5badd335c788a267dd9cc6a2ddcda372a2b25097eedd139ffaae7147ee593da74de201f959fe86f9166dd59a2dc36b04625c804cbe8e96023f2eeacdc8140d22d0fe9e16fa807021891f772505dab861088b492154acde10a1539c3466c0f40111a902bb15d37fddc4f1b9c53e651f8657360c636ca65407df9a537c7f2a3cf8cc69310bf6f2b00bbd6e04a172bac7de33c77554ae4255bf71f437b2fb976b8295346c7d8746536db31a0edcdb2e0743f4c4cdf1152df1c65d11b4f98b35da958fcf6a41a813878c4d3625f3856f8b7d85a428a29526452ad1374ae59cb3f70329960644aeed4604c54fdb9c89b507a46c3471d70ea84d4b9ae08bf43d206571f8d85a2594f7b1f23bc705822461f46d0683313f98ad4de25fdf96e4e612ac233db75fe8f7d55b7d7c1ebaa5a19308c4d35a9f7841bbc2d78aeaca3dbfe51eab8d38b60d1f4a1afdf5bd5144c332efc29cebfffe3d0a8b3ca8e065f13c143d2489e8f26cbd806eec413d904b96ed1f7b44e48a540921a9542c451389f7a7fc15b540d30e6d42758082a680e308789f99cb9b4fe0405880bc6a1fdc2ea25895b109f7b406fa69d0971bd056a61ba1d0672289aca060073740b985f0883805f75b54c492465ca237bda765dac097f069a8ec735779eb71db82465dc257b9e501fa2fb9b7ef69b64e59fd763e8baa8fa4037af84c979f9464cb70a80772da62d293e70a39c572d3a2c3108e6e188294f360c8e3c319ddd3017b5b0fec885f2ad5d412cea1c73635e9b141c3360e41a40b715db5780323c17b10864d1b45b61781fe487592858ecb11c99ccf7300bf4d0e3fbae7939f922a989d464fdeccbd3b001494998cacf4e38229b7ba49b851d09c8adf900a3d0abed61442919fe1973ab44a40e3cc2b6ed8b5c501dc9ffb37b68d3f58ff4811bdadc6464ac2b37a3bdb39de4e18a6a9b15e9f3189b8010df6250da8f18ba514ed50eb979dde95836f0505c58c90028849573562dd78e24a56a0c42f4d533023910097d8e73f2ee5bb4ec173f3b8a4b467bcaafe2896454979e753bb3dfc2f4092d889e6e7a123994e2f7fd835188f8bb668afdee1bddb8cb3076c6a72a013c50cce90e922370bb484e419b22d86a0abad1ce1af95f26aa14af36542ee25274f90ab2284567db373f1c395f0d90f09924613ff8f20b46947baa8a5e88f48b63f52251e9889aa149f33efc6cc653a4f6c7888cc2c493c0637f7c46664b1a41e82be785c080e5e81999e63d0b02f71cfb93afae9f1d64fa723a515dbbbf0e84f48876c902bf0fd5a24e21c59344547782959f72e855933087e3142d7407b0e9224ba2b5976d43b2d228f9368374512d5f59eb2d1394c879dd392a1401085f6443231fb8f208b9e3143bbe5488f185fc1eaa96cf3562cbc5bb5608f7bad8250fb1fc28f72e9fa04a4bb5819c00c00ff702d0be366bdc1f2f91d65a605a887bde087bb0d5f4a608e1d2418b33e49e04dd706357389e94dbc6ba7d7c1aa1ec0ce2ceafd5ea409c7b3a435b2a4f5bc3209c3d5bc9e0bf60fc96f75038aedd82ee23a6b6aad794f30c6ddd0fc73224f531ebc0334401741d6c009bce65dfe56432e34d5b24c0acce727982be1c01b4d02d96ad4297c8e17bd550b4dd586a3f4f2a2783790927d220dc254f020efce565d0fedf01125edf2098808bd8e42db499a8bf603f70e45620f7b86df3e5cd27e8bf6ff8b3f4d5a5403d0dd6304579447bb8aee092629efdd5b8229946cda41104fe063f2e5ef5599c9f409e8332939f48abdff0bf90fbc13b34b462e7725f8656197b97b97a437c3fa82ee97c217c0b4e4d63681f6fb9d9746823231f27b19fcc6f15a65e8453546f9594d76546febf8bf7e189fa36676d04a3c4f13277f20a58df88a285321624d0ac173fb8376af9cb224091ed86269a7d6911e7d031141c118725a9cec27523ccae6aaa92293b247b6e6d68c52a873032d69f09e32510b3a3a2d9112f09fb35b54ef7bdaad0e71781faf246283127ceba1f13767830eeaa766e5487ae2d90a07d6f3e1164da65c1459e85e5ac935d15a62951a33d08b1741ae03515e38f29b945e4a5d4bb65c54a735ccaceb626bdd373c330f74844a6ba0e781cdf8cebe829d1ad02bab96405afc68253265a88187d920415414fa1af0bdd01cc395353f8eab21e5d30660c329ec6b83a77714739f6ea24c0ad78fd538b06139318c9aa84e3d6e52b7db6d08372ad91695de5ec6f1d738a46b8672b12af6c63427eb7593be449c52be0963bee1282b615673863494c5102aa3c3370022af20ab9e9ec9a7025f14850709d08a175844d9e837a611b3a70fc03a6c1fc191b4e3536c844722596ed8a697afef863d3ead8f35337cfbb1e1b3fb23034699777bb2a21cecd31120d4dbc6326b5404766ed53f275122ba02d2ec7035c584b79387aeb772f233d524e329f7f9031e6c9f5a18cf76873fd6a875d131bea71fc1fab6b1e869bd0dc5fcbc6a6cf1fd1a1ed204938ba3b496a68fac70a948af119141bdc0d92d1faf7d1f46d08062f87b8b2b12b522a7afb73426462677b915ed1dd2e096c174fa23eb7fc710e2123998105a177fc55a90b2cb9f3f7f13c86eb4f169a63f0ca454a54f90dd16a8245bc7b9034b2a6b6374d8ccdbe1862feeecef9be224b8448b3e80c76c57b63465d386ffff26e3f14d0e7c6f2a31f37a9b94293bab1b5d5d2ab46b7279b99dc466eb4b04918a84d1385fdc74fce86698f9dcab3e3ccae7e94d8cec8bca18fac444db03f37853f416cb5037cb0ce5b75a24e7d2e52b5beefaba941c907c03f000fdd667096cfe68357b8173b62095e9c312d98a5cbc3ad6cbafdcce98f4a52634623a010814344b6de5e7c9347f8dfe0d1cf52ae1d7443ecd12805f877012abd49cae1453b4c689e47e6877850beb657efcaef5623fc99215ef89ac33b9cf32126865299829478e23a19f1321dbb05814bb263ed59b01ca72949afca6abe116c449598dddeb71c51f5cf1098bb24db154a29a0aca63cb89719b2ec7ae2097b41d9734fbf89d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c83799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463bfa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b21bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb85e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f7297605d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d13cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fadb2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d6793421429697809fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f057704b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6ef44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a84639e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b4679dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f927993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684bbf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea62f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9eb96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35afd8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c456eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5ab8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792af6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d75727ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590ef40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c0723db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f412fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd56e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f9244bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d01346112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd98d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd36699e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebedeeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26adf774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f94a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a301bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df48612344be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed786ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab573d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d503f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a94f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1eee5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e11e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a89f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca88742d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e10938b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e4982242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13fb108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e61316259942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787bee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d245460267be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f3925518165cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735ecb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c566dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf67a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e4544be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb85864ca2192c10eb1c47201fdaf2f493267d285d82a1bad6"
-		};
-		int height = 10;
-		int layers = 5;
-		XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		for (int i = 0; i < (1 << height); i++) {
-			byte[] signature = xmssMT.sign(new byte[1024]);
-			switch (i) {
-			case 0x0006:
-				assertEquals(signatures[0], Hex.toHexString(signature));
-				break;
-			case 0x0023:
-				assertEquals(signatures[1], Hex.toHexString(signature));
-				break;
-			case 0x014c:
-				assertEquals(signatures[2], Hex.toHexString(signature));
-				break;
-			case 0x01dd:
-				assertEquals(signatures[3], Hex.toHexString(signature));
-				break;
-			case 0x028d:
-				assertEquals(signatures[4], Hex.toHexString(signature));
-				break;
-			case 0x0320:
-				assertEquals(signatures[5], Hex.toHexString(signature));
-				break;
-			case 0x0338:
-				assertEquals(signatures[6], Hex.toHexString(signature));
-				break;
-			case 0x03e9:
-				assertEquals(signatures[7], Hex.toHexString(signature));
-				break;
-			case 0x03fe:
-				assertEquals(signatures[8], Hex.toHexString(signature));
-				break;
-			case 0x03ff:
-				assertEquals(signatures[9], Hex.toHexString(signature));
-				break;
-			}
-		}
-		try {
-			xmssMT.sign(new byte[1024]);
-			fail();
-		} catch (Exception ex) { }
-	}
-	
-	public void testSignSHA512() {
-		XMSSMTParameters params = new XMSSMTParameters(6, 3, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmssMT.sign(message);
-		byte[] sig2 = xmssMT.sign(message);
-		byte[] sig3 = xmssMT.sign(message);
-		String expectedSig1 = "0025fc9eb157c443b49dcaf5b76d21086c79dd06fa474fd2b1046bc975855484b9618a442b4f2377a549eaa657c4a2a0dc9b7ea329a93382ef777a2ed402c889733bede15dcf75b0d6559ae6842167d500c5f277e1b107bb5125e2a5bbe6e1cb0a6052acb8696b3934c037704d78c579b41480c3a0a487f335d1658685813c83a1d31b31369fe0064bc6700fad59f3cc8e381c0eb645037f72598a31bd2288cbd60e3e6c40bbc804005724c026036922e8b0faa7319537cdded6f5ee98a07e47697ed4d9b8991daa6798562af0929455f18b7bb45246e992d06398f1d0b8f31eace1bad63c2c0597a3ebd641bfd0af19d639ae745a945b6dd0dea2aefd84edf1d2e209b02912950836a6b88c0a3947c4922eb93f3cfe69b769539ec98bb4933c8da996561a4a59149e3bd3b4565e2607df047dea7ac67eab33f4fcf4eaaac947a8e76ecb0fd86deac88282cd86cd84209e794494520218edfde74222f75864364616a5717eab7e09d61346faefe0c5f65ac1e4fe673155c1bdc2f9dd5c55ec17cad7861fe5af3510333b4117a84c56f5965babe4ed06c813ba1bcac6abfb41de59697c828399bdb2d68dbd67f490fc48741a8e7a618f3a73b1c9c2b13f8d9676c5c9d3a17d468f543593a20d8903ea34e825399d464472d208be89b26f0c3ad2b40416c310921c43c242fba58a145d7a6486678b6e4f67db688134d1fbad29394acad511b535ae9c7c0d894dd343268a080a4e032746507da33e61ca4444ffdc8fdfc61f00762af22e6e4b9bc449613e42b610930209fce2eef8f2473df2123f02b905e0cfba8255304dcd9eb4e7d4cf9ee92f0b1a4406e01d1c0754d56f6e14b74da7373eeafc687a4820623c240999ad143d9ceeec76cb600406a8f4eb2bb196959862e1e1127029fd818811dd97f8ab09e3ecedcbac04d7922ec4f2f76b7f3c053a447c63fd9aedf6e765a0731fd4ee3b3a0a794d0f22437b0e2ae88a71d277e4333528bd373217c98acdfe9522474f7255c8089e6422de79c65112c6d6ddd98d53d306436bca504f8603fa6beb9925d84c104d374cb4c681dc2b304ccff8f1b1b3b10f58fa701445d83810b853f17834017ba9a21206fbbb066b1563a606f1c844800712db2cb64b07f9fc834f2242d8145e6144c6096cdfd1cf5a2c467e9685f457d64ae6c05fdd8275da0e94a62b99350f3528a103a6b6bb44d5d96a55138508f407f26da2f9ca56c57defd98b0ce6cb9cfa75598ca0424130c3c250a0d0da68f9c5483defbc4227bfa7332466a98c4bf1c83ae317f4485a6f49ba70b5957dc69113937a64e031023f4d98b7c03362f2b31f64bfcef0952438576f3ed4002e41c99396cafe08a3d68443a643aa55ff7853330bdd80e1260953aadaa30d1e1a1342674a29813fba0bb5774fb3714a4fa8b170569cd735d36a053399f73d0551f946da0724d57c056c064335d05c620af0fd6014197b629d90b4ec2bc26f9848662bd5d4864405db8963e83b68885e10fe99bf394e1b0ac0f2b7df872f69e272aba3eab5c4bc529fa4fda71d15f421b459f48bd3c38cd93a5ffe4949b90ba8aa902cfd63d70e70fc8e810f16abc7b98c613b0bdbbe8ce713800b8b25c5e8753e413bf23439e6857559c2f244ad11d5d004e7cfe3e9893e6e69621e8dba2288e4d04043b034eb52ca68accf3b32eae0ad55e81228974c50ab7f51767a26f914e0d647ae36196104a6cbe5327c19519324be2bb86c8c77bd18d27e858c69be862cbd050918880d2109e204a0aeac4a94aa45c6686712da0ff97038c879a3681255191562c75a2c2cad5c817ddff9568bf594120e5bf3692969aba1ab5e5071253afe049f8a664c2c7cc9f814163a998ee8b92ddcdacc094e68b7a18d60e3b49e356f948e7771eb27be3bbc07e53dc09388cbda7affb39d5a7644f66f3d2b9c5e453519532a7655bbe80859ee939e7e0f82061bb88d00293fcff3e01622500f2e8f01821edb5d452f45681de52cc4e54e26da8fbbaddb59a379a27a31861967fc256d8734917ed71dfa6643aa3fa7cd90fc7dc631ce6a5bef6a4267dd32fe0f192d4ba3faba611389e707ef27f306860be42dde9048faea9153f2acd0565bb8115ca39e5af4a412466927d84074232eab6cb6966a7a3eff29921083c80d7d9905588aa0108f5fc318565c320b5d17f3ab131d85c8af27a891b8892a02e426250a27a228bbc64ba31d16db2a182628cdafb4779b8f0e48e068755a4a41d4b43c838533c82f8b9f4015975fa013b5708726eec8bc1b684d179ebb7f91698e3ad80739252cf030983843e355af77f82e956ba60fe494878762dd1f2736878f5c6f6b303a43cb46a94c2297b18bc3fb9c1d35951cc603be084021aca25e8fccd15844efdf5ae367b71fd7ba298e034ed98f9527db0586156206ed6a4ab012209bba2e8f0c15355efa24162b1c0ead2a26718b120b880324a690639f0798f2e7203b41faac1ebbf6337afdca8fa5dcbe73daa5f6322871dfe2e5f5816afa5c75e7df33660bf79610aa182aaa59d0f9a113bbc58ed58c02226516ecd977286f8d58fec91ae665ffeb7c0b449b024a1adb3d3ed33438aa60724bdedc8c7d847c704798c2724db12a50d5744ecc772f80b952e02ca29eea671732fbe1705edd36e9a942f64483c6bf5a9a5dcdc6470a362330c935446024a36a7491d54dd81d7677ef762f5056f50c04ddd35aa86e16d3a4b7766b785c15741c82d60fc14449571cf9dd0dc55ecbe8e5d4ca8f52fd284cfedc93ea39ef75cd34664cc25076380fc474a03949754024e92308583ea204517657ec51f35697784dbbe0846b93a6600d8e9bab5ed1573e1f0f700d864a043ed9d686b330a28d10314112bb5768be82deba3e15054898f7b7ac3a6daaf5bf9097433f7170115266caa7d8d853c11fdfc48d500c477d0639b18cd604e16f25bed87933546987568b3d2c42b0c4e10b70c05d7a16030fe311a9e20ce7593489ab3826a0c0707222ae8d31be6e52b7d9f49c153877f61ecf9d1b6dfb20ca5d385588a16c366db0797bc924b9364e1f717570ada26a1e6b7134e3d8bf87ef72d9eb277103699e079f67e5959ccce0cf604b1ea999de077dce656c993d9a3f460770c4735daeab2cae084ff26429bd2aa4f3a533cf19ec00aee08a7db5dbdc56fab1007a4ea0d135d2d436dff5ea012d0f8e78637cc10f197fd6bfd6174305c167ebd74d63cdcfb21c8492845a90434e45a6ce73a310e0dbc9b78bd5fdc4c35d2ba31475cd246692fa13a40228e730e655fdd8dda53dd9be1f9f2cb3adaad4f7157c522496140b708d24e69f6eba0a47bd7aeb6be2131d35ce15fc18ef361cdce67903d5070b7f493fbf4fa0de2d77b36499888dd3efc3958e22d14b362b09d4fe50c751707de6a471cc69b9717e6cae556a1672d6b7ca3569975957cf13999076c98309821549c132eadcf1f0f627ce7755affab11b5ed63c2c9b3b9c07bfc7bcdb29d764579275c4dad26b646107813c10bd3eb93fb742574f561f3db752d8fd23aecb1e5b15fe1f41a6ac4dc0bfed115344d6ea942a45582ca5a309682cf41d1fcb31d5e02ec8505f92f642fbcd02cfd4152da4e890904d031b22bf0f3e752820e92b410af4e366d1eb490acaa10f849da3ff7743b900b02701405e50c7dbe444c86544ab4209b4f8c4e83edab7aff5824fcdfb5b5350f1cf484bc1b2abb62c8003591726c17a537fda146ed575c55e37804f672d64694e48bc9b36dfd475514ae4e98dd5984a9aad24aa34b45e75068b5b139f99cbda09776975fbca448f0de92ddb0ce444ac9079d6be43bff2a996d645bcb59e7bd467236d1476e7866308aef87fd7fd236d1967569290fc613e285cb33ae657f342b56f6809d5b66a880b02cc76b6affa2516cb8a19453f2b3dd17f45542c7526d6520f3da91809d3ddeead5d65a1710b579970fb61a3974ef4b49408157bd34515cc188c3d6de8450be783349e112a620ae4b0b7c51af068e190cf6628927cd8808523e23e029941dc4e3293c9938d50fc634e979a5367aa213a40334aacf545467b954efa70fe2398df5a345b942e779bfe0f21e8e2d6c0a67d7dcdb7954335d4fdb3b4cbfaa6331bc33bc81f5f9fda1dc0c947ceeb7c56fad107bd925c78c87223a28a8565b90a18ce927186c8556a39adac746137c6e6b4628735fa2c2aea88d15b9a8e91508bec3941c70207112f862e6dd4b4521c430c4764fea490b9db36c31bd6839d62c0bdaad428e0fd3ebe9ea2567da61c0e5315eb3d6f2aa76f87cb6203f6bcb1d341d4d5c58c6a458f45d2ca4692f932f09a685069cac657621860855c15a14f0aa937e7cea46d6098a05854f86a6ef5daebf4b440fda7da440785d6d9636a6158eb93ef74c218e0c581b2a2be29b47dee78c155306bf1e34ba1974532c17d6d65f275a9682e0517d8239e364966eeb69a6bd48db6b1501e8a201a2f532794f56644b94e0384217fb655d5d13ed121804632d882fde00fae774d3308cb2f8bab09dd9b84c2e7cfee4330934bf915c5c9c14e0828ed4a7921c1aac99638830b6d50e302f22645706f62db2d0d053533bb0ead4e9e571b1ece49d5d9f69e0a9781594235cd4a3aab5286f006453c140767f3989bb993f299c09eec6012c36048c7009575eecb6912567a502b656dcdb1811754052474edb971edb06d47160a35c6ec032e9b1874762cabc766e1a082b6140fb88ddedbb490a5f8c965e3c8fc6983d0c4e4df4fed46e7a90713e68e032f363dfb76c7cda8e9b8d58967a5ea3c2120ac0056d923a894071c8a5e88aa5c27cc23acd4b5d4a6b10983767b615bdf4dde4cd3d633cde44dae5ed87d90aeb83f768fe2570e7c4328216a38a692d854b44da9a9c88939c0deb7ae004822bd7683a4a3fa5ab89c4aa02188bfa4f86fcb04a607795c61fd2f7c0fdc86a2b6e3ceca97d9bb9e8a2055171916e1dbaa6d20caefd445288bec13fda5f7afb9b798ac3667a4b85808bc8000e29d4234ca0b9fa2c1dd301cf4b50dc543259ec84e85c8c967325d4cfce573b356a5a1927e57c07d286bc155c559486e77ebfa3909d8ec69de049e7e7883edab3b7fecd44de737ea39eee21a83cbf46afbd1d6e915c9ba8dfcc96575d0a3d53b6afeaee707dff733d6b901e363e2748e1124f56db70869d11b87f878c269737a0d2ca113e00ad29745647ea3057fdf5c57f61ace002b12c2990654a9435a4ae6637fb00db78d2ec84ab0965cbb6d0cdeeb186fbfd48a9cac70e8685e0ce3fe0eaaab80ecba1780e42eb6c3ad850b10dc3b576c1749095f7aead7f04a36d3770b645af5ec7f4b3b071c77a4a5ef94fd9233a9c8bf6ebca70d63cfd8b7ab6c8f743073611c992dbe11478195fb08044dd3be994d9029d9c7be90022edc5dacf67f0f47ce8cf8de7a71fea93185d7d7a6444ba3fccc767b2164d21155c9d84552a99d51de0f552316e43a6e28f0b3bb0669300530c7386e429cd8486fb0d717722970194189962037bed66eee52fabd9e7a3bbe7ee38b7277fd82941b0358ff8e24ceb278445956788c6c91475317267b270036048b6d1c1fab15f14afccf255a55c1e5fbae75b17653fbea4afbadfbb46935b90e956e4497a16ef8304c5a43603a9cb080aff118e2a2429a595a6292c0251d32b28528e40386bbeadcd7e0b357cf344b3bd71c5cbff64f15bc47dca42b76004b1d2567bf7fa9eb873fa84ecab14e8993d8dfe660c48c77b7ac42105ffef587b49c314925fbe5071d6a32ab0165f15271e8e6c7d423bd97f3c9b113fdaf3c6df38ff626a5ad11cc00f58e751c8e3480471133f8bcae4fd9078b173267a60320dcfb44bcded141d29affbe6fdd9e7e350de7199857c1e560c2b2ef767f5144f990cdfd6e34978d17611d1d1bc955391d4f339050d7893c76488bdb13b2031a9be5e369cecfb0a4d41a2a06c3788d3f38f3306d99d54ed737408bbe202301f1728e04b78651490b4a26edae95610ca437a40e54f457d5449a54cebe1fc73630cea63b869d3e0d7a268ab86a92577cce888c02ccbd2253e89dc661107f3c167ac1164de224b91f1a6765db3b20705c708853822a825b87194c5f098a7323abe3d41634bd592d09414578091ed56ea9e70fc17092fd4254e3cbc8c0e302eae5fa705707b5174b1c84159927f5520c4f36a61ea3840f14f4311d9e9a49d0ed241cfbc8e14bf7fd39130cb571cac23a39044751915cad4d09e3264a2987b35807b4893ae01426132a77fc5650721d4adc96a376e258ae5c4c57b10fc4ad593ec47779d34cb35b6ff63a789c0d7b67e9f5a2f698dec4891aa17f48d33ba688a8e06b48dd00a4bcd1ebb0ba86aac18c994889a6bf1f94fcd69c0c9176866c155314c54b0c9fe237d2ec770f801ce6a4f3231c48b68c53d629b222e5f540bb99d971a57b10e20ce8f9053c1844661bcf1f8d6a6f6ac3989f7f03e78bcee4af0958a4f59b828bc50e9c5b77fbebbb9dae09371f89f9ece5f84f7c37e9a2d58046e469d74088f2c6dd82da5238ef9c049b87dfc2ec438adda4eddc01fb19458737c7c594dc49b0cb28b7eb7164defb561099c7203bce1615042548eeeb5bcaeaedd5e6e96a29423e373d7da23be3594b9665796cc7fca206f894eafa335d0e5c55765ae7eb793221d792b2d9f996fa425a79d66456bc49372fe2c70069541624edf6ff50e8473b80ad51dbea7b55d778757801bb450ee44fe678d354c421940d60545a0ae44c0a108b7cf52545f0e1d4a5d55d4960d6f1d1e4b6edd72dedbe68d0892c8e706dedfc5c1ea81480d2e7fe39105f28e15ad585f57e089b4ac7ea973c560252351ef591bf2c1f0d89cc2853c978f0118bb854394a91306e1241b069315e90bf7f5d0b7a8dc835d9c00462307f61405893b55adbe6212ab2bf6f47a6099c00218655faf2a590e89cf61ad58910b156b9fc5a398c6e29cf01341d9e443e2cd68288e4fd34bb2641accafdfcdf916c5853a326adae083836ccac374a186b07ccd0dcee2e53df4aed0751ada40c649b7b3f42c6542f6568feb378eb3792da325abdcb03cd42e629e6f74cffabab29d6a15ffc6f0c3131269808cd03e0068628d5a4ea6fd0980ef276aa772d82168cf0636df000fd1a9c9cc63f98d7ab2783531c8481d21f8529bd93e815b78249ebdf9f910e89ea0b8d7ff1e1a4c594cd2c4b15878f186fd33fea9a0b72f6c3cc0dd1ac67e2560d37c886f29e80a1dc3e4ab131882b5f058424f02e67d922e8782c926a2f09b596888329d36866f4296aef1f84b75893eacaa51283fc8ea3d27f0a029b70d476a623a9125afdb70ab50bb5494faa5c8c2ffcacdcce931aeb2acc2a8e5d35af2331c9506e2f1b4ea9afaedd48644c0c642946b31330a140e59c59a7e53de994abfadda7b7e041d1ef27bc8ad33db485cb20726edacb43f9e92d8601fb8f1b65f00b75d5631c34564f8464b43c69312fd30612586fbc0f4a5bb9002ed134519632c9bd3f1b2c4631b184a052ea8b2750034de21b301617372e89a17561ed8fe4c1941b79e80df64f8854d1544f8a4f8ecb83120ff06f5c9fe5593d4aa6513a98d1eff773d3da712b9b417926e333cb6cdcfe1a3cdb5a95b941f784f6b07fdc2f5039a545a38ef038e3a549258cc1013c4fc4eb828f84691b9ff6892f121b057c7114c225fb558faaa81840e0fb888a1f65332b59c61e54e762a735364adca5b3f2ba4da825673961e8e37ce69aeec9694d9379e51397ccbc56f19645a72cf775c043dcb8b0c4d6825f85956dee9bd5b050042a97f1fb7b6060b9447c680e0594bb750aad68e81b8b6fb4a6778fb48aaa34c9a09c29fa1755739e352344961bc2899a56c8fb1058c0ae7e8788eb5a8e9c9b2f95b24e09e5b8de10c371ad281a922163e54938f0b5864b0fa5151973863c77f31864140a6e2a6daecca9af0f2f4afe43cc29d579d7a9b6d8a45dcdc373482bb50109da06d89b4d64341f89331c9cda84bc024ec8869f4b4a186f598e97625abe7e7fda7bf94ef5c067856ea0bb3528603d4a30f15140859a33706dbfb52b257e46ea29d860e62116e0d61e00fd65529bd1ed291ed8592e3a804720cb48836fdd351c6d36647e254cc0c15f47fb87d2b5f5cd7f85e93091ba68de7a282b5ec5dfb32e272780db19f77bc36c0c4f45ab9f5779de0839765015eea5c41f8b9c618bab2ce24122a571462045e0df89082a23d708d6d74163bbb53ab360896698cc77204478befe31d47e5e09bae329ae6fa4eb77765fdf630806a7561389988fd3a40a48817fdf36bed7cc9c427592dbba3512df2fd0364290e07d284baf84423dee7bb7db241d127bf172c99560de1196f860026b9f622347f6f1780de232248bbea8a441f3a081b59f4a490072f19b8f9261d30501ee02a4bff98670cdc65d5ceee9f755897e03f6a77d787515baabca946172a2689dbc0faee12a3e8c16b5e1e18b94d843221d5edae8074dccc044aa54d46eb183d3ab727c57bf2ad2fe00be3cf07e3f3e5e0e5d1795f90301f0599f1d88ffca82732ebeaeda0dd3768041c5ea392edf584b743b72e7b17254df6e11dbd48cbc1086874dbcad4459babd65f00b63cce75b96aeb17f9ff234ce3c6fd61bd9581362d36eb418794975591ebf743eba08b0a2a684561442aabc0bdb5585aab31c9e880cde3784f4d45105bca0ced46225cbf95fad0f3e368f2425a4aeb90f86f3c3d3af8db4ae2f15b3e7b0e7a8cdfaf781e1bddabf09ed3c4225652000f9ed5dcfaed66b67c050a078fa9a703eeec48fb9ee6e75b8a8d7c7dcae4fd8455b66fc46ed758caedb8c58f4a319f4ce35f4519b9082b332384ed7767e77b3350015956f7ab84b9f6b94034e7a8a875079eda90129de0f8d9643439172ac3c7b533d43e2178782282a8104d60db119dc86197b65564aaae90491fc75873ac089c237234cad8544525dfc1ed44be421622721ee7abdff3dd5e5dbfa27849244eb3ce832e7fa0bd8a57ef8d2e0dd322089abacf3a06793c1ebf9932b4fa886d813fd5b2d77788787d04150a2fca196147b06a508d7d0b0e1ef23f364f42af78536f398a27c2cad8545a66b5a16d068e5628e4b5c40b1eec63d145c15d94e67eeb3fa9e42089e1be6267ef2852b6b4bc62340a3e79de9e34455d015c35f66bedd1bc734b499ebc87acaffd769d720400e828f4ed5292e27d18b69112fd48988a8b95dd1191e2e85125148532768f4e5d97907158b7fe11748f847c13ddd8b1a56d0c63937700c15a91c8836265635f85f6a5d8a15e45eeb772773f1e306317e44d664628a19656814b01711e63a74a8051a4d8f3f7f6eed9a074b560bbcf99c2e768f4dcece3b61211c41818af61a919d487b26b32333bab43e020ee5354ea215932dd076114294c26d6c9eb00efe50fd61a6da7d4751c98bd48e0e1fcc8795521b1f12186c6beab444b108e42cc4e7b936fb2c124746c9d754abca301f9a393d3e87944c0291a9e56ab3c68c1aee545109bcbb7a5f1aee4a6d8dc9886882fa125cd60d72ad595f2b5ce68ce6f2fa1b8c58d46508dea96fc67bb666337e13bb159c2582ab400ac6f385610776515a130798efdf15112f0b1b911712ccf5f5479c89beec0b1352aefb09c919b9f300936caac5b82408d1ad6b30bfd734c3b57417db7e9724f60434a91f9650dbb1d1d220f836a7c8bd143669e053ca91a5cd594bda857e29f937f653dad0ad5452df94ba675e09fb8952eb267334a240dfcdbab97ff5558c3e4130c7472526192c97f58900e82995881cfb23b553f4ce6a8a4b5a83c63eb742036c96c004a05349b5e5a07526bd52564fd9c37d2805a052ea589d5570dd551f2d8bfd578a95203e76b7ca8becb55cc3b8b4e2d23b105ec5cc171919ded2512ef2e45a80af0aa98e3124e22688184d1182a9c323b140d8768c5762ed1c9b7225691e18ec48ec20db28b74cb0460c7184dbdd1ed4f61320088cb8c88e215d5c8447ecfcaa1949e652270a9222afce0061a370dfc2deab446467bdb615e1e19bd631ef24e15379d5b1f5b66e2061761f806a0cc8982f79a1396eb3e06782384ebc852b647c7b71c15b7e6a156ac0032499c399b131ebf9fc4624bcfeb9bef9c63ffe49ddba20f4d8b5f24114ba7f3fabe4146712e0a742c60acfe22e7c9caab22630c7c8916f075a3f9edb63654f49d7a6f7d366f0b7974035d1ad2fcac42582a8ac076471eff16dba3f3377b7332f316d386ebee955d1be778f89aa9e1cfa12ec45315784d2611294a8f6bc643e3faca8c8e0e154d24f13c9d3f9806356a4471444aef4d4268986854490354207ff745b28bfeeca5340beae75717d528d449dd79a9bd88a12f98e07afce60c1b238385b7b37a1b93d59ec5b8105d40942c1e2cfe7bebd6c44e4827f669a12ab0b608720cb99c1d3d7942d7ce599283d2ae628e6b670c0c162b3aee8531dfd4a9ffb61fdf882e0a66eb0c866f7fb0b3b779b031e0eeb062703bb6605837f8e957a17c58404c73143b5ee8763e7c0e74c4d4107feb1c743dd39e491c69e271fec9565e81ab282c5465fc5d0d98a942594ee17e4557bc04c14a1e9b3d6f997bedf3c9854f21334f67c30ec17e0a15e27116bf05753123ae537cf2a735b49fc90fc3b60f99ac5f5b353427a97d411699b4e93f36a3ed027345e228c32078a97d69c5c18d1d791b83deb2865ef0e9e9a3b9d5b0f25192ba7c96ee05f78913a9f04e317082ff28b13b75e81e7fbf8c1eaedc4a2c3dd4230fbeb1ba300aee40c9eee8c0894c45051fed1c2f7c419b219bd845df48582733c4e818811b26baf6d133ed12bae7dea0f3bbd23c599d62efac4d175eafcf0c3c3c797981df399ea62ad6e2095913d86a74bc88a6a676b3a447422ea3178cb3722d7e05f2175cb67ede648512491391586f5c99654839cb126129ad7e59241f20223d78667a6fc2762b0e14dd732c62b549c2b22705b2b405f02eb2a08869c7dc35941362c37d64d2ddf8b5237fe789999ad6818648aef6b12476cba89095b66afa4d1bc3491396315007848b807019ed74e9e033179954af97c5745cb943dcb469d933094c52d7a4163c80880e6fa24fdddbd7fcbb19fdad8a17e75e809a1ce08b6be377c3501e16676f837109c16e7ceeb67199fbedbdd5d78483370d0f61f4f941da4d77247256ce3e2aed312d09fdce505da883170c926bc1b459563775f1a5aeb409efdbf74e419caf7cc6323f319a2a0c29c63f8286f0d63bca4dbf42deb1d1a101fa86a0eb14182219fb5d013a874572b4cb3bfe7567c1399e0b55bd2d3d050123cdc342dc18ef23707c27f3508d3f6fb423642d9b96cc1d4ab6427ea98aba621434d117a202b4670a8151e7cea68656dd90c4a7b44a38ccdf5dc6dfaf72f4c9455d0d0b690af8b478b23b4e61c886cf76b397287609658fc729d03a1ecf3774edaff7e6b5607cbb15abf4176dd5fd55dc19971f4aecc865b101955686ec3fcba9f60cba95b3b1da8f0ba657a199735f83b5f41d99838245bef486f20952cd307d6a684e5f2a4741cd39e9e73529d55debaa5acd9a1c55405f4b0f04c164474dc99b810f5cb3bd2eaf17d65efee7e1893185e02e09c7b12b111fc0f1d2c9b64c95956556a02c5a4fc064e2618157f85f5ef469795f8b638461c7feea655c5061002c027fdc405acc396306627c97041abb1dbf46613ea28daa3898f466f02b2865b9f952d62f5c216250a0cbdd04950347187854a273b03f9e983c17b6a064c78477c159043e29d7e02ac9dcae2ae40e25eeb65b177aa86461c2cfe6c0f22cd11d1c495a073a52ae2844701bf4a113687701a4ed1a7d6f19f9a86a84a8ee7858b8ea0da61eae32eb4dffb2abb07315004c483e88743cd966faae60aa51820fb35ca298d83310e360603162d027e2041659bb43b719dbbcd741fa8c9d6227d24d31b7ed3fa792988f1dce1259d721a5926fafd81578b4376fcb7476ba565db4e685a2d6be116b07d5b44ea9981a5e580c55d3e07202619ceae9daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd508698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b550406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc37542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207bb7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf8125c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc56738c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bdc2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d2279c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb274e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c8788966750673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d98aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa934e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d590999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369bb2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba38448bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8bbd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1ebda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f8411421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e31372d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208ecf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40bca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a83b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e387113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c138f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e06ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f3351bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6dfa0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591ccc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d414a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e61746892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c55f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8feebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a787e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a940dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea23d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fdefb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430ccd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe050616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8ad1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d45097969f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc247a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d39347e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6ed91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a651788a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a0049c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c52235f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579eeb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a09ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e359128fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd912619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa339833289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d132c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a27abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec82d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9cdd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb711ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c626e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fac6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a944dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f186bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0eecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78cd4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730cf1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb13445b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a376bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2eafd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204dcc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b49472ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91eaa357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e20a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaabfa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd116578108d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b27eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db628a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e355897f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf9145d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff3468d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf8124324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201fd9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f2281af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d432820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417dcb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b612a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcbf0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b080199167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebebac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ada17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace29326792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be6822567ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50afda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d8686bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d969147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a8669d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e704f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce174b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734ed4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c31d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b0683921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d35405cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f304f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc2519ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e7259a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91fed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850ef76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c885dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f22b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c363f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c169776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006fd4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660beaf946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87dbcc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc37162b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db201886e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea85a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137aef78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3ccfff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bdf14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedff10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce87b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef97676486866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a246a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d50606260186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b821ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ecf33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f631391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9dd48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f70a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6ebcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed328437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a2162bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b7635375888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d45e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a3817898194f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f6341fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff6371433bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a45e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561bead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ffb552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea21da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ceed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261edef43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676be20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2dfa21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec2244b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fefdf7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b45134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b2190ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b92c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f6016204080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da096795092edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3fc9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7dc2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd8659b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669cac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101aaa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aad7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf5016975c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79fb125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e291ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c2447d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b381606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0ee7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b855152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f5011f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49debef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc3719d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b95b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c4367f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c25359724bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfbd72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e7597704084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138fbe029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce653e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c10ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4edc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac46217944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf05fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec811b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c07924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de2411e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a208590eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de01999fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c726ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a7333453f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb011eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9eb4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf66d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce560260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e982667311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa67683191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc713109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0aef43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f345532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2bed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db61e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562be1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfcf54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bfdc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c476674d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c55dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e48b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b31514ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a462632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b535bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345def5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06df734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bbecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865fd73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544dde24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f30e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a232473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459dafe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c7284f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6dae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb833f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6dac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e78ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d22031248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabebfb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59eaae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf43bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810bce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e00769878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd8378d8ac445c";
-		String expectedSig2 = "01e40f5b447267c994bdc775ce6d98b0e559cba26c4261d70927adf1bb11b3336858fe064267d0514a6d971b69939256b8ce8b3d8ba0d796413f561c6825e720e613aa3ae241524150c17b9f49ac4cdbf986bbdbd5fe9367eccc50c1c92b961cedbd701216db313b8d85b93835ab94d1ac85d9fa186e7a99d294f43e7c68d867ec746ab79f2b3a523eab1fd65cc4c1c054823f2bf93a7e535697919cd9b8d1edd375057a0654a2c7cac9e8296dc440e760a6b9e279abd066782e07edc8f73608aed1d9a96b3ac55268171f26e75fff39f50cd72a7f5e86103b8dd311193e858be1a56492deb2588b1b5ef1a361b88c67af7797d2692d5fd38006580be6874775b1316a52c00b8b1c582e9f37c8967474520d6c557d9969a020f78f424bfbfe76190e34d56f0fac45fe1e550ff9e234d9a8bbe44520a18f228bb5b0c8f51d041134dd9e4dc28bb25e0afefd311c68355753be9f3c12f85458eede1cf39442c8d51a4f9297a91864cd98ee8ce77584174e594072df3c776dfddaf36cb1c14161965b3406d4fada8ba2e47e25fd90edc05b8d42c0d3cb326524986f1b3266cbaac2f3340efed4a103d79c34b4bdeda0dfff1f24eb9d4d41cbd19022d1cb2079ebd11e376e7b03161cf1a3190e507839e2a8629052714c57ef4cdbb2bf7ec7077d3727c5dc4642f2bc03bd2cb0dbfbd4bdf0e241d3b083913e00a1b0f4f42a549a407a74219d5c5be4e9ed2efe64619aeac7df02422ff85bd1e84b827b6defd7f47015b268b4e0f11f03fb3fef025c06e7befd627ef03ae70ca72b99e4d6ab5efdd03edf3ffda079a8866843425c2929550dac4f59e1ec47a4a0b25b651f6613ba08e31aff45a365e5d77c8b7bd8b88bb10fc4ca0b2d236cf36ccfe9fc95fecfd627957b2ccc3709fa7f503a332ac669f7a4e6867f298826853975cf6fcf3d22b57247b9939d5c12f9888b3d24e21c26e8c081e8885a094bb3082ccb9493aadffc3ff600c5717067a41b74b175036cce1728ad47c38bfac77fbdd483ced51316c5810efe97c2af137c79a534cc309dbb6f91d31b52f7e173c6eff81569ae075d396a2f4a37ea71d5a14d910b5dd82cae41121b62cb496b0e797e66d6fbf9674e86f3fec5fdb6775048272fea37a6f956353a21e38237e0fe7334f4e5cf85abb817adee590602ce599d8c2d338384301855f239c9438537800cb102ccd6352232fedd2bd5d72a036786f4f291b0486061d56d1da2ef2392a373b6170179f236cf7cddead70620940013aa7ccad75feb73f7a2cbc74528c0e8be8146f282db2e1966d032f77855c690247364416f07fab1e9ddcc49e45a20c846f2b83ad7ec9a89883c57cc9205ac197a2380329981c2d772a4698d19fa9651d6ab63163a3547fb3fa796ede999e61f8792fa28f5561c206925fe9ab9a74f91409585770515ae68d46c0eabfa6e4bb937db3beccc371cc7aecf1e0106ad4d5821eae4097d8ab10923c80cd81982f2fc742b48418c9af8cdcacb29ba1591b949d0751ebad228ec7d793dec4eb2df56a69c452a879a13882a23fd1a849aa718d9eacb04d90800202de9c64fe35d77847889ea566bb8a0a0736354f405fdbf0438c89dbd3fe97259858ae4db96289ce5496183080aeb24a45377d8e04e223e1549e72d6e3a85ffb5a049f60c017b69d0b60b2b6913b384cb88d02cefe09aa17244c12cdc881adaa1e62c903fcda166048c66fadb7aaff22cd26211a78950e29ba6f2fb66fec26bf9986e843771f873e84b5048d73ccd04abdd40e4a5dfa3ac9d0dae0ed58c9bd79243d44d5fdbb8fb682ee716075e04dddb07f4152f841cd50bd42f9c11466f57be633886c9d1c825f5cea5f7ae6d43af348dfb8a1b2d0cdd2186bc6edd46ec73bb15e684d54bc6ec7e1af48d5d52b9520d8cdbc8e57f9f00df89514370dfbb5692eec74525c7da5b5a056b47cde5eb100d50affb1c89a36aef92cbb6411ab3e02846a7c7c58acfdbad52d5252a0cfb8516aaf51d2c1881c1a250fffd2091d014bc9243651cbb89c3d07078c355c098a60d72e00acb82b7b6594f259082d2ee3ed46d103a9d0e5cc2d0a557b975238190c6d44d6f4e88c0410bcf641082836fb6f766977ae623cd908ebb8f72dc6a42b21e12ebf4e1315e3422848f32600fee5698fbf1d6546cdef918d70b5144ae032e12f9c30ef0b9de7bbfcbb6e0d0f04b63443678936a2124e08f90af9bbab707ba5e3fe7fd60afbfc4a5be3855b2e62312d99f7f1f22512c4217ad36e82a3cb66d536b44d593f6dcdcdf0010b40da4d6d865e2def1bf2b6b6916d7cd4865b4ef45ee9b3c799f4cd3f558bad6184d1761bfe0539f9dc88267f1e7dec04ebd423d8b68eadd5a65d29ad681e36973443c8df5f93d472d8b05524486b42758e523a04a9a2e534ad4293a5f558f2388609854a3df975881380f9bac5c22a70be11ff53a14cd69a1fb740b2c2dcfdcd2361f73bff5fdf7139ce51bdc7ee5e1f10d8bdb743c80e4d08cf74b07b034f70cb76cdd9215c43dfd10e4c825d94d8e944745760441b223c915ffdda08c22c585c7209d414322a595dd04aa3aaaaed42260d0c6e807cf4dd84621b8f27581834fe8a4423a51ba66b00c08389271416a82877f43201fe3aba56319373cc64cb8aee9cddc393df49037d78c5de8026ff149fce50aa50d9207c842ed85322ac391786af1a011722db75a9a3eee022ce8a39d59d587bfa2f7afa707dfc9a36f60e0b0df2775d32548e33a91929ded1f4b5b61b66fb193be72dd575ba68ddcbc62e271aa507dcb03f08cfdc4c34c62d6b858920d2f13b32c27b4a4675dda223d255cfb4d45cb24906e2c3bd8811adc3136c6483b689a0662b184e5fd1fa4b25f6bf100d6609ab1b5bcde4e2767e4ee36bb8a11bd3c771337941deef7676ec15ff3314a29b4ad0a679ed4bf90264401a71fdfdeada3666604fb05958fd46be35c63f1729efbd18ee60a3173efe26ccfc47f77d3082b9bf47ea7495875f3c00bd920abb6745de0aa4b2a773cb2045deaabd16411efa5e9420993cf9aaf90259c1c6971055b56a452ad07b3d50f7adae206b3663178b91193c18ec07c651a4e287a805d73f26f16298f01a6d999cf309f44705d4a60d0bac9cb6de4b7c710d94260313ce95fccd213a6c05f920e8324a17e9ea8389e828cd7cbc35b5a8b5875bd8ff61146e37860700ba2be69e3dd75219d7b395579052d9f3812c99a2b3323486808dd33568a43f8f6e75c2cb50c6200df42a775caa87d419de00273e6971001f384c2e6a789ebeabdfd524dfb33330a4995cc1c37ff80532a69c7ba2e18a0a2ab116f3ae0cb874fa23de6c87fae3d7f0a194cc459874ee7ef96fcedee8cb3f261d01b3059709f99fe6a248fd6dbcb882504600d4711de90f807ea6c88dbcaca6b91703148b2d1fc58e6c574affd9177f44542cb339169cbc0480a776ae20c79cd04f62b848d7505f5caed8c85baafa9a8b46a2773490e652e48e4c58357cc008ebb9ced1a8f6288d23faf884cdb03507bc403b88ff0dc2305789bf05e12039536a502ddfe7b0733008e7301763468030da26bd446e87072624bbaee9e859dedffa01efa0e3a9a2ee09198d004847c61efd522e8f6210778500a01905c64585ae70f1d7694fd284fd91b98d0ff9fec913c77e166113a59f7e8140699e6593242565683e9a75c77ad30aaa6aeb3e84dd29200d17063a63baa798c8dc4f1f1cfb1d35ea9f68023346f25b502dfa48317fb3970e58b9eba6e64c5070bc93fb6679a42132312e650616c05f120f365529eda556a2092736ad9adbbe6674d3dbaf0a4b1801202a8a9fe715b002643e0db247140cadd24e8a330b32c80e914c7ff0e9496907fd66e76905547c7c3aab6d11bd53066c932ff5a8fbe72370748b9745fb6a27b477287522ff13af06fb59b309a14d266cdfaa379198761a1388b79288107b91b34d35d301addb4ffd345f2816284dc03289d6721fb867f78488035357cfa9d4dc7fda336f943b613565e3db190d9c1110e695c74e8966f634206277d8c380fc44182e6689208821f972c1e4f355f4599e6a9b151881ca3e64e752726e5118738894884344db8b83835d6ecb5af9ed3215001801ced0a549ca4093eeb858198e1cd0956420cce95ee41a1158aea1dfa99b7f9144b6dc27fe9a5086a4599a1e90b7e5a39caf467106b4dda79cc990643e0754217cd84822265ae6f45011f0a1662bf8b4977948ade6db8ff47a0843d224121b6933bbd77a6a4c2413e58fd5e9d325a1d24b039d7374588bb1651478caffc77ef54d52c6bc1d3cb8c0e1983088f9d00fd4d20fc335a37dcfd93a10f2e417015d51a2851ac79dea91e23f43d88ce2a3dd144481f17f073ac06bdba3edf7f8b5539bac47432736ecb4823885d9b49740586d0f984513a2797fb0b24d160ec7f31022f501af3466ed0be36a73f8e05b23a9f319f7f61e3a8011f8318d9dc037210d67a85b584ab48ce078ed01416721bfbe082ea5edcb5299e695961d3bc37801715fb1e5e7d89fcdae270f8f680ce1a4db609d93c3585e7121dc93b136959524b8079d9e325b3427160d6f7217dd412ef31a0cbf9426007779ce2714045ea61eee710fb156bd482d71accf8789b17dd333bd7965b72dfba9cc794c1ced4cb0bcd551ba70f980797defd43b06f12350d047a45c7003c61bce4760357ae904e34811a714fc80c31407edeca492787fac40e8062ca6d392a413902eeeec47660379ae96d273aa5bbfefbf5d6d2f32ca1ee7a78c13e37a8cbde35bd5cb2652c2b13a0c1081d4807ef801246f2e76d8db95fed0ddf56a219e0e2a9567b7b8a25f42bd328a6bf4b82c289338b5eac473cdbaf250be2e3d8b17d55022e79b16a13b13b031ffdd15a22668a11ee6266d2dd38335ad6456b9acb03e27bcf62c20a44239daa46ad539697ecb05af10eee730873c95abb33e7f89984b26a6acc76bd49b23176e654afc1eb2c48f9e079fa9d88c465bc4b13a7a6e9d44ed123d74f67a00834427d58627ac348b8d213217ddd932ff9865323028a0889edabfa5c21d48f337bc83c0ab2ab84eeec89e060b54ead106d42db1c9dc77d756494e078bad641e2522f2030c00ebe184f633baf34c0cb9a2ad6775b7a4a6b966b14a24ff6155f8aa6a598e814368f0523631b5a2b3ec845f86f8769a33ee69336ba833880d1c80c9844379d4f40183a7f37662f6d1bb02b90c65d83949fec61944fdcd7d450d7ccf870e1c69185e5a7e2dfe56d6d44c3811801002cc5289a82a5df345ed2774acd24de65c096bb45866e104c7388ac6661ea667548146c890bc6c44bb1824b2d9dbfd885c969bc8ccebab308da78fa2b2538a9d7a10a3ce2a2d6662c6b2eb60dba7fd8bb0ae8623c7eb0f09eb9f24f5f5db49285fbe583507080710a0fd22120d11d3b5d53de4b6c695fdfc095cdea56128f9728cc71d13f0332e6f597d8a87c7d52140eb847dc76c1d6aa934f83682567b1cc0c848a883c44c1af09bc97980e83dce964293353126beb1b7a7a8ab50562da1db75ffb9e1cde3b2c458cfac0d13e4b2ceb247ab60c189ce54ac0046ca20d15756a1397b3a206dbdb0fb5a3f7309e0f7025e1113ecdcb4d2d18074e8230f990edfbdd4eedb24bd2e82a17f228a7896ca854121f43c3d039ba38ae6198ce8f57ff91196c6427905181ac22e885ada2a91818ebba779af1d4ee4a4edfa0399c5d6c418bd702f1ef0588d5bb3653b3d7eb8786cc90b8d6ef6ab9b7a4898dc6681d4f000708a558ef157149e3826eeb136c9cff8ebdb849f02dd560942c1db73b2de5ea3515b064ef57cc877df7a7874aff2723b1764d41a68f9e6d234066da64fed9ad0bf64c8680befbce0594e1f63e4da7cd33afa09c69b47e2debc17a88d3c45196f2fbaded16af67d93c173523093964675f41a4f8b26c02c8561762222cf3b329198df795c4692c6e571a672aa5b48ccb74510871c17a506116398328aca6d22924025e1803a5410e726bbaf11f999642a5f72624e9e379d30f743ff707d03828e43030df3392e83120e8d0a1d03053831283c72736c0864a21b53c6976fe4bba9c0152370d6e5a7c04a780240f3b17956a9320d878d1d852ebacf4e9e32c8e5a078446a3abf918f7b43d28aa777b90169a6bb1c9d0aa1066f66698ce9487c8ab85f993ab0fbe43e44061d5a6be6c018ada8c9a2b5593fb4c7e81cb57ac489ab4740ef499388651af6cf791fd70980c80a8cfca4defa0651b39975a73ee4ada7ec9c54ae479eeab35a86b91ce28942334833c8f3b7a97b7eb3a206ea0e781f801b39e28bc970bee35584c67f3fe85d66ea34b5442a3d3badca19e52fdc7f8f86f5755b43b1807360432bf0c6722df99a2c9a2f1f9c05f9d1d9a358e5598172619404c532e490da33d2178404797eb64e0132b031ba00c0e34b36eacf35cc579a2c4e335a621a1511187c81bd424e6829a92763fab1dc10b88626137a5f147fe822a20d78a06c8ea79d352c8083ae8174723f8838e1fd14ff0c7cfcaaa5edca09c7825a0a3c94cf0e2fb1121861c4f66b2bc7e9e5bb9c0450f947510e920a95e6a241b9feee4015947aae1693a2a097ed1a291646e1a85c03c94c932eceb06085a2930c71b9063637650bf7e09ad7fbb5f5e7e429a273fd4f1a7c3d288a8d53330f2d3f0f7c9100f36cfbadd24ead6354177ddd29a5c41d5f95a68885850b5fe8c00004ddaa68ba6b6b212e14d97daab317e8d8722de72ec762de36bf7c2f1501f623b2f89620ef1db90050582a9882cdbd30971c2b0dd858a9296dd32607eb1833eb9651135133cc5aecc087ed28b49cebb25068be6d962df9afcbfc570d0cf8e0b30c2cccd65c3e1e451c4cab9b23851ef91d364a3743115033d81ff84a2fa63b02fe8716df87aeca899889f5b8e6db5000055cdf12cae1614dbb23488af69265ea4b9457f39c588dc1a57b16c5bc5eaab01646023dcad347e31ce1c3e19b5dab935daf129c8fa10ca341c246c1df7a1cefc357b34b45365104d207b648eef34a4eda691f9f2515d306e63f01612c6971d0b60d050550d185c1c56d96bd5e653b895e001187ff47a43841773786f3a2ff840865181ac92314b6f4ac538e9c0436bbfcdd674dcc6fd0512eedaf51c272b1c843da8612b5fbc7049960d63ff64cebf203591a538c8c85a351338f7d66730b7c2e37b7a630c81047ff827c1945a07d6bad7b2fbdf2d483378e1582d26084633d730c1bd1863719cf248382ea44bd4e821961e2e072f33ba12649d41ecf5044f24d25db2caa1bf7df0de20ee38de3287afc8136df44d0a10ff06d22243a88857201e111d6aacf8536273a77535e161be2d398e53a73eba229ac7913ddcfa280ef946c92987eed9c100d96139c190ec0b6c0d2688d050673e78ac3b91f40aafc4a92df24861707e02a7e3c890395039c361967cfaddc7c6dfcee4abe8c0d8ee642943bdda0bda6fc71a632eb35656f1f889e42745860f72cb3a5cbf2bca16d1bf5474da3dac6b26dc79a8ea042640a3e5b54b4ff7ff161c4f2e679d237f0f097b876ebba828586b0b7888997578f8c396b86b7b208ba86eee119cc4933f7a8dd62ad337390fb92e69c8fdb8676580b59464946536789416aa65b074a793bd44cf6896f5cdf4429a702662a4e7b8594454909b17cb1adc60c33f68e04ffdf71f36add212f2209e50a4af87e90211e0080d5332ae8b02894a9f708fc7bd85d76eb02e1a09e7bbc584fd28704fb5068c99416415ff47c0b97da37e267fdd597a831f81a5e1afdd70848967b9cee40668af36525098d7f62fa1a08afa180cc5ff4dcfcab005a407dba9f7ea9f3c11674f0f84a597db00de9cec80d96fa5798ad709b22938c0afe67ff07326c900a96c0958e06cf92dc9783d85deee989f7bd4fadb395c9dfe9c8310eddcb494a6824226ca0650166349146ae3af96c81803dc54dd4b9f7c255ae6a92a7ce0d095204cd309e8ef5e00c41df0dc1244ec5fa07b7e31f628af0fa40fef97556ba1c6b67e736e52a06eb8857094f9b005e218a85f9184ed41108927d09a8facb0aa6d520a232ba53140d3b649d872463c9c8e5fb771d154381a47116612593f06751cc43329475dfd33a2dec1e63939be98b5f00a9c0d3699479cf6368f634a2e5a7d1ed516c79ccb4dd1f4a738bb44e8bde18a1c4401fdfe5c7f04d7aa6ddece79de6f8fe8c021542f2f4f0f4bcad0699f49902ffef25dbb16889258fb32e8b77503d99e3c7a625ffd9a4f969c2d0961f85295704ea85a7de450f97e3a4e43f3ac86f16be8a35b3a49ab12e26480dfc6c8938a9ceddb0aa8226cdfa1cf02602495279e8bc85f37eea24fa1e5a5cfc145b1b893af67349cd8d5971dd902d0d2b8a2d63b59b62cc747e8861d6cf288188cd000be9a4caaeb1dcb3e8aaefb6c722953235d66f881fb9ab34e30343bd8fdb125f084b53abf40bbc0dac12a781d3cba457ea0c7b0fb7fea40f43072cfd2873903cddf1ef824ed2abc77a1ee7322a8d9968fadc3764ef441db4e91f87427c38c5b5cfb2b0bd95a2bc382f6ae19dcb2522c7a3b637ba584c6004151e688199bfabf64570be2f66bfdfc48d574b15fc38b536d21050bcdc466cea678d85fa965c29fb20e3e3877713a0c8352b7f51fcbb43ba9b93a43e33b006094a30b309010cc501b66b85ed3ab3bb251f793bf65cad1dd1d1b3de8eb397a1cc06b546f318d8ac0c9304049c2ea5cd14cd74a42c25110fee893392f3e69e9f83b4c05776c396983a3466ca23ce2b337063e93f5ebc2a6cf393653b531bd20a0ed0b104d358c04cd553804dedfd1887f66999202a1b530732667dfb61a1a984b40a09a7af20062daafdb1b583b602e96d42f77c74c77e12350fd328f6f34a86893bdec687c03455747a62953027dae4e927ad64f8e4da6b9a1991307ee8f02366585ede811a0a87b0945620c0165b1da30cc781726173835720ed74af663decd7d971e127d7e922aa844df09e7f0b6cfd7b10980ffd8395072d21a937b5cdf03d0c0b4d9b4f2c618587afec97635987709762fdfeaa5ac2b7dd8de83b12fd40866a0bab512a2fe4345f540a3ab92517fc78db310d2af63ce38467d34ec541958977857b6c5ee228baa095e65cc25d651d24401288799cd38cf2a0441aaa8c26fc45b0a10e3dbf14da8156fa02a054c60181314eb4ab9addb375940a1802e9bdd878427af72744d949796f61dd15f93c5c3d2fe791bb696d45ff5193a929a2a2f8ca18d661cfd7cd8768539e38c24317545d2a0cc642874ff3a46e5039e5d7ac4114614b5f77fffb37e28367ef081d39041d27fb29002d48426b685211e61ddf348010c8595db8a0488d08e58435f43c7c29f5e8ef62d402e8c8337bf5f44ed6f227968679081d70bec6239c7c9d3a780c6110bf0d99033caf4ecb2bc563a1bc7aa3e6708f5ab16d96fb1dcaee9cbb79cf361392d99fe300b973d94f3eb56beec73fe060db2b77b7f1315c62ebb3a22cd264f56b78b186f06a398f1194a2f7596a48adb387d050b1e012cada908ba81f3a73b77cef2809b957b08487a599f20c2de4a9d3c4692d57392ee59bbf5dd7e1998e3fee488c437355a9c91d32c9582a841453dc3c22b02c3400af2264f0242116af905e212a6abcc37052d5e684b6c1344279dde536caac3b4830a97a31be1abc900d304911db62b188444b6afa5e6cfc738ba4384a0c01add2131841e8eed06f7071703fb097de0cfcd88d35a9ed3a1a9e43cb4795815535c31187cb5b00e696b1496e433e68a1720d069ac42904669f572dd9adc334e112a58c5f61cdec7e5ceb985adc70acedab53b39b819a434d4960f29393fa27dcc2d4307d5b9744b7a29668bef7e0b2bd25302a935bad30318a47675f276af5e0c4e353def3fd38f8cd5cc8f7c6de3c9d117e9f77e0d34df0362cbde715a2e4f0bc39332b33668a3ac20b956f808481632f725059c1245482d14ce0aa87fb3eed61821383d23ca2c3f8c08e26e984c7db4e95f03763bdc7fa012a8f8d83153a9dd191660f003be85ba59a1d3bda02919feb8b3dd1b6007bfb5b58873bbc2ba9e06903445211a0f3deffb474bef06f96898935dd54112185f966eb09b88fc2a4c1b4921068d5a29114c33314c028d95039e6431be1f7541765f0aa4b8542639147e5cea89a1ee77d05dc9de90d306bed011d98826b54aaf7363e2a219db086ade6719fdf1ef4d93203d2354c3c8d3ce9eb8e66d59f2c295e5711025f76896c23e3e7cfb6e1d623ffc418fcd2a1dbab57fe0389da900d65bd054eb5514578b61aa4b89c291e3db90136758d66d3c1e244ed69ca676b288cf8aea0a17f487360a91a4e01c217860bc6bdb26b6dcd61c72ce271deaa0c9997cf1f4f75dd7865ddbd648dd6fb18cf6f462bddb46257f87c58191de0a6dc7ff59c89b353e7c78982efd64fd5df24c2e2cc63db48368702ca55df3ab8787a76f373fe33e72df268beb3ed61b91392258a64efe324286ffe80de31a1f5916a7b80bf2f24e4374a721a30eb9d5b95edd5501e2b80ac3ac2ee384aa01f0b80d38a462032e901586682f3eb6f26b5c52adf355fa44cacc80e96114c133b381c2b79226f2f5c6f120926afc4ba0161c9b643932c2aae72a54dc680034851051e7b78681be9f2f23da8f8612ab85f07ad4788ffe4bff5c18a1a17064de1de14be917f6cedc21027d4c3614aa5afa1c1cc64053458ebca56db5b677f613ff148d3610ac6943fb1c210280d83e05dbfae0a1c523f16c08452a7603a300038359a1c5ebc7ebf7487c9980d205f30c964d4eeea3f266ee493cc6dca41544f29bab45fcb5de8a7dcddd9330cbcda955670a8f92a938adee32ef40a20cff0117f840b60241b84556e0295e854780a86240df42ea75c9a33331f920610b0e18c5a152a59ede4b40ca40fb569165ecdb93fde3d44c0e1927b929d60ad80c4e295bbc6a1a67596d14dfd80fa2bd38cf8ec639fffabdd725ee1d34aeb54856ab844a37efee8c4df9b6a4474e1fbe6f0f5ea8fc725a2a78b3ae6386f1ee8daf49087bd573bcffb617809bab28220c2d5a3e9ddd316f7b4bba8de4022a30c0b27d9232df1f25ea199ea7d26c1f935e54d9c49cda958b761eb43bd452adb0e7a30e3ebe87aa05ab1cfaa1485dcd7634775c66df3a17bd04aba333d6dc73bc9d52a0eeb78dccf81951adb6187578010f9db56571afac84acdf6936f101409f6f3e8c038dbcd054422f28c1aafd0929bd30f5151b27d03e11c9259d225f72e066da74bed6caa6fb75848349610a48f4dc2827942596d54f1743765729a9453789ca01b2f19194e4bc74fd239138be66956eda707f9eb7215e4b377c0486df98b23d8af13578ca2565fb56461abc1a492615ddf5d38c1200d3c644543f08079482e014ec02028b7836c2c6ded978cf5dff4180d0c62a8cde12e4a7cec10a1da309ac9f787314bc2b5e83ed3a46031a258caf653f46a78ac6d7f35e079fbb3f28d356a6568d9776487a4dc4dba86fcb6d03674c596cc7da17bbaac12514d5575b406d9dfde9e598bce564a503a38f96a7d70cf60938bea798d363a69edc64a9b1ac9fbea1863be31ece7a857d47819f89c6b03c14d062d78bd7542c712bd231e5b771e72d1dfcffaed9c4bcc53e1a33bb60f9e40c6a57c5c5674214782d1823ea0470f9a399d7dde6a14d66e44010e3538ef8ec7bb31b06ffd4226b278b710518f1fedab126e5521f30f6a2d0b62e89dadee66deec949f85bc76a518decc5d2feacd924dc18e06f8915194e82c9136073c8aadec284032cc250555b825d79018c0fe4e85a155109403bc7d081fe49b9cae484833690936af43f966804b434fbcf17e7d2527d2f061db98e0541e23bebf3024023694ef4fa925cf8ac44026d0e3ba5663c9792e80ed6038cfbe5549092f63c6a60b8a88181a9745ab291b8a25f0aa2cb7ee40efb4e5eb802df9b4e24509c75be717a10b45fa4404c6fe7b31d23aaebbbda44dcc2cc3928b620aabcc4f771dc7947b59daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd508698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b550406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc37542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207bb7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf8125c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc56738c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bdc2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d2279c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb274e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c8788966750673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d98aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa934e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d590999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369bb2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba38448bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8bbd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1ebda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f8411421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e31372d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208ecf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40bca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a83b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e387113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c138f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e06ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f3351bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6dfa0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591ccc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d414a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e61746892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c55f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8feebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a787e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a940dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea23d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fdefb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430ccd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe050616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8ad1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d45097969f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc247a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d39347e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6ed91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a651788a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a0049c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c52235f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579eeb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a09ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e359128fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd912619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa339833289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d132c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a27abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec82d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9cdd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb711ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c626e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fac6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a944dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f186bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0eecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78cd4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730cf1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb13445b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a376bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2eafd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204dcc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b49472ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91eaa357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e20a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaabfa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd116578108d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b27eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db628a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e355897f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf9145d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff3468d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf8124324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201fd9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f2281af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d432820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417dcb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b612a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcbf0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b080199167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebebac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ada17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace29326792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be6822567ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50afda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d8686bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d969147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a8669d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e704f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce174b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734ed4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c31d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b0683921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d35405cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f304f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc2519ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e7259a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91fed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850ef76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c885dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f22b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c363f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c169776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006fd4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660beaf946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87dbcc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc37162b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db201886e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea85a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137aef78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3ccfff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bdf14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedff10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce87b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef97676486866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a246a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d50606260186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b821ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ecf33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f631391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9dd48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f70a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6ebcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed328437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a2162bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b7635375888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d45e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a3817898194f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f6341fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff6371433bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a45e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561bead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ffb552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea21da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ceed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261edef43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676be20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2dfa21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec2244b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fefdf7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b45134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b2190ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b92c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f6016204080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da096795092edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3fc9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7dc2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd8659b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669cac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101aaa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aad7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf5016975c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79fb125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e291ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c2447d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b381606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0ee7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b855152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f5011f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49debef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc3719d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b95b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c4367f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c25359724bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfbd72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e7597704084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138fbe029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce653e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c10ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4edc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac46217944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf05fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec811b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c07924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de2411e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a208590eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de01999fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c726ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a7333453f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb011eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9eb4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf66d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce560260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e982667311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa67683191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc713109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0aef43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f345532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2bed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db61e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562be1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfcf54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bfdc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c476674d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c55dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e48b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b31514ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a462632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b535bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345def5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06df734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bbecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865fd73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544dde24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f30e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a232473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459dafe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c7284f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6dae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb833f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6dac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e78ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d22031248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabebfb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59eaae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf43bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810bce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e00769878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd8378d8ac445c";
-		String expectedSig3 = "02e562f6fc4309848d94edc37e66c4fa704a494576668583b3325e859eb7c67c9f74ad76223a4a61b8f9d6bf9f9d64b74b6a8f41f64ceb5c9166787bec71f1c5513b9113bd1b2f30b524a18e34f1d88c54ce45de04ff4ec383ec2bcbe910670c1a8c23f49e3da5d162e76578295f912a532a2da4948a4c110df93ddc5d03d97a5522097181934ec58ec3d871bb206f4009857a2bfb810d4005b9dd5b32da5a0df79c8d895a97e163947fc96792b806041dbe480a83b0f6c015967961173ccff05eaa59284736ef2309792c04acf3eed3b9840c45cc03ebc337e9a3c7a853f23f9488c91f27fe7440fba480e6955fc32ed4002aed645a61713b0569bbb40e17b64840e9ef40911164cbecb72ac515d6f9b836896fc5ac94d10e97cdb0baa6771e5e8752151db53d32f7b8a46d8e7daf86e47b8366a627e719f5046675f6fcc12e90c1ce63525b98dd9134e0e543e35c3762da18c092e5fca540e302f5025ef55584ce76b4f389763e6b81ca675587e84645b683895aaa9ffe1b3edab4b1e74d9bcdab466950b8e07d837e2d42da273fb714db20162111e7fb76f919f3cb559c02b2631e61dfe3744e712d3331ff28b638cafb76d9fddc339911b7efc4c707d2cdd2c0dca6d503398f304c86ae055ad644f69d382ed7f1e9297f6aa6d8b522931d86cb82ef64de2839abb1d38709ffbc51ccc1b8e1abb8c8fa533ea900a3795c9070d469bf4dd7d570e5e2231295be081ac60c291ab9a2cfbcbb20b032d03beb5d0eead5d5c5c5d6b16efe3dc95a4fe687cad911fb1e67053dae986e80ffd25c8eb17dec708c01d63e279648a9f3ef878a450b60e67b9db042b26685ea4c0781bbb50e22e4e83acd6646afcb50eb11823aab3df92676dcd95c1f75a85eee631f20e8109aa76ad00a52f196e97b0107dc0345970eca562d8cda0df19241de07423a59c139768fd79f62f77d4646e23946b37c1df31217ef5b943ad40aa913fb8b6934921d21136fc25269e047f8f8068d4d226d7e9b20e2bf59fddd2c59ae61acec5fd8645630725ea446803c3404284dffb6882339bd657c1bbf92e959a4d0bc254110e4d0a8d6c83cf94b8e2369c4482686c8c7ffe56ad168c40f769f99c326205cc84ff15ac73356194640633e0ef08a062ed373188593a3f24594d8bb94a2e434f36ae5e14d417276a549ce352bc22911147ec791cbddf2a3f4191bf79b36221da26d7114e3c167f6d872a80ee39e5820adc14e41e25cef9300df3b93624a183ce0542ec0e4fbfefa2935e036760e7be47ddb99c10373e09615b6201b6806cadb60f5de7e2322677552272a67c45d909bfeee119811612ad05697786df4a56206f69011a3e0865254f1f0ec1826fe0fa3f618045775c5f14153f18f0d07b05df0b08a9adf7426f511435e6aed54b3f688b9747389e7be5e9ae82d148fdf94f2b6e8469aede5c68452bed66ccd180e806c3f6e04a31cdd6d5b01796ab6d92929a4d553954ffd20c14ed1f0fc133ea7764a9e55a2773b9cb12fc8f699869c6ad2072ebfa2cbb155182b0d6caba65dc772c394d40f1554b3dd043f0b25e698e3ad1d75ef5a40e3430bc0676bfce6e58dc55a60b16df3a7b1e63d588781f7ccedea79ae8e867d54429b55f3c0fe4975210a6daa5cfd4e83454eb836f23bc009890b74de521e467fb4ac600acf838d933be8ca8b3b31706a034e54d3a2d884eba3181add9a37dafe6fed5b9b1d30869ce814656d5f65f6ffb90b6cde8eb45b1c3e81fbef381581b3c0d6204e5e1c666297e7633c7e1a7559466647919c21702d423ec7b55fe442854223ed8c11e6f138ca94a227a6ddbd969a8ece746013f581e2132baa71c80344470d5370a4ed95b4af0344b9ee45ac4e346bf9ef5464abe155925eb3b6890f865403f01a46974c17eba5d2abac79ec5baa6fab5729fdf90510498bf2d21cff46fae99dcee62f6820e22fc2b725cece209e3b32920a2b3b7a3a821b52f132e1b4c9539ac2b0cf6dd9e7184f0aa0af804b57a9ee46448b0b7997ce78d346750c288f06a6a7b2a25492405e6395e037f811d724489ed8037cb18b99c2939656eecf759b06033f245586470ee62adeb58aff6dbba819b26b45bfb837f2c2d418892e8e28676d890a4c01afbc78ec47bf56d7d8013aea05ec056b368b5f540fbff2e6a9210a1b234acbe66934d50b3bf187e9f37e6693a6d701cf038d5c53f45f0675e5112b34f69d7ab34f8c69f569c38acef4e666a5ff9c8b9f4bf5a842e1b9e0889101be22840439ef3493068b0f762b56f8b1832e22586ba7101c146ce3ae884b61063a39a4e589f655a33b1c1eb5cf304b0492798ac7f4780dc94969798d6812f2d43cfc7b15efaa706cd8995368b1d73b86551cca835aa6808f56fe42e34df175d653a619a3825ac15f1bab5e4139f73cd555a7319c8221dd9488b0607e9c73320968872c1ef68117f083fc54d4dc4ad87f4d3e1016db6d70dfdc427df4ec80a04ef264e5273615ba83932cbf0805f6bb31fb0d8293912fac764432946f4841c120c32c970672c4d5d47cc2de7cfc0d0ec38304c7dccb04c84ee52781e343ff05a3453646b9d169620c6510b24d1b0084fef985235444eca06459fb3973f4b2c99da58d550adec731398371bc5262c8097a14f976b56720a51ade182549f082d56d04c8a3ed6c93719b3e817a458f97fca4f355e6ff07ab38b3ea1f973bce0c94f46d2630309697aacad93b2b252a5cdf4fb4395d457afb5ff490909a915f82943c30a13a108a84767408a4da91c800cf598f5c56efc62ede9b49db68b8dbba67970a5faba8017ba51ba86d5a006bc3fa9e5a47bf74d9346fa3e6d892816b1f9d2e4bb2336e56403b15dbbfaba890aec7752b7e458f9572bc0aaac604ec19e6205fd07bc212f83c3842a309fb567663cec5b523e045c00df3038c68e212c2e6dcace900fa2c157c0106f3ac85ba1570cfb92628b4eb2c1c84ef146072e8bf5c41b37e1c60b809e17272976cbf0e92acd631257c694130a41abd1bc2f55011b2205ba8881d975ef28a9ec080457381753b74ec9fe1175fa5c806e39762836246fbad8ced9bc0d5b8e01ad844fcbbca79829041b7d09f8f35866204e6d399d905765fb5418c8d3c63fcdf9f289128f2bc09f920d30c299b5bc47a3d8dea63d74c8071ee8bf96fb535e16e2607d554d6dc6e07cfc9b8df397c0075e7489c9015f8a677ae491220ff0dcd4c700395d0e6e1402e6f093437d58c80e6016fac57f839d1aca29a0f00b9c8d20ef546fe739f12e3023c751daa2e697254b003f31b50e40ffc7e58f42b5d21e2dfafa0a597581a6d7b63a3c739281757d3eca5293cc3c1b79d62c780225cb43be7a2aedfb3c5c4b542a35850c30750d1f7c3d540540df1d59463c64bbfe3c3ca113638f1fb2ac990d9d5feac436883167965d400ab27841b6d7d46be336d9791cc8d11d014191ab9f34938bfc216e1550db88bac59d74329fb28d8341316d27f4d57f866a8b2e0328d4c2aaae9b5c959834c6aea11ab16213281264fc5a174ad929188b1935e022ad6127be1b63de723aaa71c839fc3aa8055cb56bf2d37730e2e93491504de6544eab819f27b62d336c6c66f02308c311afffb8cc0fa4f6008336a1b8481ba9bc381d511cbff3e8d2172fed876045ce5067dd1ec159fb74f3d6f40e7199e4944cb8cd1e3317ab53fbee1f7881ba9a8f1a5f4f878ef38cd3d99f6a1efd556d56eb272aa07e32d1486e3acbfa72a1e83a839fdeafb1f7a5ba244b3f352dd8bf626751fa78f6a55abd4843d034132de619fb5196567da0d9516c98bb3c25f58d72bf63c9456bc49f1a47eb471a17abac729e1a8c8741857e4e206ea91ff3ce7b57aaf4abea35dc23e0bac069401c206b74f8f927b5cee864661fc2033e21a42f40c70c31622923a7946ba1edf74550661f2b52a85c573cdc336aa9b956e9af10fd1d94b15773c08ee50c9ee6cc3d6d3af4c928542308e0a211b06c21866cc922b787df5cabb5469820d731040ee2a674cf3a41a94dd6c13db9080f7165b71645a6ad30e1a6566b0d52896b5aa57461b746dbf6651a9a27dc7ff850d25137821769a16520e248cdcdc914c7d9e655b66821cb2769b76a434b6c8d26bf624fcf346d2b66d95d9ed45e6e5e93d1df6641a08f304d6cd2a6a1e842325f68dd903f480fc0808614fc2f89bb1bbf4d37fbef451980217f35282f201e41196af2d5829ba07fdc67d6f62edb11bef2d970001d43a69d62cb2bdf76c5e9e5fb14857d9297e908f5fb901a4b4d8b43fbfcbfd48ec93dc8b9b6feab6c1af6661d7a06e4cb3e7e4ef6ddc77561119cb08bfbb164c795438a95ad526b5d115f62c500ebd906938aa5caa7f5cb604d7a6c6e0ef360d57583cf55bba5fe162059f58f8623bc3f25ef4f9d63a4dccbb6e33485f89bdfaeb0ee58f5c0fe53c39ea04547631c85d4ad4fd76bbaae5f81c926f5d1a2d70354eb59b8e5401da6372e614d8ae43b8e91adcbaa8819e68a56894f23673902a8dd5317c22ee42ed2e8d53d0447798beb319a4a3a37a9b7a32c80c068244c24fb5ccbc9838b77158895d663fd71e3777a56112a59817d88462807bae4f3407b227189f6ccf5aa33f8b8718f8a7b400ddad605aaf9b2cd8ea7ed6d4da4e411bc73e6eb4a9563f32b2b68f788faace8eb18f1cd8cb7436b5b1dc764f275f5e8fe2bae94b4a7ef7d2b6d526b1b86eeeedca6de44441f69da5ba6918b75517d0ae6989a34ccaef091da7ea08436848662e7cf53eff68ca5110248046bbda2c0c058deb6d53f7b5682adb72b9f36ab7e02b7650641acbf4f571c0f4eae57d0ab3b38fac344f9491dfc3632b7eb5926dc183ed88efd1eb2808e07ef33dc9839dc0313dd494048b02a08fcf227abaeb42c136e5d487c1a5e5b62b00ecff9e5311df58b2798352fa900f2b50b4fdbedeba8041f321e460617b8db4205631fe147c36c51a39f26268b7d36f89ef7de30ad718b85970ece7cff36a611c7a04becbfa2f1e7b73060519e5d68c63ee51a4b096921962fe28de115cfc60cffee4d2d5db32cef1c44a8d6b929595c737b5022db55cec9b49c1c07063b544f59ecf5f4e2360776025f63238faccff61921593dd6b1c04fa92c51451a76a80c17f8d39d81cfea5a14474c88c315870963575c2bc52b274abd0021365e635125d80997c4a7e92ae41cf9c24cff337473118c4aea7d8e9a00d420e620c47e2a79e9694bf709c7b3771bc71d0c1f1e788c8c3cfe07ed0a75c423734f7a4c2716366d0d705c115c54bcaa94727876e25fac31eb0b4d24b4a2b610d54a594f3ca0737b7891c1bbc7e131932faf316133c3bc0c8c024942f118a5e45a834a62c3c3666461aad4fa6367297176af16d97de5843f98a87e41d09934b7f4365c4413b861396c7e94dfcf2e33b232a2b96a10628da7cc3550d332eff6314b8424d6b8df2c83d7396d961f07ea4c401998b471be18dd99966abddb4bf5a37c125192d25778ef66329f1f086c83bf5dd7ddf1e4dba7b7aa6130da4eca58b0f9a07eebcb4019055caded4e32616b13c6b041d459bfb4c7670abf8100c5e30dd001db88f017f7fde72ed9da6928fcc4a73c8ba622b8650555f532f83d8c0266a79e42df4d9ab092ff2034e1abb081b25e9616c3b7f0f9913ed780eb1140f3196420de3721afa4c5d9d3aa43eed6e8e03403119dc23f479a0dfaf42c71021f995ac6d4f65dd3e3f97f8961a6524ccd9f6dd0a14bfc7ce0ba0b8e840e4ba1c37857e791183483dfa73d9297dce10ef9524a15235513d8a77bbbb9b33d69cdc89b3679cbc6c6e2d75613741c91f2ebae035135681300c64e19355dbbff4f29196fbe0a6a993db8744c8271b487f1fc6bb85379da5ac8ca59e2164773085546c535298c9f6391e0062f92caef958dd6824a688085793169fd8422d5abbc0d65acddde651f0f3ac2c69daf9729daaba52a87f46252a7aa7a70cc3cb16f855ee31e686310fc584963e849bb348a0ae5853525b6bbfc23d34296cde2306ceb099174a5a42bb2a8b614a774af6740c50afb984ba2b6f33dd66156b219a4429c79e3ce04cf3ea6c5849c4e7aab55d8b0be37567f7c74c0e2bd4820de4e4b0c16d2433186a246c651a9bed77b82f4fa606a2f20f2a8ff95f835a389a8b3565065eb0a67307921dc2f9952386f5f0b52c22365e1b9e2b06eb59e2b35cd988eea585e6813d09de62f8a4bf3609558043e7dd80558fba0af188dedd6e92bb6f9cc3590d541d344c7dee221bf3194e7f3ac21e94a8ce44c98c7ac24e3fe8ae0d6d8c7d209bbda73f1ac7e93752362ddfa1a1dd3fce76d69e29bef53004c24561eb5db28d38d38e922099c91e2cb12a24237c22ac0e60611c64ac58f561fedee4b965d7a99414971a0e2d3d6bef20e3cd67d637ca2bc82e8d4e47e2c48032a215dc26a664d5b7b800b6da6d6322560f7fa3ab57058ceaadcfc50e91d1581d6a8aef99325967c3f32e58782dac661c6d1c4402d6540065126ce4eaf1f0cb1687348087ae082d615c3ad21ea77b0275cbd95eea7f211bc56bb1ccf58ae837f7b31335302d28f93031c91fe5ffbc8a5833ea61191fb0bd8a5fca16232d79cd4357363937db9acfe729f7f3187ec59b5ffe6b518005a034da590c4b5906f4631348e446f98c689796cea0d51943f3ce527f4960cbbe5280bd11dd4d461c68e9d73a5bb69fae32edd855f8badb7775e01cd25328ec11a3d272357a5984413fd9a2328c67318655815e21ef44dd05d9cbb0cacaf1e44f2917ab7bb1d058320cc85e786f27257b0dce3ffe26d38267ec8a2a3d3742bfa0bc66925e6e6e18ea232ca426334dcfbd832b71ab3ec51eef47fe88929ff0baa4f519cd3e6a08be80d5c74e5fd31b532749701bec45a129989b8b6bc8ee4d36deb795dd5df06a1d5eea53405910c263fb17ade6d2b244a3c03c5895ab085f705c2d3439e9585206db3960b0252d12eea0892fcd7bc706026d108c765eec62bfac281bfd717ee2994511b1b86f83ba99bc1712b1ff3f8594e2f47ce78f4ec2373806c0d9ca9ed217a708a6674316618e77d4d936484df25963df0247aeb8bb466850e57bc21ba1526e086e41279299f163ef4ec8a8331241fec26eb001641fd5ee8bdeb0082ec013b019cf90782676d9b8e5b5ec63f922f1fb489a57d593e4f93afc0fa725aaa06feec21b3d81e38f57a30fe473a90efd043957dafa3f121a9f98181036adb5d4fc56635680d8b271ff01a948f6bf9d924e11dc6ab8e341ee750a68d9c6e46f92edbcb4b665770980772d12da6c409286245142fa55c974f3b6cc35776504558e8307b1a0b5b9b68052a19a7e7aa9a4d7032f1173937453554b8a192115ef77b8bd7951c10889726c5bae9cb355692dd30376267a36c1c50bf854379529e68e530dec3739a185dc56c28f0e8f77b016588c4bd4106f2c445f340c9ec4d8f618f7dc1741be067c5689c79bc0b2f8a73eec36ab8fe4d69a9f12c179cb657783f379b4fc2be9b036171ec7fe03a539eb18c3455b2a118db7c02e44b12093a45f227539094d2c9e6ea94234dbc6e8c393b2454f32365534d5bf4d300308f22b8c04ca1f9d9a7c3a96a5c38b054990d7aa92fbd3cf9b894c2002dea5297fe0dfd03528ff32517d70693c4858a3a50b15663ff1690642b6d612a2645772b9e1a28f1f2a254211e86bc05f0856aeda8aa1a304dc499a2ffdb70ac23631ec748f11f0ee1dca2f4a865e9936d42a6ddd7a9346b7b622dbc009f226dd85f42973e7db0ee5ef58d88b9cdb7668431d48824a97e58e0c32c6f2c459b33ff437e05186d3554ffd1f2fe202623b86606be20603b805e57f288fcfa054eca0b9bc1f94070682a34acc5f4e714797bf4a3fc4bd2cc1b4db3fb2bd4f3d9ed4d52d9ddc72b69e213bbfda25915fb945a0e919e68bd0ef372891ce2f44d07b8562e6db1ea216c2f66dc5a34a7ffbe0fea4b3c432fec29ae5d3ada4cb48b8c3bf29e4f61fb41a030bdbd16e48ff48d01c1c418ffefba8d2939f2347c9ee0884672ce8b6b06062ae5b6353c3a082d793a4dd993fb1e5948702b637e0c0564cffdba7cb9b5798ac3220834412cd349330364602348ea399cc7f636a5aae57570459915346ed28601bfa3cfdd64f48ee66ae70af1a0aa3379447d598fe14e3edfd3db7cd36693ed93add7dc16059e312f273ef5f5b39b9a51948a03dedbdc055d772239d28de8f82db2df7b606a2af895b214666a47fa522a953521660bfa82783ba3879ec47ffa77a45c5ee3a244501478652a99279a15de8b60976cd222c5d12c8da95ad3a4cb72f44442fea4978bf92b6866b030e2fc2820a970ebf751d9b94f17c0a7bcc761d2962e481d87742139d4902ceb6e5f96fa91fe6fe58d1a7a2b15b766c3bdfa4e5b381e291148bd7daed8fb74ab5d088794fe231b65b1ca2066f272615bcf50e6b1621b32b6b1ca016e6fd3428799ed92591e35fb60ac7523830a4f13ff88865549560a87a9f56622d9695533b412e63bae0394e031b41e2cbdc600056ae22c59ec3e82e84b80633d73dcddabb92787c71f11d157deddcebe71818ba6afd3de15430903cbad44cb00fadcd7c9eb08a201ef938c8ffb6a211bea47cd93a73694247b3cb1f2dfe90e9d25a9c3023a6ef3eaba820c94cf2c518fd3176af9649f424b810706d4117d81b63229deae1d8d2a0793c1a0dbb26ed4fc36a46dfce85947437aa9873cc3397d9b87b882784ad5f6c7af1c5e70783565b739932b75773bfcdd9007893b85619f5d1b4dd98b7646b3af994c9dab794e02e941c2fd8162a72ba2a77b88f0f6081e0df4846a38b49c80f5d479c8103e9ac987c9321696e83ee8e1621383502fcddd7886998d58c1136a675700b521930608e4c8878eafd1126f8f5fd99398ced83f84b76eb2d9c84f748d316707642c428b8dde842bfa5668f55b1873ebc42e97c45fd21bc753a890b67b4b064c99cb6d52685321a30b427b34772053ea53d76336bcb0541e36c98fe45e9635bcea279a68ee5ee2243b30b506fb367ab2e634b1eb52d91fe1e68a9fe083a0af00b58cf9de567f39c10f80cbdde4948f945ccfcfd5f086c7f62914450d35286da0b5393d65c07c551f873e163ee3d0839840e748c6a6214ac4b0bfd8804b783132d0860be5b88a366430e4b3af76a39beffd8880bcfe081f5d8a1476a87a2567969e67b195f065b56890681fabb5ba902aebb141fe6e7e254e95ffd3d5801dc4d049b899d66e35fa46e08cd19af79e06ef8a00e211a39c7119c2a9b8a3cc06b8fc97a0b933ed8c719f5d7d21c8e8f4edee86bd00d15198494c22d9279163d8e0aff8e261f88cc5619c1ea1650736882056b15ffe757ef223686aac96c57ad09733f36b4234eb7656c684ee0e124a828ba413e3e4b2ead074268a5ce14bbb711700661aa79e9c4d8fe419e03c246a578dd42ff880f7f8bdfe1922dcf9fab29456cb76d31920e697384fcf7e986fef1f2554e046a120cedb0da59136a5d316bebd3401a91041d6d3503209e43746b9a3e5ea2dd2e64a15183f5921710659dc615545bead57d72e6336d393dd9a94c8ebfee9de0e1cb30cbb116e770f0464a2972e7eb2a8fda7dbf9acdb4bf7cb6890bf4dd2afaade4cb50e7d51fe4d95235cabb34eb63ebaff19f242834943945a4c5571edc442808bf49c03e5a8020951b68a430a9eef7f0d660f0921c388ef5f8b0f9874e7ac43d70333de0445f785f7acf090ace53d99301a1a6b773dda709a8a6093ffe2ce787e1806c8803246d6872340793cca8181b8ff93ce29904aade06bbe3efb95e929df548ed92b0258c7eeb1f851062538351cf6b160e57a69120c0c85755427d4c616af08878cfd3f96bd52fd400d7cb30aa0a798c0c04112ae5ea6ad9ad9e4bc815dbd01ca3925af21fc2a058e024a91f4f46a4063163c6409eeb5e4f6c2a1e859ef427a0a29e8ac06a0d6dbddd7785034cef0cb492b1f91b296db493e884f887ea31caadf6159f318edf82d63d12923cb7972830c512833a67454da2e9d372d41f5121684ae8b0de6202f109ab1460e81a5530759323300964b794c86c160d49f1297410981b4d963986aa4478ae23743422463694282a44bc0ed67ecd53b3a6a83fe89478695626f44c2720f8001d9619f72b6bec314d0c1b78d8e9e7db00f937eea2253ca6216e71bef9cc3cdf502f6f99378e024b99fb141dd460f39d669ffc0c51b02ef7a28215b9e42fd1c2fb088c7caf850931e4ea871737ed3d4aa3858243674e593b504f85a200c7207778fb0360ed5b44862bdca1fb6c0ec9cb66114f043603198261df02104bdc7b112f8d290297f6173f47c2197f91c28d21dc830d9dc0f20cb878d633b99bc538bc97da8ed9ec80896f52139f7b805fbd1835141e37cabf79241232e60f5a2b8d0964b1f576326cce3cfa5ca53e53108b77e629b65e5359b3ad5a41342eaa58db0a019cb43440d0c9e9a9292d4f7464ead646449b439b4bb2a85454889879993a5145b47202a1782838930e9497ff7829d0c696770c2d4e8701c5afddf3f696d3a9518e9c9b2dc5da018f0aad7639f365c815e003bdad234a172568b2b6b3959bb70c9793fb285cf44fd6f3a41622d64bd0695b01c628985408901d85006fd53448c86a565268ab09cbfa74c7048c66e2ee1a45184db03d533064887542f553a242d81e3ad1728d05f5d5573e91f758e5a33ff78a34c81ffa3b0fe122f6fd5c64b1c8846b66c171398c042e3622c3d440091552962b95f4dd464a87812ed9e67b6168d7ccf78501a21aed1ee44396ce8b76229bc0933b39d01e8a08d9a46aecaaa934cd168481ec93ad16f2aa4e381fd750b0242d94d459c6b6b352299de736f604040b0f952a272b5bb299f1ae9e29f0b38d451c0da39053472ac03c116ea1991b5f287fbb1966f844ccbed288ed5b5643ecb92f8a01c734245d148378cfaae4d858301cf77f5c137b4ec50fada61f827d926eceeacdae1dc99d01473b322c8b2fb4468f11066634e92865001d46230cc9393554d0cd45b2aa6efdc3ed35cdd84c24ca75dc91f3e7fb1446ec6b0014ac26da82c26bea7592b9cf83ac4a8e51a5a9281b888ab1d4066e81bfd0376e5ce45549fadec88ca405e82ac13343afd598cfcbf2b68edece4602f9c3070bdd0fc711bbf8897462f2634e2d74e03c7add73fd31aef47b5ecf50d0c349c046ca3a91019204c9476addb871ab1ab6db4f60c04a31e2765525961889032e5f5ada2cbcaba0ea9025a04fa5e41f0e5c1d27d49be67e4f80a6be0f68402dff4960921ee4c791e689e04ddeea57d896e76fb206440cfdc83cf240d50dabf22906e4c1e276a8ba0fed2241d2b7fff51a8c802873587ef387cff3e75ebd8009d2910b210d07316355fb340dd5e0994bd40f4c71691f25b71865102dd7cfcf3f154cd0a9d25b3fdd802a5d81ae448d284f0979bcaf72f3f5904a19081f369e422510f23c3f22ede785775b89002daca5c8f00e31c0f9c4b2716481d5830269172e3596907aac87f4e81ef66cfd976e523905534c13fd9f006342d4ee39584f7c196f8c6ddf7fc71a7236fd039b8e29c6fa1de4ae6288ae82175d939269e07393cc09f1f502a3099246582d4a2184d9109651bac475b4316e48509c97bfd72f97d66e119f7cf6d475bfa42d0df7d0fc5b3b1bf9621066c1f32a45061363c3175074fed91377556c63e104186f7609b5a06e4c3902a8404e295929d4d786c15a48be408d8745d9674873c5a7ad4756e41e64188016191e8da12aa4b30238b29e4d784e8950e808931f12424fe4dad6d8cb0bb55e8c0440e0148f04d50b06acd17af3a47854129cb464dcb3cc60c6ce8bc8649ed48e36c4a10162cd2b3073ba21aca3844ebaf0304ee16274905f6d85a42276e84113ec09262a267d9ffc08f38b93e244f860d08e6aeca55193b23b0e748b9b358aadb3ed7d1e2affac941f7be7fa35005ccfeb02929320d8db088245cf16f5bf811a90b37f8e30c7ee4c8ff0aa018a109e544f5c51e844152315da766f4c19033796bc9e21e981264f9f118947f1775acf373800c77a063be4473994d511c1e4919047f00e988c8912a8647fdde6c3a8a6328698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b550406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc37542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207bb7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf8125c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc56738c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bdc2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d2279c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb274e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c8788966750673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d98aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa934e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d590999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369bb2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba38448bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8bbd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1ebda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f8411421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e31372d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208ecf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40bca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a83b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e387113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c138f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e06ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f3351bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6dfa0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591ccc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d414a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e61746892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c55f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8feebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a787e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a940dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea23d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fdefb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430ccd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe050616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8ad1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d45097969f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc247a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d39347e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6ed91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a651788a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a0049c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c52235f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579eeb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a09ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e359128fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd912619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa339833289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d132c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a27abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec82d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9cdd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb711ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c626e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fac6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a944dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f186bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0eecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78cd4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730cf1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb13445b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a376bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2eafd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204dcc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b49472ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91eaa357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e20a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaabfa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd116578108d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b27eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db628a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e355897f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf9145d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff3468d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf8124324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201fd9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f2281af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d432820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417dcb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b612a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcbf0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b080199167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebebac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ada17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace29326792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be6822567ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50afda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d8686bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d969147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a8669d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e704f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce174b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734ed4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c31d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b0683921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d35405cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f304f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc2519ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e7259a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91fed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850ef76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c885dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f22b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c363f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c169776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006fd4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660beaf946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87dbcc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc37162b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db201886e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea85a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137aef78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3ccfff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bdf14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedff10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce87b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef97676486866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a246a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d50606260186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b821ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ecf33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f631391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9dd48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f70a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6ebcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed328437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a2162bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b7635375888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d45e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a3817898194f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f6341fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff6371433bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a45e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561bead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ffb552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea21da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ceed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261edef43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676be20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2dfa21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec2244b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fefdf7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b45134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b2190ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b92c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f6016204080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da096795092edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3fc9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7dc2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd8659b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669cac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101aaa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aad7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf5016975c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79fb125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e291ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c2447d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b381606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0ee7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b855152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f5011f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49debef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc3719d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b95b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c4367f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c25359724bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfbd72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e7597704084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138fbe029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce653e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c10ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4edc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac46217944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf05fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec811b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c07924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de2411e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a208590eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de01999fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c726ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a7333453f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb011eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9eb4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf66d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce560260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e982667311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa67683191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc713109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0aef43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f345532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2bed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db61e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562be1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfcf54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bfdc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c476674d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c55dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e48b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b31514ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a462632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b535bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345def5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06df734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bbecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865fd73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544dde24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f30e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a232473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459dafe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c7284f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6dae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb833f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6dac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e78ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d22031248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabebfb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59eaae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf43bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810bce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e00769878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd8378d8ac445c";
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig1), sig1));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig2), sig2));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig3), sig3));
-	}
-	
-	public void testVerifySignatureSHA256() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] msg1 = new byte[1024];
-		
-		for (int i = 0; i < 3; i++) {
-			byte[] publicKey = xmssMT.exportPublicKey();
-			xmssMT.sign(msg1);
-			byte[] signature = xmssMT.sign(msg1);
-			try {
-				assertEquals(true, xmssMT.verifySignature(msg1, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-			byte[] msg2 = new byte[1024];
-			msg2[0] = 0x01;
-			try {
-				assertEquals(false, xmssMT.verifySignature(msg2, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-		}
-	}
-	
-	public void testVerifySignatureSHA512() {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmssMT = new XMSSMT(params);
-		xmssMT.generateKeys();
-		byte[] msg1 = new byte[1024];
-		
-		for (int i = 0; i < 3; i++) {
-			byte[] publicKey = xmssMT.exportPublicKey();
-			xmssMT.sign(msg1);
-			byte[] signature = xmssMT.sign(msg1);
-			try {
-				assertEquals(true, xmssMT.verifySignature(msg1, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-			byte[] msg2 = new byte[1024];
-			msg2[0] = 0x01;
-			try {
-				assertEquals(false, xmssMT.verifySignature(msg2, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-		}
-	}
-	
-	public void testImportKeysSHA256() throws IOException, ClassNotFoundException {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMT xmssMT1 = new XMSSMT(params);
-		xmssMT1.generateKeys();
-		byte[] msg1 = new byte[1024];
-		byte[] msg2 = new byte[2048];
-		byte[] msg3 = new byte[3096];
-		Arrays.fill(msg1, (byte) 0xaa);
-		Arrays.fill(msg2, (byte) 0xbb);
-		Arrays.fill(msg3, (byte) 0xcc);
-		byte[] signature1 = xmssMT1.sign(msg1);
-		byte[] signature2 = xmssMT1.sign(msg2);
-		byte[] exportedPrivateKey = xmssMT1.exportPrivateKey();
-		byte[] exportedPublicKey = xmssMT1.exportPublicKey();
-		byte[] publicKey = xmssMT1.exportPublicKey();
-		byte[] signature3 = xmssMT1.sign(msg3);
-		
-		XMSSMT xmssMT2 = new XMSSMT(params);
-		try {
-			xmssMT2.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] signature4 = xmssMT2.sign(msg3);
-		assertEquals(true, XMSSUtil.compareByteArray(signature3, signature4));
-		xmssMT2.generateKeys();
-		try {
-			assertEquals(true, xmssMT2.verifySignature(msg1, signature1, publicKey));
-			assertEquals(true, xmssMT2.verifySignature(msg2, signature2, publicKey));
-			assertEquals(true, xmssMT2.verifySignature(msg3, signature3, publicKey));
-			assertEquals(false, xmssMT2.verifySignature(msg1, signature3, publicKey));
-			assertEquals(false, xmssMT2.verifySignature(msg2, signature3, publicKey));
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-	}
-	
-	public void testImportKeysSHA512() throws IOException, ClassNotFoundException {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmssMT1 = new XMSSMT(params);
-		xmssMT1.generateKeys();
-		byte[] msg1 = new byte[1024];
-		byte[] msg2 = new byte[2048];
-		byte[] msg3 = new byte[3096];
-		Arrays.fill(msg1, (byte) 0xaa);
-		Arrays.fill(msg2, (byte) 0xbb);
-		Arrays.fill(msg3, (byte) 0xcc);
-		byte[] signature1 = xmssMT1.sign(msg1);
-		byte[] signature2 = xmssMT1.sign(msg2);
-		byte[] exportedPrivateKey = xmssMT1.exportPrivateKey();
-		byte[] exportedPublicKey = xmssMT1.exportPublicKey();
-		byte[] publicKey = xmssMT1.exportPublicKey();
-		byte[] signature3 = xmssMT1.sign(msg3);
-		
-		XMSSMT xmssMT2 = new XMSSMT(params);
-		try {
-			xmssMT2.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] signature4 = xmssMT2.sign(msg3);
-		assertEquals(true, XMSSUtil.compareByteArray(signature3, signature4));
-		xmssMT2.generateKeys();
-		try {
-			assertEquals(true, xmssMT2.verifySignature(msg1, signature1, publicKey));
-			assertEquals(true, xmssMT2.verifySignature(msg2, signature2, publicKey));
-			assertEquals(true, xmssMT2.verifySignature(msg3, signature3, publicKey));
-			assertEquals(false, xmssMT2.verifySignature(msg1, signature3, publicKey));
-			assertEquals(false, xmssMT2.verifySignature(msg2, signature3, publicKey));
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-	}
-	
-	public void testRandom() throws ClassNotFoundException, IOException {
-		XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmss1 = new XMSSMT(params);
-		xmss1.generateKeys();
-		byte[] publicKey = xmss1.exportPublicKey();
-		byte[] message = new byte[1024];
-		
-		for (int i = 0; i < 5; i++) {
-			xmss1.sign(message);
-		}
-		byte[] signature = xmss1.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(publicKey, xmss1.exportPublicKey()));
-		try {
-			xmss1.verifySignature(message, signature, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		assertTrue(XMSSUtil.compareByteArray(publicKey, xmss1.exportPublicKey()));
-		xmss1.sign(message);
-		byte[] privateKey7 = xmss1.exportPrivateKey();
-		try {
-			xmss1.verifySignature(message, signature, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		assertTrue(XMSSUtil.compareByteArray(privateKey7, xmss1.exportPrivateKey()));
-		byte[] signature7 = xmss1.sign(message);
+    public void testGenKeyPairSHA256()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] privateKey = xmssMT.exportPrivateKey();
+        byte[] publicKey = xmssMT.exportPublicKey();
+        String expectedPrivateKey = "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "0000000000000000000000000000000000000000000000000000001f5bb70f454d7c7bda"
+            + "84d207c5a0d47211af7b489e839d2294cc8c9d5522a8ae";
+        String expectedPublicKey = "1f5bb70f454d7c7bda84d207c5a0d47211af7b489e839d2294cc8c9d5522a8ae00000000"
+            + "00000000000000000000000000000000000000000000000000000000";
+        byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPrivateKey), strippedPrivateKey));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPublicKey), publicKey));
+    }
 
-		try {
-			xmss1.importState(privateKey7, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		byte[] signature7AfterImport = xmss1.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(signature7AfterImport, signature7));
-		
-		XMSSMTParameters params2 = new XMSSMTParameters(20, 10, new SHA512Digest(), new NullPRNG());
-		XMSSMT xmss2 = new XMSSMT(params2);
-		try {
-			boolean valid = xmss2.verifySignature(message, signature7, publicKey);
-			assertTrue(valid);
-			valid = xmss2.verifySignature(message, signature, publicKey);
-			assertTrue(valid);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		
-		XMSSMT xmss3 = new XMSSMT(params);
-		try {
-			xmss3.importState(privateKey7, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		byte[] signatureAgain = xmss3.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(signatureAgain, signature7));
-	}
-	
-	public void testPublicSeed() throws IOException, ClassNotFoundException {
-		byte[] message = new byte[1024];
-		XMSSMTParameters params1 = new XMSSMTParameters(20, 10, new SHA256Digest(), new SecureRandom());
-		XMSSMT mt1 = new XMSSMT(params1);
-		mt1.generateKeys();
-		byte[] publicKey1 = mt1.exportPublicKey();
-		byte[] signature1 = mt1.sign(message);
-		
-		XMSSMTParameters params2 = new XMSSMTParameters(20, 10, new SHA256Digest(), new NullPRNG());
-		XMSSMT mt2 = new XMSSMT(params2);
-		mt2.generateKeys();
-		byte[] publicKey2 = mt2.exportPublicKey();
-		byte[] privateKey2 = mt2.exportPrivateKey();
-		byte[] signature2 = mt2.sign(message);
-		
-		try {
-			mt2.importState(privateKey2, publicKey2);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		try {
-			boolean isValid = mt2.verifySignature(message, signature1, publicKey1);
-			assertTrue(isValid);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		byte[] signature3 = mt2.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(signature2, signature3));
-	}
-	
-	public void testBDSImport() throws IOException, ClassNotFoundException {
-		XMSSMTParameters params = new XMSSMTParameters(10, 5, new SHA256Digest(), new SecureRandom());
-		XMSSMT xmss = new XMSSMT(params);
-		xmss.generateKeys();
-		xmss.sign(new byte[1024]);
-		byte[] exportedPrivateKey = xmss.exportPrivateKey();
-		byte[] exportedPublicKey = xmss.exportPublicKey();
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		byte[] sig1 = xmss.sign(new byte[1024]);
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		byte[] sig2 = xmss.sign(new byte[1024]);
-		assertEquals(true, XMSSUtil.compareByteArray(sig1, sig2));
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (Exception ex) { }
-		xmss.sign(new byte[1024]);
-		byte[] sig3 = xmss.sign(new byte[1024]);
-		assertEquals(false, XMSSUtil.compareByteArray(sig1, sig3));
-		try {
-			xmss.importState(null, exportedPublicKey);
-			fail();
-		} catch (Exception ex) { }
-		try {
-			xmss.importState(exportedPrivateKey, null);
-			fail();
-		} catch (Exception ex) { }
-	}
+    public void testGenKeyPairSHA512()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] privateKey = xmssMT.exportPrivateKey();
+        byte[] publicKey = xmssMT.exportPublicKey();
+        String expectedPrivateKey = "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "000000000000000000000000000000e5a47fa691568971bdef45d4c9a7db69fe8a691df7"
+            + "f70a9341e33dba98a215fe9651933da16a3524124dc4c3f1baf35d6f03369ff3800346bb"
+            + "d8c2e179ae4abd";
+        String expectedPublicKey = "e5a47fa691568971bdef45d4c9a7db69fe8a691df7f70a9341e33dba98a215fe9651933d"
+            + "a16a3524124dc4c3f1baf35d6f03369ff3800346bbd8c2e179ae4abd0000000000000000"
+            + "000000000000000000000000000000000000000000000000000000000000000000000000"
+            + "0000000000000000000000000000000000000000";
+        byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPrivateKey), strippedPrivateKey));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPublicKey), publicKey));
+    }
+
+    public void testSignSHA256()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmssMT.sign(message);
+        byte[] sig2 = xmssMT.sign(message);
+        byte[] sig3 = xmssMT.sign(message);
+        String expectedSig1 = "0000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf8072"
+            + "61bcd177ddc133bb0e43bfef03f25536a685cd509a5c8f2ee46c23e2d46149d0de58feaf"
+            + "910909d213823f98c5b8eda2522139675fd7a82deed6b795aa1d8eeb0fb5295e6d15c410"
+            + "c6b9dcacbac9879a55cf139c09dbc57456adee05a8075870b5ba92aecfc2f289d47e1def"
+            + "fbf10cd01a278bad1bd063813b63f79db79c38c357707a0c31c3a9d0d58ddeb2317440ae"
+            + "05edc55f2643b53c6b18160747fb27b5f24f2b0b2f8840b489d51a6adcd3d74d90a6e9e3"
+            + "c96630ff5afa8de978e03f117b1082a0d877aa0ef75b90b425b0e22acb8633b6404be938"
+            + "969ae16d926b8aa4e16eb13f8d8b9ae8f7eb16e0ca7fb69b428011d3bd9390836d46f971"
+            + "b83f093dd14b9df1165b8e256bd9d80354ce4c60c077f21bcad49ec4fc0ce33212c45460"
+            + "899ad83421a98fa5b6c153d1d3aeced17702e266353acd195d408da05eb38c550feacd40"
+            + "4a38f6657360c9f7d373d25ecdd885daa76aa3798ad57d756f0aafcb6bd46d58c78322ef"
+            + "e0c94ca8be6a35ccbb75fa14b913965ee02792b021c99dd60a81333feaab0757ee5ab0bc"
+            + "c7a5541e7871b1efbbbdf97e1f455c44327e846c856c3c2cadadd77b3fd4043a0ccb02f2"
+            + "527abc4be9a878ae61d78891f3b26b838c162ed24f90605f1ff34a25d8518b5ad57c897c"
+            + "a7ff01246712f04d1c85fcc89531d423ab4d1359b2e42037211516e096a90ea956a5ce83"
+            + "b423403945dfc613239d2defa006c04a9084f27487f81ef85b7cd75e4a0df87c57343caa"
+            + "9805ad886aba0877dd6b123775b51104c45097d15f2cfe2c8622f229d7e83440e3f48e1b"
+            + "507d47a07065f091af9bd30d24a5ea7aafeef7537d05d896192e18422792b7d57fe63489"
+            + "c05c36614378bda9cd9aba1c2f64a45ee0851d8f88dd9e531c39f1e2479b81631919fa75"
+            + "b12d864d49e03902a495965e72c99ea101ca2d02b08b0ccbdf8c38dfccbde27510ac37a4"
+            + "0ff4737aa9304e67477c0215f496f5b5f83716f44106b2d1f609eb1731093757cb4f6d3f"
+            + "5d3062b38f55fb7e2a39ecfba7dc601329dcaf8181caf7412ee160aca1ed3c99932f6a67"
+            + "3a08031a11bb64f641a2728e2fe973cabc7ba40783e422270de34919fabac4126a6dc035"
+            + "531efc2ca61271ee0fb33c95d4d4f7a00abf39f98557d5f7bf080848792996fbe0b6a0d7"
+            + "83b647de6a3fcbfbc76481c79e2d94d660c81eb108875fa8ec3263f868b1da745064667d"
+            + "742b53af85731707aa8790fe40e1b06e1bf9efc9c6ca01ff879ff74729513d346007f9bb"
+            + "71800d128f6da23ecc6babed12bad95b3694f19921f7135a37ebea9d27a8cf1323b0e691"
+            + "739fa44350e4f6afe6d16acdfafadab222878899294946b6854294d15b68e9f7f93d4ed8"
+            + "c11d020e0581b9a464081ec7f5fed13a19cb33f32baa974e2558299b6f3a1546e25eea06"
+            + "a7cd780f58f2483459a4dadf9752dcd529d09e96028c6bb62c1e08607b3e27ac235d6ce3"
+            + "b267a27170f1f438be2ee415957b105979497a702b788f7908209fe6b6335e833e2f725b"
+            + "9b95a3a72babfc451b64f55aaba51bd160f857a64e428943e03e723873753da48b300a19"
+            + "87585af96cafd52ac09e13fc989c08516a252354b0a38120715629805a215564df944580"
+            + "205ebbdb56b5dcaf84875af5e943c2694914908681e0cb42334b6f433689dc94a9201f0c"
+            + "5b33e726d7bb2334011d728cdeaa34241b01e9cb9bcae0381451179a178803814c427a6f"
+            + "16ee1a885069ddc37679b5019993d90e1c11bdb007ad4300fcc577f00cf37115a02f5148"
+            + "d663715732219817c2b82071c1ddf65e696b58c455b41f576efb23be40652fa3750075aa"
+            + "88bfb63978347741d77efdee423563165f92083b2388212b2a97f4c075a4cb10fdfd50f2"
+            + "0914fd9410fec1d180f2bdbd717388dd7b65a3edada491ab6e9dc572caf564e4c3295c98"
+            + "216a005cf0e018db54f85e1229f02c1475b25f3a01064c87f2cb82152c12e806173774cc"
+            + "ff810f1c037dfe7fad1e7f9087d4f5d4c7986aea352e862a06f49842247c296683c0c13a"
+            + "837e5359681425913b4052daebde3963b4682bc8a4d977a50e02c44c3a4a7b90f25ac452"
+            + "733012dda1daa631af5d6336ddf743ec1fb5d54ea6fdc9f463c4577f4031602b8a0ccced"
+            + "a6f634b6d6024f7b07995cde12b118da114a1218d0f3fb86b5ef600fc06a244db3f93dcd"
+            + "4f7761d2d3fa3d9bd461fedb4353000e6b39cdd4b7c8431abdfa7945727ef046208525c5"
+            + "4c128679606af8b290d52d9e224803449e5de2b3837623afc7539230840dbcd2f317dbb3"
+            + "32abd082d7fd7a06f56073f08c49fd3b1706b3586ffbb5aa0964c3edafec744d32e41732"
+            + "ac4cd4cb9de2150f42f7d80dfa0b3a50b2c2b9aefb8d613572dbe808057f45d366aec13a"
+            + "30d385f93c830cd330ca3f13b43d6ebf7d98ba8547b9eb1b6b5247d67571e0c97c215c3f"
+            + "49dcca4a5ca83bc78795149352b04b4ed9ea0d3aac261571c0a70aa86cc3650ee0609230"
+            + "4393a4cbc4abdb367b48024e2edf22885fd0a3154f9d33253426c200c5b58e93eeec4fd5"
+            + "cf80cad508d9005fb29ae4c50c54d6a5f9d8baad72ce566b3ff638fb62e3d166de11aad5"
+            + "53dad08d3c7a2bbc4a1bcddf96b3f319f74377b61b734a67b58ce9780d5ed4d0e8a823bb"
+            + "0b5fb5ce325a1d5c69a6709fc76032180f0fe7f2e7c98ddc0ae9654f609e80acfc27a087"
+            + "e54fb9753a12f12e6ff064cbcc9195190a85b779e79b87bacf85abb62f41d4c95eef2a06"
+            + "2abb0dc014290bf53bbed19a2eed94fdddbf289210669ec240face374bd200ed9769d320"
+            + "fd694dab4ef862ee24d76ac2c024a60a246cbf9ed2c82ced5b3ccd0567a333a2ccd37375"
+            + "39b0608a79c0228d579589dc48d5c1d762d90871c329b3226c8d08d420749b609c7a9918"
+            + "9675c5e67460c3e20da7c2ccf55c8e447fcd46650ed010085025437cf1ab53993be63841"
+            + "6c338245b39e4b754bef2df63a94fa02f99e1b0f0f6ae60d632c4b6e3dcb165a2c22b075"
+            + "909dfb80e26fdb08bd57ee5222ec65ff19b344e0e81477f50e2b8719d1d9a3c1a55d5460"
+            + "711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68"
+            + "c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c085"
+            + "7fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76"
+            + "153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1"
+            + "a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec824"
+            + "0e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa"
+            + "244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026"
+            + "d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7"
+            + "404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a"
+            + "89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26"
+            + "c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba"
+            + "15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d"
+            + "294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c095339"
+            + "34b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2"
+            + "b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf42354"
+            + "24678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076"
+            + "e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff"
+            + "893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9"
+            + "770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793"
+            + "faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb6"
+            + "6a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb"
+            + "13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677"
+            + "a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3"
+            + "eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1"
+            + "b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac8"
+            + "2e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc8"
+            + "67d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c25544"
+            + "39c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb"
+            + "4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf"
+            + "42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb"
+            + "7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac"
+            + "29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30e"
+            + "e3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a"
+            + "2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1"
+            + "d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca"
+            + "6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de7"
+            + "79e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a25"
+            + "0489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9"
+            + "ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a"
+            + "0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e5"
+            + "5c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed6487"
+            + "2ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c"
+            + "660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30"
+            + "c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad23705"
+            + "81d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26f"
+            + "a4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af8"
+            + "46833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463"
+            + "f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894e"
+            + "c3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e8"
+            + "57ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab"
+            + "463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb663"
+            + "10a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b7684817334427"
+            + "3e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f1"
+            + "4dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c747"
+            + "2254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8af"
+            + "f3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b"
+            + "8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef"
+            + "925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c2"
+            + "59fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9"
+            + "d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccf"
+            + "e1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7"
+            + "debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324"
+            + "c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2"
+            + "cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b"
+            + "4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641"
+            + "c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711c"
+            + "febc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8"
+            + "cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595"
+            + "197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e"
+            + "267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672f"
+            + "b34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64"
+            + "f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f"
+            + "187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f89"
+            + "36c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b72"
+            + "4da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1"
+            + "d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be"
+            + "8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662b"
+            + "f90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de06"
+            + "1b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f1"
+            + "27adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7"
+            + "faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7"
+            + "b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86"
+            + "067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3"
+            + "c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a631"
+            + "76d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61"
+            + "c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8"
+            + "fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a573"
+            + "4d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5"
+            + "ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb35093947"
+            + "35f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e"
+            + "05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d6"
+            + "62417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c228"
+            + "82d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d"
+            + "2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea"
+            + "67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4"
+            + "a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2"
+            + "cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d"
+            + "487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372"
+            + "cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89"
+            + "c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db"
+            + "4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248"
+            + "f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b"
+            + "8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2ef"
+            + "f62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612d"
+            + "c283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9"
+            + "c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191b"
+            + "d96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96"
+            + "e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b"
+            + "0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf23"
+            + "5daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933f"
+            + "c5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce0207"
+            + "44615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e40"
+            + "6a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602"
+            + "161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e48"
+            + "5c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96da"
+            + "e4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018e"
+            + "f4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e8391"
+            + "4933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a"
+            + "6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671"
+            + "f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18"
+            + "dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346"
+            + "ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d9"
+            + "09996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708a"
+            + "ba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee"
+            + "596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a"
+            + "1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de0"
+            + "2eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c1843"
+            + "4653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f884047"
+            + "5d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16"
+            + "d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8"
+            + "d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c43"
+            + "8976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4"
+            + "ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9"
+            + "ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c3267"
+            + "7c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63a"
+            + "bef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37d"
+            + "efd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a"
+            + "25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089e"
+            + "fe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af9389"
+            + "24ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054"
+            + "e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba4"
+            + "77a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb4"
+            + "19daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731"
+            + "cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333b"
+            + "f1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3"
+            + "932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da"
+            + "9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e021"
+            + "11cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c83503872"
+            + "35b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc0069"
+            + "2d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e62"
+            + "30fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f223785"
+            + "21e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252"
+            + "528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c50"
+            + "41afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d97"
+            + "2e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571"
+            + "bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4"
+            + "b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67"
+            + "bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93f"
+            + "c7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013"
+            + "d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319"
+            + "125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a1"
+            + "3a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4"
+            + "cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfa"
+            + "ee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470"
+            + "f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6"
+            + "afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6"
+            + "bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84f"
+            + "de3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c91735"
+            + "24c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73"
+            + "293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76"
+            + "470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834c"
+            + "c12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680"
+            + "cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400e"
+            + "c92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33b"
+            + "c71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b443"
+            + "71bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79c"
+            + "c32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba"
+            + "6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e41061"
+            + "4a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e"
+            + "0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b3"
+            + "9d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479"
+            + "f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674f"
+            + "a27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450"
+            + "da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c"
+            + "86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c52799862"
+            + "4de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed"
+            + "55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1"
+            + "528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cc"
+            + "e36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d08074"
+            + "12b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff035"
+            + "5f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d"
+            + "45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da"
+            + "8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c820"
+            + "6ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83"
+            + "ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b5"
+            + "75ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1b"
+            + "ba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638"
+            + "a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2c"
+            + "a34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573a"
+            + "d5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9b"
+            + "a7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960"
+            + "dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738"
+            + "740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945"
+            + "c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b498"
+            + "9994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5"
+            + "c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e"
+            + "3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf4"
+            + "30f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d354"
+            + "3a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4"
+            + "d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eae"
+            + "cad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be"
+            + "6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9"
+            + "bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d"
+            + "1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2"
+            + "b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004"
+            + "b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6"
+            + "f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6"
+            + "c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092"
+            + "ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea53297"
+            + "8bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e"
+            + "5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71e"
+            + "f8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcf"
+            + "a899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc67245"
+            + "5405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3"
+            + "d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d8499273"
+            + "2ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb"
+            + "06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0"
+            + "914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9"
+            + "a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d5"
+            + "5af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17ee"
+            + "bf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4"
+            + "bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb3387"
+            + "7d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3"
+            + "a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91"
+            + "ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399"
+            + "bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014a"
+            + "d7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8e"
+            + "e87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc821"
+            + "7a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8"
+            + "412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a"
+            + "1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7d"
+            + "a5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb173"
+            + "1523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec"
+            + "3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540"
+            + "f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f"
+            + "8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214"
+            + "f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda"
+            + "69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc8"
+            + "5c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd7"
+            + "8428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266"
+            + "c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce"
+            + "6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e8"
+            + "82b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b"
+            + "86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4f"
+            + "f1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d306"
+            + "1bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273ed"
+            + "b85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d9"
+            + "54ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a"
+            + "158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc"
+            + "4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905"
+            + "cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796"
+            + "e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297"
+            + "f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b381"
+            + "06a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0"
+            + "ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40"
+            + "141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc"
+            + "417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22"
+            + "da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843"
+            + "c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cf"
+            + "bf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746"
+            + "464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303ecedda"
+            + "e64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d99"
+            + "0be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ec"
+            + "d17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c"
+            + "55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b"
+            + "193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a"
+            + "422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d"
+            + "48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbea"
+            + "eb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd323"
+            + "3050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e3"
+            + "53482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b41"
+            + "15bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c"
+            + "839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0"
+            + "eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe"
+            + "5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938"
+            + "fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de"
+            + "0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb"
+            + "31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a27"
+            + "05bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9"
+            + "107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073c"
+            + "e17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327"
+            + "e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd"
+            + "2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276"
+            + "dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed"
+            + "5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b13"
+            + "2265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fe"
+            + "a737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a"
+            + "5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d202"
+            + "64d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456"
+            + "d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029"
+            + "b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75"
+            + "a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858f"
+            + "e38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7"
+            + "701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e0"
+            + "8f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932"
+            + "add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a"
+            + "6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e00"
+            + "1997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c"
+            + "7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b0"
+            + "51e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4c"
+            + "bda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43"
+            + "e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250"
+            + "ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de14"
+            + "22bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec"
+            + "03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3"
+            + "f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb571"
+            + "0c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd"
+            + "2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f"
+            + "0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d3920"
+            + "6f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e"
+            + "02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336b"
+            + "c8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de9"
+            + "3b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f571"
+            + "3f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790f"
+            + "fb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a9"
+            + "9a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61c"
+            + "cc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4"
+            + "b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e"
+            + "22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca53749"
+            + "61f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b"
+            + "7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd"
+            + "84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab"
+            + "149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c8"
+            + "87dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d23010"
+            + "70b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea311"
+            + "91188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca"
+            + "55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6"
+            + "fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51"
+            + "fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823"
+            + "e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6"
+            + "591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c9"
+            + "1ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070"
+            + "579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff"
+            + "261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a5"
+            + "1edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08f"
+            + "a6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb"
+            + "735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2"
+            + "c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0"
+            + "d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5"
+            + "e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b"
+            + "803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b2060"
+            + "90ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0"
+            + "e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca"
+            + "2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b997"
+            + "8a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259"
+            + "dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b"
+            + "3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60df"
+            + "b55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4"
+            + "ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea20228106"
+            + "36f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8da"
+            + "fb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee"
+            + "0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960"
+            + "026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fc"
+            + "b43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab"
+            + "0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321"
+            + "dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6"
+            + "213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f1247"
+            + "7a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f"
+            + "53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e6"
+            + "2077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de02225"
+            + "95eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007"
+            + "ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b608"
+            + "7e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e"
+            + "58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af"
+            + "99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d294"
+            + "4b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34b"
+            + "be78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83"
+            + "a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a67507"
+            + "0615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a"
+            + "74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2"
+            + "fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dab"
+            + "d73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b"
+            + "82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e86"
+            + "6ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2"
+            + "b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469"
+            + "e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c04"
+            + "7dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20b"
+            + "fc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef0"
+            + "80356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726"
+            + "a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0"
+            + "e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde"
+            + "3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd91"
+            + "73eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e31"
+            + "60b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d"
+            + "414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881"
+            + "c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf"
+            + "4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140"
+            + "518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c608"
+            + "39ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c2"
+            + "5aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdc"
+            + "accd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91"
+            + "efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b"
+            + "4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc6522"
+            + "32c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff559"
+            + "7eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce"
+            + "5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8"
+            + "c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f"
+            + "9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c"
+            + "61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4"
+            + "ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60"
+            + "745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18"
+            + "cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b44"
+            + "8113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be"
+            + "35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d"
+            + "7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9"
+            + "cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb5171"
+            + "79f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf"
+            + "5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b"
+            + "64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d937"
+            + "8ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c"
+            + "04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed"
+            + "131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac00"
+            + "88de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2"
+            + "e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1"
+            + "c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce2"
+            + "30096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa"
+            + "7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1"
+            + "568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb57"
+            + "2edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac447070141"
+            + "3f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b755"
+            + "5f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e79539"
+            + "9fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc"
+            + "4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f4"
+            + "29ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a"
+            + "22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d849"
+            + "6321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd"
+            + "7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd547"
+            + "00de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf461"
+            + "4bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b0"
+            + "3f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920"
+            + "bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15e"
+            + "abed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb979300"
+            + "4140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a"
+            + "9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c"
+            + "106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a"
+            + "10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442"
+            + "dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c"
+            + "448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb77"
+            + "52daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90"
+            + "702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62"
+            + "903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75"
+            + "baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a568607"
+            + "1fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547b"
+            + "ef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a89"
+            + "69e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d"
+            + "631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05"
+            + "d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535"
+            + "cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae"
+            + "890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e6"
+            + "6a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15"
+            + "272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad11292"
+            + "2d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d165"
+            + "13e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e"
+            + "865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b33"
+            + "3112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc"
+            + "7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346"
+            + "a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b"
+            + "77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9"
+            + "bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1"
+            + "960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914b"
+            + "a1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e"
+            + "1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883"
+            + "d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e133"
+            + "5ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b"
+            + "82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be2"
+            + "62473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5"
+            + "cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82"
+            + "ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34"
+            + "d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c"
+            + "99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77"
+            + "c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940c"
+            + "b15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e0"
+            + "67de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a772"
+            + "3d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa3"
+            + "3e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5"
+            + "e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce2"
+            + "78669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f31"
+            + "7f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ff"
+            + "e55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d6"
+            + "4cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744a"
+            + "cf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcda"
+            + "f558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2"
+            + "047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3"
+            + "d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c9"
+            + "8aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2"
+            + "980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961c"
+            + "dd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3e"
+            + "f02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723b"
+            + "cd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88dd"
+            + "f2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807c"
+            + "baed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e"
+            + "6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e35"
+            + "3e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b57"
+            + "4357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a5"
+            + "8d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135e"
+            + "df67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0"
+            + "ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b9"
+            + "6853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596"
+            + "f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d"
+            + "69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e47"
+            + "5bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9"
+            + "d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e4"
+            + "1cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3db"
+            + "ffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4d"
+            + "a4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784"
+            + "f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f"
+            + "2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904"
+            + "d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8"
+            + "afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9"
+            + "143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b"
+            + "9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d"
+            + "37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dc"
+            + "d275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0b"
+            + "bbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d50"
+            + "6271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369d"
+            + "c0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b89"
+            + "3c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb0"
+            + "5d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce0"
+            + "36a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70"
+            + "f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1"
+            + "a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc"
+            + "97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b2"
+            + "83ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca6"
+            + "8e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd"
+            + "86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f1"
+            + "8b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b"
+            + "18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b4"
+            + "1d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c"
+            + "4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b642364"
+            + "8a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075"
+            + "e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2"
+            + "be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6ed"
+            + "a56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f1"
+            + "25bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392df"
+            + "d430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa157898"
+            + "2f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1"
+            + "a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a0440"
+            + "42520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1"
+            + "f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a61920"
+            + "0a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bc"
+            + "ef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e"
+            + "1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2"
+            + "b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114"
+            + "203232b492e0681a31dfb3";
+        String expectedSig2 = "000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c2ae"
+            + "1d54c9a9c7b7def19a3bf459fdbc5c47275e9e4c2aa37bfc97839d9996710cd50ceab45f"
+            + "a2ec26ae272a2a3797b580ac874bda3345cf56e069b6540ff8f6032eb07d62659944eb57"
+            + "3c33d71058e500018bc8776f1c003171c8e962d41051f29ea36dd569ca57b3ffead498f1"
+            + "081ffb38cfae75b368721f67ec1d9b5d7f8e2913d63f309cb0aba8c572728b11dace05e5"
+            + "a00180073940b13ccbbbbbc1f8228c9bb0ffca4ed14934fbdf189888c9d6f9b53fe29727"
+            + "019cfd06da3ee308870f2e980e2279409587909ca6970a57267233d8856fc9d53e7be095"
+            + "8bdbd3f465429fc1c5d50f6ba64525ad06a238a3959a84c2a1e95e95a59582089114ac49"
+            + "7f13b24d035e01580f0f861b78df39f21fb4448e61c9a30baa20ac8694ce84606c7e63ac"
+            + "0aa380cde7a0d30ffda28d9753088163966cbd11b6f55533801d953e787bd8544b97f6b9"
+            + "3149ef0227e0ae15a900e70f71d1f16374474bd99023fab5eed63e2be6da822d975719e1"
+            + "3e81bb4890503ba9c222bc8a448d51fd233fbb104f80f5c66c55962241bcf85221586ee2"
+            + "a390e6220367c9cefccbd66d4c3ce2cf38bbcacb77a359eec38c5b99dffb4896ae34fcf0"
+            + "4db3f9a85cb8225317631d22dc0735addf0d84b8f2d3d9010a3be169f41296afe814f2ae"
+            + "d28076479140d2cbe60c237cc7fcb1ecde0a7d6f11f11e748cee5dd5393a31435a45fbde"
+            + "a245aef1993391d18c79005f1b3074edbe08ffaa2c73262080702f28e6c0a9f408aff784"
+            + "814c37526cad7c5904c3af0b5e5da7e3135191dc4884ad167b5ae1c86a28e5a0c635b2c5"
+            + "43465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae463eda47b2f0"
+            + "43afc2540d3afb0e4e0b5edffe0a8e906e557c3b84c154ce0ddbf44ee38d0c274e5bea1c"
+            + "6b6f36e83ce2374ab0aa09d51d881e768dd35fdd5dc0b208eca802e241626385ec46cd04"
+            + "f57110e12dad60b89eb0dcde05dccbf7c06718f5e211382af998107730aef24245b89ef6"
+            + "765456502e9af89e59b625ddfb50114c3db753eb15eace8e4871f2b4a49d7e67b0bec3c8"
+            + "f649c46183ae53b516454bc55ac1c79a6f97ed70d530ad84e371e3c8f3dc92407cb94f8c"
+            + "e14f76799c58f7f2fe8313cc33578764c849fb1ea20e58ad35e5471ba50b665390c76f31"
+            + "30ecc7811e84e394a35bf0859a119f97258de1833565d8ef142c842b3a27a17169841f21"
+            + "00740f8f0b55c90ec1573e660079653bfb0a45d17f5396aa2220e55c1d23dd9b79df765a"
+            + "c64fc2a9442ec286422e0af685e1ab72394934ee2dff1801a5859202c015e30f60ec155d"
+            + "0b0ec39cfbf66eb7ffcb3b46940c0aef409adc181c55c5328f671b6e0b5d18edff0bc335"
+            + "ebd2ca9afdc73dab4955f250874ff7325655ba9b3b83ebb460193e323bd86c7a5799f863"
+            + "816b6cf12006a2d114c252807eac1eea86a25e81d51728b6271aff7dce942044fa5f48e9"
+            + "4493e250c6f62d0f0a39cd90b499c85f059b2e8cc6bf85e0f2fbfd5b306088c13384757a"
+            + "9f6d053e682dd57aa1690b4bbb878f0142cf1fb2b9934be9a6c3638f4e360de688083fbf"
+            + "f64bec3b33762de91fb61571f754c2e6484234162b2f2d17368a44051b4d3c460077e227"
+            + "eb148c67f555bbbad500bef99dec034f6126cd16fcd6372e243f72dcf15bcb00e3e9c760"
+            + "0d6d31a48b97106f8a2cfc09e96254dd6474969304d3eaa4566c96fd7f0bcf20570a5a40"
+            + "1a51d368c775f81b58b4c55f8c7375ed9c644454a09740a7b92faf985fa64783323895cd"
+            + "914cb6c88c9f1d58baee856151e1bb042fae42aeb3c8a1aa1e0b6fd26cfd8158c25af30f"
+            + "b98aba4b1caaf5dda81fc4c8327286572a8a230e5fd8755e6770bafb3a0bf1f85b700c98"
+            + "5f53d9713e7a9e5ef04856c896a10a339402553033f36aef7f1d79e6c46ce0398e0504a2"
+            + "487feae2095ffa48139673a4cdc293333dd58ef482f38176de306f921e69bc234f599972"
+            + "636e8c16889ec9e1096767674f9724ced0597ed754123cf301c97ed763d537750f079351"
+            + "019448ea2897fb51df298c1e2eb4c2e27300a4924d7ba5eb33596589d34722068c6da4f6"
+            + "df3b9f98899812f47f799e8fcdba4988e38e933c3f751085890c164557228543f2862e78"
+            + "6e579bc5be47ff69096cf9677468dc96b364e1e7bdf9492d254539aea7b9c4a646859fff"
+            + "5c56443087ec4c9bf6ba8a523d630cb6974697183a3009ad8bf5d74749e5707ca39bd818"
+            + "e42d47f93e68938e8194411a1498065f3ba1b2c755c2f702eea34bd6ecbe7d3e1dbb0108"
+            + "152bc8822714984cd750a0a7ec6bbbd70593e515a445176e989fff9f6afd7daa6d7e7df7"
+            + "1612d196396603653b949e86642d13c953eea461507b91367a9a31cafa464d250de18f25"
+            + "719a601e7b3a730673103d7dbf20cea73421ffcf62339c352f69eb289b681d78f9f1a256"
+            + "c2941c40e2ce2b85c2474b7bc7aae399751ecf1b2ac8fdba9c4725fa8b6db283614cd13b"
+            + "b55966b7eb7ecd9a97dfcdbf1f669333258f65846d72c0b2245135d58e59435ea5a3855a"
+            + "cbaae2f725cc6b4f35a82a38da8927c0410516173552d054d0fb69808401ae1df88fee5c"
+            + "cea1876aae0960860bc46b21504c4ddad287a4b51dbc0490d1c948f8c95d7f793f5f6491"
+            + "356138d54641476fcf4fd0cc70a2e854fca947ddc79a823af0b3ea0d73a5f5e9e6102772"
+            + "0b9171c6830aca146e8cddbaa77a1501ab51a76d278fe2ccd9cdf2257468b7be7e64326c"
+            + "6083369bb86d9eec23dea1644cec11db72bbc842ab5ddee8eda08d0348267d48cecc9e37"
+            + "ef85d5ad0f93dd623a2db5537210b2762ad77a51ed8bb9019185c001f9f1e0de6fa0d8fb"
+            + "1d796f0e4e975468922e452b9cb4cb00eedff3416a03c80610d970b4ea5169ce43524857"
+            + "313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307"
+            + "277b9a09c038aca00a99ff63060dad783e060e4cd9d59cbcb2076d6293d19fcea9b90c00"
+            + "398e7740910f425703a0da099490e3b0472db2a5b2d2934fff8302e88ee8c6fec5456a05"
+            + "676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68"
+            + "c78d467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c085"
+            + "7fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76"
+            + "153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1"
+            + "a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec824"
+            + "0e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa"
+            + "244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026"
+            + "d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7"
+            + "404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a"
+            + "89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26"
+            + "c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba"
+            + "15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d"
+            + "294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c095339"
+            + "34b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2"
+            + "b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf42354"
+            + "24678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076"
+            + "e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff"
+            + "893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9"
+            + "770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793"
+            + "faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb6"
+            + "6a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb"
+            + "13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677"
+            + "a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3"
+            + "eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1"
+            + "b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac8"
+            + "2e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc8"
+            + "67d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c25544"
+            + "39c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb"
+            + "4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf"
+            + "42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb"
+            + "7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac"
+            + "29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30e"
+            + "e3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a"
+            + "2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1"
+            + "d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca"
+            + "6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de7"
+            + "79e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a25"
+            + "0489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9"
+            + "ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a"
+            + "0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e5"
+            + "5c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed6487"
+            + "2ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c"
+            + "660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30"
+            + "c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad23705"
+            + "81d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26f"
+            + "a4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af8"
+            + "46833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463"
+            + "f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894e"
+            + "c3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e8"
+            + "57ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab"
+            + "463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb663"
+            + "10a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b7684817334427"
+            + "3e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f1"
+            + "4dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c747"
+            + "2254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8af"
+            + "f3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b"
+            + "8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef"
+            + "925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c2"
+            + "59fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9"
+            + "d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccf"
+            + "e1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7"
+            + "debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324"
+            + "c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2"
+            + "cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b"
+            + "4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641"
+            + "c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711c"
+            + "febc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8"
+            + "cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595"
+            + "197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e"
+            + "267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672f"
+            + "b34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64"
+            + "f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f"
+            + "187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f89"
+            + "36c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b72"
+            + "4da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1"
+            + "d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be"
+            + "8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662b"
+            + "f90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de06"
+            + "1b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f1"
+            + "27adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7"
+            + "faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7"
+            + "b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86"
+            + "067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3"
+            + "c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a631"
+            + "76d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61"
+            + "c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8"
+            + "fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a573"
+            + "4d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5"
+            + "ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb35093947"
+            + "35f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e"
+            + "05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d6"
+            + "62417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c228"
+            + "82d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d"
+            + "2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea"
+            + "67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4"
+            + "a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2"
+            + "cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d"
+            + "487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372"
+            + "cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89"
+            + "c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db"
+            + "4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248"
+            + "f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b"
+            + "8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2ef"
+            + "f62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612d"
+            + "c283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9"
+            + "c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191b"
+            + "d96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96"
+            + "e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b"
+            + "0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf23"
+            + "5daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933f"
+            + "c5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce0207"
+            + "44615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e40"
+            + "6a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602"
+            + "161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e48"
+            + "5c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96da"
+            + "e4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018e"
+            + "f4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e8391"
+            + "4933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a"
+            + "6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671"
+            + "f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18"
+            + "dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346"
+            + "ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d9"
+            + "09996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708a"
+            + "ba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee"
+            + "596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a"
+            + "1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de0"
+            + "2eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c1843"
+            + "4653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f884047"
+            + "5d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16"
+            + "d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8"
+            + "d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c43"
+            + "8976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4"
+            + "ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9"
+            + "ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c3267"
+            + "7c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63a"
+            + "bef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37d"
+            + "efd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a"
+            + "25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089e"
+            + "fe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af9389"
+            + "24ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054"
+            + "e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba4"
+            + "77a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb4"
+            + "19daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731"
+            + "cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333b"
+            + "f1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3"
+            + "932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da"
+            + "9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e021"
+            + "11cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c83503872"
+            + "35b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc0069"
+            + "2d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e62"
+            + "30fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f223785"
+            + "21e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252"
+            + "528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c50"
+            + "41afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d97"
+            + "2e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571"
+            + "bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4"
+            + "b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67"
+            + "bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93f"
+            + "c7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013"
+            + "d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319"
+            + "125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a1"
+            + "3a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4"
+            + "cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfa"
+            + "ee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470"
+            + "f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6"
+            + "afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6"
+            + "bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84f"
+            + "de3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c91735"
+            + "24c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73"
+            + "293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76"
+            + "470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834c"
+            + "c12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680"
+            + "cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400e"
+            + "c92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33b"
+            + "c71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b443"
+            + "71bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79c"
+            + "c32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba"
+            + "6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e41061"
+            + "4a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e"
+            + "0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b3"
+            + "9d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479"
+            + "f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674f"
+            + "a27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450"
+            + "da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c"
+            + "86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c52799862"
+            + "4de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed"
+            + "55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1"
+            + "528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cc"
+            + "e36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d08074"
+            + "12b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff035"
+            + "5f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d"
+            + "45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da"
+            + "8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c820"
+            + "6ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83"
+            + "ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b5"
+            + "75ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1b"
+            + "ba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638"
+            + "a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2c"
+            + "a34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573a"
+            + "d5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9b"
+            + "a7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960"
+            + "dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738"
+            + "740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945"
+            + "c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b498"
+            + "9994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5"
+            + "c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e"
+            + "3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf4"
+            + "30f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d354"
+            + "3a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4"
+            + "d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eae"
+            + "cad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be"
+            + "6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9"
+            + "bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d"
+            + "1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2"
+            + "b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004"
+            + "b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6"
+            + "f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6"
+            + "c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092"
+            + "ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea53297"
+            + "8bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e"
+            + "5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71e"
+            + "f8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcf"
+            + "a899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc67245"
+            + "5405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3"
+            + "d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d8499273"
+            + "2ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb"
+            + "06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0"
+            + "914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9"
+            + "a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d5"
+            + "5af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17ee"
+            + "bf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4"
+            + "bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb3387"
+            + "7d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3"
+            + "a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91"
+            + "ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399"
+            + "bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014a"
+            + "d7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8e"
+            + "e87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc821"
+            + "7a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8"
+            + "412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a"
+            + "1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7d"
+            + "a5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb173"
+            + "1523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec"
+            + "3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540"
+            + "f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f"
+            + "8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214"
+            + "f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda"
+            + "69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc8"
+            + "5c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd7"
+            + "8428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266"
+            + "c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce"
+            + "6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e8"
+            + "82b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b"
+            + "86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4f"
+            + "f1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d306"
+            + "1bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273ed"
+            + "b85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d9"
+            + "54ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a"
+            + "158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc"
+            + "4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905"
+            + "cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796"
+            + "e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297"
+            + "f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b381"
+            + "06a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0"
+            + "ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40"
+            + "141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc"
+            + "417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22"
+            + "da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843"
+            + "c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cf"
+            + "bf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746"
+            + "464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303ecedda"
+            + "e64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d99"
+            + "0be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ec"
+            + "d17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c"
+            + "55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b"
+            + "193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a"
+            + "422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d"
+            + "48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbea"
+            + "eb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd323"
+            + "3050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e3"
+            + "53482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b41"
+            + "15bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c"
+            + "839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0"
+            + "eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe"
+            + "5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938"
+            + "fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de"
+            + "0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb"
+            + "31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a27"
+            + "05bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9"
+            + "107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073c"
+            + "e17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327"
+            + "e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd"
+            + "2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276"
+            + "dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed"
+            + "5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b13"
+            + "2265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fe"
+            + "a737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a"
+            + "5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d202"
+            + "64d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456"
+            + "d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029"
+            + "b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75"
+            + "a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858f"
+            + "e38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7"
+            + "701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e0"
+            + "8f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932"
+            + "add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a"
+            + "6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e00"
+            + "1997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c"
+            + "7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b0"
+            + "51e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4c"
+            + "bda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43"
+            + "e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250"
+            + "ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de14"
+            + "22bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec"
+            + "03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3"
+            + "f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb571"
+            + "0c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd"
+            + "2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f"
+            + "0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d3920"
+            + "6f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e"
+            + "02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336b"
+            + "c8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de9"
+            + "3b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f571"
+            + "3f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790f"
+            + "fb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a9"
+            + "9a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61c"
+            + "cc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4"
+            + "b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e"
+            + "22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca53749"
+            + "61f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b"
+            + "7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd"
+            + "84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab"
+            + "149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c8"
+            + "87dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d23010"
+            + "70b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea311"
+            + "91188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca"
+            + "55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6"
+            + "fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51"
+            + "fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823"
+            + "e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6"
+            + "591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c9"
+            + "1ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070"
+            + "579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff"
+            + "261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a5"
+            + "1edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08f"
+            + "a6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb"
+            + "735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2"
+            + "c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0"
+            + "d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5"
+            + "e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b"
+            + "803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b2060"
+            + "90ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0"
+            + "e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca"
+            + "2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b997"
+            + "8a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259"
+            + "dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b"
+            + "3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60df"
+            + "b55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4"
+            + "ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea20228106"
+            + "36f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8da"
+            + "fb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee"
+            + "0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960"
+            + "026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fc"
+            + "b43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab"
+            + "0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321"
+            + "dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6"
+            + "213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f1247"
+            + "7a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f"
+            + "53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e6"
+            + "2077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de02225"
+            + "95eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007"
+            + "ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b608"
+            + "7e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e"
+            + "58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af"
+            + "99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d294"
+            + "4b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34b"
+            + "be78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83"
+            + "a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a67507"
+            + "0615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a"
+            + "74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2"
+            + "fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dab"
+            + "d73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b"
+            + "82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e86"
+            + "6ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2"
+            + "b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469"
+            + "e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c04"
+            + "7dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20b"
+            + "fc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef0"
+            + "80356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726"
+            + "a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0"
+            + "e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde"
+            + "3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd91"
+            + "73eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e31"
+            + "60b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d"
+            + "414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881"
+            + "c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf"
+            + "4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140"
+            + "518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c608"
+            + "39ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c2"
+            + "5aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdc"
+            + "accd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91"
+            + "efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b"
+            + "4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc6522"
+            + "32c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff559"
+            + "7eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce"
+            + "5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8"
+            + "c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f"
+            + "9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c"
+            + "61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4"
+            + "ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60"
+            + "745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18"
+            + "cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b44"
+            + "8113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be"
+            + "35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d"
+            + "7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9"
+            + "cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb5171"
+            + "79f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf"
+            + "5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b"
+            + "64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d937"
+            + "8ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c"
+            + "04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed"
+            + "131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac00"
+            + "88de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2"
+            + "e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1"
+            + "c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce2"
+            + "30096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa"
+            + "7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1"
+            + "568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb57"
+            + "2edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac447070141"
+            + "3f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b755"
+            + "5f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e79539"
+            + "9fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc"
+            + "4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f4"
+            + "29ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a"
+            + "22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d849"
+            + "6321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd"
+            + "7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd547"
+            + "00de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf461"
+            + "4bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b0"
+            + "3f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920"
+            + "bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15e"
+            + "abed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb979300"
+            + "4140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a"
+            + "9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c"
+            + "106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a"
+            + "10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442"
+            + "dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c"
+            + "448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb77"
+            + "52daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90"
+            + "702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62"
+            + "903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75"
+            + "baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a568607"
+            + "1fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547b"
+            + "ef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a89"
+            + "69e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d"
+            + "631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05"
+            + "d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535"
+            + "cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae"
+            + "890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e6"
+            + "6a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15"
+            + "272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad11292"
+            + "2d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d165"
+            + "13e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e"
+            + "865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b33"
+            + "3112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc"
+            + "7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346"
+            + "a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b"
+            + "77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9"
+            + "bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1"
+            + "960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914b"
+            + "a1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e"
+            + "1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883"
+            + "d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e133"
+            + "5ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b"
+            + "82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be2"
+            + "62473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5"
+            + "cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82"
+            + "ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34"
+            + "d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c"
+            + "99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77"
+            + "c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940c"
+            + "b15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e0"
+            + "67de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a772"
+            + "3d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa3"
+            + "3e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5"
+            + "e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce2"
+            + "78669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f31"
+            + "7f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ff"
+            + "e55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d6"
+            + "4cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744a"
+            + "cf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcda"
+            + "f558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2"
+            + "047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3"
+            + "d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c9"
+            + "8aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2"
+            + "980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961c"
+            + "dd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3e"
+            + "f02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723b"
+            + "cd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88dd"
+            + "f2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807c"
+            + "baed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e"
+            + "6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e35"
+            + "3e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b57"
+            + "4357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a5"
+            + "8d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135e"
+            + "df67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0"
+            + "ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b9"
+            + "6853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596"
+            + "f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d"
+            + "69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e47"
+            + "5bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9"
+            + "d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e4"
+            + "1cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3db"
+            + "ffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4d"
+            + "a4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784"
+            + "f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f"
+            + "2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904"
+            + "d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8"
+            + "afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9"
+            + "143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b"
+            + "9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d"
+            + "37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dc"
+            + "d275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0b"
+            + "bbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d50"
+            + "6271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369d"
+            + "c0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b89"
+            + "3c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb0"
+            + "5d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce0"
+            + "36a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70"
+            + "f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1"
+            + "a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc"
+            + "97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b2"
+            + "83ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca6"
+            + "8e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd"
+            + "86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f1"
+            + "8b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b"
+            + "18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b4"
+            + "1d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c"
+            + "4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b642364"
+            + "8a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075"
+            + "e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2"
+            + "be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6ed"
+            + "a56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f1"
+            + "25bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392df"
+            + "d430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa157898"
+            + "2f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1"
+            + "a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a0440"
+            + "42520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1"
+            + "f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a61920"
+            + "0a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bc"
+            + "ef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e"
+            + "1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2"
+            + "b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114"
+            + "203232b492e0681a31dfb3";
+        String expectedSig3 = "0000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc1c"
+            + "e556901faa3fadf6e97db78040156ebdb2e7bf2ced2b6aecc9337747a7c11b90d77683be"
+            + "059022e8b4eae2196632e270235ccf92b9da827f31070fde1c7646feb15df59b17f97fe8"
+            + "2bf9d574a85366c19f48cf32e9c04f7366d5ee7899f48f94b7d84d5890340f377e9aa295"
+            + "ec2b356b78ed6ce99ac8ad8ce5bbeb00280d28eef208dd63602130aadd66cca458a21e43"
+            + "47c7008e937d773c7ae46117706a1cf43d566cbbf92bd7c45cd3a56f52223439bfe06a8b"
+            + "136aa35c76381ed5576e8e1ffe6e9fa145cde1fb377560ad07a109552c08090a8f355192"
+            + "aabd1f3fdc112350c794af5d165e432d777d7b83b40663be13b990c85e602d9ee85e65f1"
+            + "3a96d203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c237"
+            + "0aaaf4ea6dcc2b226beaab6b814e7021a6bfb9536aa4745a09a375468a450c7c29b7fcdf"
+            + "24f248ad7bf8ba0f0379f9486b33b1c656662f139aaaaa917648568943207b7690222796"
+            + "1981a8b9269ccb70bf4c88a097639ff4fdd736a006a5416a8cfe2f7685e175c1ee30e2f8"
+            + "878e165a2693f0355cbbb3874d29fe6ef76ee992122323e957211920d1e06178a51897b7"
+            + "ad577215de589c24d23411aca8ae2b76adac59897ece299db3da7b4a250244c278e8c4ef"
+            + "0b31f1079980e2b9f5f2229f6352fe258df489ece62aa76f5557a0bdd295ac403da918ea"
+            + "f4d8e7a48d102e0ef71e06193ffc698a42f4da29427c74564bdd11dd3d32803d6007b1cd"
+            + "cc26dd1390f8ed4cea650e870886724a3db11309f48682ebb700832cde372d09620eed82"
+            + "c39c2eea41520419bfea541ba2815ba1f539275ce944fb66f94f72c47383350cbcfba29e"
+            + "b33eec09b53cfc896c3cb089e72d2734b08a605df3c09a6e7cc40f14007e5535b59a48e2"
+            + "4ddc2921492fbf90136f3bc051a32bb02e043ec20b7c4da5ae92dba6089a76f1bffe50a2"
+            + "2dbfab004eb38dd3b11df574a7344d2f418a2eb8bf1c172fa9bd04ca573ddf8f137200da"
+            + "ad36ecbafbb79602d6ef2f996d02e692d10b4c2c5e6c7d0129d85e3ea79e6b13c2af471a"
+            + "2192e8ab8a6f4c96185468fa763a3ec418950bbede0f53ebf0182e5a5ee299130e7420ab"
+            + "bda1ce2e4948121b6756f03816b07b5578293e4d14da9ab6f67eec0b0cc1a9b8285760d0"
+            + "ea3eadde48c32a1cf5159611845e3962990c998cf1928a7e4d1a44f079f25a1a3047c2f3"
+            + "495231a7612b9628e17fc7758f020fa42df2dcedb74b4844481aed37c108bbf9bfe44c7b"
+            + "e12c34d661573a8f7fec1424ccc2607295d7dc3f21bd6bec75f098d7d1551f1e88453418"
+            + "6c284a5633b3e3c5e942d5544339cbe5d03f1bf4701fa4b0533f25a45d768e74a6d8b3bb"
+            + "b1dd2b38e5fccca5c49ea6dcf9a08288cb53889f73a8ac7b049d480227570a73c44602c5"
+            + "7238c4aef9184fa40ef719d8a0e2942faa96cbd12855923a0af0d3dcb17dff0f114f107b"
+            + "5cc2994acbddcb6938b789e2fef0dd5457bd12cf7ef8eaa450d27f554e447040602b1a0a"
+            + "02396f3c9a6c90c3b2b531f7ffb1be0354dfddd27388a771a912b6b8cddcce92bbee1755"
+            + "3276aec5a66e0cf9af9a5e304d6880f4239858ed7afd93368125a822bc930e827b9ce31e"
+            + "043c1c729741248186bed44f1a791ce136d9713b537d7ad336f9516f0bfa22a09a2f21df"
+            + "d24b86e16a30ec471bfef6675e72ed2d450309144da61655c6c37026cf108af5628c32fd"
+            + "0f36cf214275aa8961b22fb1e042959d81bff3c1bf2128d698c37c2cced73b6defa76f56"
+            + "49c2374da804550cf7a7d68f43c6c810ee467fa6224f0dc7dc8f94dd45eeb137157304a8"
+            + "5031790dbb065c708887a0b8c1e1f038d42bcf909174373846976e27208e5561de43052a"
+            + "6d901aef041adb39b24448cdfe62d988836b7128679358afc3898b83093ff7891c92a60f"
+            + "5f5fe9266ceefb73421c43b2b1dfa08a180bb4dad83dcdacd97a63add143d640de6ba4a6"
+            + "b5d62e29cdb74dbab64162782da7b72c0f3d5dc4fbb351eaaa423026842a7fa340fcab11"
+            + "c61a647ce183ea5b647ccb3fef9eb104ac9faa0a4366160a7887e65e0fdf4f879b7feff2"
+            + "020646088d1601a69e97780456beaa20db6db621445387c736786214485b97a09fa79e2f"
+            + "818a21eb6c5de7661b67c91bde23b6b9e3365582db91c853b770f7e4b87dfe35b875129e"
+            + "1c7f81e7102e4a774a3f7dce92a3d6322724d27aba6e162e9a75212a0bfaeee94082a87f"
+            + "01690a974b60690c83f08a31cc6fd7329a6b03327eb07b3d93db67a7ac0f3bbafd8c9ffe"
+            + "0876cc7308817a8ddbdb26b91266e40ba7668d30a02085db64cc1dac1936c88b3035cfcb"
+            + "394e82f80bf4a629353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17"
+            + "ae460dc51df1044d01d3ae1806653b1dc235c63d516af4cf5cfe7a44ee19794df9956419"
+            + "9a5f98d0a7fa6f534fe2bc6d930ed46b9f795d01f23d8d5ce859451f59fda1eb9787c6a4"
+            + "c59a68a3ba658a936e0dd7ac6adf311ff7b0824b62d079bd8197ea10c6099eed16b3b98a"
+            + "ba3003d6e24b35116f9ebb1240c01ed6aecb164f705c390fc984aa900b9d41eda3b3ff33"
+            + "ae3ab737c99e059402822c2e8e40a93bd500f75c03e4fad42268f05d84ec8a6480751d77"
+            + "9a70b45996ee27cae86e78c12b8e7d1cf123d6e861682557c95c4b0c2bc9368312b8409a"
+            + "4e52affbcdadc5af2c972243390d1074a64c90b2ef4e670c7acee106eecc06a1c51451ea"
+            + "8e7531d84c72cb593c1558cf5d36e9a59d275f88fef060f3aa60ac06e0c15f4ca4690155"
+            + "3d27ebbaf3ee098628ce948b82e3b0a6f0ffd334a28b8b1d93557499b3778159e8a320e5"
+            + "4e22ad4f063765739722ed89ac07c9738803ffc999afb8f2527436c690032206196b15d9"
+            + "09a709df1ad319de092e30f3d373595c704192d1b9f6330dc1631bdef5fb01d9ba7c0790"
+            + "cd840f3de0d0cc48b5c6640b3b7729ff9c1dc23e3b44ed3814db7352eb7d2a5da6cee5f7"
+            + "c906743319dd75098fd2c5dadf4e55b4a461969be4aa2007955dfe8862983d252d8b79a3"
+            + "3e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e"
+            + "2c4511ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c085"
+            + "7fde4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a76"
+            + "153038370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1"
+            + "a311e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec824"
+            + "0e8e693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa"
+            + "244604dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026"
+            + "d7b052c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7"
+            + "404df9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a"
+            + "89893a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26"
+            + "c47a6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba"
+            + "15ad13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d"
+            + "294c8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c095339"
+            + "34b8523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2"
+            + "b5e21fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf42354"
+            + "24678d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076"
+            + "e15457ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff"
+            + "893b1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9"
+            + "770d99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793"
+            + "faa92466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb6"
+            + "6a1c27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb"
+            + "13cbabf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677"
+            + "a062264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3"
+            + "eaa25cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1"
+            + "b34e2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac8"
+            + "2e8d4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc8"
+            + "67d8abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c25544"
+            + "39c735e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb"
+            + "4f7d1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf"
+            + "42c9b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb"
+            + "7fce15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac"
+            + "29bf03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30e"
+            + "e3ecdfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a"
+            + "2f3733f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1"
+            + "d38b03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca"
+            + "6bee5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de7"
+            + "79e350273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a25"
+            + "0489a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9"
+            + "ea3cf9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a"
+            + "0bccdbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e5"
+            + "5c9cb583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed6487"
+            + "2ac5c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c"
+            + "660b71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30"
+            + "c8b77625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad23705"
+            + "81d3e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26f"
+            + "a4ae5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af8"
+            + "46833f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463"
+            + "f1ceaf360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894e"
+            + "c3c849cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e8"
+            + "57ed52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab"
+            + "463bfe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb663"
+            + "10a9126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b7684817334427"
+            + "3e3ffc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f1"
+            + "4dc4c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c747"
+            + "2254f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8af"
+            + "f3ba7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b"
+            + "8ab9f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef"
+            + "925d33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c2"
+            + "59fc0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9"
+            + "d67c0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccf"
+            + "e1a4cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7"
+            + "debd0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324"
+            + "c4869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2"
+            + "cdd4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b"
+            + "4d520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641"
+            + "c5799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711c"
+            + "febc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8"
+            + "cb0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b4595"
+            + "197a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e"
+            + "267d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672f"
+            + "b34d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64"
+            + "f8907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f"
+            + "187c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f89"
+            + "36c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b72"
+            + "4da3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1"
+            + "d69d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be"
+            + "8038184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662b"
+            + "f90f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de06"
+            + "1b76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f1"
+            + "27adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7"
+            + "faf1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7"
+            + "b91c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c86"
+            + "067bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3"
+            + "c8b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a631"
+            + "76d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61"
+            + "c46634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8"
+            + "fbaf8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a573"
+            + "4d1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5"
+            + "ded2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb35093947"
+            + "35f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e"
+            + "05d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d6"
+            + "62417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c228"
+            + "82d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d"
+            + "2e6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea"
+            + "67b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4"
+            + "a34c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2"
+            + "cfa4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d"
+            + "487004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372"
+            + "cd83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89"
+            + "c0dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db"
+            + "4054b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248"
+            + "f5f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b"
+            + "8bc80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2ef"
+            + "f62abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612d"
+            + "c283598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9"
+            + "c80c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191b"
+            + "d96305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96"
+            + "e3d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b"
+            + "0f183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf23"
+            + "5daa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933f"
+            + "c5cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce0207"
+            + "44615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e40"
+            + "6a6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca93602"
+            + "161b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e48"
+            + "5c9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96da"
+            + "e4171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018e"
+            + "f4c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e8391"
+            + "4933c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a"
+            + "6d634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671"
+            + "f1aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18"
+            + "dd90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346"
+            + "ec316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d9"
+            + "09996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708a"
+            + "ba2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee"
+            + "596cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a"
+            + "1e4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de0"
+            + "2eafc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c1843"
+            + "4653fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f884047"
+            + "5d850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16"
+            + "d8c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8"
+            + "d4082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c43"
+            + "8976e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4"
+            + "ad1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9"
+            + "ec8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c3267"
+            + "7c112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63a"
+            + "bef7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37d"
+            + "efd5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a"
+            + "25978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089e"
+            + "fe2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af9389"
+            + "24ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054"
+            + "e01ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba4"
+            + "77a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb4"
+            + "19daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731"
+            + "cd567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333b"
+            + "f1b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee3"
+            + "932ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da"
+            + "9f2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e021"
+            + "11cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c83503872"
+            + "35b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc0069"
+            + "2d9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e62"
+            + "30fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f223785"
+            + "21e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e9252"
+            + "528c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c50"
+            + "41afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d97"
+            + "2e359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571"
+            + "bb84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4"
+            + "b780fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67"
+            + "bdf077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93f"
+            + "c7b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013"
+            + "d44bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be58319"
+            + "125ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a1"
+            + "3a02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4"
+            + "cdce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfa"
+            + "ee2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470"
+            + "f5cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6"
+            + "afee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6"
+            + "bcce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84f"
+            + "de3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c91735"
+            + "24c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d73"
+            + "293cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe76"
+            + "470425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834c"
+            + "c12cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680"
+            + "cd2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400e"
+            + "c92b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33b"
+            + "c71101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b443"
+            + "71bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79c"
+            + "c32067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba"
+            + "6bc96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e41061"
+            + "4a1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e"
+            + "0df348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b3"
+            + "9d3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479"
+            + "f5eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674f"
+            + "a27447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450"
+            + "da0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c"
+            + "86c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c52799862"
+            + "4de030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed"
+            + "55f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd1"
+            + "528004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cc"
+            + "e36756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d08074"
+            + "12b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff035"
+            + "5f68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d"
+            + "45c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da"
+            + "8ef68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c820"
+            + "6ae39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83"
+            + "ff3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b5"
+            + "75ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1b"
+            + "ba21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638"
+            + "a23405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2c"
+            + "a34d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573a"
+            + "d5403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9b"
+            + "a7a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960"
+            + "dee118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c50738"
+            + "740aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945"
+            + "c8c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b498"
+            + "9994904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5"
+            + "c27aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e"
+            + "3c4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf4"
+            + "30f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d354"
+            + "3a23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4"
+            + "d2de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eae"
+            + "cad859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be"
+            + "6c30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9"
+            + "bd0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d"
+            + "1359bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2"
+            + "b313b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004"
+            + "b3310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6"
+            + "f095ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6"
+            + "c6b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092"
+            + "ca11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea53297"
+            + "8bbb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e"
+            + "5464c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71e"
+            + "f8ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcf"
+            + "a899c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc67245"
+            + "5405e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3"
+            + "d78a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d8499273"
+            + "2ae1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb"
+            + "06067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb0"
+            + "914ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9"
+            + "a29e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d5"
+            + "5af7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17ee"
+            + "bf976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4"
+            + "bb5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb3387"
+            + "7d90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3"
+            + "a21d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91"
+            + "ebcef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399"
+            + "bcc4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014a"
+            + "d7b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8e"
+            + "e87b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc821"
+            + "7a47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b8"
+            + "412fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a"
+            + "1df3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7d"
+            + "a5f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb173"
+            + "1523ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec"
+            + "3dcf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540"
+            + "f0d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f"
+            + "8faaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214"
+            + "f54be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0135db6662b577c0e4c22336bda"
+            + "69a525476689329fff05de538dcf42c511602923ec8b6795a40aa62b3bdbd90116671dc8"
+            + "5c2d85d7316a8be107260e66b60d12cb0e264dc6cb95025d0a3ba4e97a19ae8e78588dd7"
+            + "8428f0a6eef5df595811f6163a97f6ce70c64bb25dd6c986477e40e342fe059b241c1266"
+            + "c34e5c02aeb413e1ec8aa74429f5d19392d1f10fc69749e23869f11bc4aefa6456c8e5ce"
+            + "6e39b88bedcd9a7272c9e50fe187c374a36d9343dc2d77b1487a8a50e80f4ad9631d77e8"
+            + "82b44579a9ef3694074b68d3b4698913ac2e3e58e4d53d358d2e578bb93aa15d0532584b"
+            + "86e78a3356e6bdf0f0c6b7d76eb282932436b7658f0deedd2906bf2497b193fe10bc6d4f"
+            + "f1e9ca2f525c3922565b55e176bc55996976db45c8955b45e649f111e0ab6167b291d306"
+            + "1bcc8dbaac26895eb8d753e7db7ab5c49d48f6d8b70ee8e4d58259df5a312d38422273ed"
+            + "b85db0565f1cdb7fbac26101904fa5685ff61993783031c0eebba04e4bb9b8ce016f47d9"
+            + "54ee8ad65acab924eb86f6b742e8cf714313f80d8916a1c80ddabc9b195948b29a82323a"
+            + "158479c0b521be15cd62c46d2d61a7b78fc648b4b7fe594f5cfbb99f8e42b6444612fecc"
+            + "4cfc0a2f9d74797fe73bb8116bfd24478d6d632a250ab166246f8da2dcde53c41cf0f905"
+            + "cf3ec5399ed44976361326c17adec177adadc2fa9b60fc2ff2e3612fc15f703a39bfe796"
+            + "e1aa0db055ea63ab393868b2d211930fd67165cf378ea0ad8de0c629b045a7443fa41297"
+            + "f352d4e577eadffec65b40ef8a598dd9a5a60bd8b6b8bc99c408c05b522498691a29b381"
+            + "06a919a0931019e9d7060dc618275772993a3d747d31f1b463fc9265b746c3d0e964b2c0"
+            + "ed781d2c3a2e3ae08279dff29fed0a5e49a4d70000eca6932abc913317d2bd10ff73cf40"
+            + "141d0adab9460b7ceced7a72569b4810fc34459038e718bbe5d856cfbf09e7f7531d28fc"
+            + "417d14bdb4fdd7ab0156eb637986272cf7d265b0a266826c49f7a6a22b51695bb8b45b22"
+            + "da51950af3fc1d45cb1604af954fbe130255ee7c4a9c72f452a0c4da085f63d00a8ec843"
+            + "c4e28215aa44a048c209178398031ea670e7cbcf13b46eb9b0b14d7bfed4cd311104b2cf"
+            + "bf67963a2a83e334b2ab635c7ca1acfc40d031cba1baaba6fafa28de8a9681838087c746"
+            + "464e1fa8bdad156f3fed84dcdf2e79f37c8448f7972490ebfa5f1fb19685d85303ecedda"
+            + "e64027d4e01eff6bb92314606b7f94d036b048b0f229844d1a1fb27e795d2051eb050d99"
+            + "0be0a9a44061ad3668350844537d2df7f21b5705bbd509c3e2d8e2a5b857f3286b2c42ec"
+            + "d17c56972dc46f81aa042b7d3f3188e1b929cf013d7260565a19e1bcff60bb3f2264b97c"
+            + "55727e732df6ee2ce9dc33768aea3d17eebd7b996d0fd615808ecc224035b21e9d28023b"
+            + "193d60188fa640380f602c87d545376ac5c1649f05d6d2353aa97dea9f01121305f14c0a"
+            + "422066be370c707ede7f7062731d60f191f0ef59c1d9f4c80d33a112cd0dbae024ef0c9d"
+            + "48f9ccf9092f26d5f32fd584211c545c80fe7a3d025d47952682bf3a001a4a007298dbea"
+            + "eb3e30ce86403107caae1559c455110dec4e2b1438c1fe41231786fd0728b2687ffbd323"
+            + "3050be657c6a3949cdc1284b88a9d830a7f3cd30bf4cdf8fc71838a03fea1affe19961e3"
+            + "53482676208856def69f41b71898841b814bb9d1e364d18ee02376dbbad47dd64ad50b41"
+            + "15bb5c40b25602fde40ce05245c343aa430734dd768a3faff36861949af2bb8b6154f70c"
+            + "839a5789e2b4ee2717b90f068e7336139e2fdbb6ce8698be055276aba2904b71d91b02f0"
+            + "eed6edf51d6dfefca76c5f338383b2456fc4c262a45bbc77a2c0ec5fa31df5d299933ebe"
+            + "5e7ff03c0c6a3ec4da17913e7d4a66f575e1041cba43210b367f670a5552e1c0aec43938"
+            + "fca0a0269d2f90adfa36f9dfc1ed826e1b6d5c235c56a0cdda40f135678367e2b31c88de"
+            + "0f246af962b89bd5da8791154e49a359fb3c7fc9d89b6ee260a426d6ce26c896ce1b73eb"
+            + "31a73779b666e343b4dfe65ba11bf5a7ab1d6ef21822c39de91414698b459d0f81c72a27"
+            + "05bc08c76190f32d67ff8be902508c9eff388ffd1bfbf7c601e59aa129b90f875e45dda9"
+            + "107eda2dc9d15478785ce6121938bd299aaf634d9628cd3f8495364f8b6cfb8c5617073c"
+            + "e17818df7bd4c73484ba953277c74becc0943b842bbf42cfa5a0e811f4b66da54f8e4327"
+            + "e0c335ab23bc9e4cdb8b05e6f82fff9df63d949b2897e1dfe9754a8a0616fa32d55e25cd"
+            + "2620f7ef549f9566c51cff7365db7a2e53bb09319e021f5ef265ebdef164fe844d0f7276"
+            + "dcec42ae714388e1aff665a32e6b810e30c65f70b96b4fc9651331f1e549bb51a9d72fed"
+            + "5b9de4802b4da8cef46b4902f5188b0004936324a967dbed9b70f4edae090f43dd963b13"
+            + "2265be0d897092f8310bcb092cd50f6ce6fb133c756c2448b495ba2d4eef0dcd3d6467fe"
+            + "a737af12d41ce47570d1b2b9aea75328d0d684721986cd66bb4333842bb50b69b367ea8a"
+            + "5d0695d690a345f0258012f3e3db9d74b4372f647d6d08141d71216624b2ffa71142d202"
+            + "64d8839b8be50d47247a8302ff2d52524acee80efff9f1f4f0eff23e9255b73b35eaf456"
+            + "d481ddb17a915ca5b29cc530f66e1664815d1f16d3591948c393b5c97ce9fe3a81eb0029"
+            + "b3fe498e611f33bfd84ce434ce49357e42087330b0c01c2c16e6155396444a4b5e8d6c75"
+            + "a001b43b43b4b0d60739a4f78fad4703c2a68b701bdbaee522cde5bf5abcd9413350858f"
+            + "e38025c23d17db8da158989fcfb9e52c283c4dd48112c3817df41f207ab75a6f7536fca7"
+            + "701fb87a24d40da59042bc2a4562133d940d4641653b51d15297f2518ea671cc789e61e0"
+            + "8f5fab391c7eb1f121b7d41c34ba97a47581f81dfcd08e7fdb5256da725bf1b2973d9932"
+            + "add08604b2fd3595eab51752767a900c3977b024f223bd2c4e90fa98afb7d39ae0c1478a"
+            + "6d8592290e59b3858449008f18f249bdd1e50b0a9127e437004469738e15535baa8d0e00"
+            + "1997b4c642ede79aae666b2582305da7000a327014e487c2996455aad3e12746fde8291c"
+            + "7147d29c5de976df6f326d9bb2b520b3672c82310d629d270fbd5834e2773080630e33b0"
+            + "51e8fd1dadc8cec7271726e9f7a02b070263a40a4608b66c5f32a026f5e2aa81e5271c4c"
+            + "bda381223f9a9fe149789440ca9e871a79708e84ff2669580d6baea2f343ba4c340eff43"
+            + "e37d8e226166f6a7127c87a6184936187089fddbc9f7881eaf66fd1743b2b3a4ed274250"
+            + "ea0bd98b9743aa73a438da5929e53456f58165941996b19e2790caec5e7f8007f881de14"
+            + "22bff2d00b217175c595e058dedb4aefec91549f15c626e7b86a65bda898178fa639d0ec"
+            + "03253bf7eb3ccbdf03d1bb29fc0a89fa24a40713d1bed82f27b19e275c76513f73db70d3"
+            + "f9ac37d3177df3e5f8e9aa9991775a8c20e1c14ec6a8ed46c4dce8540fd28f9f824bb571"
+            + "0c8cbc8000c77f1e7be647883420e930a94e18fa6e10b376141f6e19ea09d2e36a1460bd"
+            + "2a0c9d915020cee0d2b6e5f7bf34c34f7a4c98b1c3e3d7b742f0ea4a46e07a7b1203758f"
+            + "0e50fd846bd2201d6a4384dec0fe198a08a8e1ac1ca180b0fbd0e384f2a5eb81044d3920"
+            + "6f1662e9aa45e02066aac78e7a4a6f0a5bbafda32844e70ab313ced85b67c8ce157f4f0e"
+            + "02123a79fbb8f1e99929120e465c0df45d60e580882d4bef28f1d17ad76a3a711f88336b"
+            + "c8f0728c6d859504e1fa58e23f4db8085c55f05d42cf0789e0ed86fb0abcc28a65462de9"
+            + "3b3235eef13cf335bbd840908e5e39680700a52b6df5a27f949463a90e057c534619f571"
+            + "3f624bef9e7486541d011eecf69d2688f250f1035f18ea0d05b5753d6b26bbda5189790f"
+            + "fb7245037e8555a9b459563bc8dc3e374941d3d8fa4780e57e2b14dce8de1270b1b960a9"
+            + "9a93934b02306e449287eaf8f53eb959713a064411527a17316746a310e1098cde49e61c"
+            + "cc69cbdb99ffecc82fdabf8d4c77d19761910a7c08c6700e0ae38a1f8c66335c10fe3de4"
+            + "b2d1e069e6d33493b1257888a62013a3e2930e29d0f34e759a4ed44a7952fd555586cc5e"
+            + "22128894cb6857d9ed1458cdcbc51d6a588a5c1704f2e288a026f7c87b031789bca53749"
+            + "61f64042144f1f4f73756d453c774fb7393c1217e8753a4eff8b52f935a003494eb2064b"
+            + "7a2bbd1825d95be8ac2430e97720b963eb2ebc2cf9bf2710eaef878b84447354174c8edd"
+            + "84e03c107756c49524be4e3eea266a32215a2f789e429c241f6bb4b3fc7c56a954a47aab"
+            + "149b458f1b1865f019bef028aa50bea52d9d34f3890c1e00fd182e6de248d00f45b152c8"
+            + "87dbe63b6837b79cbcea44747ea52564fa661486a769fce752665a059722709a13d23010"
+            + "70b7bd5112b09484f9f281142541d1a664ff7525df6ef255778bb9952b6dd1be63eea311"
+            + "91188a8057620d3a0a396dccc3e4ad11797a113492407b5038ed52fb93df9d79a96b8dca"
+            + "55df98f619e6447a7bdb94e3243cb70fc067d7e87e63d4855957c180ecf92980eece0cb6"
+            + "fec9643d98d66b6ac2cac8313a8e47092f63d963df6ec87c02fcf2bf14c7768fe3ddbd51"
+            + "fbc1321d968266ec524e12f2fad86e6df61e2b38011aebc31d86c6e2616cda44539d6823"
+            + "e73a0966b787f0ec97cde096cb4d96ce93f0dd59c5902660a0b72760c887fc8cc887c5e6"
+            + "591b8b1527a80e039fa85efaf9c146e744da525f41cde7379c0fbe61be15de8012ea00c9"
+            + "1ef0522e9c7f37792819efa1d18583b5afb8f93cf19268ef59a5c89f67d92a6fe5e75070"
+            + "579f0e7f50d81508c78cffc9ced04a3dcee9fe2f31e3153e37fc75f90226c1cf577842ff"
+            + "261ccb3923c499de877a7447200f7bde3077ec129940a69bb7905ee6359d969f20def3a5"
+            + "1edf5b63d265b65abb5e60f845c56da01fd251c76e9fb75e1d8fc91fe34f8c450fc4f08f"
+            + "a6291da634501d6a6ec5ab5aa9f6855852f8ec3d419702c4c84a1fcade037304331bb6bb"
+            + "735680eb30799eda5b53999d3e5941658935b8f289c296701b2fc6e546a2c5eaee9dd9f2"
+            + "c20f645136adcbb9e0588c5f1df68cb5409282655c124115af588693739d34b2c7b16ad0"
+            + "d8255c793c9b2319a8ac9382cf2c7c1ba6739acb1c9d6a382905872ebbfbda447bd773a5"
+            + "e7779c05d49cc9b458d2942d2f2d40eab65da9830d52bbb89d315deaa93b78f3b7fde79b"
+            + "803c3db01e0083a6d8d7fc7dce8e3850e3cf8104f1dd799b81dbaacd11a50ba8b02b2060"
+            + "90ae2d166f5ff1e8cabd8a4559a5e42ec3aafc370bbd856ab20f43871004f43c05ad0be0"
+            + "e3ee5737be57ba4fc831b877178cc591dbb3fea6e925b807aa1acf226efaedab4095b1ca"
+            + "2a2a816d3f46d97ea8fa55c7384fd05b4ac078909d446ab0eb5775320e8e7019cb44b997"
+            + "8a83131b72c6a89d0b58d5ee47459607324229c0868f8bb3af52ee107a2b62ba13a9c259"
+            + "dbd55563d033effcebe2216770fa8aa25d311c744a32f9e527ca4d953122ac7b9b2a815b"
+            + "3a0e02bbb223a7417e97e19f30c4e40f733588dc3d1a69e6da5b0e7dd6d2ab8c82ac60df"
+            + "b55a38ac1ce907a8e915cc8564c1d85b3d74bfe0fe6a1e483230cce75a9a8075bbb897f4"
+            + "ad2bf6d6841078ef43ed414bdd1ae9d6cf7abe4adb8579a4c92abd3c002875ea20228106"
+            + "36f0ecbf5c40e43dc9191710643ce06076dbd1d4aeb38702fa83da29cb567a20e60fb8da"
+            + "fb9552251f1a908ee260bebd8bd1f81aefbc2ecd389a499162aca830e81a60e62a1b3fee"
+            + "0e9b4cf07c2717bbc4209cb7ff4b4f0d26b14cf605a75497bb111a14de7e6fc3fa963960"
+            + "026b9b0db19c6f314c32efdcbd8ec9545fb786dbc3ca1dc1b4e9b1dae53f817c313829fc"
+            + "b43a3b7e7e783cd1fbaa63f2367c5d51cb4f936a84bc7ab004d4be3c137ceabb5252edab"
+            + "0749c067cae8a8ed3e85d160d0dd10564a9587c639121fd4139df98168356341a40fa321"
+            + "dd6e6e76ef65c2967b41e9f8402b6319f8cc5de2a1ec98ca28356a44bae39b66b90666d6"
+            + "213e34909964530189249e91e9e7b899247b278308766d780c4b77fbfbcced4cc39f1247"
+            + "7a266f006ece0ef8695473f108c55b8c1037f037a8f872fa4095b396735ef28227deb33f"
+            + "53928584eef27076fd3c705e114907ff995faf0538534bed514db765a9d209379b4a28e6"
+            + "2077d7a25c8cc9d02563e8fdd5c0ec6d3e7e59ff0a2684bc054a2a9f053ad44e0de02225"
+            + "95eb693d5e06b596a0fb5120a94266c66cc498806ddb359d6de1d7321550d64feca08007"
+            + "ed025ea77eb3ad0d1f2dd47d1dbcf2f6839c274e1059181634a6aa6c683c648c7397b608"
+            + "7e26ad7848e332080e51fef34236ccd8a21b670ee4b6e7cc90af38f2e03d8ba94cc1b23e"
+            + "58260fa0ad6d97842c97cfb5eb0bde115eff312e58fd92268cbeb6e9018c9040776ef4af"
+            + "99a437d995e8e204315212b93ce27d7134f0e11cf0aa1ea35ce02ac2217859e15d97d294"
+            + "4b83f3c2637f5d7da6787f5e65bc6d970c0ea503fd72269459484d7dbc0b386a9971c34b"
+            + "be78357553dabeb0e06a927059c4192a47d2bfc46d71988347d9402f09f94bf723d1fc83"
+            + "a86d80ec8608183f46e59dcda34e6051a8b69d57a067156d21582da03e986c0d01a67507"
+            + "0615980bb80d582697431af346d66fd0b936f15a5edf9e67062c4792f373abc0db65710a"
+            + "74b64a984e3b588a822c96ac1a0bd51ebc7cdea67a73582c26b2005c5b2e886b5cb9d1a2"
+            + "fe8dff7833da419763d144c14b12e0ca3df6e19fc9adbe734a8d7869a25d5f7684a24dab"
+            + "d73400feac894dbbf2aa75f9ea2d3cdfcb9666024cff8395bd4c01488081a3b0facfbf5b"
+            + "82c0c78e9801b68e5e8c5f0e921a587de219f2527911e3b60deffc6b3bcba00ef2e93e86"
+            + "6ecc01e23304ba4cbe362c93c8e8374da81f1452bec07c7f2a6ffcbc9c621f0c9be3c0a2"
+            + "b5880dcc748541e40ab2537940527dc2189528adbe0be9fd17e2704c29beba00b3d7a469"
+            + "e610cc262e0d4b9fe78099822e84da9ed66eac2a567da9ce7a92d8767293bd45a3c29c04"
+            + "7dc10cb0792b224b0eb5e7d590a74a44cc10098595189d3089505b48e4af0bf61780c20b"
+            + "fc82ee694c1ec4b04391a5a302b8529433bf1061db6ab2b2373755f5c6f4e49e3d244ef0"
+            + "80356270a46e94234890a4ada01a26860ae657ba7483a3069d61b2328d9f9b9e9239e726"
+            + "a4cb80bfdb760e8ae3e6d39d7e069e83b872bc709298505406f73de6c1134c6c76552ba0"
+            + "e0d60322476b983ea0f83a37e3c2aa04a95adcdf70144eff8ef4490862acf728b7a8dfde"
+            + "3bbb384e166eea0baba1a261b7302855e69e0c1dd7074e600616c5d987e5b3d4aee7dd91"
+            + "73eaf6d8b63d789b104249790566d942de3757f0b2f07efdfa02cd1ac37d9e0da9ab1e31"
+            + "60b8ef80d48a30d9195bb984f18241afb9e788d81b589a00204f9eaa424dafe0fa18e81d"
+            + "414400b38db77366292a2a202e26bad1fae0e61dbb314dfabbfb5c3bc058645bc03de881"
+            + "c5006c66871541546020c5b27a4cd122c7e61dc1a82ab347810e7751ec76a68c8b63cdaf"
+            + "4e4095e80c78c516e78b298e1d01384895f73f4be1a0fef2771ce52bc16508bb9d1ba140"
+            + "518df0c26e87af648e95d216e035c4af1a1f90c0465082f97d966f5ebeb68cc94bf7c608"
+            + "39ef39cc0dc8975017b02bd690dfa805fab9e8c02c1c617c760dc07c3576708905d266c2"
+            + "5aa0e926e0b0f972d1e4bbecb75baf734f74f939d1a6c54a9481cec48ed05aeabd071fdc"
+            + "accd724446d4aef8c9e58605d9353dfc445af6f9d4f0bd754e8c7173ab52bd3f5f56bf91"
+            + "efa253df4fe7167f72586b2f5fe49715b57571f95bc3d620d5b36d42fc81d4046e19024b"
+            + "4193080c347a294b852e5df41a8f4ca0828eb9ea267fc2ccad50dcd4e2cd14af2cbc6522"
+            + "32c40d36c4bf6ce0f0e71f7b2c2ddb905796a427d8985be173e1b954252df929a59ff559"
+            + "7eb4712d23280bbb87ade4dae58ac3177b553ef9469543330dc1d7bcfa5731e7a6f9ffce"
+            + "5739d1d82a6a67165b0bc29574ee3c7c77b7d36787199bf701ed373cf90c3de9f406c5a8"
+            + "c382f0e2977a3dba618bbcf828e46f148d6bedb9bde07166b6dff4df32c7a781363b793f"
+            + "9f11aa55fe8babbfd068d33466161a3d25fb8e393169647ab6de7e28b5b366c817e8b55c"
+            + "61360b8ef55a090391e4f9d009cc98ef87ffa51965dce5455f1c1cd68d7a8a38e06ec8f4"
+            + "ba32404842f6a0edfd3644e490fff75452ca7c0fa40c9fb1b5ed68888f44858ec4926c60"
+            + "745a49dac5232ae4cc8151c12a586c88ade23cd4088cababe20ef2b4f5986f6cdc809c18"
+            + "cd6808667e8e6e26799fdff35065e90217b0c767b44d7ae18d2c66f51559e1e440126b44"
+            + "8113cf99090fe73644f5ee34b44d3b89e7e08f41420ecadb0b6744c77e4c7aa2a8a787be"
+            + "35c431264b193404b358fee6513962683dd02cfeec587d369c3c37594b4fcaf75aa2674d"
+            + "7e3850d34054b46aae9069964b4c067d37f4f663e21dec921df78cbb26ae40eb3805fdf9"
+            + "cf1a4010db009f1a8d32e67aaecd0a15a54c27f0d16ecd4932809b492861a063a9bb5171"
+            + "79f9c4c9e16d3a413b9bec849d6c22123efe07c860ac4c21c58028d584f5dfefdec531cf"
+            + "5ade3e5ab6b4c7dcfd08d59c86524a0f906615042fe24a53a8ba8f9acdba1a537206732b"
+            + "64c50afbf251feaf5b94287db89c85b2bdbe71269cef67ff40f9bd13a97a018c9597d937"
+            + "8ed078e88faad09fcc52ff9e327bc316dc9f5b38f9f2c97a36ada9b57dcc85a0f6b75c1c"
+            + "04d43db1ed2d5b37002e4c44bbbfc44f6139042deff50c1ee90fb979178437fcfa2561ed"
+            + "131abfe476a3cf937ba9565637d239efe875088098d265a9abd2977f43d84828e010ac00"
+            + "88de136c791ef2bcf0db608b08b7fbf856e19ad699cf3f24459f5f29e5c8aedfbf50c0f2"
+            + "e258ee6322eda0c134c1eb8f800ce6e51e4458d809938182fd2d99b4866acd6d0208ccc1"
+            + "c7eb0393fdd6ad37a8655987c2f0dc3211d65a5e2586c58d66b5743b47c6b8bf0b98bce2"
+            + "30096c054d53e10215bf5c3f370b377a871ea9c5473d66cbcdb81f3a4ae07c20ec55d8aa"
+            + "7975a3a1ba41263656bc3ce30e9cd91084087e3826cbd505289851e9fb025df72c0338f1"
+            + "568c5d5f88e0f8e2cc74c019f412b9fe5911e92875e01550c1d3fae00bc1de65cb32fb57"
+            + "2edb3b37f4f059c1fe17c2b1a71a9c086232747688ec6beb1bc47e2163eddac447070141"
+            + "3f6d5cf4f8ee9b10de94aa8ab9674a728ed80254c44241591c6b6d2aec099ead36a6b755"
+            + "5f83ee5707a85c50aa48b16b975fa354ec409ad2a505241314812da2e89c445d79e79539"
+            + "9fef4a6c23d21d106e347630f83728600a7afd592b5f16122ee3bb77c030b45b88728acc"
+            + "4c64caec3e68c84c15212e6371102c5aa110e83315b4ccc3f3482fe2132e88458dd448f4"
+            + "29ba064027f02029822f2d8b715b014262a1ff26fc3b7fbb9ad99e7a449730e3028ab19a"
+            + "22c2a02659931b194628cb3225b74e72923db77e706b3a1b5038e11ca72ef5a2e4d9d849"
+            + "6321b7baa61a23f7256c085e2f335f5783a71bbf639bbe0de48ebee4a3282ca195a4b9cd"
+            + "7cdac434ab4d34a6252e103559c7d5da26adaf8b78ec65f7208d5ed8de17233317dfd547"
+            + "00de63e548d9580b0c82bbbc345242cc805a6d16c8c774ddde350e4f4a44dd65cdfaf461"
+            + "4bdbc2f07e7d648bfe75b208c455f78f33ef10c96e3c591b5fd6922301b9eff2741013b0"
+            + "3f8deffbae8a391be54fbf3adb2e82c821dad090e5d1cc4c1a9706f6c26f526b59ea5920"
+            + "bd5beb0f797fca552892c84f6e4217ee73940804da4a10bd1ccef2c69ef05d62e418f15e"
+            + "abed1a6faaa755431e5216e320e82e211bc7cca779a87a8c194cf34f7ac87282fb979300"
+            + "4140e16ff2948409418a885b4a5c8cdffa16ea49b60ea25d5f25fd90b100ee1adf81681a"
+            + "9fc8db142d729466325eea688f1216d209f2f58ed12a77d74708079fd959881ebae4a35c"
+            + "106c9996a396db73fd57fc6760dc7e77ec0a11ec6ed99796d84482e7093e1262796a153a"
+            + "10fd8cb1ae7d095bb7b5f7a14d06bb891756a1422662b346696b52b5ba7e55a6a15c8442"
+            + "dbba583bb35fa8ba9767b095c660f3586d20901e0cc8eab6b278c05069f4bc14f745ec6c"
+            + "448497e0c899752a8bebd7410611f7ae2f3bdcaaa437e6d4d5ce0540bcefbd9bbe97bb77"
+            + "52daa87d67efa06c96574a591508bd5f612ceec5637db28ac9a87846213a701642739a90"
+            + "702f2a82cac103a098ff8a7d83452eb0c73d1ca8be74434f96b5928fd5b80d7b9a295c62"
+            + "903bf8b891e499473bdd6fb81c0041cd1c4f2c0519624b7e6514b97dc46d0734c3da6b75"
+            + "baf6e9e1ec6a0bbd19f7584fe106f242cb33cf7073225d7f21ebae5cf4af47626a568607"
+            + "1fa535ba0292b418821cfc881921a44dcd8a1924d628ebcdf6ae2bcbecbb8fcbb01a547b"
+            + "ef79e7299f3723112deb17a8c48c13ebbf597aad43614774ea6b0d94a04d01604cc69a89"
+            + "69e20c46b4aa4e65c86e6d8f1f2eafbac2f6871bb48f5ba95be5070c2ed34e971423352d"
+            + "631b916740ca924e32a3e37bf3b562973bfa921085a8ef11c23f49dcab48f41650c2ff05"
+            + "d01ea7d6c8a3f4cc508caae16d1cd22c6dd9b0ab3b00d17964dc49a0a3cd46c6de66b535"
+            + "cc21859ecda555705d836625493f566aa5bd466bd608a80181fd332b48f4476c00541cae"
+            + "890ffdbd39e7b031b9cfa869ed6d164adcd209d28a23020ac2d84418f8902cef15cf88e6"
+            + "6a61b462db846c1c286a4ec0ddf72b415f6df41cd8a1f399a8929c1be3e33d557dd94b15"
+            + "272543346c474a10f55cc789090994fada9147912759976478c56f9797b72c0e8ad11292"
+            + "2d0da0134c32d494a648dddba3fd3ede4cce6dac13fe12eb73cc6e2caf3cf4b0f605d165"
+            + "13e327c4d0f259f2b7b74ef12bbcaeac489dda8d9221a67ac2b2e8f7e6a3fa26e0a8c70e"
+            + "865a702327bc643c509942133e0234958829dde872eb1b9563dbf8410690dcdd1c2f6b33"
+            + "3112d10d1fbc139e60d6b28be628bf0f6b4daba3f703b1415b83234404e40029244b0afc"
+            + "7908b9470c2761c57f7dde1c2bcf0ded8e8e582d1d55e16bb3c488b21e526ffe79674346"
+            + "a464dc905dfaa9e31f509c4e7674d0867b775a2c05df3d24139cb630aa3a5427c49a9a1b"
+            + "77a9e2c9e6d31864bf7421fb2444e65c0e82828ec9df8163df91dba7cec6c9c2dea44fb9"
+            + "bb76e05326e00816f379ded481ebd86beb8e31cf2cfd5414e9b667ee1df4bfc1325b4bc1"
+            + "960023b9be593a79d9fd77bdc4645dac8cdea884e8e91dc5eb0c566ffb6d5dc0c76f914b"
+            + "a1f906fb47187a2b51b18b5ffa9b5dee44fb5190cfb0bfe7b88da4940edf319981090a9e"
+            + "1d47a490f0ea0384b778231974d5e00fac373a180987419f520d971a4c62e8dc275ec883"
+            + "d0566059cbe85329ea7063d4d7d8bf3f43f0baade5693c00c1db1d9f1fc43fea35b0e133"
+            + "5ebae28d07411d27a010b7bf5fcd8a31467ae051e12793864c9f8c33a1bdc9c345e65a7b"
+            + "82ca1c47a8a7cf1cd5a394ca0ce47d0d3a92266a645d52ed6597742597b4c82e90439be2"
+            + "62473e9de0520fab2bdf89d1da3232c8d0c41a356c62486931f0fef50bd6c583e52e9db5"
+            + "cec0ae3a20c5ad66303648c8e92861ac62024dfe483a9636b2300c71c0a362b59ff0ad82"
+            + "ab356802d6347be916066bc47be137a745aa550bb429c8af3890002bcd2ec56d62c83a34"
+            + "d2c7e0d6985f2dd9d4c5917f659f2fa05f461693d012a25b24bbbde2a97557895a3d639c"
+            + "99e1b8d91c9dc356bfeda2856d8ddc9e8552b436202efec45c586dcf51c98fc2d0996b77"
+            + "c2c620e5692922307c7e37ae8180dff59d9b751a04b8e102f485fe8676e78f162d36940c"
+            + "b15a0371da7cda3312db6b22e068368f90b2cd7eab97e391867de4e93f57e897f90d23e0"
+            + "67de85417bb01c6259e56c2c2e4236246f35f0b30dbbe836c342ed5123fa68ea3502a772"
+            + "3d212561e74b1127aa82def3052b2050fa6144e7ff8c462410ab81f2a291ab09ce7a7aa3"
+            + "3e6a7a72080a4d3f0edea734f016077127c29a205d8eb1eeb2bf9cd14182ec2e390e33e5"
+            + "e8cf086a3fa0cf5ef1cf6ca9df5dbae8aa0651a590e2b1f8d7f8d97ca9c7041452916ce2"
+            + "78669e701edb863b7eb4841f43cf89e53f50dcc58446aa9c1c4807ae7cb6923ac35e6f31"
+            + "7f77022d3bec14d2380ee869c2a5fe784c3f2948a085e8691151f09f9e1e8482d24de7ff"
+            + "e55d7dea8636fd6e7d7caf6fbc04fbbae849b9a9dcf3652fb5f8933f062a44ec5f4118d6"
+            + "4cf44ffb304c1fdd007c3be159be6190304801e5398fbaf83e89558441aec2e939be744a"
+            + "cf9444e44579b7a4948a3d4f95c0763de6a44ea062aefb1d19202d0c8cb28767e9c8dcda"
+            + "f558200656de27146d53295bb10ccb534e2aeebe0d79f8f7f3e9efaa7c21b2274d3d63e2"
+            + "047cf0760fa4c697f905207285ae08faff5b49058e817d2445e68b4672cf14fa18de51d3"
+            + "d18ea2377b35786b93b9549b5d328e565a4d7ff9a91ac293d881925849bf41c9df7478c9"
+            + "8aeb9d7ae2955a514e8135d62f473a54a974ffce5afb935d3ef64070dc0dfa797b278ad2"
+            + "980381c7eb53768bfaaacc4f67686d04d0d213f6fa8c4269e7d496ac9703b3ef2670961c"
+            + "dd4bf4330bfd51cb6c5b29725b87ec02f83998c6f8794e95ceb68d2ba476c5ebe4727e3e"
+            + "f02780ecadfe1398caef054ecd302857fa7e08464c3e5a17f30925be183629472c05723b"
+            + "cd5cd903c83049401da96c0e27f50f43657bd4a7b142d52752a8dd75b7ab99f3579f88dd"
+            + "f2d065db84b413286a5756edaa81f7c6c16e0be5065c13073c7d494a10912a005b25807c"
+            + "baed97792be1b31c81176218d3b83f13f233e138ef791268578fcfde4c7256e718d33d8e"
+            + "6c8b8a1a206ad6b7e4eec170e185487cf119bb14afc356ac2acf3a0bc4b4f4f89c790e35"
+            + "3e58506b25ab38e957414880c5bf407fa07479d301594b141348301ac76773cab2673b57"
+            + "4357262fa6410700e950d1997e2bb603422a4f3948545acaad7fc20f7460b89656ef45a5"
+            + "8d2f552c671df53132cc308e6a245e081840a302c789297cce8534e568d7d5872caf135e"
+            + "df67b793349e4cfe9e89f19ebefbfdaad8553c0d568eafa64a21e44d4ccd121ac03c3df0"
+            + "ace06819f6ccba41887c14e8a1331b1f58cc015368e1fb2463aba6db95604373114b19b9"
+            + "6853ceb93078e345bf857b347823aeaa0c6ea2d0f0380bf1e614d70ca14069b75e5dd596"
+            + "f79a1adfd41fd6be048d50d1fe7a1cedbf49f2e06000fd3022aaec322fe384d78e0b784d"
+            + "69eb257a1b5fd07463d446b2be9491c79ffcab9701272c5cacb14a9a87aa46a920b78e47"
+            + "5bb0fcca727d7987c67c71091c4c9e639c536625955d19bfb79a57d49731dddf77c25ae9"
+            + "d2af26a67074536eb75282509ed6009126a88facbd12d159b073ed31eacc07cb1e8805e4"
+            + "1cee8e546343b2aa018520a15c58c515c4d6d4480b1fdf0fdfd4c7dd2d5124724d2ae3db"
+            + "ffead157c5e85d3420d383e84fbe966ceb1760dc29c65c7bf3b9f922b98b2c9e9bff5c4d"
+            + "a4c8a4cb1b9d6ac794278fba2f9b4e7d5f13d0fe524ef62600b85454ce22a23e64258784"
+            + "f67e74cb2b2e3ebcd6fceb8f830dce7fa8a067acda25cf66838d8e43a2b503c0d416af6f"
+            + "2c0380702153e6d4a95c4dee6034a855248c46b6c646333e4a0d40bef18dfef7a087b904"
+            + "d0215533087be78b695903406100d48e51a721b8c3ba3c833580cfb6580390bf329285a8"
+            + "afdc6e7cfa54641d871a8c116ca5761980aa4293207c74bb88a95642733b046c2395eed9"
+            + "143aeae81fd7d1b66d1b91ccb6d5fa402315bb496ba52ce381e4d285950a43c53264a56b"
+            + "9fb5e4e135fc229715889a89b3cbda934224319b570e5b452745decbaa8d2e4d4729624d"
+            + "37ebf5a331a3e3083525e9dc4aad677936183a600372b403c80a04feccb178fbde3826dc"
+            + "d275bb096b6429c8c0bacc09dd401c68df3ed4e7528a5e4345ab2d071f851f0468feff0b"
+            + "bbf361dbbefc076a9a6c740fe2dd16be72338bae45cf624bc00766b8ac51b2db11ef7d50"
+            + "6271a5b6c3c376a606e988c6881d0c1b3b968058223792039d0b1e9c849cc2b08214369d"
+            + "c0e91c8ea5b6fd087d1a0d71d6335eab4c9abd4645914f252e0aa7459071b0bdff263b89"
+            + "3c35d136493aa4ab4035e63ce50cd8392b98e0dbaef300b5b96339d08fc00809d593bfb0"
+            + "5d74d389ae722854e716599ee841fe41aeb34ee811ca30f189f175d8a06b5151ccf35ce0"
+            + "36a8fe18b3f97647a17e730f8220c5cb3b43580c6863639c7a43684bac602d20387ecf70"
+            + "f6799c2e8c4cb1cdeef1fc13c76bce9539928e5b860713a86d586df751cef82837fefda1"
+            + "a289da5abe79b77bde4e8f4b6e76e20b5507e632663ee1fdfef1b1d40ada4c97d14533fc"
+            + "97f457a929519fc611bb305d0a3b09b5633b9b7ee2200d97515d12813236868299d7c8b2"
+            + "83ad064f26d1824423ff8b70adae9b280ce3541753a6d94c3e8ce173ac14e514b287fca6"
+            + "8e28bb038a6ac0b2b5d949492243433c0b386e3076296e15760ed5786df4fdea9d6c4bbd"
+            + "86269fd48455390ef0af422b75f2470d57a4ccc1413ad77f0d2b2faf733ab3952a97f3f1"
+            + "8b8000acb1655bcd159ab8aaeccff7c4dda98bdbc6fcdc71c64f2d22d173191e42dbeb1b"
+            + "18c3f30cc26caf75b880f07aa0a4454974ac07de1e293940a179e30d31b29018f385d9b4"
+            + "1d0e4671ffc30bbf15044cb856e44d2553ae294f39917961687423cafa89761d113b925c"
+            + "4f6b21c240511c2fbacd4e086723aa930f35beae975df7fa2fef1c48790357d75b642364"
+            + "8a4f56d3a9ff26b85588a69a50325cd812b9fdfc70c7f16a78b5b13c2e11e78ca213a075"
+            + "e1ea48cff23b1b0bb73580228b1d16b311f90a33ba8e09a6fae75930d353b3c9b57b25c2"
+            + "be8d3962fd8ee81a168762d73fcd42f444228324394238d78626267e3b8145c73cecd6ed"
+            + "a56682eb495d13fb6de81ec70197b02c5ec77ebe30c07f0a530a31d66a36ae25041774f1"
+            + "25bfade76f33a985857c9b2ae7194dc43667d25e8fb4eac1e2d84b6137a64b5c1ed392df"
+            + "d430b66aef559a621a4e0c469e908634d94e39337beedffa41d7638d3dfd432faa157898"
+            + "2f32208038675a9d9846fd6cf2acecd22f72d07ad0fadce4791478780c1d8df0ffa59aa1"
+            + "a9e06827b053cd51b5a84a3b3ca459c31d2ad48d0a69782798308d1864e097b04e3a0440"
+            + "42520a7bbd8949ef7d8080ad396ac086ea1820600015d0224222b6fdb9d5f62595e486d1"
+            + "f59fc9e8d83f0bd777bd0d92bdcf1aaf28d4f1bf1a0add96a23457eb053f383cb0a61920"
+            + "0a7d2735e0d87e8d1f4af946895ff1933b7ecb909d85a143a78ad7d113bd79ecb880d7bc"
+            + "ef0633516e3cd91aa50d9254dfb1a8b6b85d648d9031777ddd0c5d6a7fd01727e89d308e"
+            + "1c1cfcb576332e0b1726b724c6dbe784b450d81d82afc417d288c25febc9a61c99f475f2"
+            + "b7b788dd988fb929e2f4538436c08038cab0cb3770f6cec07074fa23e2b20204bc865114"
+            + "203232b492e0681a31dfb3";
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig1), sig1));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig2), sig2));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig3), sig3));
+    }
+
+    public void testSignSHA256Complete1()
+    {
+        final String[] signatures = {
+            "006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf804a4744"
+                + "898d77fafd84eeb618115067f23f5f9d5d882269b5987de5d5428bee7784e049da83c891"
+                + "856b5fb20f17e1ecb6873c3a87e1a630e327b36981a5622aad6d9f5c557d7132a99e3c5e"
+                + "ffc1abe17560b3152bbc98a0dd701b491e62e281824c43848a0d958ab132ea905b4d5a0b"
+                + "f4f65eded991b01b3839cd188a90d76bc2b7e96be674af7638436b3a535dd0e8e3501e19"
+                + "34a674671cedefc300e7add098ee28caff5d9606f92e22c7769720f54aa4849528cd4837"
+                + "47edc348c81eb8f159b9e395166f34db8a19539dca8f00c1620a45920a1412a6ea88f734"
+                + "a4bef32d400b33bd98d97054a6c56dd93df2b3a316713f67aa59051405c5d5d19785fd47"
+                + "f6149e3993e2476c87dae826bed2f5319878cc8db6bf515022a5658b06e08e4900a93935"
+                + "27765d9fa852ae7621ce5aa0648413bba1c650ba3a82d1863c17785f4b65e3dee8c385db"
+                + "41331f6f1ffaa7fc0ca86400aa4bdae6fb713d4445ebba37b4423c30e601c567f90e3f14"
+                + "31497667f9102256a69876d47202dc6f734e27af4a69e44ca7959d50704b22451bb2bb8f"
+                + "8ccccdc46cee70c14b6afacd715aa25ad38801e0f556e2e5cc204c5a995eea1ddc65c385"
+                + "512b41294b5912ef3e4dc94ffac6294bfd7acf2415bf765859cd9fba85b8f451809f4283"
+                + "42e5b56097122f1fc7a022b8032971814761062634671375b2052b06471391653c5c3214"
+                + "dc96ef984a100d28521f2a28bc6d7132929f8eea1481ac636bfc728c9c819c193e98ca73"
+                + "3497c72fee51d5d822d75acf9944db04e5d1cbaf50f83550c6fd4a360cf30621ff2912cf"
+                + "72b9aecb43ee8ad5985c2241253d2a5b265e64af505d87fe317517e83a81fe07fe056fd8"
+                + "db3e41dbf48a161b55c5d47278c6f9c6f333fba22886632acb8c2f3ba8263052e7e9a0b1"
+                + "02032cd9de760e38764f3fb6cd3652eb548be64fba09caf0c6ecc065430a3c7a65810213"
+                + "e42253966b9a8ab1dac361021d0c55513586596462c99a1f5b79e8dff022ecd0d2338835"
+                + "6f8bd3d22b28307f96d84a3dca78deb2ef353a16b706df68aa468a8f9f69f97c9bbcd358"
+                + "fa980f6a356f1ff38d2386ee3060e63d104193a7eb3ce1aa69504f5fc565052e060f229b"
+                + "90c9a6ba81924aa582087c3c2719cbb6bac10aa57a5f487b9b3b433eb2bd57f90e156604"
+                + "23d88ed013033ec1fc50990df8f775721a40044dd89c0b5913d7e7711a980cb014d2a2a0"
+                + "07e3d9968d5e3454be6138badc5d9efe53eda4ef225b1fec37a555a7e9346007f9bb7180"
+                + "0d128f6da23ecc6babed12bad95b3694f19921f7135a37ebea09d83abc314c79cbf3cd12"
+                + "64029f713c79586c386dcd20e6b18c1d7bf196dc7bd2154baedec9d9ebf262ca81bf7739"
+                + "ad02cc6fd58924d0f526bc7da6cb7501dec26ccbb61248830ddabdd60df3a14b4c0a1d45"
+                + "5b24c47a784a8b527a7c39ef79d08247b9621baa7d29ea8d2eacf77f787c9d028b95c7c8"
+                + "aae0ef9153fecb980779fb5bec3015caef9f5a6941a778faf418a0dd4da88c5b10e8c651"
+                + "08de5f2b00ce093c50bdab5dda9bb571dcc88c06196f1ac23feef71feb5940116a7c4842"
+                + "59a214065afdcc4b56440a5d5779574882aef274dde27f411967ce80e511b1870d2765ab"
+                + "31ba5a096e2a1596a1b06cbe867be177213d920fe60bf08d22b4dbaf8f2b18409145ca5b"
+                + "b8f8e1bd87cc2cadfeef75743affdd3288ad8b0890c472d783394a85316f08e722f8b91c"
+                + "a88c8dbfb764e81c2d7f71521c339a4467bef9a786b5bdaf79e9042cef62b9e49023440f"
+                + "e37e4e4e50924275c2977f48a5fba67e901aafa9f9c2457030920fc5f6848c6f637f312e"
+                + "b11264c2f9928dcb697a308ed8fde7091720a53f1054cb824bdc8d6e44661c3d3fecfa0d"
+                + "4037f858a5363300d76b44febc85ce8966c014579eb6905522db9b679e29f995b36be3b9"
+                + "412e4d384db76c6764748f7c5361750d1f5dc2d8aedaa39f2d58aa6559fae1f63fe85cf8"
+                + "67923bdb2643716e365fe04a507a3cd3518dfecda3e51b861090dfce7d7c1b3cc6f0fdfd"
+                + "9b64364c37f9f1293fa60c8f5111bc21118932b0a7223fdb680aeabfed18804594ffdae7"
+                + "4d289c1ea58a33cf3ab8807deda2c9e1d39eb78c44e53afbdf0deff99b308cada91a327b"
+                + "6af600e51ad6214f4d16a4d5e0c2d84ade4bfa973dda97bfa8081b1cbd453e3d1fe1a7cc"
+                + "582ac459eb4ca25b81586d17b5dd50f7fa4e015073871aa935b76730399ceaa99b71ace7"
+                + "1d729742d95ce95a93b699ce1a9aba27adc797438742ea8910059867c28f6e5725b66c8b"
+                + "4ae938ab5fde7e1b9b8799b5e5fd0014437ad01691deb6e970fe429d0978a797e6b973c9"
+                + "0cbe55f57f28674e11c0118297f7fba8f400d40f82bfb61abffa766ef724547a9fd5d2a2"
+                + "f64d8bb33d53a7187b27f3310c0c772a93c920cf17b78726a0f2736001c2d7950baca885"
+                + "e2a212a3062d8ed9c4c4bf8060d4297133be148973bc27d6e3fd2d7c489a34cbe3a9b7c4"
+                + "6de2a6db68aa2229f8985c832ea2b9b73177013a69a12f1f0f5e502537504675fa47fecd"
+                + "3b27c580f7697d47e02235629824b363c348943d28c50fb200c8fbaa0e9b801d692112a3"
+                + "8cb125398b5847776b4cb0937f857f69ae3c82bd969c9e2a51954af0bc9529fc180f2155"
+                + "f7f94fe79799ff878956a51dc09edc775da42908b94b2ad3ce16cb2f994b5486b332caea"
+                + "b4409d47f6c39ed00c4c5d460f44708dde11c6fd26e1237581a2c6b81741446b0144fca8"
+                + "603ead4acc7f49a261e1ecfe797203f607eadb54fa5765542d39de25d0b2b3ab04411b03"
+                + "4b31eb324c00f013927b24c33d70525f410498be2d7358ea01474861341c508622cab0f1"
+                + "663dcf17beaf051071d6545190452d78252d8968b0d8cab0257bcf0ca1609c7a99189675"
+                + "c5e67460c3e20da7c2ccf55c8e447fcd46650ed010085025437e7f64e396e89cebc106e7"
+                + "bf6c54b886c8197f19d9a7245be704ef119c787cda0d632c4b6e3dcb165a2c22b075909d"
+                + "fb80e26fdb08bd57ee5222ec65ff19b344e0e81477f50e2b8719d1d9a3c1a55d5460711b"
+                + "88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d"
+                + "467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d28"
+                + "1aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "01994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c299747f"
+                + "908e53b302e4bc0117975de9992ad3fffbea32b86ecab009951f08d2d830f850796c3c67"
+                + "ffbf48f370cd19b6f8d6baeebd2b8b13661dda7220559e211bdf5c480847e3c00fa13252"
+                + "edf3da2441f05e9d4761138ddbca836cd39a2f444e87aa676c266eb2c251742b59129c16"
+                + "4ae1776547db60fcd71eb605e798de79fd48b54736511b7fe1b561eb28f0c316ae7697d0"
+                + "65384c5f98dea2dec130352181285786b091f2b166383c55a58a7aad54a1520a74a74515"
+                + "aef636deb77b9fd8fbc8cd982243d503ee360b84303623d9280f9b379fb8f0183e3a7f87"
+                + "698db93af07a79469b25d0c90c79f389ff1247a968056b8e9f6c123d0c3455f41c786726"
+                + "6f4d035e01580f0f861b78df39f21fb4448e61c9a30baa20ac8694ce84606c7e6322868b"
+                + "0dd3c3439f7c01d49603b5d56f27dfb5316ec1376583d578be061f88bda49568d71f3b6e"
+                + "1d72013fda003781c1e3f7a2d84f0a5dd3b1a06c56cfaf64e6f1bc1ce02403c5b0f79300"
+                + "33dd6f813ebac9335865d03893d99caae239c17ff9ede587d58ad0042357ec654e1d4992"
+                + "ca85fe67435e94f7202678f4a0bb5d98066a8c34a617dfeba3d60bec876f6f06f5c0bb6b"
+                + "b77142cdf44021d5e7d44e2fe34f4e72ee73e6cd68d4cfca61e8e7caf64230fc9b270dca"
+                + "d54de7cbd7c6deade777a292088ad1c76a79b0dcc26f4080aa39967db9dcd659c2ec9ecf"
+                + "8f5a7dd0abe0dd74b99cb5cc9a17517a053652a8afbf8bd56a76304071bcd9b0ad1d0df3"
+                + "6f65f17338b83aac6fd9900b1ac613c3af74f327eeeb749a53f6777d0f5e71f529c54346"
+                + "5bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae46fd0b9e1085d56d"
+                + "7ccc968bae0dc0d1f94700f788ce48449a90ab4c2727d14224b0d2703e5b220b62862da2"
+                + "af2cd7eecd7d4483246ab87612bbe1401ed07decb7337efee2b50bfb0690e1d76b81003c"
+                + "0db62b9be498aa11731c0186bd68c30566c4ff56957ae29cbf197dd7a218d753541c2f63"
+                + "2772219c3d25c2e900ff7d685d4c3db753eb15eace8e4871f2b4a49d7e67b0bec3c8f649"
+                + "c46183ae53b516454b04e1a3f3a854de28469ffd213b783fe0128a5c12982d2993d51771"
+                + "97f102d4bd26d932ebb0d5c3fc6c93a57e185fb719e3449b878ef63ec3ecda13b385bc82"
+                + "0568baba5a58524e2da63a0628f5ef530b5823312f0e3cd0270ba3928d95e89a13975979"
+                + "319554c204d4e893fd7dc3d59d14fe806a01896f7b0fd7d445dacc08a39b79df765ac64f"
+                + "c2a9442ec286422e0af685e1ab72394934ee2dff1801a58592ac207a9f15f32db4802e78"
+                + "1beb41be0b97653f1c36daffde0345e6b74fadce3ae24d7ffab9efedb9ecb5cba9592825"
+                + "b605d379a3b32f0200145f1895de8322ed32fc26f338718d55a8e278945c877ba42c462c"
+                + "0cbdf7975df9583be83a2db540d8d9fa6ef2db037c63cdf6b6f42ecea0f2a1da1b4d69ca"
+                + "ec25420fc842a4c57fdb3394886f8d1cef3ef6762bdc6dea1f93446273d2f6926a920d64"
+                + "1f58c742bbc04827384c6ae02e96ed314d515903936a3dfe118ab5b391325cbcf3c8b020"
+                + "df1db6fc5ba5d3d6888d64bbba1cb963466baf66070edf20f31af6d24da2244ae329c147"
+                + "60b1f36b137c75d9ae4bc5f26f7c747fc373c20ac4d4c038587e1a92dcf60708752da440"
+                + "5f242b9a024b9733709b4125cefa5709b210dd5a96178af6bb017ebf8761e91fdb27f189"
+                + "afff065ad0303ee1340b62283de4604f329643148fa7b92faf985fa64783323895cd914c"
+                + "b6c88c9f1d58baee856151e1bb042fae4281f10a9d8089304e7e05d8420197caa981907e"
+                + "609993ff7f4214327cab1d64d0bb97febde0283b3a7cbc788bd2a73e70f7f4495c380b4b"
+                + "8a2a49a53c0042ca04b92ebcac3aace002cea307286cf66357e5e097edf4a50b8d14f56f"
+                + "d26a2b22d061ea4a33d54472be18efdf9a587cc6e93ca31ca010d7389593585b77c6df62"
+                + "a97e7d0d9d09cd1c397a5a7299c13dbb449f8d54d1b2eef2eee0d659d0c94a8bf7b3c5da"
+                + "71fe6713b51a6fa17a6f0b2caed679a15ff38ef120df61704119d0cb21d6a17a15fe5393"
+                + "5f317e5b77479c4c18eb2f9dfe757f87094cb430739e23ee4066644a2232dd8b0b140e22"
+                + "5dbb8914622a4fbd1461f46dce6db746e716328b56e38e166e48d873ae6a1f7c08b4050a"
+                + "afb4b21f992922fbb30c0c9d9d59586036003d2f2d2cd78574f7f68e2f48833a61574a09"
+                + "7ca3ffcbdf688d217619ebfc5b5f3ba1b2c755c2f702eea34bd6ecbe7d3e1dbb0108152b"
+                + "c8822714984cd750a09520ee27b090a28a7306fb1aa76f5235283013bc3fc8734dfe644d"
+                + "31514cd1517a05393b5342182ea2b2cc2a9fdbb502e2c2ec2dd4ff4e84fbcb28e3d8c07f"
+                + "ebc2301119fa36cc1124e08d4c5e3744c0a1f9421bde2b81a2906245c830a086909f61bd"
+                + "3c205219bb0f63cb42afe6517db96f3f5d939cf0ced8d8a7ec2656c13230e998b04aee35"
+                + "673d3b10acc64588e0d3e74ed61f8ca878fd3b2ae919659e0a01783f9c3c228a1c24af30"
+                + "ac4f5cce260de21bcf6879e8eedca12908e6440f3054d0fb69808401ae1df88fee5ccea1"
+                + "876aae0960860bc46b21504c4ddad287a4b51dbc0490d1c948f8c95d7f793f5f64913561"
+                + "38d54641476fcf4fd0cc70a2e8e1343c5ed6f0d49f2679a9c12836a33d11deca674bc60b"
+                + "01389316faf9fdb093f64937f134a3fb592d40b3f6af83df06e834ba00d9eb899506be4d"
+                + "9bf25871c00395c3e28cd168fbd6c6d4e146f5bff31c8876085bdc09663fb8387378293a"
+                + "8dad0f93dd623a2db5537210b2762ad77a51ed8bb9019185c001f9f1e0de6fa0d80fb23b"
+                + "2b7bf8e523f711e0ac88d3a98a64156219a59f5c2df88a61d37eb05bafce43524857313a"
+                + "22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307277b"
+                + "9a09c038aca00a99ff63060dad783e060e4cd9d59c9f340e78488b0090ef1cf16991be0f"
+                + "f81271288c9232be17b7d604f875dacfa6a5b2d2934fff8302e88ee8c6fec5456a05676c"
+                + "14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d"
+                + "467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d28"
+                + "1aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc8e3038"
+                + "926c4b98780fd65b4a33c723d041c203a9e1c5c4bf2739b45d5840870c72ca7ccdcd6916"
+                + "d5e6b5fbcac59fc5b6c05b1559b7893eacb5b48d8dbd5120c2ae99988b49d7c580ca3ce4"
+                + "673f8700b55de80c0dc68b914f800efba17bd6baa3344858712ec0cdf54ba393c71d69af"
+                + "46d0f65640435bd0200e943a27ea01152bdff5aba3abab82b1a2dedab39d9f0bab23407d"
+                + "a278fdf6ded66ff8285473a9fcf43d566cbbf92bd7c45cd3a56f52223439bfe06a8b136a"
+                + "a35c76381ed5576e8e9da70f0fa7919b3d1445abcae14473558434a927c009d1568da24e"
+                + "3c1d93256a50c794af5d165e432d777d7b83b40663be13b990c85e602d9ee85e65f13a96"
+                + "d203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c2370aaa"
+                + "f4ea6dcc2b226beaab6b814e7021a6bfb9536aa4745a09a375468a450c990d9fd04315a9"
+                + "61de8bc1d2cd717a4848cfca8f8e331e97ea3ebc55cad91355ef793bef8ec9436397b959"
+                + "559560706d1b792d3be14de924cc04f71d99e0e3163cd41669467b781f2759ec517a58c4"
+                + "b7a11ca7d13142a7a5be04f87cebe9827a2457cf41db224cd508a5ffcde62a2a54ef967d"
+                + "12991ec9357a57333098df55239d2a2ba42e84fa80cad014d04b138014767c5be3877a6c"
+                + "130a10f9f5c8e83ec65628de86f6904f7dcfa79686e38922b40d656f54e7b3e8d8b22fa6"
+                + "975037cbc50ef71e06193ffc698a42f4da29427c74564bdd11dd3d32803d6007b1cdcc26"
+                + "dd3095df49e3dcf05fdf4905b206c00c4bf7a8661adbe20b04f8a31134e2f386ffde1cde"
+                + "c317fa5aec343d0c832a754c5348b1a1c89e79a57135ed80c4f3ac3eb0380c475b80e7d5"
+                + "e4f3b3af94bc599e34cba6cdfe390497f5b9e9eeffd54587c0ab05606b1c90ddd35d1e61"
+                + "8db21d6044b214f8453faa84225a251aaafa4df78b27067c877cb428f840b3199fbe73fe"
+                + "1c120085cffaf86a00c317391f30312209e6d9cb8b4c813115dd1fa0517928be1aa4222b"
+                + "9f64f6ff8eb01dc2ddcbf758c95d7c077be001d182daa82b956c473e32d2ec75ba61e998"
+                + "b10ce5ad205b8ceee821f36b2959f083749d924e7779fe7d83bc6146812290822579bcca"
+                + "0e8b3b550b21214cf1e0e5c67b352075b9c5628756376feb6aeb779d2a59352c5a2a8521"
+                + "bc20fb2cbda4c3953983c56394a20b9004769ab280aa5effd5dd6c0380a077767c318e5b"
+                + "f07fae3392b1cd42a5c4f29b906d8ad611bf809f1d424eb5fa3f0e6f7a0c3c57439aa22d"
+                + "650cf4d9c4af79eac7b8193bbccb75a94fc00957db274e4512514351415448a7f2917fa0"
+                + "efb75cf03864b5e74c82d166d0682da6fea6c3001eeee520c85d6565456d97db5a261466"
+                + "8b2ab4d475f97ab1f5afe8ac20b46a2bc8d2707e836d0b67afcb25b7be36c90608b0f756"
+                + "cec5c3bedbfaa02387bb5e4dc0bf7ae4d42c0548641250fd19c7a256d5f23879e3144d7c"
+                + "339fc1964ab767c401769dc878df1b388110f46071fbcf1390cea1d0ab42f860700495cc"
+                + "8b00bedbe28e6afde5e8421a991159e1f57160affba05ee2c45efe882fed3653d1837f38"
+                + "9755aa658384ab68eaacb36077516507f849a136ba5dafa4b65b80975ac9f8119418a99b"
+                + "3e2bd518e1e7a9eb8af7f5eba849afba85260e312839c8f3020802f5b0a09a2f21dfd24b"
+                + "86e16a30ec471bfef6675e72ed2d450309144da61655c6c3706bfd3ab7025d37daf17d0e"
+                + "852ad884a98394ff7a08bcb63f6c3a2b1c9a06c494f8bb926335a31bc1684185e4c5c736"
+                + "3d7240ddd7bd1700dace25f48e548ce21d65f7decfcf5baae126d7131248b67ce5cc471f"
+                + "e815abbb40798bd53b8e25b9559eb0c556d18f42dbfb7c3b42389d09fed688731e20befa"
+                + "a039b9fed20f6ffebbe31e01b543be905e9a998991fb44e82b8e6bcd88d09ba15ee58b38"
+                + "0a44ebc18229828a55e37ccd7299afc57eec259cec93baa8703f4fccf1ac4f0b689d9ec7"
+                + "b2d31d09893aaa6e3ae084009cf8ed266227aec31d3eb0fcb2725f72aeba4e09a806143c"
+                + "0bd3b265310831dadd6929909d7f3f6d17fbf17ec3888e3c2789dfad83b43344577510f9"
+                + "74a39fe48d922c2a22c6fafebfa549473d9432df34836163cf60a66b4c24a94a0917600f"
+                + "92ccde90b81d5e5fd70e567e8a89edb0560e164928ee96a35fad758f18f0571959626930"
+                + "948bcbe47cfb642792ccd83bc11d8f883b6143c8eb738480131502027ca456fca88d89c1"
+                + "0dba6d346978d1b5f60b271dad4df49e780c1ccf71d8e6a6b3a1d0ee2e6d33b72b4d7112"
+                + "f7fd8883031a882189eff767a5d42b8ad26f8d386dfdf8005da8afbb4f8ab0d94c416c5c"
+                + "dd07ec8fa389e3d81394eef2198ea5876d413ff67606a551ac9afc117ff9089b50d725c7"
+                + "1ba357f770948fcff0392c314dfc777f37d2317839a443314ef2b91f69276696de1724a1"
+                + "f207f94dea2910a2c3303ac0f89befba9d94b3d73940e6fcd7142135577aeec7b419c655"
+                + "c8e85830d403fce77a59e5fcb7701d2f24716b63d313a6f2e27ff492d450890d61d5f92b"
+                + "b9962502d1651467a795fab27c874b4ffbbe201ba4b02ad79a1fe2acb7002b6056bad4dc"
+                + "9433beaf8f2c6e6b0ce5b1ec4f1d3478aa2147ba2d65e355638bf465fff4c2f634043867"
+                + "5d8e7ff8ca30df1e400260bb09f11169db9cc1575dd1c55e8b41edc066f770f1a4c9ce43"
+                + "a4927242d0eae4342e2220ba0d5b02fc1431a720f189116d0994dc734189feea865e49c7"
+                + "f0f6c7a1bdf2934690c2e6b03b3d4c618cbca4cfed4e3a5bc0204aa307b023e24f17cf41"
+                + "cb8a0a9df14a4ee68cf62766a0d49a07e662ab75a4bac93db013d911878947f9e2c2399f"
+                + "df7ae9f2c9e421f1751f5ed1abdb4a16b25878b67f4b56c23b904c58238c944f61f837d0"
+                + "0baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d82473fa78bdf8387b0fbcb02"
+                + "9c564db2906d29b8a1d40b1d36ece89f25c0c6a42bb045058b00511ed549f6cbd1b1e102"
+                + "ddbc84ce321c841a5f8dcbeb8a7bdf3b839be4aa2007955dfe8862983d252d8b79a33e9b"
+                + "1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c45"
+                + "11ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d28"
+                + "1aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647489bbf7a"
+                + "4c5b7b7904296d3be50258e80a4ca6b59a6a22a1c63576ee508e2407cfb66454d3e4fb31"
+                + "0c896480822cc3800f0769622463743c17601947db673131d542d7800af123d2ac2d7058"
+                + "44b093404f2072dd430f48bdb09438a21119e1b3730ad685a39c4e8c67750bf56795a43d"
+                + "93be6d5d6181d05922a13aa08d95fba85bca65919b91eff118c81038214df51fd883fb56"
+                + "e353334d0e3cdca46afefd63780c4a7919a28e3e827f67951feac797e39e96e7c3ba1919"
+                + "6f0f0175b91d5c7c5976a1a1d928fad412c4e2a9cd8c841970fe3055797a5dbdaaf32f4d"
+                + "e79602dbe35d0fe38b3f5ee7dbe1f6942ec427d8b15cdeac92278b2a105e647c3ad49169"
+                + "48d22a6df03aad05a9c5f476bc4e70169ab50710c2177cd78598fea88681d34a7e4f9c4c"
+                + "b8ff3fb1dcea06f624c0fc879cc5e307ed78eecc6f9aa44d382016ee472ebe19b30716e5"
+                + "5e380065ba32927ca73bfbdad670f191876e671787e859cca6e4c895dda8730a40d0c73b"
+                + "0487ce327eaa9c85815d308ee08ed25cc5f1b48d302eea1568fee7d44f8a40b4496cbcc2"
+                + "5b6dd7b199323db3873709c2e6bb8dfc7edc95724b541840a32d6ca4465e335c4bc8fdf5"
+                + "aa5ebdc3b3ad9874954771a734c9bd9ec525c081b95802534cc71cab2d9a3d3bca09403c"
+                + "9d6dfd7d7d35c0e4ba866c9ff837c3d538b270aae8d8152db295b20b7a19e2a3aa383163"
+                + "d9162fdbb7ef5341887a5eb94828253f6977a251607fa2e6853159f0b64d5c8fcde6d18c"
+                + "c6dd02e72f7ed723b74da9f3129230a75980bb9697bbed6475f974c20b12a02ed7313ef4"
+                + "ca5f473774e0e4b5f520cdeb4fed526ea10375656c18b349f984119fe43133794c2c7e26"
+                + "72503f150b5a740c04e3a763af2ee0c3b14de8e0516222ae91bf50b7e5cf92f5cc6df8ff"
+                + "ec7584f88a9deea3b5ab4a8d8ba79c42b9bcb028ec911c4c93526315ad80b1efb97e11a6"
+                + "92c4160d6f9f6821c0625889b524fe437112df5845df7fe8d9ebf47dd29ec45448dbce81"
+                + "dc4efab911477ed9408cb7a15978f2cdd7b779aacf3a4d4f44d0d87b60e4bd6dc145b0c4"
+                + "0fcc6ddfad114281d87b68d4f51e9bcb317ae7436f2cce963a327bda95e9e460ab660696"
+                + "a528c393ed21f92e38402dd50f3d643563214546d0e4037501b3456b4dc5a02d1e4b9b59"
+                + "c18d0c341170d73841ff7ace5c7f1dae32da6bd6f97adf3a52dbdb30a46424abc17a4a89"
+                + "340d3345ebb08fc03a7ad4578a64d1488b879823a35afbda39dd9ee9d0ab8ce94bf864f3"
+                + "cb1105ce74bd99e85b6fdd97fc3c54393ce901e43344d05f31a8376265777b18d853553b"
+                + "95a11f2bb099be5d433fefa7c97b8d3b229e423bcd1832ce5bd731854fd5e31d0ccb4421"
+                + "e4f73f121f4b79404d06786e3318fb337dfed40359c63aa685457ad21b69d365da9ba2cf"
+                + "1e665d02995180df2f35312c4e45c8f3ce7215be2ef053807341c00acab30a0c7dffe8e7"
+                + "5fdc71704f35f13de8ac21b6e39482271d036fe5b8fd6020eb0ef06f80490915de57255a"
+                + "3f9a31d2d6750f86ba57ee7320cde75c4a49bd4d5efe393775dafe2f209abfbdf7762c56"
+                + "0106006f4905dc938f91bb88a5f3f6c0176e38b4bae238f34b2f1319c2d48a8be83e5395"
+                + "9b26e6109f8ac292b9a88cbb51dd3369dca35e58774a9a02d7fa47d47557bdcec261a7fd"
+                + "6dd60437fa9af3324a36ba88c0ac652c3760f5a35a585245a0c5978591b1ff73ac058ed4"
+                + "38e359b09feebc290d3d3a5ba3670d62887ee338099e94d14fc40257e36bfec7d5f3b192"
+                + "062a003965acd650f8285c2d9b5a1ba46ac46042b985d5f631188b2fe1a5624a92752d33"
+                + "17d177089b68a35966fa021dd0f8e4acbce56d1643650c64ecba2243431c06d4065cf738"
+                + "e5cb0980da72a35425f568e3e48e9c7b73ec5394e45842607a4fea3be2853ba6bb6b3d27"
+                + "f7af28f0e41cd6b42ae977f09bf4f2a1d594afcb83a2f0ca20c7e442bd7052a9ac85fe67"
+                + "cb537250ce91a255d3b7fe4b2d052312b839aa59d306afadcfd503b303bd2dba7da45ce8"
+                + "dc8eaa303a1f9495038a3e2fb77e4bc35c5399556b58004d9cad13db1c1fe7ad4c66c888"
+                + "82396aa36c267227441cdfdb5a5c89dd23616a7563b5249483ec60e25e36024b5251ed6e"
+                + "0557a7414180b084266c1d731d2e42544513fd2d53d4eb964ba1c67a4e13b192c5e41f5c"
+                + "5b4160e8336a2956c1396e55b2c417fb5855d4aa56dcfc54f8e19c6eebf9eef09ac5c387"
+                + "35090d692b837cd08e50738e106cdb3a5b95314744b8d67bcf40645ab09f1233559173ae"
+                + "a688279d0c31cc885227e88bb3e02064762052fa3b0e69f70e44dcb1410ac1fc604f27e8"
+                + "6796c6d3819c82d0971df6b5cd89f2403623c4bb252e60af6717d4c2cedcf5dc96015c5b"
+                + "700e7e915e60aab7a1f85bc19e2b6a5307a51988aa1abab57a201a488b7d8f18716f0bf8"
+                + "aafd9c7660dc46a29665836e0aeab04806bd0fc5989c4f63f7ba4a58834d8d5447f70ebf"
+                + "67d33c57ddbee6574d5b741af87217f3c0ffe4f87c48e2d49a274968b87a254eb41e3461"
+                + "dfb92f63bf91f42f43f33f7ce9af91ab8f28d2fba20dd005a1c04633f3c5478b93bf6583"
+                + "f3f7da072dce7ba305b717af5930ff609084e1f4c1e7b2725517188a734e7dfc69db20e4"
+                + "f33408611ebe4dcd039074bed4698035c36336daee42097c826a0352278dfd649d7ce54e"
+                + "5b18060fd25679d745d701d24aa7efffd56419df616dabad7380353034f639a981931fc2"
+                + "e6c2e54414fc3bdfb77c15f5b5181120e745215d126be23f8cc9bf2ecebe045fccd5d3d1"
+                + "e67dbfdccaf54004460e013c27cc86975f3edb844be4d037b07cff57cd14ef994df7541b"
+                + "1650f50d02c77b79d34204fd67c3f2b1649bafabe7bed873702c06d5d0e49d1b372fd3c3"
+                + "8453e9bd59d04ff5409825280d38e100f29eccc045ecbb69cc2c9f37f898b2612c524f38"
+                + "09415d3a07297d5264bfd1c8c914b49f8e6088c80bf1700adbdce7bba60518c29485c0a9"
+                + "998d34d2065628eea51d4005b39785ca870e09710e1c93982cf716daa1c350b4dbbaa17a"
+                + "95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c45"
+                + "11ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d28"
+                + "1aec9d627055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d730736706cb"
+                + "adf5497ad1a81f2bf7f3b557cb6bbbd5d978bf852b88a085e9cf4a5eb5ec10cffeb17a96"
+                + "74d3b049db297acd8bbc53127aa6b8c1d8a75f5cf30f63dcc540901e5160e2398d80b465"
+                + "926d043481b1378fbc29a6e10576b9dddea2cd568b1817517cea8b70f0060292bf0f5e01"
+                + "8c9a6565b290093f97cf3b554ffe3c288eaac2da5c29ee03f0f3d9f9f22e3cab56122645"
+                + "5b8d36436efabbdd3d17b2acca5cc37a571a6989f0068d0200ea3ea68ea58630606fca85"
+                + "8b76ea64532d6f6414ad68f3797efd3072d3990abdc7c9af995dd021d4329e150041267f"
+                + "9e60db55309c5725a249a6cedade7493df0a9f5a689b502c672859b8a19e6e09ed9271d2"
+                + "575a3c40e58a2bd97e7cedbb5b62c58b68ff0299b56e3669bfc3c1762f0c3cf5090a82fd"
+                + "00a18fa308caba36c6071216fff96d9fa47f89a4c543d20f3091a770fe22961b72f265e9"
+                + "ae09a1f590b163968ffd8ac4888531b2d5b89c9b64756c07f34c1a9463b3ce2995e4d67c"
+                + "bb8061163f5ea86254aa3a9ef9c2df31d03f9f47fb0a48232c5d6eb223852d7312052f18"
+                + "1aaf4a1f4c0c7f791bf51eee3941b020a28e2fddd367d7c42ed36eded94e11716744a06a"
+                + "e864b279a67ae6dd5cfa8b520f7e71ad9836dee9b6ea564b36c1a62b092524a3fd872430"
+                + "cc5e6a6a35a467c6cf4b9f9ca780adcdc0285ff33e976621ff78d26da6d2b66cebe39229"
+                + "0fb0ddd76ec55b72120eb909adffa19cf4e33cc4b3128ecfbce1e3ffcd37deafc13f6538"
+                + "4bf4c2a7e9b5749cc9e6155287ee53fe83251048bd8f926e6f21a34fd0f760186572e8c7"
+                + "a307cac394c3968c6e2b0f2a8bc822c9ec2c6ffcca5167b53a5923b6b5ea6f555cf6ff45"
+                + "70fe1716170c3bb1660b01cd19951c5ec0cc40a91c68a9382866a46eacc2eb88d05f2724"
+                + "f37845a62ea87c67056e1eb4128e88b9687b02b4dc4aa0156f649832cccf4f79dbc06fb3"
+                + "1697c66c46281993ae4ab53de1b1fa57432cb2f0a8b46d920bbe5ba26153c2f52e2ffcbd"
+                + "9331776c162f7832ddc91734b944200459426697104296c18da11dab66ee3159f2de1fad"
+                + "4ced3c9f5b512ddfa526614e4b91f7c9d19c8c1bd7ee9f415834c0f6cd849327a057ebbe"
+                + "9b86fb0354feb682209df2b6ff81af9d5272d3785cc6a195e01db7c1c25cf50351927f8d"
+                + "560ebd6f120889e45bae8ecf90b82c22779b431d1aa6bd9d772a9c0af93e245dbe9ea2ca"
+                + "05be0a4c521167583e99215b2bea53e7393ab9f7195dac2395a782c6b36ad395d4cbc42e"
+                + "6d64b61a7f655ef39caf328d0a17c1aa4d8feb0291f8246fb6b74da7c7de45547dbaa64d"
+                + "ef8f08c193a5cf512324f075a648e951d50d02f9fa463f7d4b0c27e999c35751e011d88b"
+                + "b0c4b5fe8d2ef3b1c27b0a207260d58fc172e66590b4d71563395df3ca5e740039ae85e1"
+                + "d4a93c8e285f7beadde875dec895a9ca624cfed01f34c167cd6299e358b0b1b77df69710"
+                + "4b14b6b0793305c9017acc469e7ca8c76405010b6d3b1edf561330b8149a3e5b5cacda07"
+                + "e5ac5bbdb0c3a71705e7e8ae5c2476d254fcd25f1c17cd27d90da52969b4da165b83394e"
+                + "6b845a24142ba057444bae362f78130b8fa9d2812a24a2c729d23563cada0257ca32f552"
+                + "561ec70203951d8d3f35f3e8d33cc3a53fb683cccbc7e82bc53a20f9cb21d07c243e4cab"
+                + "727d11d9652ef8af29e6083a5e5b6b2bc7106a022e4b59e93aa70153ed1db4593a4cea98"
+                + "4a159b313867ca7493589d364707aac2c88028f472e6bc1eb56d40901719727d8725bf98"
+                + "e101c2f225010ce0a2234f1363949c612087630c54d1cf340ab9a6c565cb17c59d58e488"
+                + "274cd4fb5aa50371dc0f70cdb7ff71b9d6521ed30d68d41104300164b160cb36c4ca606c"
+                + "ab9861b7b7017b837ae3e2922d5ba35b3acc543900e4e1c28ce98ed71155ec342285a6e6"
+                + "e00e6902a125f149063d36e8e4938b57f0742fb62ab729ba4902953dd09d72f4338043f7"
+                + "f2b14c2b4f84efd29329fb23c41a2816d3efe74a6153f84afb29b8e5a7c6b72ef7bad3eb"
+                + "23cc9f40ebdacd55454622ea1a4f7a76be76a4a19c5e30b28b8f90d50fe8c1aa9f1adb64"
+                + "4fe49449e08ac8d112bc7cf42e0bfa9cfe40c04af153f1c07b63bb44550772e6507b6aa4"
+                + "ded6e01bcb704a1b71efbd27e63153d30fd91101242f1f19c73a50738461d68bda9e855a"
+                + "51b6a9f379714859cca45d68d93e6a5259ee9a794322e6f5f7588ddf5ff7f9a23c41913f"
+                + "44d8427480b9de69ae18e340a12ea1369cd3907a85e7f30469e0341cb75fe4af0cc0ba47"
+                + "5159322ea2db6d4fbb48d3e8292de5e4b2d6fedf0af22e00ca81285348cf5e556fe743d0"
+                + "e83cff826ca1a032e26155f81431d5f40f8d95b066ac7ee17988b0254dcc35aaf2ec1140"
+                + "8ddcf8ab534b1e980dc956906f760c818158cf383a903de3707687a3e6e247c01e3f8209"
+                + "6fdccf12381b514b47555e0a25a52b8dedbcdcfdf196d9d108ac98b2cdf9c54ec6d94c3a"
+                + "0a2725a0c6a4caa101519d474714bb08f07846be64a263c869a8f85daf3203a0fe7e589c"
+                + "b488bbda15f8dce2e3bb94ce7164b44ff839f874d701bae37f5ead88186f8cd88586da5d"
+                + "efa7b4bc6e2db81ec9dbc3d29b333b6d9f6b1a2b241c8f6275a5422a929ee18c6ce964a8"
+                + "96d6a0d495de1eaf456cf6e7ea8369ef85903eb119d9e72150ef62baabfdab6b9e4b1bca"
+                + "ad34e1a6a6e2afd04fc8d0d256a4c3ce2f275990e8cd563ffb37cc2da9f7f778231f061d"
+                + "58bf5d150f52b4df852e887c9deafe3f1154627493658bcbb42f0121ed32f98fac0ff8ab"
+                + "ebc10cb1e52504fb85be939733e873c56117af98c0c071f27b51229db32c2379a900bfc6"
+                + "81f09ea829f13989fdc033f4a9be89b3a48a6cf198f4c3c64fff5c459c4f4c4082adde4d"
+                + "b31635bf7ea4d5b96721f268e2e87c7677a823f4e20edd4f4906e7b9b90ce9de6632ee02"
+                + "b01f1b6e291e4dab42a74de484820c9b4209cf13d17fc0e6e61b2e5fd921d1bbb3f442f5"
+                + "aa0b18eb54307494812d5d24189ab71f8ad57c99c84626730b1c2841fcc0a53b3b8fd5ac"
+                + "637c7ad4897c5b711488c6f93b0260a3dfec76e39f79af59578604fca6af74968331e470"
+                + "9c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a482"
+                + "6b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "05b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b520aab69"
+                + "aba86a4676d6cb0bccda97e23a4feebd480f62f50c298b5f8b4dc8407b36cc0e762ca9c3"
+                + "adabf18bdd1e06a3f6f16ffc9b7a58b02d29f917ce5e98318151357b29fa50328c1c8aba"
+                + "336ecea624fb63cfa145c1490b4d7a503a0719df0e9220ac1aab57c77ce87c9e23ab0801"
+                + "27302d6fde978bc12cd17f6f817a184ea6e71fff019329e5f26644fa36bb95def273bcd7"
+                + "153c359e00d8ac6005acb2e2d6d5042ad5482bb43aaf0b9beace816ada62b51023775ce2"
+                + "6a367ccf29675ff2ee0e6f3b64227eca734775e5ea84566f43ef63c0e5d5af30626ed9ab"
+                + "65327325b4c3f9b4031f70571980c4e85cd90b8314e0199e9ed7fade4c0e2b9a67286a29"
+                + "39b4093109d5494c7608c58e0beedf22fb15c68ba8de2e0f2a687a5a70e29a59c237ca23"
+                + "c28dcaf6e0e5f6be5367465787bb067854da40642211930d28980d36b8012d1a7fd64f9b"
+                + "bfc4e4627c902cdbdadfe3a1e362e713dfef1472f9dbcffac6c05d6cd4f82ea17d1cc910"
+                + "e5fc113e8142c7527c2e14194b622df786f5fd16722e5904cd62cd565838e1667217f462"
+                + "8d259bd59a06cdaae9c90d77000b31e3b584ccc7f30962b0ee5deabfbd71f806a28d18ac"
+                + "45320e3876744809fc8926764f6de7d33f56b78a0049450d76c64253c5645e88b30167bf"
+                + "368d19c3b9555d39af09baa425e9f1f9fbedc1a23d052d50b41fc4110b8edbceb20c0c64"
+                + "99e76811dcd6180db65c9f9f4f24a6f2b4b96915645d9fffca2917bda8fe6a0674722181"
+                + "fd4687b2e52700bab560da72ced49c0b73c2cd0580fb6e2a19bcd2d81c75a06a56f207ca"
+                + "08e332ba06e64ad8c5c9acdb0e67dd35747adf74aa66d3543486852246eb17addc277e26"
+                + "65a96e43c99f671d70bf489fc9a44e6e7dfead15d35b7bcc1be658639fb247be6b4ed49b"
+                + "0addbac3e3be56149ffcbc694144c7a9c54ee5264d02c4f55906ecc748b2c0efb1372bb2"
+                + "df547cde72affa6d085846a3cf3036af5d74a7a75be309e9b83e76d927bef3b21dcafda7"
+                + "831e484ecaea0d969c96f7a69ca42f807534257c1cab421e29d63c52b3bf8c4542bd11e5"
+                + "7c60827c1004d74a644d7539bb8d8563c1453912ef23317b4e6814c7a1fa2f16bceeaee8"
+                + "c15491b9bc4abdc27047eea25882428dcffbe72dea42f564b3e236561346b87b793f599e"
+                + "3fb4c733c9ac1661309bfc955ee74fb524ce903bb200700326348db4e3871a6d3fb2e362"
+                + "f6d91ee2a402b886be661b91a9b3841432cfcfea7c67ceeb412047f3d1423a7276e508bc"
+                + "fd73989cbd87e659d123554fad15d0f0b4db23fef82e57f36b0924315e1e175bd4c78653"
+                + "91d72124cfe1da4782597fd5307f3c071ca2b03f27bd682d20c88002356b4ef86926311f"
+                + "338f7fcaec94b2142612ea1f1a013f7b2775fa611d52e2388dce088139eff39061597fe2"
+                + "483bc5da820adba2d81dc3553d409863a73dd62b41743cac6c16bcc5fcff4b2a65331c09"
+                + "49999c7d781d2115f92e10240b5a62adf4c7f7161d8830ea92a7199c567e34e8b7912bf4"
+                + "20791b10f861e5a6aadb299646cd77280a0600659f5b87c737ae1aba713c3cdcf7bcc316"
+                + "65761adeb5ea23131f6ad80f9571d2b22886e6185d7a11392950513a98dd34a0382cb5ce"
+                + "67870ff192503b4dcc6f1055bf80a609a034b05a7bb6b006775a92de29b125ee3e1749df"
+                + "c4f0ad20a27e8ba5205e1ea52ce5b01dbe0a517f76c31bbb60aa359a23f2eac13bcefa2d"
+                + "8ab231a37a97c094fd20e0dafc784579afe8ebd4589048ce9901e1702da1111f5082d673"
+                + "45432fb3a062c3d723cc0c8768438ea6a53070670a96ca678b93f16f9347f6452686dca8"
+                + "f18a0772df206b32b3c0f8650a01374ad3ed27d4b348417c7f0c8af885e0e13cd68c3734"
+                + "5a323611ec73cafc409f7b239afd52c58a78e7065dae7772facc096ce4e7d1eb92302850"
+                + "4ca79802e4bd3e04bdd3b900d01eb598595723baf4827599014eff32d17c0df91aac123b"
+                + "01df9895c932ea4f623085d0fcb632d88993a1e7b5e1e1871c2c160819d8e8f8942e258b"
+                + "ca25f89d8ab4c1153156aa7432ddf33d136be1851a51c4fb456da7ea95502d823ad9bb62"
+                + "90049954fed47f92de0a1cbe4d651813de5fea790f5b25eeae4f5004a3bbdc6d4b367527"
+                + "6962f86b041ee8391455ee394b3ef3d95bf4fc424f0ef3b476152041e881f29aa771dd7f"
+                + "1863341ff102b7606ec9393955427c53ae7dab1bfcae26b6c01de4fad27f870a0a55a083"
+                + "e5b672ff7182548af516eb8b116ce2e1e29212159d683d1e2fbc301c86123751a5991ce0"
+                + "2fb0dbe2ea4d71241ea4617d55fc5a684bc083b6f55de23f6954529e1daa478a8a64a9b9"
+                + "cdd0af5131d3881903f9113245f28fdb139ac17509a825366247fe1d99937e13deee8a50"
+                + "bf239bd623ba6b3ee93cc39a44e4907c995c7ed3b3ccbc028aa2f7e2a9052f9ad3a81561"
+                + "d65bdac7b57372dcfa6e34ed2f2bdcf254767af5bd999b5ad48c6ea9793c0ca5192fcb50"
+                + "3c9639da0ce98f8ba127f6e8f89159e28ab1bd10a5c3155958bf2b7cf4c9435b7254e969"
+                + "cadf2523acf1960d8e13febd2f219d9cc872df960dee1177bef9ea1585e150b753129502"
+                + "105c5b4ea532b0b8aa294721545d89d6d3d527b2e572e17d1d45d1e49ccca48bec5df70a"
+                + "a506c3cd7ea1f0345ba72f00f0d3e0b0f13cd3465376b85ed41ecae2f48a5e3590a5cfc3"
+                + "9f3fefeee6a57ddf9c204f56ba885c61a21a1ca9b6072fc2923d9373e3a991f8ec9eaa54"
+                + "8d79ee137b515be0744ce2a301a3af36aa09dc3aa8905ea03884ccbcce7386e816fc5f7f"
+                + "dfad404b067a5182f468bab857b295f1fed4fe3f8db5acd2e1f9721f6bb3f5b2c05e3690"
+                + "caa77a7f556073308a124f4a4caf0f878d8778c9333b7b48b381780544fcb6705df4a3fc"
+                + "01a33d69d29ca85e6111fad72abe215ddbb6d715be8a39d19812a8840a31e17f88d8c118"
+                + "de29f8e5b0b4360c3220a80e9b777a6f9aa5187657a484aea6a3f8c7209740a326293777"
+                + "9e353b7ecc5a7b73e2938ff7983c4effb41e5a9cc210f552cb304dd14ec5d92e8c1d7200"
+                + "b2e0a7063dfa3c7b36fd9b0acf0260a3dfec76e39f79af59578604fca6af74968331e470"
+                + "9c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a482"
+                + "6b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "06cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e2e6654"
+                + "2dc1c07bd65fef62f2a8b9c32de71638298dd0f69dcdc748a51e143d800f629943c49a56"
+                + "566cf13d72c0d19744c75af1f46ccac678385ce852b0aa9e6c00dd19360b3ccb3a110523"
+                + "f18cc02492e221ef624a312fa3234bf17b2df012748b690871cf1b0f68e8dc2df0627e6b"
+                + "95a2bbbca7a2fc851548b112439a36103dcbffcf658fc22344463a6286966a4b1ac88310"
+                + "75bfdaa3847a13eaafed8b4480077e7c9ba19f6d91220f2999842ee9d743b0413fb06410"
+                + "5f0a0824358974d4bc1519e8f83fc3a7e83f3be5ea03e4475680dd31a7c1fd4e99e8b303"
+                + "570241469cc1cde6353ad7c97763baec7890ae4d7a2199a0744114545fe773765218fab3"
+                + "6ffce6ef4e98f6d7b24ace8705aa81b534225a0f2bbbad16c79a07d0cdc9b4fb86f023f0"
+                + "8b484af868f82ea9e3dd49816a4d54a507c5dede45570717d551a8453dce4d6570e5e457"
+                + "69656f471e486adb0178229bd0b1417c114e2737c76dfdbe3f7a96af33b6a0478e02d076"
+                + "927410078ca60b3bad250f01e39be83c3a45fdc26ae0ab1e4d80cd92a06aec6d8ef436aa"
+                + "a356d4b35ae297b98ff3602fb1cd7b295118c2607cdcb34eccdec9dccd3f2ac2688e7261"
+                + "b2d4130325851eec469b378ef1694510262ac4685afd6edb38438a7d62f189df75985e04"
+                + "2fbc5c8f7035e05790ab7d95bb2866547b98785e029ad95a11014a8749406398875c5893"
+                + "c4acdbab24153abc3b33f85a5a2096d04b981017e80384e8a5366ce823ac9bdc357a9d01"
+                + "8d12d0b8f4e6da201dd14ebdbce809b1c76af800c23fe6e4ea92aa80287c38d77cc2e906"
+                + "7d0dfec357f4ccf49f5b5305f8dfbd37e2383cbd11b8ef1c1556ccbb96d474bab8c353f6"
+                + "2d87b6fa0786b655e25bd1ee72aebb4319e9fc65a6e7cd4c285d991b606bb6028c4c7eb2"
+                + "db7723457328fa1d5e2f6c157c4e869d6f605a2d244c98e669f1609e073b5a01e33c764c"
+                + "5e50228ffcb77ed7783706a99c9c760510d5e118036453dacadb96a2a11cc43a12078e2c"
+                + "c0bc7286963250ddec578180722f9dd53dc73f5811ab9675f3001ac2cda5080b0d5cd6b4"
+                + "36df526fd9907621cc235a3caec728dd8755f02747d0e8055e202e509bbfbc92c5c50dce"
+                + "18c1e3fe7af4c6a83700b04fffd0475ef08b02b9c083a7bfbc835b2da937b7527ce6d183"
+                + "cd9895ab498577253ccc8b5b57905c4ad7be3746821de2d3421b49f789fd9354d1ebe763"
+                + "2fba9e71e79f7d760c8d71c80013743ccffd78664d7bbc32683f676194c2aa95b7ec953f"
+                + "a9eb9d81d49a061ed7cd4102e079912b890a3e28e55ecb4df30987ff6ec74a6446dc64ef"
+                + "41f329346de509a4ea95e61198ab527a7e37a46bac1c1ee6e3d8b91213a440f908ac1119"
+                + "f287b10f4ec0546a418deb1c065aea3c1fce7a326a7edbca9f0c71e43b8852e0ee2e2161"
+                + "fa22392be9ce42515e77161a0a719071fe07c3c311b9599df2149c136fdb4d1e748d134e"
+                + "49990503bf9c05f96121af7512bd4a56a6d4cb2ec949d622e1747686412364a8bb5485c3"
+                + "32412fb6a24eed1bb22fb4401db7ab742515ceaaa0a604fe7d0d9a01c86fc2da90db9dd9"
+                + "ce265d492f0257ab1ad56e3f3777a1826a028df443943322abb11b4a179ef559a0809eb1"
+                + "68d810af7ec52c45692d208474a4567a72d697e2d325de4c8f6177ad3cbf54f72c39de5c"
+                + "cad2a8f7d885641fe14dd7898f64c28d295977e6b2cd74707c6ef739106abe9378009d10"
+                + "389fee45c34b86040c83d3b8b066da957c0ad4a039d91f03da10444014c47f38e61aaa68"
+                + "5382a540a6d903bfdb339eccf25591135e1be33249afd2bcf1dfa9add5ffe0cfaa4a866b"
+                + "cdcece58c029fad29233542f4d01bda72e2ae40f06c1268033ffa86d9f55361932c72af8"
+                + "70797c663dd7295d54fb5d4888bb9a0c07f73940a7b77c2cbd59928c84c73bd55683673f"
+                + "da7993c2895530538945b24600f85227a2cea8a8e393716f5597175dd9028b34ad245f6b"
+                + "3981df04011dde743081703679bb523588e7e51ba9a02025e46fd35dbc2c1de6ede0c741"
+                + "e66f287b483c415d9c5cd46fc2ae6c7e888a433003ec4dd13c42a06b4d7e9dc0f735866a"
+                + "fe825507b6440a282d6f0977316d3b35a0ba5e903820d058cde5f96232fbc64422b66ac5"
+                + "8d6e35d8b9078ecd596d40d836447b3fe4e38cdf1cec5eec0e21a8e89a966952c68b78e0"
+                + "333660cfad6aaabe466343128ce43ea8a00e6ee0c21d22bf15194a68a97273cfce94ee39"
+                + "9dca71a0995d319d928df962b7c21587004152257298646e093e4fe6301740f32eb0d0fa"
+                + "c7a68e88fa8f7e6a03759ad379afca083239cced9f19b4e1e45dafa49874751e2a11bdcf"
+                + "43a5034d4262b254ed8d605e10b162c48f8268be77b563305d6e516105f147612a9243e2"
+                + "fdecc0db5eed59b7119009915a92396a3cc7d8daaf2f457c6c4f77119628abec045c33f0"
+                + "29310c5e1010bd940777f89632520ecc96e2b926ae92bcbd4e4f7037ed56f196c9e495b9"
+                + "b1fd54859110c7179944b015fa4581fdf709be02a8fa58a1e07e749d5bf64edfb166a722"
+                + "84bcd4b548c0efe1ea0fef7e65deaa7e4a47871785d8725f7aff0a8b8f19552845a6c1ae"
+                + "25ad74628a93b00b23632698850f341c66985cd203448527a5cb608a661ca800ecba14d7"
+                + "53a4f64578be106808966065a1e6501a539adba2124fd5a7d5ffe053806ecc6998e76e78"
+                + "7f23a8111687461cb11d7c1ea39d828d3b29cf796c3ef65a9a2c4f455a0dd2a8adae4f1d"
+                + "164b3c5cd5ed5c5006547df0d7953d56518ece379c9258863c539f96846da074198fd3a0"
+                + "ab0b0792d56590c0ba57e11ad52dc9496f8b7ec2ef8dc5d810eef944f972e9c3d43c9ad6"
+                + "6b9e5ebe3e1f9c2a985a1e1a09ce5863699ae7413237ed774901166abd7dffab0af42037"
+                + "62e5353445587cda22c6674d896cf813767dbcffb7c4e877274ae1922de5fc37ed041174"
+                + "48ec2a6c19b0c8cea3bd32603da50df8a25aa0246da7d1add751f79a7d08755599be7678"
+                + "4ca22962058b1e50c481aee05a90b0db8a85c07d21a1dcd9db42f4e6ae0e503a5cf94b2a"
+                + "2983f55a7d3713cc9ed56ce326a229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed"
+                + "7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a482"
+                + "6b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e633bd325"
+                + "591368b31ba0624dd6ad1234a1fde1529ab6072963caa8d85b08c9b874d2e9e85634e67d"
+                + "7ec2faf99438f55406b5652e876b33cd216bfd508bfe86e1906eb873bfa619450507c418"
+                + "a361e791d47114d4d5425100c984ce80a8f4e647c9e3312e49a6a32bca660edb9edcaa29"
+                + "c7f06a85e42cd78e70687ca67f18b107868e94f4bd3b91fb4d7743f77456bb13eb459cf1"
+                + "4d6ac88b473d5d1b05099150c1daf8a10de0041c72ba1789d88392276ae1d7f9c24a210d"
+                + "650c9b58dc58a437fd4e8f20a23f2c42056ba556c88f37e60d6eb710e6e6ff27aaa874ea"
+                + "14cf210f3bf0ea399d5c7c515080bddba5a141f89eb99984582a0c3378c05f2f58645ae4"
+                + "532dc3e4bf7b1f345f2be89f44b575e7d16abe9c68fe2c8885bc2de13bc66aa85ed2a6c6"
+                + "499264c52a99a90a6144db80d80a20675eb11ff8d237bebf08a571a70ec81248073f8459"
+                + "bde59242a529e6e81069776e0535441d18a3e839966219e3202b530cbce34a314c6f6082"
+                + "40f8d90cf1ec07add9ee0910cd8e819f5baa53f0b082386f98d5e6c6c6a435d3f6d02a66"
+                + "89cec23d406fdbca9ebf392d1315caa1b3b5d5b8cb0c6bb7375c64b935708f0012dfb6f6"
+                + "33ed2234af72a154c110fc41c2a8a5548db2d8ef102f607881aeda6ee24b0f5c4f598c7d"
+                + "d9656b57583f05dcffc57f99e126abd32c4ffc3e2a59ae6313a8a7455c9f2027dc23a124"
+                + "316d04c9ac9121b29079aaca516f35f02bd5cdc599276c8fb5e43ae5b54738054d6ae6b9"
+                + "d534e8839a6368a61dded9769841a896271411349ee66a8229111d10c88dc21f4dd84ab5"
+                + "aab5b6a42db9be5dd15bcc505c569e41bf5b15bbcbd43896d61e70fd758f389bc11aa9de"
+                + "2c0d0ab7e6f2e48f921ea5ae08352f2469939b0f95b50aeb7c29768ee2799a7050f6baa3"
+                + "21b70267838f8d78dffaa93368493e8971f22e0811496e8bbbf247dbe9fde951d8a5b025"
+                + "3515d62673ed9bc06eebd5c0a779027cd124c5218298be9f8e3e47334bf5341976c18816"
+                + "1bc4e95ef24e54dadc5c6ae911698f45c958c1a3c3f77a61774cd2307248e4a9d0e8d077"
+                + "f6686d172eca303980228823b1332f56b8eb54ffc0b179dcc578dd5d571adab2cc9b423a"
+                + "4b8b6d3f6c6d39673eb1e688659b003381f490bbf58a66986857ac5641db508f4a0d1e11"
+                + "35284778bfc2086ab1254a8d0307f3288bfeb835dede235c9ea30aae45962dd049063c87"
+                + "1738dffe8aed893477a2fb309b3839f99176aea300a7c06a18c67b2076c244e79f3400ae"
+                + "adc1549ee8a3bf7bda1590c3d15fca713e509873cc90867915f3886ab14b575d047e2ed6"
+                + "178f952c41a59d12ac0e6ab97fbc9c4644a5e1a30bbd05d52c912a63298f3087de45b8dd"
+                + "4ee871da19f3a7298e1f62eb1872959d52f2517f1b4d2db257b699ebe1be75292c1da59c"
+                + "e870ca2876da22976c2c8f8cb5d85ff85e667c3c56e0eb78c76ec4d17f4f110c4eff8685"
+                + "4aad12604b8de317bb913f2a96ade99f44037b2ce0c8c9193ebb6c4f492f0edccebedf6d"
+                + "3c465e45f1454a5b282e5367e6edec3d6d0e8e7e715c93919b576d22249a6ad1b9d9fd5b"
+                + "213137fcd070aa5ee73f16055b0486e2eb00ce0ace65ee2dd20f8e2ededa7b3768b8c4b9"
+                + "b0d4413a8ab796f44fb406f20b853f516ad05ed4536d8940dbcf405f946956f4a0ff13f5"
+                + "02ad758e95bb0961f3895ca68da6f433cf49df9297b44477fc57c63a19092c39e23afa22"
+                + "05565debb9e5c5e7a7ece41f5ac17711ad03d3ce87179f2cf485bb8e554afbe0df808b9e"
+                + "5d4e6c02adfe0d744cce0674990d4830df5105ead5dd97e20af446829ccdc39b03e27bb8"
+                + "927e15f59c7f8a35e3bde8877f2a84fd07f17fa8939ed51ad5169852d9aa90598bbab435"
+                + "1be9eb4c4d39badead509d04b7b9499ba27167a85d68d10076eb2b57349b16858edcf93d"
+                + "43a061a0caa35c2697f5af586acbe3c8c305d80cc9e34b25ec19b1e66cd654e4dd826bb6"
+                + "70725cd4aea7c085db37e308e302917a22877f5b5219328b1a656083bbd37d456115ac4a"
+                + "859452bedc7e39e79e2257b8a861d3572ff7c0a65f82fd2980d35972cc4ddff28379ccb3"
+                + "427f96113171b1087841d32f6f2a611c8d43cdfa36fb2c9b91dccdcfc8228aa26bd41b86"
+                + "a01a9caa3107c3f8ed9c79d7e5861afcdbc75e7e4cb53a9378f1826efcfb0b8730606f82"
+                + "6c046a5e24f4e19fd0beac27555f334316bfe175138626b21810017dca03531ffa5959f3"
+                + "b9653b3b3f4617c91f4f1ad20b1074fc7adfbaabf59fb9d25784826b5748f359cc35ba12"
+                + "2a7719493830ec31e6d9a8408c4bc516c4c3ed334b2cb4b98fd8eb42b59717e431965e73"
+                + "22b7fc67b1f150f845da131dccccf32f55975d6b7b898d5c749578b44e2301c2644c23d7"
+                + "77cd8321ac0d9378ea6d1ba251e1811fdbd698712cb9763d2616a2f09b6d8829ea94b658"
+                + "32b77349dd527985cf208baaa75ad45e788579ce1d71f19ea79d4d721ec8d519d22888c2"
+                + "86209001457bf61550fe5170587b99fdb0d2ea3165cb0df8090facddf036aea0fc1ce535"
+                + "c656c790430f5695a1c552579548e4b716ea84a2c84129b05b2582102edf979735c2b5cd"
+                + "f9a06247d668980229aa506bd32263400a1d9faf888cc270b1111a63892f88d244c57180"
+                + "d27e26c34063d49fbe776cfeb54979cdd7934e0a6609b40a9129a9df73ba6bc36a78eb3c"
+                + "acd808461fb41da93edd42dee2fb6211422b6ab9fc005ade80c5d846f1a1b83bf92f5884"
+                + "d82f6e8ebe0b3f9596c3e01edf24c89af2659a6aa4183ca8ff5075c9eb29da35bd700787"
+                + "28422a684ed76d095da1337b4110e4839665f56c95a35174ee946fc8f37cd585054d3907"
+                + "94048a24f667cb74f9279f83aac70ebc364a7ce5e2a356c1bf73f7b8b598a99b6deed765"
+                + "f8867451d62723ddfc04797ff7deef3aa27782813c182c2946ee0106f95f2ad4d7f670ed"
+                + "12208f9d138754a0eba619a6f4fd45d299cd7f08152fd5b7a2f3c9fe3911de6eee1d59ed"
+                + "bbb07e903d5fd6af134f159714dbd00686f75800c820b8cef25c2edb7e82dae38e1e5e64"
+                + "3eb7045ac256e9e93e471c051aa229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed"
+                + "7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a482"
+                + "6b2c83dc9055bba150d47c43ac58f099040e06ba3fcf42a08dae32984bf4099c6ae94670"
+                + "013f2d14375d9b56e64c069c67ab3e15658c8336d1b7327f83f8e5daee3ddc76b369f3cc"
+                + "354c7a4685852cfd47e4d8724090d5d629ed3c2b672c61f2a0d14c178a4d9eaed7fe9d1d"
+                + "e7bac3ea2d8d94148694fe1993770fd56de255b75ae30069d3d93744fe7f82d106a54784"
+                + "329a4c73c548232dbf6bae651655eb3b3f62c85f698f0d86655d01f65d3566c9d2d3fb59"
+                + "42b2e3d846e173417f7a8c57ba38a7fd443eecd76fee81a1520c8e9e660e75b23eb7ece5"
+                + "88a577fdfcf06e09ef5c9a99499d52a1310358893639b5b346ee21d1fc2ce91619be31e6"
+                + "1c5f7821dde339476055039cc1a8f045434bc512ff74fc75e451aa20c174ee7a5fb6f686"
+                + "04830e72b903c957fa86dead167fe8df5cc67a3411f6e44a2e96ecdcc314cd6f7c659021"
+                + "8da02182c16ea1c87fc258f30e4c3368fb924a6e73738c2e534832855d8ea0cd1c002b89"
+                + "01cb40b66c7e3646b1dc14ed70da0e3d8a120bbc1eb4e4d81003c7ee2464cfa9169b18b5"
+                + "290cb0a443281ef3ec3bd9e6926c778f15320876cef162a07ba8cc6e61216e2a54d45d7a"
+                + "33d361bae1839ca8e62b4b4e4905b216bdccdb167807f137eca3a98a68d2b5e21fb8f0b8"
+                + "bb275f5b551ca77373066a7fbc0b8fff4969ee54e5e4cb946e8d5163e1b65fdca260915b"
+                + "834794419117c2614f0c835522a77a19dc3127378eeff43ba94689b2177801b00f2fac68"
+                + "61bc1b461dee1c53e5861de9a21f5eb45f87dcbfdb8189f972ddc970f98d903e5d08f9f5"
+                + "663cfaf400ccbeaa9777c9ae178583ee09b5a88a6f970d527658322ed87f1c8d2cb1df60"
+                + "07241440221447b6d9307c978cd964d90d076d516f02c258bcf2eaf6e7720ee365fbbd39"
+                + "96342d2edeb87b8ce38abe9b3a51b56f92384c3d8e3ad9770be26a60af23095474a368ae"
+                + "cbad10e93b8607500d2553d07b52a3cfb6b9d13d98fb5b5a4916d89849f0f37ae2290367"
+                + "6f26a1b2088b4912dd62f17f05f02e45eebe33a0b514412c18a6e39819b3e92f7c2901bb"
+                + "1dfc375e927afe468993400da91c590a19ebe5b8047d2780176b1341928691cd8ef916e2"
+                + "f8bbcee52d02aabe5a1155e186bff3af82245f9c5f24a647662dffb193f3f0a11006a05b"
+                + "7e9fb035e55b4cea67db00a65daa87ad8a64fd37e17150e73cbc0619a09453201789a655"
+                + "884002fd6e93aa49513a914fe5460bc4804158ed90231c811b928b5064c36ca9bd29e8f2"
+                + "db2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c735aa1aee"
+                + "b9dc98b58c6a0269785f54253bee831361c3140255b755a8c42b978edbd21c99fdaaf736"
+                + "90cfc684e7885fb19dbbbe90b108fcae3748d4421d9ff44bc9ea2b4f28d5ae451a89f7a2"
+                + "fe184ede5e448acf22de728ab6e3310802835869ccf772bfa5c9794978481480d0522aa9"
+                + "7ea4a3b7636c53da9fd390453158482fbc3f9692b4e6a8c7530d6b2c3546da72b639e4be"
+                + "34991d32035a846d074a040ffa6d39610593fdb023188f9fe238e392c9e6687b2226f467"
+                + "78b1d378a306332ecabed3546867108d132cf51737d0513c91d0bd4db23e2157abe891f1"
+                + "25518416f1bb70230a1e629e948ab31cc03c53cbc2eb5f485b8868490243b9e37533b522"
+                + "6c759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee5f7d7c7c"
+                + "1515056df6db5252d043730434d4900408dca27fe2628847002db7671dcf1f959fc4b7da"
+                + "3253c62dc0b680a3854070d321aeed508be02cbb51e4f23734d337c152ec688ba863fdec"
+                + "58493e03925913fe0214cc1fcffb23f0fdd6ff13cf98e9caff86eb89a3ed8412b0e8212f"
+                + "04e83c56181e80df54f3d22753de597ab1800784cae7b55eab78cfaf6c9f9e6d587fdaca"
+                + "3ce632ef17d03411f34c6bf05707e957c7d2a3a801961d9dd44c1affee6c6274f8c4ea78"
+                + "3492dfdb6cc0e3a41e40d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5c26fa422"
+                + "ee686aceb1a99e89dbbb07cc61b945389e76454d7feb4690d5a5785958a67b8f018f4df3"
+                + "17ebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b776989e13"
+                + "e6232a978ca50954d9f7caa964a46d62eefc8c448cefd16f2bada187d674cc4aeeae5af5"
+                + "b2063963a180c41bc0fe3a82be90bd08aa74abf08a588cd2dca2e2d256ef5165081a1068"
+                + "dd608d722c42ea612f394df8915af997ef54e95be44e401948131c23637153174b62743f"
+                + "e5b6b104bb01b3365a82ba4e4ded915bbf9b47675936feeb9198164787ea2c97164286a4"
+                + "da425a705cc553d575641b7970333f334f44462f8cb9bb0969d7a35d3a2091d63735f651"
+                + "ced99a2f5edf917841b3d2000ad65227f7f226a1220d61a062fe981b699153f57522726f"
+                + "91924aae820689497df4fca303a7a2f02ed762a75f083f261d144c7d937cc8ec9825875a"
+                + "6e9e1fc763373ebd097edaf9644a7b3d2355fa5b69172a26a4af6587626e3c0d9ae0a4eb"
+                + "982ec6bc3353c504baad39f51d5657e32d760dc5ad35582c44b5aa78130e0ac01ae32be6"
+                + "41371a93813755cece24eab2339142748c055a31e13f1529e5ddd0924e92343a5280bfd0"
+                + "5f3a44d116fe436422b470075c05b5eba896848ab73579515e1c2a2b5163d3eebb35accf"
+                + "e3cc0242c6b90ee2a4dc99ea534147d317112dd2ca051f1df61aecedcea981979341a6b1"
+                + "05952988fba768bfddf4d8a880091ac114a4fe137a8f0856f5521d27cbb628cb6c8c5fe5"
+                + "a307966d8531f3316e2770d883bf0186d1debc6861141633e3c2d1f4fb62b2d3b16a9eff"
+                + "6c71e1ed34de8660731439527c7ff66f8994bff0919298818b7717197f0fdd5470f4c123"
+                + "3c3eaf66ce632cbbf501148734e7c58022be9f515d9dbb3fa0e1657082799c268724db4e"
+                + "2d534a9bd6e243efc22a749c97a53e72d8f4429423245f625164a060bdcfe1a4cb682a2f"
+                + "5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd0653fbff"
+                + "088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f8c81f2"
+                + "b04f4a9765554e8b8022eae8f9f655303d6491284e4ba7d5e8222f2822a7a7c1367853e1"
+                + "5e647d3b64bb2979693f3d1b82f01e9eda03ee11d0c816a485cfeba334a38e8656b9a973"
+                + "f85d47a53b138a545585799db9b45289f014576663033daecaaa72010b62db7491c9b430"
+                + "fcb00e7aaa181bd962d357a0a7e20f858a2fcc7e7567e85c3abba47794609dfee73196ab"
+                + "ab814d619194d5185feb85c6476fe88a82243e0cc60f2b415dcd86a04090e03620f1eff3"
+                + "b8cf062aa967c603d29dac6213bbfc78b48a47c5a8831e987b2646d46447144d365e93f8"
+                + "d464e32bcf7667028f551bc6c3e3076a70a3b6c4ee82ff0be7741b4e5f2bf5373c77b9ec"
+                + "59ca5481e6288ea2f9d1b0e0fc88613595212b1a95706da32347e434046e0efc188e9914"
+                + "945cc988e6bb04f6ae63703da43ab4876f522bc5a464cf9181244e9c144f8a68e66db5d0"
+                + "169c49bddccb9698ecaecbf3ab7ca77a2962e2dcd22e43246a1d4353c67bab65219f1754"
+                + "1ee33534f67f258bf358448f98511fa2a14c07f76f234666abb81f404342435d62c4560e"
+                + "c49757260aabff7abdc12bc3cbe643386eba0cdf7ae1828a437256ef19ff3d37ea0db347"
+                + "da8dc122b4675016583b073fd88aa31ee0aa6a051447b765b5a8a0148b3b4cc49d47c0c7"
+                + "588d9ad6cb74298cdfced510345da53e9565731a203fbddb454539bbf5525f715f1dd73c"
+                + "ce1d1dcf63f592d930d0e43b0dfc1b0e3a0fe5de76c825bb6cd548a4e34701b70b0fbf37"
+                + "50cb0a195126e49ab69033ad12761b3eced4e2b7f447b3703ccb66addb585b5353af7544"
+                + "eb7fba6636bd12ab396c3dfacfb070d3b8999306a5e42b59226b55dbb78164f43259cac3"
+                + "1ae4aa66b5427da4b6f39088988a305ec5f177e683d8a35e29672fd9d2495425fd477350"
+                + "f3af1b936900e322a3a679f239a127e5adaa345e6f80925242980a78aab55e13143b99a8"
+                + "08b080cfe94c92fd17d5058c06ca45ba9d70f1afbee59ce586e33783b589254a9b23c923"
+                + "ff6f25ab78b0e925c0ab608fda05f99611f29d0b27a2353fc25eec2233eb2dcd6bb00517"
+                + "66668fbc15a2a2e279513f4e1fde1d6cea589cbb30a19be843f7b7a4e5a9d38508bd2346"
+                + "dcf7083582904c6440b13a1b39fa83051feb96940b266a92084c4ca7cdcd475615e3252b"
+                + "ea92f2078ffc38e08803c9192839d0f938af61c7f63446bf935eba51f4cf0d731f11e54b"
+                + "c6c132c29b126a4392030b02951772d64bd1cf9391b5336f3c0d02351f5bc88cbf2cef7a"
+                + "7ceae0c22f14353dd72e5c639f37d84125963196187837d0a5c940abb61a1b4fbf3c68a2"
+                + "6c2e86160fd21a9f870de378ff95765c8cc0ff405ba66aa86569c5765e0c902c3e354498"
+                + "fc212d60591164ac943688352fb14b559749d4252c338a943cf827b8ea16aa7f5fa1fa8b"
+                + "da51cd282221e3a03f2f7902c79de8f469cc5f46067abb0ce06bddf66780748318996ed6"
+                + "07634c4e919c62ea3b2fb6425634c6b349b6160ea397abd78cfd49918df3b451165fe3b1"
+                + "baf23cef3fbc899186e66fa96824fa25e2171acdaf19abdea3b6626570dccdc427dad3f2"
+                + "585c3aaec29647247facf840f27183095b2ec7b7e3d394b354dcc3463585c17eea4229a9"
+                + "f7d3059f29978d482fe71755e0ab7ee3f0df7deeb1d4327737547403815021ebf7e0f382"
+                + "050c843d03602f513cd4780c8dd74bb830662a4ccf4dd8387d7ea1f4bbb4e843ebd6da5b"
+                + "8c3e7504b9b151b2573305d2ce18936408fd7fb4d5934f6149496e11c4993e771a52aa45"
+                + "b2ec387c796d941ee04cf5a0680b2a1b5897243c62c3e36422de8bb150530bdd5b3b717e"
+                + "31a58906dcd87b7ace50cfc3a12344bf9c6929afd7fedda234464831845550d517062dbc"
+                + "7635b717c05b7b9adc5d94ec3522895a4035718eb6cbd5ec2c117ad1d870b9250b06c776"
+                + "ce34ae4f554e612722ed1fdc4186d5dab9e955a488f02dfb88a3bbef28aac15dc238d53c"
+                + "185532861bdff752a6d73614724024325ea4f13b2d538d2a5a659176ea7b1dbd658ccf96"
+                + "23016411ddfa62a23c46f9aca242e83b8cf5dd27accc0683d5d8ec2925344c8e986e4908"
+                + "21b58cdd50b14619a4c257d46a12bcbcce5aa21e16543c60f200996bce780d50287d8290"
+                + "c24dc1651ab131b44b2a2ebc7f13c40ba2edff496c385f62a512de14172244a7a4641d87"
+                + "a7d7ab65c5230553297472165bb3ac97e20a0d03a3fcd786c717d0cbaf058933403716bb"
+                + "181907a9bc0fd615a307f7d9fb9b9bac6a009ce184a2bcbf789b3467e40c2b2214b9d0f0"
+                + "e3d14348cf3c1fde628cfd9fbba66ec2f502ea95e49e38bcba72e4aabe21eaf50a7ac92e"
+                + "d8560288d1f24fc185de19316e6c2e300d1d05f67e497089fde120f701856ba8e4005ecd"
+                + "50f46a213e2e29ef742276b6228f80ff79568d226cda6e749143407c17d67f3c6309b383"
+                + "0f9599ce20926615088212ae770fa58b17286152ea1fad5c9755b33e9efcc43f62eceffb"
+                + "fe15c8130691e2c745332d195d9f975cf0a787b33c9f95f1b668e7c3abb5d1ccf30090b2"
+                + "4017a62334a12916681d7544652d8df41c38ddd2c6afafa42bc84b0ca2353ed9023cc2a9"
+                + "75ba2da748693a0b70dd782e9c706f1a065ffd3f056abe63167975c6b1b362cbe4250f7c"
+                + "5ccc6afdd98752a07161731985ace9060782754311a7a619538afb535645f109c35bc51e"
+                + "13f4ca259139729f8b18d4dfef0279736257f7ebc0efb38cc00ffee9c48506579cad0703"
+                + "f2255e63c0495708a4e0c20776794f39f76c3230341121bd3431637f4b0cd1de7dd1a6f6"
+                + "e092b627367de6908d5a87c8229352cc4f3633efd4139289d1c5344428a20ce7596ccb0e"
+                + "e2270d59e6baac1c5349cab5d3a3c90291f1c1359090e1b267396cdd01aebd8aee397c95"
+                + "09fdf6755091b4075164901de8d9c453d64f8189714aa0481ab52f9257eeb4e9bb4cf651"
+                + "79b15513c075efad49f8feb4db9f9aa209f0772e985a68aa4fd78b39aa696512dee444b3"
+                + "adc140415e4f8753ec2fb1f93c041e15ac7e266c042ca507cfb1155fa4acf95331ec45a0"
+                + "ace6c12f9105d877028f4df37f8585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a"
+                + "8ff881ef6fb928c0d4a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd"
+                + "5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "09cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c1016ca454"
+                + "596562a3818c3f411c52092c52812e78516952fd9d0f04ca41b211d58ac01333d7b4b9a3"
+                + "6f4a974d79ccac3194b8d5ea4b5273b3b8a4acd1cc9519c67ccf05a9707e4b06d954f82d"
+                + "e481834ac12e1c4551d796f9c32afe9a0087b24038967c748b7183c76d59e53b2b903ff1"
+                + "b56bc56011528d66d4817226ae123d139d7a1c64857154cf762fb6da2791655744f9adcb"
+                + "c169b5a6eea13081f2b2c67d65c8162dc004a04d50ad491610a83fb71e0e38df13c2c582"
+                + "6333ac327299476c5805095da9ae395759e82b65290b7fc518efc10c1a67aa9e28b546f4"
+                + "fe88658fe55e51b2d204ef13c12ed71a135b62b5b14082f16e532caaa1f3bd3d3c2e96e1"
+                + "c09441feaa7eb90c5772d2bf29138cac502347e5d259cb6a1c86fe77d54ecb8d8d1f4071"
+                + "2b3b29fc41c90889cb6f46b7c4584122e8dde577506cb73633433e3a08078abdef167ca3"
+                + "38d62b6bfeef700ac1c932b0508f6d39103f691b9b29468d16258e6135df930a1edc1e89"
+                + "e2fbf425cfdcf6da1bda291e9ff187592cfe70ebb5c94380e7fab5048f19a25e211eba05"
+                + "4636564b86545fd2068a750cfc0a51f45f7b9ff0c73dcaca2702290610303aa2b311007b"
+                + "299427dbfa9ae978a4f433eca6d99584f7687a1667580934d90ea391c5a35f6e22d9ea9d"
+                + "ec2cd39006f3eb4e6a8d259f48d6d19d010cf2c1e232d92b56a05d7ad9f1464aca7f2eb5"
+                + "1339d4e08463c1e4532f5a5f6553d4ff5224d74b263c4e7c18bd6697d6550ce2193c9e05"
+                + "4e4cdd6ef902d8ca554a3189c00808e0a6b595d029f32ac45d9dc67dcf5bf223720aa95c"
+                + "f9ae45089eed114bc2077cafe7aea348f3686527a1c74f827a5cf77468c7fd131dc55da4"
+                + "a5fe9f54188b1f4d1e6cb3299fcf891697212142ef82823dacdb21a26dfbdf4262795f1c"
+                + "ebb1171580ace52980c2f0b8ede17ded4e5f634a2cb6557da1d47ac4d7ef37e780c10583"
+                + "1116ea283174d93682b44d1300d863aa3c4267f924ad88305f1edd68ce6bf9cc610dbb19"
+                + "235309faeadbbfc3853e1e2bb0de3f3fd4110db82c910365efdaac8c9c3ce826a2ce70b2"
+                + "c745478a24ffaa7598dc14d97981e0d87e74fd40ac74ddc4cfaeb56c7813ea5f51401e7a"
+                + "be2b6523a0c40e77ebc263236d1d4dfea7b922595c7666755ce2d7cf18e49f8bf4b10a28"
+                + "e129f01fdc0ce6b5898752845d22a5e3d6ba09c79f8e5dc8cd46cfba62d7594cd665eba5"
+                + "e5b04428e57b12afd524689c9bf0de9452ec17d9d54b22efaf8507ab964e66b60e8746a8"
+                + "aa19e9e71185534649f1c08fdc93f17d77cedb705eba9555d95bc105c9b658316cb0930f"
+                + "cb324070095c52862656ec4b9512b7f869ff428292d9a1bf13825021b65a3aa5c0f8ba72"
+                + "2a53033f4d9d50afc17164cc34b7a0719d72797eb32a714dea3b33bd9365665d0bbdafdb"
+                + "c883a8912325a7ab24deb29d7a3e2ae8ec32c26dfd4399e1730c6c19eed5dcb6a9c8a522"
+                + "54d538ca3ea66853cf9d328f832b932319d2fc691cbaaf3fb13a6f47b10b5960e7de1d81"
+                + "c1d88df33069e710672b4efd281a2946de4efcd8f03e9b4f185f0bef25b9c51d96d985f5"
+                + "2911e0c8f0a8634bed247dcff60bfdb783615b510f0f827341554e663ab7642408584788"
+                + "2fac5183f35e9f53155df6ceb37d7cb09a16933a63ca01ed56b2839c8fa0c45f6b0a699d"
+                + "2d08dd7e432b9d6fbf30768c3097c45f466b7956f9bab245825a04aec288a08bc1147108"
+                + "0ddfa397d5bf528b2af991e04e726da808c5683d42a73119c0e50fa92af50c62ef2d9108"
+                + "8b71f9bd8032a4d7ab5a6ec67ef5de3bd0aa1a9f1b8b143487d477f9c0a38cc9a68d23ef"
+                + "51df6a6d45e5f77f918bbce6c4261e4c9c8e9de904a977a1a51cef2535ac4beb77e6e75b"
+                + "b468776d51f9c2deab3b08ef107c8f246611686774c9af9e10d783dd14258fa60118f337"
+                + "581210d26015993e11de60a72fd0e57dfa8cbbccdf3699b5d8208c98903906962ec23a4d"
+                + "e0af1e9c627598fecaeab3d87a72dfaddaf222210452388a7bdd461f18eddb006b04dd5c"
+                + "699975b5616e5e6eba0fe579fd2ea1361ebe36fd3d3245a437dc68748cfe6f113e2e012b"
+                + "e11b4aecbc9f8e02b7441640fcb806d47226de37d7791f2485121f2397b45f34935ce45e"
+                + "223fbabcf93b434281bb732772fe4430da125ef281c7570bac5b69a8b89cdaf8e1c1aa52"
+                + "d9d4c33d5af0715d6d809cbc93e977a78fd7f382480a09b4e2e48e9d2e0fa04eb1701209"
+                + "0dc61d42ef5bd9ba3189784955645a588d296f38d3ce654c3893c070818cce03551bf810"
+                + "827bfaff75378cc8ea1a53c23504e96e88b31f3947214ad2b826bf62ba15543432201fb7"
+                + "95cf63c5c8fbb0f04bf01cc4ca8e2d295ebb261aa40bbefc6f0896f8b9885945fb1d401a"
+                + "87a2464669b4c02e8ce2fa588c5dbd46acaeac200783eb39123354ed97d6e27019325425"
+                + "705ab8d70b8dd595eb2aa27e2a4ebf933fdaa9f2f17d76845b5b94c71d26a3e75b558bd0"
+                + "3b7f95bacbc21bc6581ef4d38c61fae5e38a2bc94d652775b6f82b8818457237cdcc39b0"
+                + "9442526d43531db8bc779722e13300de3491292ffa093141f9d50cd3a92c3991aabb9e30"
+                + "200c329f59391434bde8a52c7f54d917a3ddf4ff27b66b162ff2e90561957177fc80a32a"
+                + "5804d2ca9c938d01bfcebd721976c0fc64b335c6b845a5ad3d7d3aff748e4b781febdeb7"
+                + "b427ad97a8793cd9708c7cacaa75ca72f167e4a1f60ce6b6785fe1d636f414f325642265"
+                + "2558729411a99c30147d8636d09e3e580b8e65434573def99636590322b00904888892f9"
+                + "d8dec07ef77e9d5652a2b77484f634056902b0474d7b108a44499bc93a7e767ef0c6f3f4"
+                + "3df40da99742a0fd06ed2e372502f4e8bb6e2742e6d037223b45f39eabf25a92759b1255"
+                + "933706c6c66bcb89e468c01f005225c62c22afd7573a069d297a2c7932dbd967d71e545f"
+                + "52537ed2dd359a6216363782593e8f698d1b171a160acee104cd3707b095782c3b9b4299"
+                + "482af5f7247e52dcb81268ada19a8eed5d17d512aeb134a08e0a84b79b62c2c1de14c982"
+                + "b71ffcc9965b7321dacfbb60368585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a"
+                + "8ff881ef6fb928c0d4a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd"
+                + "5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebd35e62a"
+                + "3bf8be2a7bc32bc6bb3fcf82f9439de3243963a7f5e8859b009f59f68fa6dfd33cedf8e8"
+                + "82ab560a26e99d6e9ff9b21545443d114aa106f144b73c221d7304128c88642f0bda4963"
+                + "7743cced0694b69bc232d1657d7357a0ad1c4b95c761bfc02a33908582f8ada2444902b6"
+                + "1be98c878d7dedfdeb2eb43ef0a618799572b5682eea5aeedf1610f3461f894265fa7b08"
+                + "bfdd96c32c6a6fe297edff73fcfc679ef59c4568bdfe9ed44c199640a70bb5952d16c816"
+                + "4c02940e7c2df9eaa9fac4fa8a2b7426349ec487ce3e3399ea70e7c562fc8433de693021"
+                + "0fdc1306ee6907a7e7d98ef0c0082ac78b4fa8cd824afb8d55e4a11dffe8f0a39904b739"
+                + "5007f370dcd3ddaeb9d51b38b55e8b3b50f63879fe3702be4d3197e9927cad54bc9cf97f"
+                + "d866189db5cc17c5cbeb98ab6c66d159e83422969fdbaf6746e297e608671d57c2a9b056"
+                + "d6e01883e54073556147d11aaac3c681713defc05a8dec10c6700f9956bf10f3789a5677"
+                + "2536740f7b9956524fc2ed56028e2c3584e41d702a36a76adb749d66c65a0438cf2529b3"
+                + "2da4460d24e6e24b0276b73bfaf0b644d8c0bcbdf422963eaf1091f96ed3738490e1f4f1"
+                + "fb610b9d9d84658c07da28c42cdf46a08a6e2197689a08ddbc3d71227725dca51083be9b"
+                + "9c60cded369a9c83d5d4e173e2340148054cb6eada9359680d7d3b82037a2d56e4183145"
+                + "2f222a1ffecdb5f789fa71d23477c1502a22a9d1ac2a3d57645b09ba338618ea587879db"
+                + "b0afdce7d9853995511a7ed342c76a40eb124b868f2761f393eff0843221ca470481a240"
+                + "f4d7cd5441d5c0eea4b113df0fc103c6b2078029f778e6edaa4d7147e6a5838eaa7b5e8e"
+                + "c0448b09fc610ac07e1315d7a933e6b5c0a9e46b424fc582d98554063f70981e73de0a74"
+                + "9f69853aaa896cd8999ed8079f5476f224e429c8a01158a36400955f10db68ea44855a6f"
+                + "b0cd9c41f4d6adea377b75187ae085e15b893be55e72100f630ce7a7ffee3ff2ee6c35a1"
+                + "cb493c37b1138f503930270adab0d43be8a378f0e9b510b185c72995a06ac69d43a1183b"
+                + "97770cdffa935e26cf9459043ec2ee308bb569d4fc24b04cb84d604a4c2ff28d2acd64bd"
+                + "f18dde078d414ba922cf58a49d3f34ebec076ffe9e2ede4974b8ed3d32a7ef1321b7593f"
+                + "1cfd8d0657eafd77d7718f91788f2339545a69a08883dd33987ca1632e856b2a437427cb"
+                + "fd5d52bd15f408aedc23f4990c2a8d66a8c811a78602745a5982f4750c41163527f6325d"
+                + "a328aa5e4b3c203938f88605efc7200ae74d310e30f37d3680026f1f40aec7e226612de1"
+                + "8486278c551bc76a58319bdd02bf631265d02c849f87b419e1440f45b5555b2d8aadc1c4"
+                + "a6e55584f642ada8bedfa0d4546cc8c0a1848587e034c320a7db1fafc6a1515854764326"
+                + "a3ac4885d948f7a65ac8ada9cbcda5d8129dad40fc46917af2da711d79cac915c990a660"
+                + "ae36249e4ef920538528a5555d0317052d13f6c48bf14761dc9ac8171d2867a81acce2f5"
+                + "9bb740f8f7d63bb3faefdad93e695172e858f94a9b7c324ebff72ea0837dce9dbf0b8816"
+                + "9769e188b8cabcd9a8c004ec3fbfcd45592a58f13b20274ce6ec9c424a14671ed7fb2703"
+                + "5791e159c7f89a95af51142618e34392faafb09271f324648428c34eda8e0a67c8a47eb6"
+                + "5781f18dde0d3f3e1663c2681fef6dddf4a0865c15cf20044146155a4fc61cccf95f7c66"
+                + "d9ecc45b886eff4c2a9aa268bfd4fe83dafe9f77db70a64908eb32c864f58bfea9ef7f42"
+                + "f90a002a87066343494221f648c6bdd471bfe0460b00c9c163bfbc5cbd08808951892a29"
+                + "2f0bb8ec07c4a9d1e7cad52c085069fb4cea253c25f397e898977f3960cdda5d7e276dce"
+                + "c8150aa1e90fe0b7c4d4fb88767be2cc643da803da16e625b930e9cac781fd5df7265b8c"
+                + "30cfc06d4f9c3ea10df4f3fc2b09de24717b6ee4f37f6ae0461776ab425e5eebdbbbe4be"
+                + "ace99d57b537483acd1268429fa18a17368937109508f783e6ceb302b973a2ce41937b44"
+                + "78c97d7acf46fca2736b75067836a0f5c90d7247359c2e790966613db3ce44f76cbe8d2d"
+                + "c5f4ba6bd48eaa05479215cfd7315daf830b249e89821c73a93d3a1942466f1118fa55c9"
+                + "1f682c0ab498bbeb0161be5cf5912bdade63a3ca1b1add9b6d34f79a6902b3e43dcfdaf9"
+                + "4c8abc470b79abc0011d83db29ea975d5f7227f8ff5dbe9ff4ffdace89dadf7002f8f12b"
+                + "bf62446367f380c58200e2800f6a87a81699a7121f30c22b06315f0251f21afaf0044c17"
+                + "d93d074a5d9bd4ed829ba9e69bbf3d5462040487bb9a0f865df3edd370a8dea32fa9a4cf"
+                + "9c82427fc5e2fe1832a000c767b24dc0e920654950a3eebc57e96cdcf4c1355a12c84b6a"
+                + "58646ee809ecce5cbfd6700866b865b28fbf87c9820eadc183a097bc81cd176df7fada7e"
+                + "d7754844038b3a77371e1d7a47989e662dc76231aad1f93b33ac933b90129c5ed4c8c850"
+                + "54cc3c4299e3d80bf994d0b5ba667cd3f0cf19b01fd3c5774e08cde192fa010c3567c0c5"
+                + "8089d499a02f91ecc1d0d44ede178714b42b5b086860a584f3b54f93d2368fa6bf6e6d37"
+                + "1d674abb336692c207132def03f01444094826c30fa009159e80a84717babd7d2d1d2ada"
+                + "ad57e845f8ff0bab8b1244bd8434c0df243dd120a26a3582705ed6f296396dc82e0ea149"
+                + "17df201ef0f0c17b4fc0e9e093029a669e64b6356f5ab810b5088b20b42a82d3c1cd7892"
+                + "c59177274e2572ab9e6d1f6e76965af1bd53e4c115d4d9fde6d475e99186bde72fb21b30"
+                + "45f9712530976c4fb260f3b98a4753b37eb1c667818428caedf8d2212bb79d0669e661cc"
+                + "e2f2d1adcd08f19bb68e39ae4b3f9c0ce772f46406b354fc94f2e7f5f29845f92d64e6ad"
+                + "cd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d8efcf94f2e1546a637fe787"
+                + "16e9a40c03664cfcb26188303d0812f9d708a99c84f45a29e7e7483fc6a23613dd2ee325"
+                + "59863d45eb5954905ae83f57094eaff4a349896f6272430eead83bac308803c2b75eb835"
+                + "2e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0"
+                + "f554bf393bc6565486a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd"
+                + "5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0a4571d"
+                + "f5426261d040767bf18d804c9f1eb4d258d7eb0cd45df70d9feec00ecf4fd422f24aba95"
+                + "0e45ad45b0a3768c46f5427a55c4eb4d2bbd0ae03ed6eb06a6551b2aec848ed9b6179b2e"
+                + "cfc62b472b80dd4c2b514eb8a4fa67bd9486f6e00bc838c49336f0e6b6bc09c1abeef1a2"
+                + "63116f2b49f6074a40c52d786c64e5f3885ec4a8045eac951f0405b4a1d14ac6a72d392a"
+                + "eb153bf3b1a58d3ca0b2a5528fdc87aa01f5167504173dc677d0ef20af95be736f34456f"
+                + "8f8ab9af4167d7c9f393340d187726ec8f99d444b7bb6f5d79fc6ea4ec3c45a6b6b723a3"
+                + "e0bd6c159354aa69bdab614903dde06a9c95b02b22c5249cd50a4db5d03f45347a38d8db"
+                + "b80a9525eb7cd823e0c8cba1a009ec4eea9e2bae0dee4e18052cfaf7c1e8bbc5e52a0765"
+                + "ad3692105fe101ddd2190cf4ba3c0c0ba94b665cb0f68a3ff6509fffe1e9db94b9681999"
+                + "7f4cc88d008582734d0e4dd050d56ce2de68596fc53a1a052a8d2af32d997812ca09b03a"
+                + "b55252afc4f7fe8a5753306f41aba25a8921d6306d6df18ee1ceee1f83e887cbf6dceb28"
+                + "34352803527dcd0e67b98e4fa6815e291c39455da08811686a1861ff10cd14e4f11d2a18"
+                + "5829a6944ab4d231dfd439b731167c168603c0fa9548948244b32e9cabea3f2e4de6eb00"
+                + "01644a1bba6a7b04be72cd2b136125fab2f397ffd2c2ff5a27b5fd8eaa694d043a10d495"
+                + "04f3a4e259f317e76c80599e4953d8dd0bbad7cf33a8864258d7f967a0c48bb3885e29f8"
+                + "ec97ead44cfd4812c4316982c1a5fb6bc6f625a1dc9ae568ff191fed64e2f39904d49a8f"
+                + "ae9618cf83566b8ca2847ef6d0cb8234282eef3152a8d516e4a91a58bd998602765a5902"
+                + "d6cda1e601dfb45d1775ba17597ecf657e0a1f83bd4895aa2842e86addb93c876b6b5903"
+                + "ddd9ecfd38df321c6882d8220d58d979428fc2e3acf880b7d29af181a5de7a6370db1d67"
+                + "c6b2d6ec29e556938604e17dcf36e017914efc8c9b06c24d65274512446ec2eca74ddf73"
+                + "8607fbfcf5e4cc3e5810fc0348249d1b0fc4fb36b5465089dde9583813df57726620d79f"
+                + "0283b785d9dcbd4f041c46ca18985f8c6c01954c0ecd1283f985ec40ff9818de7783ddd8"
+                + "eae9c19c6ada78feca20f56aa0440d96e9d5cbb3e621ccc4e79e6ce6df27bfdf95676633"
+                + "64de5ee75c5bb76cc3ecc09a1732af0d119d45e076e48c75c4aad82d420aa3dc52622f5e"
+                + "6d2be9ae089a91da6b467784148d09b0cffbc582cb4ede2a7320e03bf8cd2b237b341bec"
+                + "9f54e6d49f7797eb53836b1cec068e126a074b510e61620f3352da9b98ad1380de751f52"
+                + "403d5c6c8f3d2391e8b2cc5b6f71edb2ac94b01f113a2759d3c55abdabd3fe819a54c517"
+                + "f37b0a6fb307244fd5d4ea1f17d442bdcc49cabe55afb5f128a85f3f9741d19b1a81e0f8"
+                + "9911a6b74abd9b4c592be6b4b18d21346316445e2c7c284189f908cbadc3e103515ebcc0"
+                + "45142d2f7e0566b663509a2736a4c765f248c85b780f42bdbc213630516a250faa5797f5"
+                + "c3adbdccffb0c60441c80582abdfbfcc6849dcb0e18ec46635276a9514fc39f7bc5232df"
+                + "ebcc2ecd66b9690ea5d335a0e6adb7add31b1f4a151df084f76691b7ad48840c8397bec5"
+                + "4c660190948aa949306f31b4d468086c7a47113b7c024e618199470c86673b51a7f9c3f1"
+                + "f83d5979f3769c4246cbe2db66bf4a410dfc9ccc043dcb6d66fc6d1d20f5567f4c028c22"
+                + "330b05f55aac3ce5048a1309293ebd8de60d2d95297bbfcf921b4492313742185336b5ef"
+                + "206c7b5e2c4b2efb1c25a56d7fc6d3e6e697b81e894d0e8beac6e4ae48338dc4b82174c0"
+                + "57ce3d96c84a0ce7dc639f8ae796b578feff427485efcfcccbf3b6a052fd9bfece616a08"
+                + "e4253305cdab740ab18b16a2b059e68f02a763ecebd684924f3c2b51a029d036f575f525"
+                + "be694e50f6d392c46bb06d40d254f4e565e7ae7c6f6fbe2ae93a96a61e5ab229b3e1c5a4"
+                + "fb3f3739461c53e372e583ed6aea784b49c65dde2e317f02b04a1fedc1a406e5ed0af1de"
+                + "7620ed75a2582123989e87b4685b6178c38658d85f0da6f0df9bd217f087deb4c6d4def3"
+                + "8baf0b20c98326fe43cfd062e37c109101211add82c5e5ad0b4e8a80bbb567516c158549"
+                + "c893d889c82e6b6727b3d3532d2da5d50b86a4f5b4502d990f107303a8141caf12aea749"
+                + "9c2b6af8b4951cd99c88e6e77da8e0399a4781fb5ffeeadc74196aa11efdf9cbafb9cb71"
+                + "d040112e61cea55e37cf89f15f4b7916254b6124ca5f0f46f6cf5fcd5d76ab35ed7a9df5"
+                + "e527d3c87de55e108964147deeab03f906309137ee4acbf14bf240a69d43a7cd9dc8518e"
+                + "fad0b2db71064172691efd2c4b7f18d81bdd8db3df39eed97aa5a404023b972ad5ba4550"
+                + "9c60e78a7842f062631bce472cecb104188a444a9435670acb1a0fc355dfead888304877"
+                + "a39e216b619f0e922d8f4e33b3d17122695241d7e1433c78d9f6c7b9e9d7c5d12984ca02"
+                + "b3c87e90b6f2af4f4974a30297bc5ae2bfc812b45a72d43568181795729513e3c9d9c6b0"
+                + "981262678ba5eac9bc16ec23efab4ea696f28a195086b51037dc2fd433e283d0ccee1bb6"
+                + "c07d72b0a49c141a1b5c5e195c5efa02da9d5638d0f23996f73c19d6359e4b1ff761d042"
+                + "e9b9737a4f8e6d3b126b19633837a9ccbd7de5d3f288e5aa9fa71493e3e8ca2f0d1f5e3a"
+                + "31c029a1dd5d00c1cb2100c2ab14ffcb1f7162216806db0e4ac52bbd4372130a1c888d02"
+                + "447063cb0bd869a483f4a8f07a61f9d9eb0207478f23194d1f230d237ad50e23bd184ac5"
+                + "a5213310aa6704cd8e6da622588a4c16fd44a54c1dc1ba6f7bfc9bcdcc1c661523fd2f6c"
+                + "577713a8f7498981619ecab95ec42f1e1759932a3cdc5ba049fbb3856a2e18cbda268fc5"
+                + "99146eba584f45a2b7acd9057c00417bda24cfbc798ff87dcca42e0ed4c56c5827eca22d"
+                + "09b9feedec97addad5ffc75e2a38c78ef9a7f7d47dd4e2113a5035c87994efb5dc2e3014"
+                + "2ddfd1e1fe0a3443ed7308df2dce03f3526c0b9c06a40338ec77d33ffee72441c334e3b6"
+                + "62e6663476b89ef26abb232542cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0"
+                + "f554bf393bc6565486a44103d8e30cd3bd2eebafc5b78b4f4e2d489233af2e529860a7fd"
+                + "5c91aaaabb0c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce7b7df95"
+                + "6797bca627ab03e01bbbda8c37cd5b7591226b7c5af8405cf76a0f97a7963fd35f115daa"
+                + "9474c2de662c9406b505b3dbaa877edc9750c1ac1e145dcbf7a9a065561c1adea5c6ce70"
+                + "b26ed74fdda3f156f3bf5ab6befb0d79f1c79e3df8c036ade5c916115378e7d9ee2b5d2c"
+                + "ebd94e53fdae54e0a22c37f1581e323bfcaec2d30baec9938ea398721a3f05c4c23eae04"
+                + "3a3ee5851ca98bb83e9a96d270e767f4123f81f7771c0d2e426f053fb7e18c69102aa567"
+                + "dcb1732109329508ad7b1ff3f0ce4875e37a47128929cfd7decc9a93b29ac0679c6177cd"
+                + "7cc62584b6795dbeea8e406f4ca4388389f68d6d35ef511986696170a51143342d2445a5"
+                + "08279b98986e43dcca106ccd2f1b69127b8c22ce090937e79d650dcf93eb47e0e4c74127"
+                + "183bb38f7a855797a6bdf873454aa208032d06ad982500f7f917aa44c5b294d792a65c5e"
+                + "d4c0140d0ef0292d504255df0fa49feb1b75ac08d1a842400420df6f116b74cd1c8cece0"
+                + "81c295ad7bccc722992b88d8f43e4022049a18080fb5103241f5fb770b91adefb80da805"
+                + "1bcbd7a54a6c8a450bfcb7e4d55897f81692d52c1706c51257cffc7c17af02f7d18423ef"
+                + "d85c5c9cb9041f0487ee8aebee1fedee12c6041209dcbb9528dd5d927789d291bde3edb1"
+                + "77743c2ac0df6f973d238a0b5aac65685de044f0d44c78c080b19f734f699012af17fc0e"
+                + "d95e425a2cc45522d06f1a02c746b717a980e61f90568b3e246fbc2f7ad43097eaee464d"
+                + "747078ce8caaba482b12b1f5323b8612abb31948f744f9cc615dae60c4ea39b95f6ac863"
+                + "f68ff68e3e637219ad2adfcfe1d5917c83d3ac10f71964e71c40a4a12a541ffcff34eb45"
+                + "d541c4881671ad7d48b55d62eb260844aaa32b6307fe59fe8e68e0bcbaec5e74268a969a"
+                + "9710b2f5cdf5451bbab88064bb23a0d716174177a24f8e617da9dfd81b43bd13611e8077"
+                + "37fd00e4cdd4f697a4a4f3cff8f3402b1f06a2e48cd54481fe0c775a09a9274a312834d5"
+                + "25312908e493e4ff58e81e3fc1305cf0f7b65f830bb433873ff0c991509959f83546c29e"
+                + "b126b64121997fd58b39a35a0995c7376e334336fff12b6407f934ab702e4e1ccb48fe0b"
+                + "98a5f8fd64e49e0a6319b6837ce65b5197230c5788e4b87af6e3a80396bd299f4752e206"
+                + "7fad434dee3d593f9f536ff1a2cc1a03e791fe69c788cd3d31356d8c5eb0a96256f4fdfc"
+                + "1ea632c2a3f4cc9245619738bd3f80107e1acbca5fe67d760fa047e86ea68017f3c0adec"
+                + "b7b7cb8a602c405f36d58100b79c8729f71b1d7b357ef8689f7979f3963c1649896c5da6"
+                + "f2b83a10cf09d42c47cb4edbd432a99011548c7d5e257a2c1d42ba19fdc9c1d6e7275d91"
+                + "870368c62173e4be75f939e7e7933be2434e6aa7cb335de9344dccf1d4b47f3be1409926"
+                + "7910b2a7e8ee8616054f1b431a5e8d007fe6caf468e4235efe6730d99c900ac6b80de38d"
+                + "4154aa9749d75bdd6521fa1235c2ac17cee5128ae707c2a01e789adc2641057c897be134"
+                + "dd6c38342bf7b4bf64a763a4119ecdb4fb5decb588ff0ef7a20ad6319729db446e27beb9"
+                + "8ee39b20ea5ac6ffbbadaafece100c6ddaa63b0e45bcca30adb8428bae82073b2c494c68"
+                + "c737091b01d731d61883a57f4262aad733defe7f13323df029463430674ff40c4b35583e"
+                + "1c47fe18044893631d5aca6083cced498f285990d852d925a93e2ea8c9027b2fb7d88615"
+                + "3cf0781cdfe498502b13d1ff546449d721a5fb4909ab2389f9c33fc1276b238a5f927129"
+                + "c82360e430a1d539bd54fafa2da6b5f71cc1341ffcb7b700bfd6a6dbfc8b2ebee5dece3d"
+                + "c1d4a5c9b58ef40bfd9f7edbe8d17d934b82345d354500177dd77b4b3c7ade83bf1017c0"
+                + "7f939083c2248dbd0080845403ca0959e2fabc2592e834d60e17dad433c30b677cf71d2f"
+                + "ee5e84869b67c6b13fc72dbdab9aab06fd90f1dd20dfc41d97fe93503f5a667510815608"
+                + "c7feb81140cb1944024fec6a0911156f60b7bde770269f5fdbfd1366ce063d1acb805318"
+                + "4ef1db98332a888bba29d9f63a88d520d40f167fb8ef40c6ab0bd6447b4f1050f092c249"
+                + "96e16311801fdb87a1647b0f953b6b510fe3c4f41872df454f48ef086784a40a550725a6"
+                + "a485a925cd90b0fcdfbd6335273fe013166f00802461d87d7909922522b0a13195b9381e"
+                + "7ead39dfc7993a4951702a1dfadf79fee29edc250a215424fa486bdc073ca00c6f0c50c7"
+                + "164cddf7051bde7d63d97bf5fd780ab0caae727f72aac817afe71e20987f6e2ae0918068"
+                + "b47e40c5fef233454caaeb087433bcd909c1c5bbd8ab2be682a2b7d33267034ac16402ee"
+                + "7e92a0193e8e43f4da7e6fe5f0fb8689ceacc5f530d615d5b5a5db1155d30e4f2187d721"
+                + "731246d809c4f620a34f0d6393597fa5ef7079393a0bb810536235ba15591c5f6e695462"
+                + "bbdf9f5dab32760bcc1a91bda6aedb4b497889dd9115d63206ea34cc87db7ae893498eaa"
+                + "7842ee3b0e7de627c2a73fa1dab8befb78904a65bacb6baa340235532b6e5a69eb4e27e7"
+                + "bda343dab41c05fe4c3b68d2252db55610e56889dd708b8d9e3b09a0a8c748cfe0eebb07"
+                + "eec5eca66b5016d497519eedacb6bcba9aebf407900d3f690a6fc367d9fe985c1425e8d7"
+                + "f880eedbfcd14abab2847f0b4fdd5e593c87929704ad2413b41a6bb6e3d9aba0378dbeef"
+                + "1f233ecb06db0b6cb55dfd9ae7f29344a01cfae710e9f19a06de10ade20b9bb75fd8fad7"
+                + "8ff97592cad02ec2ac0700835a5b617ac64f0a2e5daf9b367e70fb76eab49b36c53c6d3a"
+                + "8e96049b25416f77acd7d6c2125eb2572d20d56e5329152ddb9169b1dc95354c4e403a7b"
+                + "4397495b5f8afe35b62de4bd059afeed08612d28c96d802037e8b4660fc48ba6d703fa4b"
+                + "48d7d4ebc6d2a7ab9146d0e9ec79335395f7fe3b5279ac55245b2b40e8353ee6a0b414bd"
+                + "6338fb46efe2bd8321a9f3f47a3d5f9c4e8837b3f9e9ebb9049cc716f23a877c1db75cc6"
+                + "4607cb3edfbc9e3da395d890b4015c5430d2dcf6f4ac29fc555f3381c24001e0688dbb5d"
+                + "1982225172c042a5d7fa4aa0087a75012cc3d52a2ab3f6fcd2b48cbde8f791c8c4e21ae3"
+                + "a1de22f9c762381b2a4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1"
+                + "958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f47c0d5b2"
+                + "c7731d6aa5632ee1cac7d6c4409fb7c2e64772f6ad6df2d9f38d830a963e55c7b80375ec"
+                + "0059984c13627a6deeae3db4013afb15747568915f816e6f464d8455973a1f92762abf8d"
+                + "eedff1274e501d04a61f66e2c8db2c88f4260715626970d4f06158f11631040143f32160"
+                + "38b4096ac719301f761308fcd50360356e1068e71f6c7bb22c0c8bbcca3ef3f401caa88f"
+                + "d4b71b382d642f7f1610faf04eff88361e8b057fd2385140260119e222c1a14316b64354"
+                + "f0159007ecc6c02f5da72af3b307987a1a96eb685bd85cbaf549a5ec3761af543e43a661"
+                + "b8f18b845e6f5b8ca18bd68529f93e786e2e2b9e26fc1eb31f8f6c8d4df2274267ea8433"
+                + "664959e98668c3e0ee1a30516234f31ec5dd92da42ade1f287592b5440777118949ebf1c"
+                + "e67779ad7e136337a0499903a4240e4b1894ce378e2fa7060c13b2b3734f1034e09ea62c"
+                + "7ab3b82e9540a4d8a46ccefb0e4ab0acf4416a47ea4f70310d8fce41515d944fc0906a62"
+                + "f0dcd7ed8b4b94c0a108836ba2088d889d3b8dcb386fc5018fc81e86016418f7b05f42ac"
+                + "afbaff45963f98916f63506e4b0f5b95b1c72d194070099f1f3063c40c18d89a506430d2"
+                + "783aeb0b4b2730ad344a317ff1ed320d90f4bc46ae23f28679bff44de77fad17ece6fc92"
+                + "634e53660df9dd8ab2456399a123a0687cf2a870ef95a1d01a1329cb342a10967578e570"
+                + "42ae856dc940126dcb24ef8fa3a4bd2e1be36a3b8ec2b90937d79ecc1bc1f0a83f7e9924"
+                + "0281758f222889ed5b14e1cd0481d0a4b1d292aa740ad99740f4276405589f9b2cf30791"
+                + "1018a3e7ba89902a4af49d5170b698742f6b6fc86e0663bcff144c9e27a5fdd824b12d58"
+                + "1533bf42b4d4ec51974ca71aa07e6b4948dd78305e97d97cdedad00a338f497486e3dc02"
+                + "c45b1a5510054ac5cd255fc288be0692ac964be6971d735118aa7b8c0140a41ff700d6a6"
+                + "eb55cb810345b9260838fb266476136097a923de32047798b0aa60ee5c3cbe1bfb9901da"
+                + "e729c8bef75d6dbc830b50b38cc0b52648283ebe41e6e50c28896cab0be3b1130ef39262"
+                + "a3c85fcc50c81d53ad80b6af5fe01f50f20bca81490e41cfd755c68446be3c6bc4908e99"
+                + "ddad9ea4c793415fe34a17cdba200c3e879d233c4f077af252a4fb5ca91e5f151baf6f6f"
+                + "61028e5173e83623ac8c2249761ee79f7ae06ac11d10ee305f20021147c571a26fcd5c83"
+                + "6943c388c42fd74f9e5d6176d22f246f22489a866ac5fa9edd375d273bd9d3913d09cebd"
+                + "44153f1828adbe4bb82bdb52e35cd221b0b5ee9ddd2020b07c884b3649a99dfe15e4cbf3"
+                + "0a32125928bb24998634842743aa616b1cee296657a8e9e1d0a43d4c920860b68ce0d2cb"
+                + "0cea57a1468f99f09d8a60c40150ca4b4cae16c7292f586ed269f75f275d47f51550d68c"
+                + "701766da5d46dd8108267fd861907d41086746406d5fb916ad3da0bfc854b9b9073afb3a"
+                + "23e5e70dae66c0bdac1ca9a18cc77bac100e462a4e8c25ddf93748afab3b15952634a802"
+                + "d99415eabf68e8114d5b2f0b905cae3a8d7174d5cba05eea9d184695f64fac6a4e2b4615"
+                + "01f3f06c65dc52b315791ea35ddc7720f06497489b1344b902fc6c4866eeed6c816b6153"
+                + "68cf76f17bcdb12a02834ac2bee3fd3c3fa64060029c183742a756022dedad385548f85b"
+                + "d5762abc43a30ec5e99673c40f5544dafbf679a564c97c85f1abc18943fcef75000dfa73"
+                + "6f94e9b38678f38d73c18799c4c0a7e998b1761ee03395383c1f47578f2f8e6c2c02279a"
+                + "ee9961c86a3153b270caf6c787fa89b112e2c97cbe4c3dcbe6efdc470c4795660ef597f6"
+                + "059d60948cbf9241fd17d6a8c1591808703312c2019280ee1435be38aba70efd1c83c81a"
+                + "7632fe826b2673b78134ec163747695792008c18eefee0d6ab50ead1aa8c523933b8fa00"
+                + "34b5b54407341da8f7528f54f8bce44608e36663ed37870abdd7cd80639973bcbbceef86"
+                + "702149190a85ebcc25b4fa81c8f34af0e6f5a5969d6392d28b7bc4f290c6cfa28955b7a8"
+                + "571632edcc600b39a1ebb370c6351a08f1d8117ed7a841e8e65225c8650a6c4249c35ca2"
+                + "197b88a1e5ace362121fb2019e505d4b2a2b75d44cfb08bae09bd7b11c0cbcd6af1e0b59"
+                + "6f7b1e4f86834cf1224a56aa4c8ed61f54d273e1e950bc75829b6b80a583481be847eeb4"
+                + "3f60a7c0b3693945ddfefe62970a478127a3533c906825f411fcfd2d44af091f2f20a723"
+                + "aa842800a0df19fa8618806e73de39001978697b311bb776182ef7c78aeeba5cb2328357"
+                + "72d46cf9eb22f7704a885bd7a6fadf6eb2ef4ea92b6ed1a2348dbd89db0eb1f2225ca1f8"
+                + "a6f6f2b40fba42b0d16f7fae7539fee269ae60d97a5fc0cfe2310b2c827d023219cf635e"
+                + "f336389506842995d90f75309fb00142dc995cb883f248fc98c5f04c958836f2c3ccc83c"
+                + "1f35b49f3acb98b858b3b4e6519142521243d9ed73326ece1f2a74f289ce59ab11c60712"
+                + "67406b80134df30f8f574cc1c0f844d4deb642668f5f76fb507cd70711f901ad1d3122a9"
+                + "3e5b5114e100ddbef35cb8d8f75f4807b665da528727bb9caf8d370d409b7ff745c50aa0"
+                + "26a6d8a10220eaca5a51d3552c1423facc0bb428c70d5f24571efd589e89d2785132e93a"
+                + "5afcd566a114190cbc2819d089decfb93f51e0a0bdd2dbba76fa571b781a1af4ab08b0ca"
+                + "e4d9b25a0fa475f40afaac2abf7c73bb340c839d43cee5c71939d7643b5d43e3d6278300"
+                + "b8e087f5248139ab2b3804e77f20fe5a2377b83908f07f99b5bc9eabcd758d322b3cbc6b"
+                + "3073446858e60ab2c91a40385a4ea8f0008d21755b8f2de9084acdb332b0701b476d6bf8"
+                + "dadc695d6d4c5c86490a9e018d05bf57528ca004ee78149db7a5484ec821dfc81cef3ffd"
+                + "c1c01720f77c8d9aa38dd2936d376b67f9ec1f6dd9281ebf0279df4a3b7cdaf62e475b42"
+                + "8e47b1dff67f3d2dc9a329cefe8afe353fd5c1d38ecff1e5f2a31c230c837cae048167b3"
+                + "bced8e52717d46c19ed37b193ddbf6bd6eedebef0869e49153d193edc1f0820f7f1143fc"
+                + "2f7e2f2893790feff22d218a7b7a75012cc3d52a2ab3f6fcd2b48cbde8f791c8c4e21ae3"
+                + "a1de22f9c762381b2a4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1"
+                + "958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b924b20"
+                + "28c3eaadcf1aac3e9e17079590fb454a78519c591aa4f434c57d569104b327c180072d7e"
+                + "54d55668159d637b2206dc4b0e62b8d4db4fc324cfd134a9895f8a9d00db48cf05384032"
+                + "93ed4cb278c2c2164bf2c6b73b12d4beff5e13bbda6cd2592afacaf0aacaa09943213426"
+                + "10963197d24ba3dddd4c8d73a9fffcad563e1f3a5c14bbd832bf8b07b3c9b5e9efbaa44c"
+                + "388682860b3435f9692265b615d6002b4b8733bf7d2b7413ab603f64a3438b1fe1c3c642"
+                + "853f3d610f35dfb12b93069e3d73018cc1da74dd95ba798f54a31ba8c5c65d3234bb3786"
+                + "d51e5dd87806cc963a2845e513119e8570dffcf2c5e9debc027ef43a02be40180b6d548e"
+                + "f3abfabe6c43aada3337a9014f7ec236f0331c068d388b121cb13da57e73173a69e1cfd9"
+                + "434b9ae36ea4613b118ca05d45f28e419fccff3157f5af8e33ced2af3ba149a4a08285ff"
+                + "9385955e966bd2af4891d385d707bde3df8cfe2d2bc77527a70bc9ecdf42ec3d046cc9e5"
+                + "3445df9380f9e19d11d4a9d38920378c2e6f3185189f5e5080295ad807bf2fa5ae0b777f"
+                + "29a4a5925e3b00b4be8af26aafe9780079ca4833d6f5a1b7a3d498b38d12b4c327299c09"
+                + "e0e18528119fe49c98ebeccc2beb54aa649118870f2db2b497891f6882cf3e2d52387485"
+                + "82602fa9bf4bc20bbd7962b113c13b296d591815e78ed09fc113fddcae7817a7ae323a0f"
+                + "7443aaebfd0c3d19d8f505ddf1dc2f63d2d344c3785978de0e6555c5587c5bc30f6f3a14"
+                + "152764abbdee93671659abe43e6c03dde1f8fcfae36a79981d020e339b3ac94fe4353930"
+                + "74da7685ba143695f7d90f54a6f63bfa83a0a2596348bba7c1570d5ce9b2e6498672f30c"
+                + "4d4c8a5374ccfb166ae7a4ba91d439d35c42e73b3b1e925c6094b07a994626cd6c5f1eeb"
+                + "e07134fa5d47044702edbee817e6c1a49397838f4589b736f59a96bcff60739231ec325f"
+                + "e9adae43a58f1a0c3c9364510c206bf31453abe9d26bf7180ad24250c224558c6bb2ce74"
+                + "af2c952dbc5c77767a92db8375db70dccb087ac63264968017c7179540fb123d55872ff4"
+                + "41233fcf66fc7ad6035255052a731ab9ec2d3322d6d2e167311bd0c2672b9f5b04d7b7d8"
+                + "7685e79a322c6d849403f48204bd3e001c300f88f0d84e86364eb6e8b956156ea7cc5c67"
+                + "a38182cd24d057214b02da32cbab42ba240a04040c940d9758a8d081387f6815e646f428"
+                + "67cfc9e8fa08697c3ab66740c756678be7ecdcb0c5114afaff488e9078a32c2c660e247d"
+                + "be018f004bbdd68fefd8fa68ed952020cc34d366d260fa47bf8eff2ef8ab3995c6f99adf"
+                + "a8da3c3887b46a3fd7a1536667caff0df421e64022854a951e5ad65405f85d245b5d0906"
+                + "ecc4cd59c5e7265aa43d1ea522bf144786153ccf0cf1ed1d0ca5230850be8d1172c35abc"
+                + "69562f00525e02ff7d8ef409cab89a4f5bc77f27604fe07e94ec34c92e56662247840365"
+                + "35a244a6530d6ac6a7bcf5c4b3496875620f2baf8ff44284a1bcb7fa2e740f3d9bf17cc0"
+                + "8338420f9b0ff4018bea0289572330fa9fae364dab581b5f8830139602cfa9f00eb3bee0"
+                + "7ccaa37711b1c1dbf61f6988f3b42fd965932e1496b601ae8eaa959b9a7db66e03cf0cff"
+                + "91f820d2ba11d74746f3ca61e14aeb7708177365496037bc6f3f3c3d79affcd68fee476c"
+                + "14bc7e530be355388c1f6ed0fd6393982a3bf9e4a7569553de3baac54e6fd144113dc600"
+                + "dbcd7066cb3a6eaf099a90c627887b94b5a2b37044a38d181fa9e61013afeb952e0a0374"
+                + "cc8574ff774c714f9e3bc976062cce7445a7a06c27598f7e7097d17fb02dba8ac0761c63"
+                + "508688d07ccf869853265b3eacadbf3c8418f95666f9d13c4a6e31bd4abf4fafcb02df21"
+                + "370e4c090e4f32b1f03f083516d07db38b5a6fbf9baee1d80be4ef2f6a5e35f66fe9e944"
+                + "be2f1b89252a295365381e4e95b3dc0a6db4f44d95c64e72e8f0eea55aff71e222ee1c69"
+                + "c23a89512c56b59bc24c13c5091199fce38d330dcefae71afedf5b6adb6b929cc082769d"
+                + "e544ebe42fea0ee19920688e6623b14b91c050508b65a656b9fd3a8cda0c80ef64da2381"
+                + "f8320b5998e249d3c3a4cc3e43058014a9df9477fab2c0b3c93da8023e4ea106f03366fb"
+                + "1ea35e09b3b282b6d9279bf6f715b519f4f8ad2bd7d0cceffd063a9aa196092a5ae6cfd5"
+                + "96abd6d24c6f05e23bfa0bca63fdc20bdafbca6487707fa2a8f258800ccc8e00c761b889"
+                + "23255ed6e359950dbed3e19ac8d3bb69ae1082b697b59eab6b2a101b5d7169fbc03940f4"
+                + "117f27a11a95d162821c4ce463eb69f738d81734616166a21258d57646191fa20e119072"
+                + "782a1c35698c957e3df14df213e7ff19b24980578e883daf7cc9e80c768b44927ae08fa0"
+                + "59e1b8d922230cc216e0cf9efbf40d2b2c5a4189f633ac754aa4f1ed0cf9f61423fab0b1"
+                + "36473c0c6969748666acd5480ebe08d50003b7ab86feb615c5c57098968c9994a7ecd2ab"
+                + "bdb9e9f39f046eef25aa6acb4a4af40834dee75e3ac0dad86d6cad26ff23758dead83c7d"
+                + "b7a84e0e0725cc92cffc942c5cdf750bb44bb3531fc90db88f3f0bf35da16ccb56af77b6"
+                + "675b0ed811925e8738c79ecd735ef866dc1ca0f27e850ec56ac2de54602b59e5ec85cf07"
+                + "7bc32781c1ffd0a5ed68a20d8d08d7003182d961ebdd5fb5e2131a1f93c1e9110b261b78"
+                + "d8282d4104babd68ae69ec88b0b7cc2103d6966c1f920d6446ca3ef0b9ed0cdb6ff390ab"
+                + "f67675d6acb99257c0d4a2579fb53d876db0fe0a652f24a2de95e4c30d9e9b3d16bbfcb4"
+                + "ce7d6fae22ad1bb375a43c32f57d3e7f13fa910d8701d862f0ad9e79448e6e452bd96616"
+                + "67f8bc99bb64eb57f869036f7ea0ee91e2fb24f76611c5716f3230a962d2bb7e67232eb2"
+                + "799da035a536e5325c054a4596029eef23eface2d09b445b98a24d5d308dd575cb1ffc48"
+                + "296635e5575f20c064a0b7392cb893eea7203b42a92081f864c1fea4055fc17552207873"
+                + "c1cd9fc2d063fbd5bff115dbbd8536be1ea6e11989fe66962a32d5b876b18b8c700115bf"
+                + "8594732e66d1726c38d4d835d0c1f4f6b291e011185b7dea770409e01d0d5a455218f1bf"
+                + "cbd1ea1b593baed37f4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1"
+                + "958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "0fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a09838103574457"
+                + "0b2f06cdda0bed27b0fa68939197a2a9bc5297fd75fea460d8f473a17511a8485aaf7f4a"
+                + "92526816d0202df8e08de6cbbb9ef233e032e685e660726e00e400631794036889e30822"
+                + "601bc8bc1c04f10f4e380ec7647c05af4ff603bede69f323488e55f2d0945ab69ee50c8a"
+                + "738508c3244594bc607ae8784fd2411474e493e02e5c854dd4ba2585ab96ffdfc0ccb612"
+                + "820f12dea8a4c398b8610183175a1d740b62a3631412ead199ecbd62a76ab4bd009e6db6"
+                + "5c537b00c15e156443bee98d0cf8292ff0af572e3ce8153ac58b12fa086d342c606043e3"
+                + "352a272a84ca131455b068b4016bfdecae4c38218991a1488e4480bab656ee22842771be"
+                + "cd807afb99f5a036679071b5e4e73f2cc759e357aaa5285456cb90f33cf663498072cb9c"
+                + "717490fc2650b17ca28c8df5ce97d468e2e5998bc48181019cc040915c0f451597a65720"
+                + "3361345d751c0ff445d0ddc81331df3141341b8f141b7fe3ba7770ebc107798cc4d6b91b"
+                + "429522f3a07769b4cf05630a663c740e6249c90aa7f1a5aed19de1d1d4e1a18e45bc7241"
+                + "e1681712acf421d13e49e8d6cc05029dd7a2d3346f37d2aa4bd250da3fde62935d83a554"
+                + "dec973730cc9e5d56d377570fe2ab7ef965703e7c80dd193e94e1907270004ecf7c64ab2"
+                + "2529e3cf44cddbcfcf1454a3b2b59c29dd72a82e36fb5e86118678cb668f4fdd12daf5ec"
+                + "c44eb65a8213278c831b1580fd553b0b62ca2b1232cdca1b2c06c75c22750967dfcc570c"
+                + "42e471350a2b7e733216b3a199051cc475b6fd92ae045823bf358936294210b81ff83ebc"
+                + "18d4cb93efbc70feb6c1b2aa8594e62abb872c956dbc0304501e0605226ba939834ec266"
+                + "66c2e5761e02348c46cce8979db9ddd303f2d8b6830ca7d7ddbf78a3302ad8db1e79e354"
+                + "596fa13ba392ff70a0cd5d9760a56c74e220c657a91117a3ff864182a27ad6092ee94d90"
+                + "80eab46c6a8e78c8f2be5e4c4343ec9fa08ae14dc56c6689b4e60f243ea8d9a895f7f387"
+                + "e3076f203a90b3ff734b4fa643fe460d9f96969659ea63e0eec63b463ff710d927134609"
+                + "a6880ac61949abea0b5dad3d8139e87be6f9ef063ceeca433f594af823d223b047705dc4"
+                + "7955a9a3187ce42ace4b629bb731687c903e2048622cdb7c6d4096473802e1e1ddcde60a"
+                + "1230302e1e205e3aca460847899f4996bb8bd2a53b95e7b3a52e6da59c2ce4ea2413cc8b"
+                + "5e3c610d115977069db9c56f0e561844cc2e5f3419df3a9cbf523f3110116ba13bb6a7a6"
+                + "99eb680fc702ab131712d9972a7580a99cca6adc30d3d5f4c61410affa821d267ccd85be"
+                + "6c1779b6f7e488ead2cbe271d2aab5d21bd9cad69c645f2ed0d6c87b27f719b1172d25fb"
+                + "e7a6b3fa87e9b35499a872ecb699b1a6f921061ce70bb71fbbcdc998efb1f85766bef0b8"
+                + "2672fc32ef73b0d9d9a5debe283c0e548b8fcc25fdc814d309c4152dcd1dc7f743f03de3"
+                + "95d580dfd2890293d3099af9a605a686fb4fca35d64c12d70af73beb36bdfbc841e85cd2"
+                + "01ee1477e23da37f82fe2050b60b63dce75af2cc0d37509d2b63069b09c39595a8d77d3f"
+                + "0f7738ea14f6f860ca84ad8ea53a0030e88efaef3866291c453e3a4ec151e4c2b7b9a720"
+                + "e4eb37d0d51d0d5515a04024009afc739930381a85e5ec88e8b193c27b54bd4e0ef12707"
+                + "5e944393ca32d253e0da7117e33c77b04d29deb5718414d22579f59ec83ed7c66c7c6959"
+                + "ee54074f0c0d6cec55b9efe59cf5918bcd7f78113ea8fd5439b7097bce922ffb7c7195d9"
+                + "89a9d0d09ec5648894c0eb3ac9e9f883361beecf9f9248d6c357b59b8f813b3ab5f882ff"
+                + "a96f1d68e107f277012ebf906ac6b30dd0b807b35f4a3f52e5f4797a42f3bc17c50dec41"
+                + "1b84a107303cc86f7aca05e844dfc356cf3f62d8299575fbff3583dd3d5b7b3c8dae1d3a"
+                + "5513ba60d1940fe054d7352e914099c1a3f98d5917777ef98582fd5bd62cfedc0ca10f79"
+                + "d126301b54c7ed2f59fa046f96c6a647b6868c2663fa6cf864e8a4abe18193ab14c5582c"
+                + "533eaa59befc5f44749f55526d9ef881b2605e6cf9f583417f527aa677bce30efe21bca5"
+                + "4a11056e51dec9973eafc9bee22ada665c152ac5a06ea981a3c71f33f6e633af2468a7ae"
+                + "2a1ffecbc501b8e462cbf36944a819bdca04bd3d7b21ef11bbff3d003298a8bf984591ce"
+                + "5af6f9d8d2b6b05daeabbb1cd215f43a1c7c1ce67c3e16e7797ce56895ed9405fc60f3a1"
+                + "e29da46db8f10831f6cacd5758e6712e3c8f9d84cc04add1fcad6d039b4aa3289d62bcc7"
+                + "d3253421b80e178b1878cc7c9db39ebc717755045850f70432e5d7628dfff4871b125bee"
+                + "5140673ba5ee5663ec2c46ba8c634f93a926aef32d15706ad18e95b9f7fdb431a07481cd"
+                + "2f3fecc88792aa2c8881fd6bc655cfc92415c5127c4c5d74cd942c4ab43cfe8afa3dfb65"
+                + "bdc4c5a015fbea401e5d8152e14095a9a54338dd4bcad18d322cbfc2be7df4d13cce9b69"
+                + "3cf859e44741f1d904591a0d39f262c5d85f2c542f8d7882d41b3ad407e13a6ffb7c3bab"
+                + "2a30141a44910b80219d1e32421ead5530f35aebe125321b510320cf86cc8e68b5473e81"
+                + "5d5624be8035651713a317699f7ecbbee107ab4eef59275defb939e02d42b740bf140591"
+                + "bcbba3c4345d440313c7df7c850119660fd9b2daa4c029736e5c1616306d5ac987f91cd1"
+                + "8cbc15d12becbd9eb445966445d2d1435043ffb287c6b01ac328e445e03ef24563915d94"
+                + "baa7372f46c1d508f2c8b3428d643d405179b88bdd29bb4f42d37dee05ac3de3e8ee29ad"
+                + "cfd70029a798c9f2f93fe818740cf75490bfc43be2373d2051f76832b428fcb52e538dd5"
+                + "2065720c6275f2f94463c37d276595be9bc5aaf7df8422c03ff964cd3056717225d8cd02"
+                + "0ac00b04edfd498ff0a1fd1cf8d67bb02af36f8b723c155df4b19d672f5faa32885a2775"
+                + "2a09779f09f3a41f20b87cb63eb2dc9266980a65d3f206bf5079815e0b2dce5ff09c1ad0"
+                + "c7d35cb8ddf75e7ef52adb0d6897773953b1d1e25b81b16b1182fdc1cc2967d626d3fe0a"
+                + "eb767f3f607cc6c0cd7adbc13cc1f4f6b291e011185b7dea770409e01d0d5a455218f1bf"
+                + "cbd1ea1b593baed37f4e07f7b9d5e779bea4e0a53d3181805c90e9402ad7e86ff5adfec1"
+                + "958b30ea390c04dcb08f86fce7280a31b8cfe9845d58378fc70842721c2f8dfe14dd7fcc"
+                + "2e980cfd62a1902259eac55859b5cb807065f709c7c9383bdea1f48a85b81fb0577bbe0c"
+                + "f7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15bd575d375882c3a"
+                + "cfe716256c7eaed48400af04a47ccde1498662bae27d3e2d56bfba5fd7c48753a374d0a9"
+                + "96e7d41b020b4ab4df7b30af072d092dafb1517a299431d2c23a73c32556001eadd61cfe"
+                + "e7c0026ccd059ebe61eabeb8cafaf206ff36adc213aaf21026dec33cf2a7674d76de0f3a"
+                + "497f850fbba15be620812dd21204a49cbf25a01fa872dcf5b17ec6c2d5396fc5637eb853"
+                + "1e40501c38f40b755f036fb7b51d0b52a02fbf8b24a7a7ecf87d67cd82d2f6b620747ddf"
+                + "3ce6d51547771142c270c5ac4220b7ea68875f8891256ae7d8956e7b2f6b44e174e99b59"
+                + "10f1e963430f84c8ad2e2ee4351e81f0fa4de0ec24db10c9fc9f41a990a53817c952fc6d"
+                + "eb4d6531577244f6c9ef6439f8bcebb60fda5902e5a824f1e8486a7b889747fc31e420dd"
+                + "062e95ab332ae173721a68a887de05e5e1a53d4c2440215f1ccfe84000425cce294f5a41"
+                + "61fd4ec5be1c4b86e81325b40ccedeecd16bff28015982fafe08543ee9e3fd0b2bd80840"
+                + "95e03ce5b75cf5d56dd77f369e3fbd9a66cc1e174e5f101cf3e968ba82b386ec966ac42a"
+                + "e298062172af68e46502d7699b224d05624b19e83c7325a6cdb9c7cb74ab3da4db374902"
+                + "9ca651debeb36c5e5d046ff9bb89edb0d9807e2c7453bfb651c15f57ba9f995b83a7d9ef"
+                + "415c494f0222df56cb54c8a260161df8743f3eb218319ca9e566894065184390defbd673"
+                + "3d6ed2dd6bf330fe5a3fcdd35be1f998184e2964d3e0cc73e0eedb888148f3054843bb7b"
+                + "e79f079961261d913988b6bf5957489ccc2f41173a47866cd5db993453b67663f677778c"
+                + "fba1396332f863d4c20c3ae0e11df868dcaf088f4ba5003177c8e02dbd9188cf4dec2d1b"
+                + "caccc3d856d44d2df68ebdc5e0955913040b441a2830ae755d84bc9c0acc58a7c60e677c"
+                + "1892c430bdfe91e8c277b502b6cfe55f01bce382949ad5ab07a7c7235ac6fd2c797a273c"
+                + "5e6d8e8e88792a5026ff43872ba51927d362f5a3f07e5907848fe9836aeeb300d74efd4c"
+                + "7f49e54da688a0a8daaab3d7e6ac5123d50d9ddfa55ff067e79102024edddbb975bb1ad2"
+                + "d965aedba0de43abaf111d17d7e31ff7284bed34e5315bb5bad0a10f2d4c59dc0f32a02b"
+                + "d44d4170b7c84d5bab6792400c1a02320075529ac15aa5226ba325f91dc32088fc23ce69"
+                + "899d87ba5d40abda982612064cbed6c494b6912e2a25da7d883298ffb50d93486da5d099"
+                + "d78174ab11a0b9313ce48b584a0abecc66b7cd881dbec10b76e382922e4bddcdf6ed8d1b"
+                + "5e42985d539d48873a2ce79f825605684280018125987450666fbe707a7465023aec06ec"
+                + "aeb5bcfe6f4d8173c581627a2aa7bafab417c8c772df17942d821a3367110e5827f84440"
+                + "b708c080321f926cecc33f281d93acae7310b8de0a9c686a92ee4d06dd641ea4f3ee3e08"
+                + "12f38b80cc8c85185c71372b2cb349f5f1b03f3b7fb5f6ad44339fad30e97b076b2c243d"
+                + "22def5d3708e0f934a5b000b2229fe966af39c302babc89638933345ea65e7f050b57049"
+                + "f6957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421dbde48493b"
+                + "54684c0de25064f5c3c9a1ef33f47d6d23dd045356e018331c0c3e4c8bd9d5e259084843"
+                + "95a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e125562c8a0a792084dca8e6"
+                + "239bb9e63cc4c2f2f29d477dd1210d5a715feb863c2267517ada556489ac2d9e111969c1"
+                + "79cce329feb5c5d974357af3a683e6a54c296124852d844236a063316e04754b0a80340e"
+                + "bd1550ab34227edc6e0afc96800cad73a26d1e9a29134bc914e38cf6427abaf463ae1dcf"
+                + "62ca8f60034772e6aa1c3c53e2f5b5b1659e44b3185ea908ab9d49ee524418fe603aaa51"
+                + "7f7d9e2ce586c69b00372d1e5b4d72947a6e1c1ae684a355ae1fc996c55d207156fa164e"
+                + "06f73eeaf14374d8f60cb7c956dca0a778317de3636b9f6d052dd6df969cae29d1ab1697"
+                + "2f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b0e3a3be7cf8fe4"
+                + "136d3fac803d552cafcedf0c0ff505b4fc9719550d17842a702cbac71ea5e59932c11473"
+                + "f7b2e06331ebcedddc590c9699bcce9e9f092a267e6a3937522ccadad7c1e62f2c49fce3"
+                + "86a02d82f933f96c35fe91b338bfbae01f059123f410cd77f3f64d33d4c49de444f95642"
+                + "1e2fc46174f15c0335e6165f289438968612f6b0d67ca5e1ab4a01787134f97a828bdf49"
+                + "7e582eca52343b1bfa189f4c0fd96393494f768fce4c9257c78ba33af3e75a1212ec49e1"
+                + "02093bc179b16903062627c6e9a301b0750d208bcb182d13e63b932cc936ca59a2fdea97"
+                + "0e8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e536752b2"
+                + "2b091e8dd59d3b99e06d1d4c667fd1d2d4be5969ebcad5e4d45da5a8b7f921260569c758"
+                + "04cf1925617df102e29afc0661b0383fcdd4467a65cdcd1661d77a274f0a96b5cde9bbae"
+                + "07442279b4bc97fe7456117ff89b8b83b8833fe56d598a8a9e6bd94c371320999e16970c"
+                + "f10a9910d001aef59471280056e2644a2eaaf5e973e5281d26d1329053f40768d69fe882"
+                + "1d81c2bcccea1442fe26640c36bd7db0ea61da449ea340b22e4818097972a352390a9abd"
+                + "ecb7170653bea3d8e71375f62656155a251ea6317adf5f3b24db23fa75150770812aae91"
+                + "ad06f56316c266528c8141359ae3d077c483d06989d75bebdd3986750863627d7f2d9ec2"
+                + "2c7c6d2fe8b2e7f15a0892a7bfd8a92cc4d91247f9ce4662609f6faac17517bbae3e8491"
+                + "7783a35f14e2632866fae7603d5cedccf5ff6f3cf8013e35b1e1edbcfda3254a21006b50"
+                + "52a84580e718e4b35d27bef768945c5c8ac1ddf811c48e26ff3397ef63c08a112ae23cd6"
+                + "6779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4869aa01b67"
+                + "a73204b8f0cbaadb040ed9dc55385c60d342f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1018221950446e8726a50c6c39dc45cc0f3e07a16341212470105183f4d65533d2e40413"
+                + "796b28dda8e6cbcf11114d7f8541c788de2619b47ab45ec20becc2c8d918ff13a55de58a"
+                + "8f48f2a1a2d49b6bd8b805c41d931c7c98e8908dd84f576a5e5b35154e232940dbb83997"
+                + "f9f3288ef8351148264ba7137a854a401c16b0e4c82b42b97e19b9acf0631cf7c4f919b9"
+                + "a398f01096c577c71a12bc431f95e36482c64a34780fea0393ef639251948e105b304ace"
+                + "0bfe9534b2b330647a50841a8236a7aa6d595da82c5a3576c22c5e51f4059753724f9e24"
+                + "71c575984966533c0db248905969f40d7c0897f7bb747991b2f5d86c8d41182185c540aa"
+                + "7183201b0c03816c62c977925276c75d9d9916b9790d69a7acc0c97d2fad3c9c979bc3bf"
+                + "ce319b9b90f2b8748e1f6adc71ca50f5f4e99286e45a2842677c4cfc6749030824832ae4"
+                + "9b9e0cc60872a52c66c61976945e96d3d4b3e2caf06d9f1fc2ca85c04542536269813552"
+                + "d51fcb3036215140ec0a5776300ec5eb8fd356f7c753f37edd9f5277dcfd450d21d0b406"
+                + "13657f8f59694cc4d10929c06d6b04e56807358c2a99e25bbfc1074b476cfc609fd25630"
+                + "cc1a7adefd0bd1a70354f52bcfbbebd1d71ad82ae168a0a2761f55ca357ed4c05daa393a"
+                + "eb5401f2abea8d545bd7d7e9e0d2614f9d16ff08ab03929f1dec723355403c93422dca44"
+                + "5dda3865136368bbbbf5b20ba7025a773b01c3502c5866cd55db03cc7a7862163d02ebc9"
+                + "58253246689300a95196f44e68093f4d38ada684c17458d302499c070892e67b3bb7aad0"
+                + "1acdb0adcd4b4448a08ee5cd447466b7f0c9e62dc2ddcd7573d20c5bc32cd6e55ce43033"
+                + "71a63db5dace631894696e323997b388dede168f230ef0463e1b06f507b4e4efb5bc59f7"
+                + "bdbda6b6c125745de752714147b8bb4c7025c9e1e7c4c27bf9eb4e7a73704db66e388218"
+                + "bf1b9a64379879d93d89a72b6fe21f1984ff4061fb7c82172768911c1f5116fc627c1d8f"
+                + "e6243c3a357ddb14fe45ff224aa0bf729b5194258396dc62218c7a2fa8ed2338d4dabf3d"
+                + "f8e860953c17b268fb9e83211935f60e6c3efd12980987a884f5ba393e7f4e718bab519d"
+                + "7202d206a54585396230c58d50f31b8afa30299cd3ae381bdecbcd6d1036274127d91378"
+                + "570de56e0699e643f36f8b3ff0abc026e3034c3345ac20c3b7853e004edbffaa2b58f0ba"
+                + "ed14654ad0e68cab878b3858981d7241174aab9abf2710de264cfebfb91d6e18d84d477b"
+                + "ebdfc66ca1b1cf59dbe1b1720ad52aedf893ed1e18bf0a96645d8748f98c03839ec1a90b"
+                + "5b6e44b1d90e58328e9260ef56d5c459df6ed69c8111ed1e9f067d1380a568a26b9a2156"
+                + "6f43445edbbd8e6c135d2f8d261261549aed0cf9ccd2cefd51a75b8cd5979e7844f662e9"
+                + "f39f074f768da87c7134fb13c2cc6e117c20d5a7a158e0252fda8304eb79db219b6b8bd7"
+                + "9299607cfc225d16e7ecaaccbad45e2acc8208c650f6ac6885443b0035dab6c47c6e4470"
+                + "93cf4b6c2fa563419949064dc5e74da1e8d191d4ce5cc0ec6ee5ee1a6e793d90696d2855"
+                + "0ddf2906eef517725def5b4724bef626d49f4124df951f0f9ca9006754eb5e192a1e8761"
+                + "7d382c73a9d5d174277796a7a7b1a2db7d12b640738c761ebd6b7feccd1fb9ef933250aa"
+                + "ec3fe0328d4e25737d47c365b82c47f2874ea09d5fb9fba8e6afe33729d181faaa1dc302"
+                + "ed8b8c4cc67a27c7e4839b80ea9308b9f0f289cf57f44b0f6d36ccd45cb0ae5f06d94d1a"
+                + "0598a4e3079a43075be450771589fedebd9532dd2b87decd5448571db9b01c66226bed20"
+                + "d4bc776cef6105edfba39b37247b8cc17906032ecdcbb0007364d8a12a15bb8187533ad2"
+                + "e1f8ddd31a82e17b99844d66afbbb70ec955dbc0c26df74eb35ab2f0a3756f249ab90fdc"
+                + "b325eb903504551f34793029f2f22fe013b07c7711fa477131dc7ee229af3a075a66b3b9"
+                + "390d70456b64f933181de87bfc61b6d0a50e3729cea96d05d2f51922d193b1493c834181"
+                + "5a3b5dfd2936cbdae30a06105568a0156bd19f752c680bfd2ca3abb69619313e604bf9da"
+                + "49979dba6e84d6756ef54b63192958efb6d323a00b9e1cf4b6f5397a2967f9406b828c43"
+                + "ac982b28e3cb03721be46b0c18718b710fb2ddb29c00d9c75f292a00ae285c168aee071b"
+                + "606877e7cad1b405bd88322c0b0851f92d59ddfc506300ba3506302229d7dc19d094efe3"
+                + "59c4f608ca5aa6a71990b36cdcfc5af6afdd84dee63f810146d1172aead09a8f0ae66f06"
+                + "041b030cffe76c22fe9b91bb86d2bce3a205be0d1b0bbbac34e9b3802a0424077508b32a"
+                + "cae03f4617ac184e63b45f8399c4001d4591e858a98bdf41d45f36934173e162df807b16"
+                + "3b3ca43a650b866a6539728c01627775a9e04c0ffa5b338a525c039bb8c069cc88f20cee"
+                + "f0efedd86254fa5a7ed668da7224bbae2937a30ca3fd6295302570a46ac85b4697ff6443"
+                + "e5cf3669ae25b2e87fc1360b76d8cf31cfa478bdbc49f5ab015581d026fae8b0940e3a64"
+                + "afc9e6344fbf54101411af7119960bdda4ee7610087ec60bf3277ed4bec40cc8f0b237ea"
+                + "a267801364ad865b6d6465215bc39b2018aa06f9a3d3ad3f719afc1144376da88aa9256e"
+                + "eaaeea2f47fad24972d9042e7f3bfab53a9e6e67f6890b651c2cf2aa144225ea8a59250c"
+                + "72072035908bc40fb6cf588a6ff8795a25f6a07c7f14451641bb3c71795e4386331d412e"
+                + "995a4a322948fac3c8607f69dcea30e26a7ab40df2d0a2b108f3788b430ccf30a712e4b0"
+                + "65fd76a1185e8bb50c4b6bb3b5d49c9dd1e63781820b156b069181166d5e2c0969e81499"
+                + "08005428c67e6e31199370174d9709397b932647ed8279ee66943410e275f633a578b1a3"
+                + "22b4a38863d272e16530c8458e0db07867d2ef233b641f554266ec5b2577c3c380c075c2"
+                + "42d71a05d3ea402a783a0cbc6cbf51c6a5606177f9408f56d2b009e8e2fa81d7cfd767aa"
+                + "342f655a991cac57a1d3451508016b70a21d17144a4f5c4d5d7804e34bce09bc36723541"
+                + "24e05482ba2e625070ee4fe2c480c399b174c151a622c2279e73ea1fabf9317cdce950fd"
+                + "50dfc18d95c18fdb071ce28907f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243"
+                + "134dfbdde74521a6e0d8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7"
+                + "d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "112d9f54baa2e5a127ae6876be1157072d10fcb5d0aa1a2c20bdfeae57588880bcbb9a4b"
+                + "bafb6dfc6d5fde84efd20165941b397a9b4252bd5300d01dd49ce04f1276812d6ba47f45"
+                + "2046b2d14a259b69f8f59d5f18f7eb94e79802a2e274ef2a5fd22654dc431ced07d39e8f"
+                + "1ceb4ff753cca6a09d14e4690ceb7ac720686e76b24d781cf20aaa868597728695742fdf"
+                + "07cf03f6f0020c1d0d3880644871f23e24a88e7525085950cc70d45e86823ae135bd3cc9"
+                + "4692b43545ce08cccf22b40c25819bfb439cf28ad38fdf202b9eb25c918ef8d8c6a23959"
+                + "4ee58e72de20701bdd3c46987ef28cef61d77d7aa38670b7aa7145bf83540afde0e4f51c"
+                + "9c8ca639c48133c9d79f656d1bc429b74e28c6ab8fc4b28a650c2dc22a801d89041e2e74"
+                + "2692dbcefff895d84ccbd6617853a15b7465cc2c9d31bf4490ffa3bc3b2264b1be3525b7"
+                + "956c95d8aaaa245ad1057c2f10eb8fc5695ea87b37536d2aba97b35246bc76f71493bc59"
+                + "38cc159cdbf6299b25b6fd451fdf2476e031ae490838f111e62b609d970a6848ab72326d"
+                + "6940628cc31cbdea1d165001fefd08a2cb6e8df476b3402ecc1def57080629ba454af2c1"
+                + "4b73aafaf26f7191966170a98f65f6c3566c381d4e236fdd4eec5d426a19f3dfced96573"
+                + "96c4dfff45ad72019764a5ca8bb3806acfe9f6261f3c6a94a8015b231541bfc19a86ddb7"
+                + "494994c22f175736cce9d2e25921aafef6f7359a10fcd2f8581a99e4ba34c803c9308d5d"
+                + "0d6a8c8b9a5c327f2a4cdeecaf64a99fb5ea9345ee02c999c3b02c4fe8954255ed1bfc58"
+                + "ead7d40af492c40318bdfef6ba6708abcd0f152b4d042280b9d7c1198540b9cbdef6e7be"
+                + "71d9022256d184c99bd6127f679553b73380b56e341917d0e14ff96d95cad886ce4b23eb"
+                + "466bcd1922c51e0b6e3c7e550755563f582e0b607a3bc7f9e2791f3bcad679139a9881aa"
+                + "43a04dc5fb7f4e18da33081f08edc320cd22f1a38c5f4d3edafc7c8458c7e04669f876ba"
+                + "41388c923506daafa8c77398d292fdf6ea020d778d92bd723ab13743f54c457456712bcd"
+                + "e27d2b2ee5e755d753224d2e8f0525d4d3d30efbbe9a3a7967be98e9f3d67564eca294c8"
+                + "a1f856045531d3362abbc2c47f50708b1170921fd1cae4778fb19ef922792ab823196c4d"
+                + "f775cc2524b91c3079b03ae97339d323f29e7f0698fe1cef3f782f9052c91d3d59db381d"
+                + "5b31d007a464fa41f18c079c8b6eb113fc1b87154e8bb3007aa2c6cf8d2843a035ca50db"
+                + "4b91b2d2c66fd458deb486e3173a0ffb787663c2f923134cf171bf91c39b0d16f17fa225"
+                + "a7a0f2c42c6c63dda7b4ef56b5fe9a2725fece75061c50143a01c8f659b55a1a229ebe88"
+                + "1a00451680f590096fc49a89a8e922863e879f87d60b8d3cf13cc43bfb571c59dd53c396"
+                + "fcdaeb71048a739d09eb5554079e0099cdd864d55bcf77ba1f471c1f5bc0b01e1f64cc09"
+                + "2691d6364bfc52806f01c7baf47c605ee7c02df24104cdf996e7bc3f56eb8ebfbedff1af"
+                + "8b0ecb9ab9b155e6a273019e568210fb55f14bebcd0c0701d66188e1da9fd1be4db5720e"
+                + "41aae0579c320c259db4c17f526f67441044f0ceb346cec70b77c522853310f9051aa3f3"
+                + "f1e5f80caa522926b1cafd9b0198433782605ceb03e106e8437b162f750904c94151d762"
+                + "58fc3a1c53bd11a9e3ac96d1509f3fa1556bc8c7470f44e24c94546692ba00adbcf28038"
+                + "43969dc5eed6e1df63cee15d4262c42107e60d5909783cffb3fdce3c1a577d24d63831b8"
+                + "87588ec6c94d62195dc8294b739ae9513d9c4d7fc80dcfe3590d1bda2d17cacab8c4d3ff"
+                + "28df24379d63b41501c93e293e2b44ce75e2afc0c5d8006dd2324b7bc39230696215d7b5"
+                + "ddf3de2d0fcad99ae0e0609575dd59c1819a606b155a86fbdabd892fa060714ec49711de"
+                + "59d75b7a5c667eab7f3a81214cfafbda33e2fe2eca7b469d37d1185c9072bfe35533b473"
+                + "dd701ed357926244f824d9fa01887de1b5cd131f7c577ab7519763fcc17a324fa8eb9490"
+                + "ea983008210e6f351f6273cfe755ba4e3ca62acee62d0e4a90844aae7a9e19c1fe67f7c5"
+                + "08b597af14be61399218fd9a743c8269ec4d33dba3316f19d54cdcc92f7f804321c93ca8"
+                + "c21595fca4a847e5aa53aeab519b28fad58b3832ca77a39295cfa4857e2cf4ed6f619b73"
+                + "aab40fe2bbd64b90e24df5e5972f5b2d5d70cb404e55b33cbb39a1586c12a6231e9e6b57"
+                + "95156362224796b622536b65efc941567f81c791b188f686d0b8105e56cc075e01593996"
+                + "2ec4a28039088474906126fb85dbb21e63ae0d8832ab8366858d9eff26b05d93d14be61c"
+                + "7ff3c58ac7765cea3104bf69af09f5e15579b2a324a6051a5c8cd82b3aa514dad139dccb"
+                + "23e189d547d15a83801a7ba5762cfba46523c80026a5d7030543e2c0db98def7d34ffc9d"
+                + "fe42f471214d46f761b9f433cd25629e423bf92cb01d5202ffa61e7a0ebd8deae02f27a3"
+                + "db30d8ae2ff4116198fe24ca12e1e8243ccbc0172e7949c2a6f40cf0f3f0bcaa71fb9f39"
+                + "7a70c44ea2289853bd60ff8ae5227369fac701c8629cdf5e97e8c0acce98738740a36093"
+                + "8d3b0ed0e583bd1c6b61669b155bbd3c4ad375c05a7b7d55d5c98be66797128f294242ac"
+                + "faada9fc8e9d8e07bd203dc89f7eb3e1e3e13c58716c09347c09fc8765de3b066aead654"
+                + "0bcc818010b19d37d13da4377f5fe2757d76215637b0e9bdb735d42b97ac586c41c3868b"
+                + "5d1226cb5f27e9c7f37e8d49df4f9a6303dd1ab3f8e24c28703430c77beb3d7041612d79"
+                + "844382c36f7c24aa183e40b3a646f6636be77f1241a4790d73898773a4e34363ee9e6d98"
+                + "6ed86c4a67695e14a68f31875208dbdf909c551e75150ec8bb46016e17a576cbeedf95fb"
+                + "8f20bed662c87e011990c66dab9bdaf412ffdc0d9bfa4a376fc75ef800ffff35da20e4bc"
+                + "97a1c43eea60ff3bdf3677f890da0284b666968e769bb94dc63a79d902db591035d449d9"
+                + "599db8bd1c82a8417a52db7cc576db8b32dcbf6537f78dd1d406ecd0baec8d2c31cc73b2"
+                + "e5dfe34b3ead72853d387e4964622578ea79d90d77875e1ee92ed681543f2edff426ca83"
+                + "d072b3d74853c6bb354f3fb304f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243"
+                + "134dfbdde74521a6e0d8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7"
+                + "d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "127e83bd4dea423df79468b9cfbe24893609795d3a712defbe1b6b32a04e273ebff19024"
+                + "25f4d2dc78be2d07af1176320f68b294eea6668d92b31525d501fec8b198a9409d7bc845"
+                + "6b64d5af87e334a03bdc02b5b42db43358554ac50b0c7ee306b0e0b4e7e6a040e314e858"
+                + "11a24f06db8cb36f87108835d7c15e3293d08f943dc720de04dfa6b0fd101939f0383eee"
+                + "dc21aae2a836a86ec029d6fb8b777dc38b371b88d314a2e528f5c93b37d28e531143c81c"
+                + "6fe92dc5a9837ace6b73d4b7a6991374e12133df267333739b2aed412311565f668a6538"
+                + "3bfc0a3bed6202cc305e59fa7f327406cae3b53be57ab26a8cf2c5103d5e62e3293c2ae4"
+                + "2e071155444300eb35dc575d8aea948730e49a4fb3f2239a6521e3d284367524b4cf9233"
+                + "785aded231643046a5592032debf93012eb81e6322211fef8a878f322c065f87ff26b62e"
+                + "2481f8d80f6580fae5de14dbc17e4bdc782b0fc39617d55098873a83f5d21a53d701e66a"
+                + "b4f4a94cdf13244788c7d158d71d6675cd427d6390f07c117987e46ce5b5b4203a5c7ed6"
+                + "c6397c01fecb40fd4184d704cb02b6d720eb54c660ae3fbfbaa6d5ad4475cfcf144d933e"
+                + "55cf85bdc128c6cab20128467cee7080e600bb960ab459c5c9a4bbece85fba5689b4db7d"
+                + "4484e46984ef468ff6d1630af6074896bf490590ed0934373da2db32a63a0d57a0023b06"
+                + "49d0ec8c51797abef288fbdc35dc2a29bea0712258cbb23071e6c0df34a1d0c9abfb00f5"
+                + "3272e770ed4354e0b799532598dfdcfc798f1136ac53c43d1d93185b43c637f7e1cc5571"
+                + "f82d5c4d6c0e77c4382ffdab05e62d4b1eefed6e66bb55c7fbd8914b22ef7c9f97c4672e"
+                + "10a6394770ca4121a41e5ec9f557f9399a3451a3fb106450079e269ed804bbf9ec46f403"
+                + "8ff60eb8fe5538f7e54ec354527e095ba9b8b00b786e3737f335c222281de7f57482ce89"
+                + "f5ac443ed0ab3f109ef444610230b7945ac0c310f14032832ec4beba6d7eae42994b0dd0"
+                + "a5960315379416b93b46560993229c0687d9517c39954f6e4fcf059e971fec6eb386ce4e"
+                + "093ddb512106eaaa3eaee5f2aa0b47ac1d574e06b52c0fdbbaed9bf68e10cbb80998e3f6"
+                + "568fe688feefe1b69f0b7866fcc9276a054e2b907a58445e75ac6197a1e5ea145d0c241e"
+                + "d8c49b444d6121cbb34b6edda5b13d788bc5c5be8776f536dcedf969c44b07c217e6b317"
+                + "8dfd94562766fe1accbfda15c2a839c12f2118e4bbf20581e6ef3503c36f9d782087a5f2"
+                + "bd6f02117c95371ecca6f5ce5ef8b448ae93e22ebbaba20eb8f589d95dc6be819b9f5bc3"
+                + "4928ff447021912d6960acc16865a0758b60234dec2ae38f81b301ced861350ab2bcbdf8"
+                + "7f235fb67d184bc4223ca514fcd7415526ad4851c1718441cbdd68920c3491e18c686a04"
+                + "63b8d872bff97ed3f4b58a03bbd18588a6effed47f6553ad76c966af3dca17754594ddd2"
+                + "9c7b0c3cbbfe322383dc226b328957a3c44a91f4d8a6ee38b96409101d4e400f90b6f9a6"
+                + "2b1e3ff0a09f3cda9d65a02c0a099ad9166029ccf5c458af0d1395246905a4ab926358a2"
+                + "3dc981c1cc84639e28e48a88823915812860fb4db342c520e4d1fd44dddbd0e4d5af623f"
+                + "e51b3cf81ac9f79e052533aaa468cc2886e6e4dc0b24add5b578dc2d103814db57fd0a16"
+                + "5becf8d63628d13d497fbe907e2ad9ec1b44efe4c01183e4770c7f9e68ae5409c75269e4"
+                + "883bc6bedca454d07d7d6b380cab14f8fb670765b62c40e7dcd5ac1b6d9fa6bae26cd04a"
+                + "46f63b77b8edd28c2351455938464fec10f1224c4d575fd2752d7df433e99b9d21d727a9"
+                + "e9c38d8d25e5ff18d73cc01604701489e9ceeb64eabf9585d80270cd46007bc33dbfbdab"
+                + "cb6bb90cc97338e83b39c24b2b7365770cf4a8bd81d142eceb2847fcb9431ac1440b4603"
+                + "2232a4d0837ea2dca57a860736ae2d9c18479c542c1aee8e28cb5884c651774539529bd9"
+                + "c930d8e6313cd45eaf7846488e17568b244d35eec1c1c1f5e82b3734051fad22fa92fc1d"
+                + "b58b27963fec1ca9ff923a39778aeb90bd6fac28191d23ff2b7c3f1e3548e1f2e7c9e837"
+                + "f1b7c9869853aa2c553f60a28a314891dec0c29fa2c165bcf9aad261374efe3742e1c8a4"
+                + "6152fe3b0b6f0055f78f3f630c51c1831664d3235431bee09a640819c66d66276ba5179f"
+                + "e8e42d3a3104ef97aadce12ae48a4279a4687d99f01b27c4956b85ed48ca0b4a243dce98"
+                + "a5c4d5e64284cd2b1c2de43ea906ad21193a0b4011df559c9768cb5f8ad5db4972bffbdf"
+                + "d4188146ecd95ea0445311bf116159bcf5885e1baa382a610a71c1083d6d4cce0a2447c6"
+                + "9235cc25f2ab096442a6d7122456f0c933ab1ccbda77c3b6197dbddcd539987e51041fb3"
+                + "a1ec150bd8f84e821caed229a07f493b6a4506e7903f768d8d6f98f723f3183944bdf52b"
+                + "843ca4393ad586bac30f9b8438649dca25998ac65d56a13fabc86b8f64d7f905520a6618"
+                + "d3ff74c34554f4f767fa6d339cbc79a70e8770646db529cfb1b283f348f859ca66349e45"
+                + "506d5a5088eff622db0aece9da781c8df6c0a6ac85fc24035dee94e5a1ec758ea15a0688"
+                + "079213a40065221d55cc188d34fdc75c93d0883f88aed51daf4a9b3fe2f0729bf43ec5ba"
+                + "590067855814c043eec60fc2839e5b730d52514e0c69d21f69a682f2c32461ee1c9e4e82"
+                + "d63ca620f81f7da3151cedba158f0ac33634ef46bd3573940c485749729eb7db1409aec0"
+                + "1f09d6cc9ee7714f95c787d28c06ed0b4cb1b209a99a584f1ced187343c7155ea0b4722e"
+                + "4484ffede6013b35a6556df7d300356322244e8ffdfee70f362f3cdebeb2aed1bb066c33"
+                + "7ace478158f12344f1210388c47a576f5f1cf1322874944828c8d7d6329d099387e447ab"
+                + "0e3c8b86bfdbcd752bbb0a9996aa63cadcfba05aa6028dd49ffbd2fee1cd8e105ba82871"
+                + "5a51f34cdd3ea3ea3904097bf6a71b4a35cfd158bd9879bfc2dc390d6e5d494713b14044"
+                + "1eb27dd4f34bc34ee87efcc511777a465cef2461227de3f96d24452316c1490f37898dcf"
+                + "e204791af7c1854dff85b2cf7a147eef4437d1b74a1e32b7ffd97755b8b8bb77ff8e2213"
+                + "61df97e56677f0950aec8aa57fad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280b"
+                + "f411e823ca0e4a693dd8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7"
+                + "d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "13e6a6cba48b9f747b6d47bdf7be588c5f82fcb13612f16838d88d7be681ad14ebc78848"
+                + "c9e053887ed436a6470bb99f7f34357811e280409fef6c81ecf07c47772bd52d7ef3333a"
+                + "be7844455951d380c0aa440a11a7fc7208219c1022032fb6aa38d6dc06a5a93c03af45fc"
+                + "74ad049336ae77e28492605b6d429d44dfe4099cd935d16bfd7b1aeb0d4bd06ae0e58149"
+                + "7a600805c6c56e214e01b18e023ece14cc72e1c497311d633faaa52c37160871d008c282"
+                + "ebbbe397ac6ceed1269cb19299317c59521b0676e9afd6c30b893393f38e3eb078937f0e"
+                + "4a5d27f8e2d3770d30e4340d0ffebf72c34275a71ea2d9c4f7a9cc88df9c290625a62e47"
+                + "db8f8b64daaddcbeaeefcc3d6c859023b80fb20bfc145dbfb7a1fd179e5e33a54b8a94f6"
+                + "86f693523a9897e4fb92cbaf3f84a97f4386f105d78ee6101114557c5033695837c9e9e8"
+                + "e72ebd79f52606b83ebf961134c8fa1c743c23ca98c4daa349df008fe3e01b6c49b408dd"
+                + "7e666108a75082e105d9b6a63f1719d2e209ff4ce6e0599b00c8a6a10f2a49e42f6db064"
+                + "e37d08a240592aa3caa535e71f5267fed1b8067281de2dcffdb5c99f16946ac23369d993"
+                + "39e95332843a4beac243e12f5fa7f7d77d8ef8bf9937178da72bfd616da632505f4ab6bf"
+                + "a2d4af7ccf9e54e5e2dd83992c2f68c470a5248d219db5dc1d27a3adacb2926d03e68416"
+                + "3c7f36d5aca0e6c264e7861d870c2dcc0c9c17d344471837430e337e3267ebd4117703d6"
+                + "72e9a7172317387cac46b241dea72dd19a2ba88fd6f296074e9eacbdb3fdb7505ca194cf"
+                + "02416a1824c51ccd5b71aae79513880fa35037cce0015b188752130ba1bb4d92dab1394c"
+                + "b79850e837b30c41fbdd9ec6d8bc4c4adf136b4800eda945eac0d43b60bfb29c802779b7"
+                + "4d3234809b720d3bdb2acf4f5512b1bb3a2a7b6d858440dee143b8f4ba71972ba2816e6e"
+                + "2480318ec9e7b973be3c4c0e5b9a30e083c58a50697dfb3cbde13074f571d01656cf11b4"
+                + "67624f7b1740f8bb114dd44d16ef9d7831af3e9746a1e7706eab43c56614028c44fb0299"
+                + "f558a4c3a5434f8268b1fba1722153b56f5de6d7dfcb456627e6afb1f63720af52d5989e"
+                + "7a88949b9f169831a5e6017931cc201a26bb00babe22c8b3542e9b99f4c2b6d48cf0426a"
+                + "097c4a330c989aa386a6177b17e53c57d0a89694ccc58e2ca6d54cd35eabb478e5f09d07"
+                + "7ee482bb7e01eb28d7bdcc824590fe09f255936c404bec936307771078f06e85ebb5152a"
+                + "cf43f221daa12632b74624ee7bb49f276c807755c7d735bdd3235a5060548edca8b30de8"
+                + "53ac790562c18729a42ce7cadf6349beb2c544a2e65963d7ff135d37f7ae7c21f3952762"
+                + "a458c5005a4be652a51fb9b0a70a61855260785f7cfd6d66ea17aed9d0eb10f296e1f6cd"
+                + "637b3d0a27cce2d68b2d7d194d28501d7ee1aeb0bbf68d028f1b4fc9d15e1a51916294ff"
+                + "658960678128e1922233ba8f53edd120184ad0a84b8c594c5f832ae8ec9dd9f0e543a674"
+                + "791e6bcbd9b7587d70a0343f00fc0d68cc36ec951c965f7f37ea126148de1becce4b301d"
+                + "d8e1e25b9ce2801786eddd06431ebc0f031e5bb2805592c1d67ccab71ab2a7a804d98c8a"
+                + "84f4bf77742256ea78fa7827fabd6c90b58b9637267a16c67f736cb31ed657c60bf2a598"
+                + "4236ccc04005a3da98fb19e3299a7cd5c17fb64fcabd25f6ccc6ebc2fe51c2ce102c368d"
+                + "f9f24601070bccb5d7c72a33c4459635e8f94ea9d9880951ba8315e4a9ab6357388b8ea5"
+                + "21e0966863c9867ff343c1d6700c06f7ba7a2b60b47f19d18dd045473cc5fa30a8e2c1b2"
+                + "4df20b780a5e7c9bf8fa93ff2bc92dc35c36fa52810b96223c1c766bff08f5072c519e8f"
+                + "47d4125e98347ec5b32bc6dca864949b5f6da8df69d469e9b54aca9c033b0461a7bcdc7e"
+                + "22513db7f62f0743c813dfc0b5b314f65f693cc43faab4a2ffe3b3dcf110025dc28d93aa"
+                + "bb827fbe9271d4b4018cafc61c375654db368d06c74078ffe4b91604961f8b616165af6e"
+                + "4a8db517cee0fb6c1d0b9e158570cd82a88dd1774a9985e8839f4cd310e9fa7205662847"
+                + "cd7cb0990794ce6ef4bded0440a82562716bb0f0d3a35b3e33399f2bd790d22917cf7de5"
+                + "d10e829bb648c1bdcd8bd08d484221e8b70ef450f9bb15c711e32800539e95ed9418e2ec"
+                + "bea61e9296dc40dd373c21a5d589f706eed240416dc9c0f3a9775f004f53b741ce420826"
+                + "5ad09618ebe9cbbf1ff62b09d5bfe546d86a4e51159e5fc2a3f66e8c765ee84c8d8a1758"
+                + "89e438c0e7a8f18ceaa2eb53d65136eb83f5cd6bd0ba68bbeacaeb5f624f4e477b9aa5c5"
+                + "0e95dbf2b7a2fea25928228afce1df649720781d2ce81e504bda3a15008f139e96232135"
+                + "116488058175a9395ca594004b9f6417c7bf0b051e9550aa7b51e97ee078330cba85fffc"
+                + "f68d66b8caa8e591e8836d16afecad56205fc3c97eb9f99843507d1201ce2f33863006d7"
+                + "4de14e428556f507cc7630d30791e520c8e80a8f0d56ca9222b2ea5cadcb3f94216b4e25"
+                + "4475cccfa1d01e7a44c87ed2760798d7b1de7c54246bd9d708b4f2f7be13115f50a4f90f"
+                + "f917149633055edc3084b38cf4eb3418ee8bdc6e2106915d056904cba9ac144d1086574a"
+                + "f015515a5e53696683349633e9090ba0fb2c7bfd83cd4e3cf63d4adeae46ba870c3b414d"
+                + "4730f7def55aa56769046686f20e977ba385750a0a7fd2be4d4915eb6458848deb0b6c54"
+                + "f59d95ba69bdc4dbfc9099ad723add5a79753f9ae1fe9f7e4c884fcd06c56bba9dfd6a6e"
+                + "af7df8fa9b7214dc6d5108c7c5301b38557520a86c514906d839525624ac569543d4a19b"
+                + "0ff6012d5ed5be35139e3b3f6324396bf81d1ff382bc352d91727a843e3730ea0c4b2da4"
+                + "1e0b71cf5e6c074fc00103a1521953fa52a3bfe7db0c3f5287a4a89a76c93b429687dc54"
+                + "eb8bb1be1799caf2a6a837fea49011c052ad502112060810f827d9fec19f71a951404930"
+                + "2ee79551c2de36850e5739eba676bc906d0a4939e451ce274906cb848e68558566180f3e"
+                + "4237f3a3332d8080e4f7e570b5ff72481d0cc87792ec9feedf1f241427b60015f4cbdf89"
+                + "5d9577c3e6be45c5a72f25d342ad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280b"
+                + "f411e823ca0e4a693dd8b32f2f839f10ebdafd50de18e34242e5ba64a9c36363976292a7"
+                + "d1129c0dc4de408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1454676496210dda18357eff3f7814ffaade88533c14bfad4b7b6c0fdc1a8f24bf30dd84"
+                + "0a8e52f7da39d007f92b9c15514ac428de42cd810f95ac656c8454c62c9200884b507014"
+                + "7c24a1391706ac006916aeaaf6e1d53d8957f734907e876e109b8055a48bf68fd544d18f"
+                + "df055f2c4fd5900ef4364b525dad68c3ea2b920010478584fd75bcdb492301e66cb5c908"
+                + "ae7d8b4b2c1f9b21ccf19684392623b1c8f72d7f16ac53b48fe50f109053977b02d7027f"
+                + "6591aa0f33ffd04594843ce08b71bd911cca255af7cfdbea2ebce4d3df1409f501ae5d45"
+                + "015e310ae14e7a08d1bc2121c2b1995781492170fac1d10dbb8a0a9c73ca0b89b850da5e"
+                + "16defe8ab18ba8964191f2baa507a8ef09fd2ab4ecaa13bea6afaf0f8e0ec646fb037c23"
+                + "0bba547dc2b4196a2d4608a55dfb6c3c611bca9c8cea05218f7f4abc1faa24ace2c567a9"
+                + "35533a6027f7391d4301e6852349a690a3d9bacfbccfdb39feaaa34bec6116e824824153"
+                + "6a3b7b02002f407f8c2dcf5c3da99d8d4cd228f963f9714952ff87796614cd049b05b320"
+                + "e6e66fe208d4a0a507e8ddb406e9d1b505f1295c1c00a8b0173ecfd079c4fe66ee3d9f50"
+                + "1b19afa594682cbb19a516360a7de00847892a7c01ba000dd2b27bf0488e73d2d6540bc8"
+                + "a93b6ab98f372bf916e2cacc27c487bfa9340ddf706f870723d973705c027568850b5dcf"
+                + "a99e509dd97bd4385a138bc48981fb24ba3e758a66b19e1dc79044e77d3040d898ce347b"
+                + "45bd8c818970ba69b238101da0571d8e5ec21095287af8837d71b6d58264ae89911f7ff6"
+                + "9d0c2f3c188957951ac33a25a65065f5759c28e97a0490507a849861276cb181a01eb79a"
+                + "4d58d44855ed51bdb4aad2d3b3f1277cc918af2b922e5761da214c317f82a18f90b6bb69"
+                + "a3d0c698bfd6d6bae147415168079af05ad566101bae93b9992c62c415009ecea68a7eca"
+                + "09ec1b891b01706729456f194d45c610bcc15e79388103a81706f3c83c853c1252624df7"
+                + "712699b6e00e0b6960d355e9364d1b5246a2fc35823eb5c4752d795fda761c9c41259fd4"
+                + "4a1d6f558ac10d01f799975f897754b5dedde0dd8de461c465ad97fc47b970833e880b38"
+                + "2fe2e556155890824a019e7fd6c8f0f37b592d7f477377b9af5b7d132a38e0e7f4ddad5a"
+                + "abab130f8c47f443bd3dbad545d157b10096f4dab0b790bce1eb227884ffaf318ba0c830"
+                + "28b2523cba86bbd2fe35e048d03e213c4d46d7ac4f3287df3dafa13b2d8c8aff82da5185"
+                + "e5e3f208b1bf6a74cf20dd872cbafdce3c85f5f8581f30306b8c65aaed69c714b89eea9b"
+                + "2cb3004d277d8129cad0e8d79575c23705850742cb608ce37b5862e7a406e89ee18fabc5"
+                + "ac3d491355a52e2e45037fa6b471a377d217b3e86f5cf9b4d0162cdd1fc458ec05671881"
+                + "c34fb833a37591e1d12f9b4897aabf7ff949f247653032a08d50e2aac74baf3914aa8bef"
+                + "8670a92074c09cd7671e83db2a3a3a10c7afe2278f2247ed71709b5f3acdb0742b5fb34b"
+                + "dc5fc5452fa330ad7f1a1233db65baf8dfbfdec5ff9f6229cbe2f66971e5ae99688dc0f9"
+                + "0ddda8a4ab319743377f005900dfe78414c11ad5cfa841d86c62ef7bd9029ab7655ae83e"
+                + "1f46822435eb777f5087238575a2cf5528bd4fe8283d1bbc0c72ef6a68e09ab67e5f59dd"
+                + "4c2bd3677dadc97435955a5b3e3329fc66c9ef5f4922aecd297a69305db66d1552f39709"
+                + "c8448e5c599025af7f1668cb00c42e3438e725432b644972c52a3149b16289319777c08f"
+                + "acca8ca8f6e79e9aa0ba4bf513b7ba3aa401fab006c5e1b12b43cb13d383f33ea760aae1"
+                + "9c5bd28f2decb6f624f4962102a416be1be6bfef71d705620aac9b1852a000102ab82e3b"
+                + "7e27b293f1c9f623d45a875c4b05a186b36dd67684ae87b485eb840589e95a98416bad95"
+                + "2afb22692e0237a20501aacb9b9215076e1349c012cf62272d41c203caeca1ef91a0c860"
+                + "70c577db32c638f4cd32a2bc4162f78cfc1be99bb40a7f1223384c3352b3876aa396daa2"
+                + "28f9c06f5c1873c46bdd8963e4f191b6561e507058af2411f62f879ea3dd502097aa2582"
+                + "c3954094551a0ffcf44f9ef46a24aa3b11f802633691d4817f07ff20318bf1c57f61ce24"
+                + "271eab4b70b66b10453d24632bf5846aff38bdb5290b4f3600ccdd1a3adeab8f5ed5455b"
+                + "0236c3152990cd536a3fa19468728553a51daf65e89fe403117a8379aefbbb1a06a732a7"
+                + "98e0164e1ce93e7b0b5349b32a79f350d4b80554f8e4b4c210547a6dd48c1a4942d15991"
+                + "500154f2c5d11193925a5eba4f1c72b1ff051c619fde1a77300ac523f0f7698e77ca65b2"
+                + "cc13547a2dad2a2652bb15aa0285d30b4fe3524b13574bcf7f37589e5020716a4df71eaa"
+                + "16cecbee355daa29eeb34b4803bfd25727e1db1f2ac35ea4d2d2e82c41c1ece63052b60c"
+                + "3bfaec2485e9e902ade31e456c81318637b64208f398dcb1a83005237d6356a877f0c096"
+                + "db1985bd29ef4cbdbd945a5c4833222dc5239949cb807f352284a2d187d675ccd4b71d13"
+                + "fdd7d3d250561f091a0fec36d8fdacd2665540baa66adbff3abc78e9195cee255808aa14"
+                + "786148755bf7f25537919b06a2143801ccb2bc547019f4d48cc4745b62662683c1275ea9"
+                + "b216049bc7a76f86cdd3810b9071f2112090281020fd20ae9d4b1822861883e796e5c6e9"
+                + "010658a130e3a9f0fbe7e5487bfb9c7aa742d882baa843ec87052a1e798901c0595b4bd2"
+                + "7fb0ae50ab56b7b3bbe96d3598dbb736bfb3eaf900ba2969009268e04f81b31b3f29e97e"
+                + "c70471975f8227fae58e7d6321bd0048a7b438aa06b7390141597033061ea2872eb3146d"
+                + "1d3f84d60a68ea49ba3ad520925169bc4bae951511af51cdc274fe84680152dc3762cd8e"
+                + "0d7a7a5b808e7189cd063e265df583e8365e78d433ea0452412b0e0ccbcac0f4d81f179b"
+                + "86a9759d9fd972a7b6a661a1b1d3acefab07fa337c9c21c6a919ab95580f6f2659bd74e0"
+                + "28ac52e9ed07c4287a8b38861e33c8ec934407fe4aba59537323b15e279d48da39c5ce9c"
+                + "7ee641e1a53bed794ef62b5dc24e9d5c871671fbc746439b17326bc00f91c44719a102ef"
+                + "8728fdd497224505c02249bef26f24bfa8a4478311b58152b2cc8099be1c1b6ed72861a5"
+                + "cdda7d7474144c88eec207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f"
+                + "9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "150b194ad8be8ef61afcbab20228f63cf3daac7181e52ff00def82994cac8cadbc0afd20"
+                + "03db8617889a0023296ec9d028cf0dab03f5a6ac2c5626c62d536decacf9ad52f5b4363b"
+                + "3ae3a3a3451658248edbe61993cc7fd9b6e620f40573c307fdd45100b4454e53f2c3ec13"
+                + "3a04aa9fd652313b434351d996ae76f85016461773ad3b75c23af84670708e414e6e7269"
+                + "72352bff2246f6c0f57b2cedc376a8a8cb1df74d83e7aafc10545a521f31f6e5945ae14e"
+                + "f4f0e973e9fbdaafc57d41eb0ae8b277c10f9029c90be5c50c6b400fc0decae22b5d3eeb"
+                + "3fda02765058b4dafd6d861a54526e51efd1b5cea8d7994fe7293b86dc601d6140b9d3f7"
+                + "f083984c7bddea56acd6b0a381a59db4fb97cb3074a7dce6cc8087c832b5b1c71909bfed"
+                + "4efd41bcef99defd1e6e7b86efdf813c23322cb7083eabb538ee0c105fbb42a33484b223"
+                + "2b4728af68665322e62a515a0c16e12b9bab4443f7aab5d5555814e1cb3791dc3ec2eb14"
+                + "39fe4a7db79d119db0557e3900afedf808bb636e1ddf5fa15e5cd406410771b5de007684"
+                + "37bc29f3242437d94141b28cd5eff641b21be2254074651e4e0e0888d0cf81e6f5b9c3f5"
+                + "6b1a932eb852aad78120866511318298437fbd3c58e7af626dbd32e2033e5eaa5fc16860"
+                + "97e515bd69715d05d9eeff926515a754d3d0684696ea28f8eb88bc8271b839778588d16b"
+                + "56603d1db5534f20cfe0616f2db4e35e0624bb03c99329758a28ff722d3c676025e1780e"
+                + "d3a3c1124a49090b0930dd621e43eda3cdd731a46b3a9724ad32fa32c2901ed0b3925aca"
+                + "cc9cdd9d3d3a196a60a83d83acfe1a9cbf4ca1bb29ec264b94b4bc510736d9916d8dd841"
+                + "2188710e0ea928c566b95b23eb556bfdce5befd3edde10244e98f07e6e3914f366aa6c5c"
+                + "326504baf1c38c97d20b7f447359a213b8e34c0f34556b19380a5477b768200ddaa2ef6a"
+                + "c4b47b9aedb389309aecd9386e9ff169eee2a6fb5436bf4d11742752e4179bfe90b58b07"
+                + "5212e82b073ae89b142069e348af3db79510eb975aa478e4a0b948957b34642e3c521d6f"
+                + "5281d7c8b969f265edc3febff1eae7eb1a6ae68cc2c0e012343eb5b13c466f6df5152dbe"
+                + "c1f3ff7348c6f35c9b9dcd22cfe9f6d7ed39653b16c816b57a99057467eab40ca1fbc48e"
+                + "27d2abb23306068b1a468322ec54a7bf26d4d0f716104bd890a7e40bda60ea55ec2c8035"
+                + "61379fb4d531e510b9d2caf0455e86d1dc1015c87da07288466607ce1981641c9dc8ef56"
+                + "545e394095e1ae7b94fa672017c5360c7d0ea62f09e96b301f7846d9c949ace8bb9d64bc"
+                + "670531f081d90c8b06b1024c41d98b7bf2d45c5f9eab78f473dca5152182391bae24fec6"
+                + "154e0b52512101065e9e5d627676c1b63f3426a2881b184125b4ed06e85bc69cf7203985"
+                + "f083e7f7c30ccfbabf4655ebc350c94325ec9e4bb84f8c8b3b7b5b8696afa1f520fbc200"
+                + "839da20cbccdc72273c3994bd34721684d09743cfe4c751d9f141679c1f504b188b38b46"
+                + "59018d4d2d3feed1ceaf8725faee719528c697156c59d806ff2a05b74f95c654f301c5ad"
+                + "e813f72c9b6a951e2b3d694619aa0825005395282e9bd7d8fcce2c57846d8ce337b7f8d7"
+                + "7de1130682cb93372d18120f27a062e3636e759487edb2ac8a5c9e851f58545c218e97e1"
+                + "e5b623373054a70d6a9ee4b98980380130b572e82b0b7041c71782ac50a9f7287c9da9f0"
+                + "4ed05317b10170f1572f9d6614df8659704af759b899ea2a5918b885bce49cd218f31163"
+                + "1079315566fdb4d6e425910dc6ecfc604312bce7b96121f4324dfc629b08bfe5c109696e"
+                + "7dfb47ff14b764eb0556ad4ce454bb3472322efa28c5867951bcb559120ede8e139a1acc"
+                + "c52eda02c78b32e40608b095b7a629cdd2e8f5a94c06e8cbded10981600812f857d89068"
+                + "61a728ed5d7574823be6fd270d28cb11a1b1729387ebf9e933130a7bf73d4322a11a43e0"
+                + "1c47bf06d76f2422d8c9079d3e93ebae2f5857d27d2fe33bd351cee66d3ff5906a10165f"
+                + "79f4b383f67b9815589a9707eadc16f09585e488dc1f54c93e87158c5841d32bfc0b4118"
+                + "80d3cfc09157b69a3af10bae29040d4db2c5925aa6f4a3cb36c89f026287ca05fcd645c3"
+                + "267d365cb5ebd613311e817d4417248dfe49fd3fafcba7fdeeca699c3c4396c7fd7a1ba7"
+                + "3433ec745c9ee1c7f6479be808716eb9ba1e2724a247967fea7dcecf0e14356c2aa863fd"
+                + "5d42d5234679c07893f4c7c7f10721378b2a6acb034dbe4d7082b700bbab28466fb960fd"
+                + "bdeeff90cdd3ca53b171380b16dfd8c7cc560aa8d6b6db5659a73f34f0cfec292f100a3b"
+                + "ca4783a168e5cd287033bc6b19b361e0c5f6c90e7f0a21cdc1a0baedc4cfc5abd00eb85c"
+                + "6123f1de1cbe334e1455090ab719545d744c90d0aacc37b15c03fecfce830bf47b058ac3"
+                + "e191b7ffb2306461060daf7ee368775acc6ec5281bc79a21492e15885b238dedd0ce33f7"
+                + "3a00f08400330b6df02f9de2519e52c32b08dcefeba3f738be726c7932be0de263fbd0cf"
+                + "26503400b6de7e7842875df7dadd48af7a709797a8268fe64cb0d8ba10ab1d0f7ca396ac"
+                + "707dcb512f753173c2098a5f0338dcdcd72a11e6576fecb4d7921461af44500bc2f64a23"
+                + "2ccf3888a17171c6b8b53c28a0c606bf208a9cf2b7126ba4b65410e96dd7e3dedd4bc3bd"
+                + "c8b334b0e2656047eef365d23d538ae179d2657d1da099eddf792a528d7666278c12b512"
+                + "ed604b0f4c928d2ecdd3aa30296919e0583a64dfec68ab2ec22b2ef5e25b0cc1318e9e5c"
+                + "8c75c3c442f642a6bc9b3292e0ae1368d0b4d38e9a065ce2a2774eda18f702fc7e756383"
+                + "e45f595b50d57e44d9dbff60861e261e9ee42c1543a1a2a5a0d3cacb0731a2f56b321faa"
+                + "8ea7811f90878acf15eca61751c2ea90957902f4c7838542466e59918d3591dac0a85473"
+                + "47a3e3a3c5e0ceaece465a8fcf404af49e736d12b01b47573761d7bcb509aadf1e966025"
+                + "f7bd397c153bc54dbc6d798281d2d37ec52f63cb04cbc9941de9fdb2c10c2376ae1b885b"
+                + "395d73ea478891e281d832372d81e8ac44b87a6aa87e6a2a2b3e44431ec1daa25832067d"
+                + "1c38b9a9379e2cc9e2452faae56f24bfa8a4478311b58152b2cc8099be1c1b6ed72861a5"
+                + "cdda7d7474144c88eec207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f"
+                + "9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1685e55780241c3bfeae16e9aa47ca1781a8a32313637cf47a90a8020128c40c18235c78"
+                + "24578da3ed4ec6eff8a0ee0405e65555151d5e247b7d9b8fc2905e20b4aa91eb87381884"
+                + "a972e21020d2ae661b52f31c54a2fc5d1d09aa8d6f3220907c18d08f3b14042e8d745cb7"
+                + "033b1c6a44d75c91dbf823109d831685d77dcff6aec9fab81f8b7d8a07fabfd26f1fea44"
+                + "7194d6ea01ab1a6640be5cf00eded97b52c7635aff9a71275f429ec63a292b5a849a3db4"
+                + "e991bc44601b45de6e2faeb6f8d436d0943f99e79827193e66855d5dd6e34a9984a210fd"
+                + "d3a2c7e7fe61eb7404e4159c496d720c50f2906a427b4a9edf4598c294d274e302095093"
+                + "5a05d17c6cdd9dee2d00f969d071b269117304f2edee613f7b1826647a427c53ea7b9f19"
+                + "56153214add3d243adc6dee5dacdcd87632265ecba21488af414f026c52cd480d7bc094d"
+                + "8e01b82c814539db6e7cd7be6e74d0b53e156195ce2b7fbcbca345ff7eff58131d931ee1"
+                + "1ac6a46c7d2070a4ed72d3a4d25323b34b793bbe13a6f841639e3829f682a82be037cfef"
+                + "7541a1e211ed637be1e2728f8bb3aae333aff2edb22fa1a832f8075aa3a93e6f282d7e60"
+                + "c987bf0fae9cf5620c3480a5f8f9cf181f455ca40866dd5632dfca464e9a42040de9ac7b"
+                + "aff04a9b530ba11d4378b5c79226bc4fec29002a9fd62f2e5d1b8ca6a635296e2b4dfe1d"
+                + "aee421157d764f9a9cb0b9e0d0ae66479eb3ebe17b82b78f104e308db92df5332d60f74c"
+                + "ac97dbdfe6b6d0b1da4a79e77bde280cabfd11bbfc2297a81bb964b207e82252108738a6"
+                + "013c593d58e231f9830c75f4acd2c44e89af2afe2c2cae2f8d98ac05caacf6c815a7ee7b"
+                + "ee050d87d7acee8a38d8c04d5bf42b7c69f72a2c0097087e03e62767acb8898d14f04455"
+                + "f5eb4d90eb0f49981d7e386e956288de2c2a2acd67b4ce765c491e730750dd81c075e74a"
+                + "f5f03f3a9a25f4c1258ec830075dfb5fbfa9db24da5ad5028b01b485fc717997eb21692b"
+                + "04bef44305708d95f8394db55e82f631fc0e049915a10bbff2b0725adc407ddf5efde0c9"
+                + "a30d3b101dfdc31306862c45829625383bc60fcef4f2f6415a354d34ac9b3914c1a571f9"
+                + "edb034f87e38de2046210950633239c9700b8c8a3bc260ac013d24a42743302a612434f7"
+                + "6b22fbb9f2a6ab9ff0d66f8ce88a176688941f966d79601720e66beab479698aa9caa415"
+                + "cd27b7ab48c5407e20d460c0912630de3d2ca4e9ba2d331a67e3001d8acf56e7f7d4de83"
+                + "df9537a86d3fd41737408934b36898b860543265e21e6aef6992ac39c20def0d2c58339c"
+                + "79ffc8a021604098876327635b53db331cf6e4615251a5bea6df9fec81bf9380e2706f17"
+                + "368de1d931be2f60c002b259455f10065e701bbf4b39303b5c361df424a95b5941ae2b64"
+                + "8ea58457d61ab4b7ae7bce45d02988900bcdb4d362a9652154cf29b90e4525c8e84f9c2a"
+                + "ce19928b94554a5400405c405b51d1f4b081fed1f0457cc3ac0b30cbd20128ed31d10a2f"
+                + "a873a5304f6cc51bc724b3e27c177d7638b236a10cc918caeec08480ebcb120d911b02d4"
+                + "597383f5433bb6ff487c64d22fd4f110acd4f786afbdbd1ca174ed70b74aa6a60ef1ebd3"
+                + "72e78364286f358bb7e8bb711ef30bf6d5d15c54e8ad357e63572bf5c731f7b04c028656"
+                + "676af572578926e51d450146cf9f3ded5cf658ae5fe54012953c5a107f49be08b18595c8"
+                + "f50524e422cb506d9cf9ef89dca3a074f1e5bf693695c70ae32996a50823ec1e54a11702"
+                + "5e514cd6e13c4fb89e994b9572b64e4424506a442bc95f1424da7f0288b2ddce32a7b9cd"
+                + "2cf5358b04dba85baab9139f5d3461ed62b335ce202396450ac163dce5d4dc5285d3a458"
+                + "bd615109ba569ebd03f9c3b77aa5ea33113d8802c994db68cc201aade5e713cef415eeb4"
+                + "4409d534e90a7f7ae6602d7d5411640d6bb86b6002d3c5aef6911e5f1bc9a268be4790f1"
+                + "9fcb44c36a5eb3df8a2660416381e6c422579d3b17bd7e6b0dde78b9e50c8dd97c760a64"
+                + "6e21decd60b84cef479d5eaef1a95dd2c82051a77b7758b63e0549d0f9f05b21acfa684e"
+                + "76d94f2132d7f3d14a0feb639b6756f3f44e33dee5c08413f1cf7a655ad28cf77113578d"
+                + "846eb3071bda1a46d5a5da44503e972d4c4a30107d586bc42f197080bc98dc286a6baf0b"
+                + "90169bea1c27fca5cba27f48ea2c88c0c5010bd37575f1927e7e41423c849f6beb420b40"
+                + "8421b231961afbe816d4cf184613c8607a343762ba7cb8918b81a437b9ba5607da28ca41"
+                + "5cb0946416832cf2773c60397c629780ec97d12dd387e33225996479ba51eb0420b37a91"
+                + "dbc01cc02ac39443a2f324bee1e242e40e5dbe21e630f5acee6f637631f07add8f5e0808"
+                + "79067e862f23a07eef9fc995295657e31557e8f83b1542ee598ac37c7db0b4c4ff9072f8"
+                + "cca1a250f1a5bb59445e2d7539b598fe73517c0ed0814a19db909f4cbb31f14420c7b830"
+                + "784aa5411af0c0e44d577f40f1db3ca0b107385d7da7f1f59d5adb6afc6c284e7e2a64dd"
+                + "e3ccb4332f89417e91fe2c8d734147310e697d3432047c9028194a3cbc00c4e9f2fa230e"
+                + "e11e74bbbfb26854bb26bb8a82e362ad4f6184ff77657eae62401eec537d0f4c01a66b23"
+                + "f0a4d7d96d46ca904cf4a4d4df15e5160f7905fc367f72ced01f1bb918b392376b0682b9"
+                + "d6c4feedeeb675cb3328abff32659632d2258b7a984ab8bc870a39b6da66af28bed791e2"
+                + "85bcf7f964857487d20781e7e5663b71972a5e79faea12f557bcbde684f10bbe49265cbb"
+                + "38a7ca60c2f62a2f0311cb9625f873e43e06f1019d0eabbc3821296b7f322457b1c05e22"
+                + "83b76b0d4cd21f9c9c5ef963c3b16a37a3149310d5186bfbd951343a47b0bcc6163ae387"
+                + "51f7ff5a4c97a3d372c2d953753821c2a048e203bac4a108db3efcd6bd176654b0e3b124"
+                + "871be273d03acb80f076f80868becfbebfa3fe1dca553f11995500ae6b6ca68ee57bc1d9"
+                + "b8490a5f1bbf602b1a2b6ef0e6cc2d7eef7da6039ca9ae8c5d102f840630ba4309335c3a"
+                + "5194bbfe4592a2d03d4cff422cd59cc98f59e839c0e119fe7776ec0341ba89817f708105"
+                + "d398de01b41bfa2c185c2001de672d8f9c83410df28d79dd6a4244e5e902acd5d7818a01"
+                + "986c3689be07c82d33c207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f"
+                + "9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "17e9855cb60d93bb2d02481af386e18eda208da07a6d54dbc916d1e3adef6a0774b5147a"
+                + "2c4c33b244babd82ffbd7b427e5dba03b2f03d37cbc67a77fb098e29afb7093ee22c3524"
+                + "c2d9102e86c25cb10969f4242d8e2892749f0f2d1187a689db7e21462253fc4295dec37d"
+                + "bb5a5ef7990f6f133e76a8f8310dc53a2a19da091765dcf87b8f66fd203309360ab016cf"
+                + "5ea6cf73565502ab2353311a9d6f27c1b46b6d232aee48756c44fc161e78a33b253f10c9"
+                + "d5b7d93ba81b6658f4b08e6823c4639273bfa5052047bc34cea1c83f4f5843d05ff0f60a"
+                + "7293117d2efba1f4f5d1c1f0ed21c57b39024f8b53d31c58bd8facd0e781660000e1acd3"
+                + "60966a1f021c9b4399a4e81f5ddb877f76949275be657444dded8a2108c65783631f9ea7"
+                + "699de7a8a45c2eb8f0a578afe09eaca68c14dd287fd82eed58805e7e5ad848442055c4ac"
+                + "57f775790b7ff28dbfe44006fb9f76a13f928f02c0d72062baa4b0a85c1eef7c3e794343"
+                + "ff8a3733226004bea8614444ca071b097229d2f394ea7815b80b1c21553096c6e6ac0914"
+                + "ec22be15021ccc730959c8a350cca9e0ad2076017102f1c06cb5651ec5c18d5ee6bc6287"
+                + "28004dbb8479529bd317382fd2e60b98328c945e3a500b7c6f979cc64089d839a163be6e"
+                + "03b235670d66ad4f77adcf6f5f542280a21b357fecbe4f63e9875893fc9e3f6694a73d1e"
+                + "3a2a14c8ce8e3259fffa8593aab69f8d72c10ca43f7406634f7e2f14ad4be425aedba884"
+                + "b8347cde4d16421e65392d0c634556a31d0c374757141a759287ccbeecd1a7300e9933d9"
+                + "8a8cce53d28a511df7d1b9436f7f1698e44e46a36d41500ea04428c634cfc45eb5c8320b"
+                + "6f465bb309ce57cb2202e5fa3e7e96985e1e8e92091a9c80862660e4c992f4d2ec3525c9"
+                + "9cb167c2de4aa94ccab3903f394c944f34356e551c5f99bda18687536859fce93d2b6934"
+                + "5b43f550c4f41192c81720e8173af23913e1807657e8bdef97847c4f1376663f41c2caac"
+                + "249c10bf58fe9f64018b0aff45c36f1e9a88fce2f7dc53721b4e009cfebd60cc75f438f3"
+                + "2785a032059f23804c3990a5ecf48aad9eba74fbeea0c8764af325ef39d6c2d6c5aa2b16"
+                + "d8b3d1b5a34a379d14e7c6d4d292f6eb8db6daf8e3a54e2b94b2b74596f0c9cd605be31d"
+                + "2c2f48ae41e4b63a149a46d1f6246c99f80c8663a52e07547c9915433139ea009d5c04c9"
+                + "76f039fd77c006cca0b3d52f4cc5a5fa0d135ec5d503c474ccbd248871cbf32c83dbba1f"
+                + "28a2f6ffd97e4409cc471094b2d17e0ba0bc11e3d023c055695a881dcc35be0e9297688c"
+                + "dd232a56377757992582c94b0037afee28cb74022f92b197fba4a39864b767649ac2a59c"
+                + "1c3ceb1e7397cc977d7e4324c497814dc2361bf5448f4fa97ddd345bd162e6f19036140e"
+                + "158846d32d4d764a3248d59b11a8fcf833ab9eb73bf41b0f14d2f48189951d343192e5f7"
+                + "975ec2283270fa63c47e2bb2e20599d7b2fbca5e6c8145e9570cdc3fec9f1745124c34c1"
+                + "ab679c048e4a5d690b14ba4d5df6329ecd9d68351fb80fb6bec68725012818c7dbcdb81d"
+                + "a42a6df3f2370d23c781313cb6f3c13d6e2044211c6ca9aab77ae19b0c3f3a045b325c0d"
+                + "55e374992f19b508cc25d84341accb92c5eb07a49809483219b16f6020de242bd0e11ee6"
+                + "062d2e7873d56035d65f8a745aa94f70a76f779c379b4d3aa07a70f00cb89d15634b88fb"
+                + "7bdd4809f55f24277cd33ccac1d41b2b850700b71a0bb460e73e84d42e51f513d94e9320"
+                + "050b5e6ff308ab7ccbbc6758c8d4282c17963b67326c513a753de6e14b2778dee8538fdd"
+                + "6806a7abefcc7d85473444b46fa263201c27b84ae995eeea5ec0bc3d2e259a770cdd0f53"
+                + "fb1f5e5118f5de94b04fe96d3b22fb5debd36e5a398a0a630203be681f6f3ffaea4b86ea"
+                + "54b4c34578d4e3156863387936a9d7dfd5fdee46dee058fb9ae38e3d9e3c855846ed5a1d"
+                + "94e46e719c9ff53b21619c16ded399df4e08413d3c5cfa9973aea539b54878f9f23dfdd9"
+                + "e86c531d9e5355256730649d20464ed112b9bd553c2b89fb81d12e622edb5a09097ca740"
+                + "2f19a713202ec57a28bd56cead573b1033c415f5fdbcec3bbcf09e59fb4fcbdab6143dd2"
+                + "0dbea544aaa92c1420fb90c106751fc049def24fded46d55c8262d8cd70737fd50e0fe0e"
+                + "3605015d53545b17164d6854eb9c402517c9b1eedf213f898c9f0a8bc644bea4e6f05b21"
+                + "0cc7f0668968ece6e831e5d7c01fd5ed88e127c146538c42249ea0266960eb011b4d444b"
+                + "f114b654ceeb2fb5bc38a2c033741f0b2779c9e56d6e2715cc7fa639139dc065cf2e9bce"
+                + "c1eecf01b5a9650a6a6833f3c23503c6518d151de857400c69382b2c3d4b2fed81ebf0d7"
+                + "2a71ac7e14105bd5aef4f612c1a23be4471e7623bcebeecb3291426582e59b3dc027ce49"
+                + "ec976e1bda4e2f33528ea2a1a6b960d14478262b15e736af16d92b2f2b4cb4e8c9d2865f"
+                + "33f29fddf4d5f6a4b91ac1022cf502c062523146f9fcb9c17410b45a59e7dbeb911cd22c"
+                + "742504ce7fdd3cfa6e6c7b243b9fa9117ab459ce5b1f829954e9a25dfbdf9daf3e6d57e0"
+                + "216e13b3ec68c87e31ed1a4316567e6a8c445bb8df2766b14a428f3206c2194eeabe9d4e"
+                + "f53305901f4cd344da483876e21fad770df3e86736e264837286a958b15c78caa7812152"
+                + "a59e5ec6512a51aed06d3678d90a040e738710410aeee82d54b8681ac4ec6d0431fe3ae4"
+                + "221a634cff6c0c32a50a1e6e80b2d0fc95573e84ffa136a36f1e362254200ccc23471cf9"
+                + "ac5748edb87b3269e15471d03da47eaef41051a7fe3c3b13daa81c05e12dfa53f699b2b5"
+                + "c07c6654d7f24768fd86e5ac62356df93b621cc0981c4f5d3125c704391be0077934134f"
+                + "8288afcbdec8ee2c17bf3d3fd231262ffec37c4bf3e95f85b4cc924bba067a6470cf7f13"
+                + "d6c5605cfd4fc92136af2c464fc5bdc81209d732d95ac4dc0a43391ff885b7b2837433b6"
+                + "1cf819c11de8d1c09f28effe0c2352dd0d1da8fe2df05c54c0150917d6a86d2e37790381"
+                + "33f233ae3a4f3cd8504751c1f4e5b47a27b3a80371aac2386785819e5d9d46511d2c94cc"
+                + "54ccc05d3ec86d98c8ce8139fe672d8f9c83410df28d79dd6a4244e5e902acd5d7818a01"
+                + "986c3689be07c82d33c207bed1b94f8aa2f5c5039a8066ef158e4b6bd66065fcae02997f"
+                + "9b44175f8cde408c2053d71fa4a02f68e2b7ce49c66f25f1b06c5f700df036615b34b7a8"
+                + "9280b388531f3d942dfc0cc6cd13db48f07195b5336fd4fbbfc09167e5e911990c763ed2"
+                + "48b2a6e029fb55fe62911ce2b25c775858cb1d77c953d306569c36a8218eef73f6245180"
+                + "172ca23e05958b038cf4ca7e58ababef4f5804ac7d4f71bba810b5a9224781c142777281"
+                + "df1330159a77f685ce17bfcace901b7a18a45413ce7250097a5b09b54b7013dd0f5d6527"
+                + "0ada384eaabcf10117b15fa2534d2068d48bf793d6e312493bb54642262b396532466e2f"
+                + "d80597efe641cdf3fcd5426e2b79e3b744d7c9b03a9296bee0492d7c7cb80ed7d45f5e7a"
+                + "bf9a05e9f68e552ae98a41a5bc726d3793d0f36238eea0c807d0009dee53d30e37830b66"
+                + "9657ee848532ed18d5df9f90e3ed668dae422b79bd60b88a3e2ea05dfab475b16218983c"
+                + "7f4a97262e6ff753f1a27c7cb36accf10d2099223624ac199fd42d2b838853b2601cd8a9"
+                + "3380982e233fd34dbdd6f169592fd29bdbc388beb736bd3e0ab85600aaf7c16775df634d"
+                + "a22fcd9cf94e175c4dcd0486119a6b98cd364eed478609a575084bce3db89cc39878f241"
+                + "70d1684f388f5fdfc4f57aa7a2d6a00a41d27ef8feb5c03a5b8e0b24e690ab6794715cec"
+                + "771cf31c07a4f27c5bb2a57ec294fbf30928424b4dc5126011ee41a5b978c89f7577d717"
+                + "cad1e7651a87e775c0439c90a5edb75914baa818efcd1d718e3af6d502fdfb7ca3786582"
+                + "480105f9acb14cfb6467f84344cf7598c0809d0e6f84565f8fe2f2ea1992adfac576f614"
+                + "86cd3408c9b855e5d6782baa895ae4c358e03af41bdca3e566894fbede74972bef286673"
+                + "913d19c79978d8d3aa7d5727cf06100b87e8d0e7f3bf921f45868bb3bb65e2a213dd8ff1"
+                + "cc731758ed8768969ce2ead4535af777e01457923ab3c503de662e8f46cf01f26396ec02"
+                + "285ca196f2d2e173e62173c418c5c6bab4ac991f56eba8aa112bd08a97b926db2bb03964"
+                + "1113cc1db9a8dcc43d3fafec37f03cfd60d0315f556d240366c5f6a5da092e7dd51b1b69"
+                + "f14a94de058d05d2b6245cc3b6455895598459a50a17cac5507a91ba3173aaf65ae8ab34"
+                + "3b2cd0cae32f775015937cf90bc8ea768d35fedd4703216e12c2088c6427b5b2e7d1fd61"
+                + "2df88a8d5b55f51c054c083c701848d4364d02ff0c906c566c995f1b244265f3e2e55a6c"
+                + "b3e6ddc35e928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4ce480091f"
+                + "63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31660341f5"
+                + "8410e21463605ca59116c57cdabfc15c2ef37687b3edc6d17b1e6fb4ff9ac07234bcd5c0"
+                + "199bb9a189c5f2e52703206a4d3b4d1e3ee9a4fc30768179e64280e37b616bd0544dd67b"
+                + "98b349d859b20ac230206c03c042e16fd34a29667c3fe174bfe2c9c2e7f0fcf7b323ccad"
+                + "aa563d50f98972bf07c2f5d65841afa898c38048287f21fd27154ee4ffbc4b1068321af2"
+                + "6af8dafdfde73a18ac4f69a114b521d541b41d25a9c0fe60b32e84519582e90fb4250cd7"
+                + "fd01b9798484e91759eecbeb409844b5cf3483297af2b7e175ebdfb7989d13619df83135"
+                + "1b8169777011e83989fc319b4adcd1fc9c7cb9843934ea166d318df5aba90234c8e0e1bf"
+                + "60f9df24b9aa0387fd0cdc40c0874d72702bba1dcf74fff4f95e321ad43255afc0e43638"
+                + "cc286baf822b9beb0a379148c2960704914d75b4ee30d6a715d28fda1a1499d5c99d7790"
+                + "03792496e78fec2028de27ba83a7610f9da89fcd13fff235e3cbdad71067b0bf4bae3b45"
+                + "6971ec92e4f288ee2f42d11c23c61075f8c34a51246030867f94dea95170db78f96079f8"
+                + "57c176e5ea1386cb97368f13d6dd6caabe907e47ac1d6f128a9b8448c4fca4719fba6c1a"
+                + "3b1700cdd79c697bf8ab7ebf427087ca98a5aaf66995d994be6012a54deaead8c50a0c81"
+                + "3dab15fed0d505d484961d2d5ee66aea45edf6a1d4d43f768d1eca94ee6a84a089cf5448"
+                + "0184afe6d7030a057fe6d6d5baa64debc0da934fc6f3b76d60b7887b484b652c7364fedb"
+                + "d09a9856ee28f26d0b1d6c8120db5e28c0ea5c3018a89528ba3e9253e01c6413559fd95d"
+                + "fdfca4f5e38e764662a8ef882f620f8936c848e7070b02b871c3e0aa89def96ecddc4021"
+                + "e2432097460f5ad3dfbb6d63b6919356401a4d61972449d88c19e9de15dad6b4e5ca8064"
+                + "709b36377fee9a366f3e401ff4085c8980e345a6c0b90f3f349429e67e7aaa649470b409"
+                + "b1fba7bc85f1703e5cc875547de495cd11b99768dcf15fce0d5093d2a6809b08960c41bc"
+                + "c7e4d3ed02662edec79d6166fe957bd54f71fdf8db6fd3bbbef0b5ba37ba98d5aeebad2d"
+                + "184e95bddbdf842b84140b10c8e9acf2ecdd89766fe0387681cefbfa576edc7622e1639e"
+                + "be8f762569ba3704edc26dc704a5c01d03f128e8785470ed6516b5dcef55b82da77ee0b0"
+                + "c08dcf9edca28fa04702041960d874c994b62c8eadbb3cbd6267b9fc568ec06d4f79cdcd"
+                + "496c4df22fe3a9bc0df149f39d83c8690e7d8d0af6362778393cacf32710e43657df466a"
+                + "6a37e0032b7aec14a96ec109b68b85763f402b1d69a8f54b700515983bf3a964eb002a5a"
+                + "88050c4965cce9574ef402a05b529fa1aec5a92bfe688036df8316515f5ed7bcacd14d38"
+                + "a2484940b34996b6c0a103ca0bb041a80e19d38741c2b39db1c188a9bf47916231d43f52"
+                + "46de6340ebc8bee8d7d9e73f1f80aee0a7a7137b191fa91ba00d876470fdf38bb93125ad"
+                + "7fb033dbab4e5aba83cc55202c63639e5d2efde3a63db4789082d9cafdb283ef1c224551"
+                + "2af3a48742ad6b20a01ad330c2615aa9b4c52adc28c0edbd3f16dbb235aad854e0ba4dac"
+                + "350d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04d5a27192"
+                + "e3da65441f820dc54a8f35b7fb38842b1af99b74347df3170cf463a77ca430790a66ea72"
+                + "62c462e85d8c3079d951b75dadf5f24c471078bdf4d50461ff8e1ebebea1539bec4f97e0"
+                + "4d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1801c8a57f1d1ded9d0b09250625293a2b0077a3b657e0d05a2f2334bc7f5c1bf2c5bad8"
+                + "20e92b262ecc22563d59767592e4e57ae62a939f8bdb1e6470cfffe7020b927c6678decc"
+                + "ce203186f411321861288fb7c6c511b245669a379c97a57ea9a481b7bc8086f80a4359ca"
+                + "427af425245afee5845530fa792ef34fb2323f098710acb0ed85a25ff101b5bd6ab3d05f"
+                + "28b979622d91ede3c616b79a67166e58037793369a03c20b1f0519567805089d14ae0379"
+                + "c91020bb68652bd798ecf63f72865c517629abc0764b5da3a4b97091804a355fa23f3313"
+                + "b84e0a9b46f1976e3d4a196e7b9db6ea0f09c16f483e17141942702d7d54f08e5624a3a0"
+                + "cb7801321f0de200f75ddf848554dadfc6f4145d943860762097a52a5e53c3c5b07b32a2"
+                + "1aa9363e2762c16c590284f83136e7d97a5787075bf4b427d8d02b4bb2b2ec08da17ca4e"
+                + "8ef862deb2fd3dd74d4f6c94bbc94340f8a100e0e8015dc2965e973a972c1bad8de8bbf8"
+                + "d3e3359afa2924ec71d00d84de2740336e73a50aa200e1fca4ee16af6d5ccd9c1812ea62"
+                + "874a93e33667cd8bbe9e4d1ccc70adfe02a4bae1d8f685666d8caba1a1c0f9bfd24aab46"
+                + "2bb52481e7b3c119d4f2b4432edaceda7865c42034bfbb926cd40d210bf83253d01e2b64"
+                + "54811579ec21c1d9b4204fbaa6405ae523d862763418b872c19715d835abfbecd2ee30c4"
+                + "a4f1b5aeb777f87b8e046906b21e242728e51f21f41801ad72ba44ac090f68fa86bc55e6"
+                + "e59a2ff7366ed8cf1ab79bbb4c5d7b95f06ad3858750b02b03e94abe302e32cb2e742488"
+                + "e72912f66fe8a2aaf6ea19ed4bf6d8d5d1e2353ca34224b1506aec88fcbf8816145f8c07"
+                + "a4577d299b4fda5c03b6a5fdf18b4d40d0d91f21f4141e1abd66e933d75a5734ba1c3ade"
+                + "a089d3d74561ff757aa1587cfba5ce7257602ec99713949e153dff27665c7dc6c8dd460f"
+                + "d9fc06b745ef32edced2e62da31ad2627544ea794d03e7bbafc6229ae50e2eb0012ff0b1"
+                + "fc19272985f23deecb0c9047fae214a290099539519e742409561408f3d45bcffe71b31c"
+                + "8eb16af21c5cc1f5eed8c682bea75c7eceb1d165aaa928485cac93e94036c488ff224aea"
+                + "7ae89ac52c56f0e20139a6daca48c937455ce342de3251c4fb599f965f5eff6b887a12c2"
+                + "df124f536a8d2ca8f1b0955467dc50d30d5d3f0163d1239e495670598e45a05a415bad2a"
+                + "4d879eea20a2bb9860be2ddd94811b2505957f5eb3b673964385241ae4f2b0df8a887580"
+                + "dc2b131066a559b9437fdd3802518a79309143da5393b534325c5f928823c1704d0dc54b"
+                + "a36ea5b6c7c6e8152bc250ad6493401ffc0501b0712cf2c4a96059ea7e0305acdf15eea0"
+                + "ca7c43a0c5074bbc14103519c59aff3590404525bae8ce552df78559354d60d36aa6315c"
+                + "ad27dcaa25ff673b2dd587e4a55dd78040b7dae6e24ca332ea30908abe9e943e0a9a0098"
+                + "e196f6b00e13c772db8a96a72ad0d64aefb66901890227d25291dd44a91123bde979a31e"
+                + "23e41334d6369ff0f82662ec93ea2728de895d82280ce13bf6c1293e36ca4d9efb455670"
+                + "57ce6f151f39d94c564a975193a2e09fbe7afedc7d1e387e8fb20a78e20b76840011cec2"
+                + "3c0d1b3bcd4c6dd99c3388187760d5448d26b8f4abe29afcf4eb84aa7bbcfce9e7034fe0"
+                + "3b16b201035d4a476093cefa4c636c24abddb1fecaf20666aec676df9a35fcc56346bb1b"
+                + "541837a38f2f6403b364e3f51d1080821614e705b2322dca06712703c8d1da1995ce66e1"
+                + "033d2bd06732950cceaf6acc885d0de8b4353a749905407da79b9ad1901c5e69f3aba9a8"
+                + "5d8c4dea4d0fac468e73037cc2c12205fe6671a3be8f33ad83896876509a7b918c969c31"
+                + "0f6b9fde22aeb86a4ad0eb8160f8cedcaf2d77857c84c9af63e7d539d0dc968582e45bc4"
+                + "c29252d1a33940dd73b0e64242d65e94b43c7a3075dadbd1b1986163a0dac36176bcebab"
+                + "2bd3c7fe279e885e22e3586af8009fd064b75d1c84e62fdfc7444a79c038f8d6788d187b"
+                + "c123d6df6ffeee5f6615f51c6c3a1817afe6b564fdf9820536ee3c922f98051739fd5e5a"
+                + "7c36dcafd4c948527e4bcdeea40ed157b05477890fbf505d5bc24d026fd01316ad7a6ee8"
+                + "1cd6e92db7d47fff83efa61e1684fc901dc57c5cc9a1e5be232db5e4fc72ba121ad43ba3"
+                + "79a14a4061ed7eaf68b564d79dc27ed40c027b0ce2186f548c0c081a00cff2835a0c22bf"
+                + "4b4246684cbea03d967dd6c928695c18d14243f7871a17350bd27e147f7114a27d2639b2"
+                + "f4eb1864104b180ffc2a7937441de99b90573462815861b672a8b9ee5441c246521c200a"
+                + "5a79767d9abbd9e4e736b0e29158c43e07012bfbdb4d07f6e77834408e44ad6eb1a6c9e8"
+                + "721153db53f58d46457cfa6c762d986eac10f725f53f7359b7c30d6a176d70ab370c837d"
+                + "1f1356e9a9861d0014df9a253ec61c781ea5d0bf252baa5c980d984502f3305be82f8a8d"
+                + "20e7496188e8243a0a3d8e07e276d4e3ebd4898b885e723920b9fca0596557079698af24"
+                + "4456f09aa365e72d9854c3082256bc8d6ecbeba1e215fe19e1cd02aee1a934cfcf88fe8c"
+                + "d9f237d6fb7f3cec60ceb15968fd88dc52b166e71431a0606aa7db40f555c6631f6b0101"
+                + "9c274a5e035817fc6a46002c0d1be9ef69a640241f5a5df1716fc331f9c5bef0b7ca0d90"
+                + "593cc94ecc27c526aa88565c436a44e28a981f100a5ed0dd32f78963c37f9e58b283de52"
+                + "48d980b62bf798f1fcbec1b18983cdb66fe532409a714ba683aaa24f824aaf9d39b839b4"
+                + "2e940dfa56ead4e8e814c256d12b695dc97050fe15804c425950d8b12e634369ecfac032"
+                + "b35f176155db6d8cc92cac661fe97280467da2ae8b8f0ca86bd62e31ec55dac2184a159c"
+                + "5062cc2856efa777a133428a7e93c4b2da399573b267e81e4014e1b7be77beae8a909fcd"
+                + "e9436a66acb8c3d0eaffb1d94396373fcc47c162fc7ddaf3e44c6840540b37f80128092a"
+                + "71dd68579d20fb19ca698417e6f36130a6061d89386098fd9543bd9af63b0c9a036a35b1"
+                + "5733894e99251e8a8260b6b15afda7da017571a6c396596b3676e62fc35d9d336dfb10de"
+                + "120b730f5e668ea7fa9ab309702f0030d0436282667f9f2b4fb849088b139e035728c3bb"
+                + "ba3e613731974cb83198d1c50173d66966ead89987cac392387220ca93481f0be699e2bf"
+                + "139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1906488a14d9b3e773cd340156d50e69aea8d1e56a889cd9a8196ffb0daf83226b1b50fc"
+                + "17e100982928a7bff45ded1e73d891929bb6d57150ba61c575f2dc6b97f88a1d681aa8bc"
+                + "75416a9be5d1e478f6c8268172110c1dae6c6821f4bc3b36e93e60496430113054b9d55f"
+                + "804640296b412d1651948b7e9229ab6114e8fd72b95619646dfaa893cb81993ea95911ed"
+                + "ffb1d340b725f993e044debcaca3897700c72cf633b4f4ab891df6ce001e699bc077f348"
+                + "1d4a9d0887bddc533a383c93578c77c050bb4ba0137f2b20de8515730414cc79dbb5e528"
+                + "b5cef3adc1c71960e2997cb22b8172d906778e17e1df502c25a439b193e136e926132154"
+                + "fc9256feecdc84a0bbe507ae1afcca1e36461d0bf285deaea9b3b51fa8ab43d64a6f959c"
+                + "2364c2c411ba6347cb57bec581234ccfb23d87d3aea815ed33950b8c057302d22eec37c6"
+                + "652fa5b590edf8274bf84414bb84543ca06ca72a1ee62c7b53d27a5de9bb0112d9f67b50"
+                + "f566b5314da5721f93d534aac23d6363703940e66b1966a3c9855c167a82d06f42bff6c3"
+                + "64dada10218f313e8b63c5d4ec6884c4b3efcc30fe9bf94887610c5f89ad2f5c4c04109f"
+                + "db53e04f47d6abbf5d5d41ad2e48d8eb6e225201aa5216b5acb5dcda178d18f0dffb43ca"
+                + "a5f2cfba3352932a092b4897cb9026f19f8e2c7c902c08fbc6a9d7d857441708781f0764"
+                + "20a1fb606c517d65fbfc29de2c1d6f935afe6c1ae6ca1e299a2a5eabcf1dad2361f715ff"
+                + "6571ed59ef25c8ee31c31ddd8293e034eac7d51d954034c852228b87d9b371b196c27847"
+                + "0cc686e452b73a82a3cdf049c2e14bd006e5ca7cc075e1ed5627b65053a83db1496cd921"
+                + "f95b8dbb3ea5623508e1a4d8d4d63c6fa5283b4c5e6ed3c5e838ade06eacfe623e00b5e0"
+                + "b6a9cff5e56fbaa92c221a5888830fea13f323787f6ac6148dd9be520bddb2f1584d02b1"
+                + "1b0385a4e011d27c46a5aa236c0bf37901cde0437b8859ea39d902f540eac55b1e3eb04d"
+                + "398ecfdbfb1be2683f7b13e760c526637f33719686f4435a5876851cd656d39f26a9dd66"
+                + "a11530ed9591fe5d9cba3e5a32607d591795b20c30495c3c97bf67e6b0d3b7a37a4ec880"
+                + "9a231685ba999d4786bb8f5c00910ec601b04aacb87c9a7e8a49e2b14e684cfcec971ef8"
+                + "a90636ed01622e5a43908d4c0f43d3b9e2166fb99fbcde24a42adb7c46792b43101ab564"
+                + "85dfff106bdf8c71d9926ebd5c353d321649eb7f16c46e5d5c87ad9b729e1abe4cd587a7"
+                + "6bac24a21e2df697f94b3714e5fe3ba7db4ea57b59c3cdd994e939f3e7130f85cce9b4b1"
+                + "a86f32f0fd5800567a797ff9a35f395fadf51c0687a5096c04b3e3930b7493a6e916e6ca"
+                + "ef5f38cf28d7ff1fdd97f237771f06d24aec61c168e08023a57b1c3f5914cd37920f7246"
+                + "713387391ea6f522e222bdefd71938902e5ed70403966a3af7254b5333298881d7fae9ab"
+                + "abbd043b8a4e2404859f1806e0f876f4c907d749b232b8b537de2465cc9bcea607c0cc91"
+                + "2b3d331a6f8a0a6d384218bb82bb5ff8b5c7f754a6d90acb4f2981a5f8676b6fbba58398"
+                + "a30e49b8d49accaa6f1051744a23d9947d74b8a401d9b42dd41e688ee000ada73c10fd7f"
+                + "6228056e2a5e9c98c1ba264ed0b6b10359eb8f4dd9acc3214ad9cc3a9aeae4085d3f97b1"
+                + "829a902be22a9ae7dbfd8347220b34b1d1b4625a3eb5af1d6cbd690682788fe71bfba34c"
+                + "9af34ed4df7f9952c05ae29e6febf0f463e142e344aaec2bf039879d3a036440a4840d8f"
+                + "3420dc04294bc64abbc33c7b4832da145f24d9036798db73cb99070f51c8d414e377d9c0"
+                + "59060f32beb25694756154568fafe52f32a92735b3553daf4c6ec45e9f212d1c0f50beb3"
+                + "8a12cb9c643101c1232febc9a82f30ee657bfe0563c817fa995342a3428173113ea350ea"
+                + "121d2bf0757f24c8035b38f5abecddb67260b24a1e124353c69eb02d900363c137e82a86"
+                + "9ba34abcdfb5af1c3d8f6158170f238082423f72513ed672d78831470aa681626615d0ae"
+                + "6dbc91180b674e000308277fc0cc7ee964e9ee24ddae6d57043298765da895d1644e425a"
+                + "a100bf88e19e3bdff4204ce5e15e11d24a8b4f53a36b41e6865ee97b9035e0f60bb80781"
+                + "98e1aafeffab03a99b826e89be0a08b22dfafe1dfb58f2a8ee24d0d709558a2e1cff8887"
+                + "b5a3382493658aadf7ccb2a09143bc320670836e083f6c4d5bf4c8841de216639e02c23c"
+                + "c2031af9f34145369384484feb23ba731808a48f3661bea0257cfb8031cf1533b8421ce4"
+                + "e829084d34bf56a8cefdc3b68e850cb9a175b61778c1af6f71af59b3a08cf5ae16b1f94e"
+                + "bb630f5f320f28ad44a46b19605ea776b1c85819c716e51d4907bd24b4598f9a4090e322"
+                + "bdf36302b008325f34fa3aa1645ed13cbcc69907413fbe6459b1faea31e08fbe88102cc3"
+                + "b1ecc24d8401e53bfe7e4687a18cdb5170977f86e84e4249558f0a1cc6095d1e273c086c"
+                + "bbae6526fd81b303f00aad9b5d1fd25b772231a613d08f020640ae675b189cc203e1afd6"
+                + "71b1c024841f24c9c78a78c3a2635b8289cfb7f46500bec7921220c128b14231ac0fa8af"
+                + "2f1a8df3d0ecc407a43e48b5aaf5dd823befa8b94b4d9a57ae60f587c541d2fe403a60f0"
+                + "843262e367952a576f2a7d3401353e8be0c3e3709115d4c92938a52524d5334457b2a817"
+                + "5f157a4d48429e9edc613956151331f20a504c448c0a6d6dcdca8ce9a36bb159a27f1d44"
+                + "459bf22e3ba24ce83b616c6829a5d9699779a27b5c63a38a8b4cf5111b54c04fcf3c94a9"
+                + "7acdce93ea5e0e60b89de0c1a053ad3bece4995554a41a04cdc198addea346fceab70926"
+                + "57954890d70c1ae74662b749fb9316cdc00989253e3f6fd2a0fc5fdb89177b3de8635cee"
+                + "f13a2cf985c7ce2bbc2a737ca426bdd24533b9fca54274928c312ac7997349778a685acd"
+                + "672b0ef09270b693bb00fb4f41578380d43540834cf990bbe7894aecc94870b0ed659eb3"
+                + "05438dbe1f954ac4556e3c07c09dec930891b2fed95599c76be50c9ce888e90395cb74e1"
+                + "a99e94bc42842d153e0c677ec5a4213ef1cb7bf48452d347dfbb6c60a28d0f34a475c247"
+                + "80ed4ee656ba8ac4c3947667c12f0030d0436282667f9f2b4fb849088b139e035728c3bb"
+                + "ba3e613731974cb83198d1c50173d66966ead89987cac392387220ca93481f0be699e2bf"
+                + "139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1a4e0d31122fec26b4016b4cfafa9f87adf5c9174611955bca177562dfb72522266ed362"
+                + "136570864a62c3110b99b8c90b88dbc1358c6a04f33fa55bf7bffbf0cc4970ff2fc02e98"
+                + "cb7e38cf8b761f8375e82ae5ca838717f203229c716615fb3cdec7afa9d4c215524d55b6"
+                + "6cc0f34852055b40a7d0709d9723bbf0bacc329d6553a3647c44f9d5201408c9fb7f6cb3"
+                + "804ac0b52330fdc5a62d6a5f73ed3b1edfd2281d6786060200412905fa788dbd7d668254"
+                + "18d407857b237a58dc4d34a7e29feb7063452fbf7d575e67e6c75bba19a704d300bbce99"
+                + "f1e0f4c672082adc9ebfd8fc994bca9585811a1d5e8b9c83727fd7efecf4c643436d2932"
+                + "4db9e29802e061a10dfe9b622d34944c3a16d55b3b06ca73d32499e5b426c5da9da6ab5b"
+                + "359c16801f7554fbd0f964ec405cd0be2c147d21d6ef3e440d549c57af282b2e187d5c36"
+                + "19bcb1f01c8e87f6d46ccadaa159bc8074e33dd1c4467febc6ca84de82b2dea3cec684ba"
+                + "a0e86b5713103f483ab0e493e95bcd8304c64894ac865a6706a7c389f0e62762a0c90b01"
+                + "21c610bf8dd96d7549cc65a4fa925397c22bfd747f3153677c76c5d44c85caa1ef98ee86"
+                + "22201c6c931392d54367176e82c230c172c1b5c0bada8155a4edcc370a89bb672036a849"
+                + "d6074dde5be747007dd2bfd1a02f9ed00c58fa66d437b2ac651cbcb23d6692cd0c0afa0c"
+                + "fb2e7837ee5fdb2ef1a7e6e16cde5d78bb1c40208bb2297d555bcc6e0dfb90084a76473d"
+                + "07b15ad3689ab0a13cf4d56f36cdc39749e1ee297e4cae1ba93aa3a8e08f00733614cf85"
+                + "900a4541bccb4c2d9fabaf565f70e803dd9b9119e834b908eb254b37a1a5181499a4c2ce"
+                + "2455ddce3fe8e306158ed9d5f6094a5d418c406b1bb7b06d50fd6c8c91a799ff146381db"
+                + "72d5f240c9e7c26da94b015b331b8a0a039082b46cb9adc30c1fe0ebe3935b7ff9b0c0d4"
+                + "739ed39137ee55db1784e1e3c30bde911b8ef76d5961fc93e9bc948a9024b5775344ebea"
+                + "d346469611df9b4771bf8d4602d36c8a32b01195f79b9d2ead9598bc8ebf35b35627aab6"
+                + "1f8c547820a56b1e80150570a7f43b9c4530b1dda5267f40073316e63456f60176c0ab27"
+                + "96ccf608303dd2fd772bf4821c4827eaa99db49ae6e567dcd56aef1fa5c0a63be2f8385e"
+                + "deba27a96466cc7def2125529fbb53fb23cc78b6c021d9c3ae3e32816b6377b4c5a72285"
+                + "911e550212aad03cc8b5427692a9a6a536373a91d689a9318446a5aa58857327145f1d5c"
+                + "00d7e8eb8685dd077e4377860183293051bfd5495b50451a2fba1487d2ba4a74b8f74e2a"
+                + "aedd698278c161afb39f611840cf32587218121cb49e68d42cc278065e3acef23f7b93fe"
+                + "bc0ffa43a6388218f7e142f46e60eda1d67e7f5bddfe68ff9ca39cc6b773cdeeece61690"
+                + "b04b2d7a13996d94f5cbbaecd8d914b3e9bf63f72430d8b8a226ac2e8ca5eb388d33c15a"
+                + "bdad62dba3ff4fbc1cfff5f9a51b2db482c95142a4dc58a14dd1d77c2a24c9f60e3a7213"
+                + "71580be29ce7e4816d9d8caeb72bb865a330b6a3b35022789eb399b1a4e14800dbc20b56"
+                + "542f9c268d45d4b32077520f4082a0a49e74ea51105d77ba95c530c1678c259b331b87d9"
+                + "20b04771e5fd0e145f8ae9f7b9c245080d7a8f58678c7c0ba273ead2751ed4d5e3d53b79"
+                + "dceea6d18fc25d8aa71fef9c6d620a58f73fc641cb75244d6431055a90da70d1c9b914c4"
+                + "d213a058c03c7801ff0a937269c32c728cc74344461893029ddea3d32ba7787d5e4fc530"
+                + "f2d261c69b0d881893040f18f2e64dd4c7ed810bd90f0291030e9ba618985ab5e392061b"
+                + "fce8060fc9698056c266cf0c93bd82f69989b2a273cd7ad40ee31d344007df6888bcf87f"
+                + "966f04b50bb6a32c6b521b216398aa7dbebe49937b07ddf3902e55e3ec50227e034c1621"
+                + "a166d8e166a210eac960bac04eee19e7de8cfac7aa1758ba39aa746a0e9ff139bdad4955"
+                + "51e389e2b4849122875c19e430f9b29d13962298d8fd9cab42338fa458266ce00d2da579"
+                + "e87130479cd523ae729216f62285ddf34674b48fac75a7ace14c057c3ff8fe644080a4b9"
+                + "2f75707535e9978d61d01ae043021f4b096d7b8517a62c05485467eb5152b4fcb23d67e8"
+                + "070c7387da5012ce20e90eb4a8ffadbbd3443ec7cf68be83042fb0be1e5bfa3d7b7185f4"
+                + "73074557a1e2e65a6c747a52bd2c8066cdbc9a3fa6c8167e42af40fffff2ba86715c8cfc"
+                + "96edcfd4397186d8ba1677674ddf22e65565615cb490f29bdf94a224c42e68795ca70d24"
+                + "9b1dddb69ad90c6425154eba691b75d7c93f0b5f36226bbf58a75a7d5eff2e2cf978a4cc"
+                + "3e8fdd2f7c2160c2aaf3b6334130793c8f5924a9fae96c15e68880adea2dc6b99688d61e"
+                + "8d73fc7fe41ad795ff8c82d0248fb22c04da38fe4f57d31c4bf7f112d30827c1f8e1449a"
+                + "af77e03e0e2f265f72dd3f43ef47a91f6fd54244c103b00fce084d0a5836ef8cd89d581b"
+                + "1515d81df1fbad0fbc03e3f140733c17818c66b0f17215858cf1d272853799b936761c34"
+                + "75b98bf5de6351d839bdc23c502d98376c20a3a368a4bd4719157b16552fd175e9dbf075"
+                + "2801a4fc4566e6f798dd3a0644c64b54bc299fbec16952b25ffffcd0925d1d8fa30d84db"
+                + "0e4ea1bf4d1fecf705b23ccf8441f5eb043c79b544186b237e8cebbbb62199347570be74"
+                + "5cba8cd885b803343e3f68c37b3ccf80671bf36fd21f6ebca33a91b9699f3f4e9c98c470"
+                + "579c52e4e2711c8f958d998997d6382687a04304257b049b26950ab6211f0210ab1b4ae8"
+                + "26bb174060d1c4285eaf5f2478f0ea1a2fe2f2a533c8ce346a8d1eba50ea9e8d607af430"
+                + "70bdcd297f4c6aa9b221cbb0c64c594b658759e80c95a502acb4a9a77122397d4b38e1b7"
+                + "abc29690f7e849cc7218b5728ace2c45a0a553c837b0e02c99a71b145cf777d70f9cd958"
+                + "ffac019beb737f653a5947ad23f0a5ced6114558ff99d531af775324573445f772d2051a"
+                + "6969085f2469938c7a7d80b68d2e3365d61f423767c207e96a90fcfb8538d4b9cdb1fe07"
+                + "55a3d0dc112bb47777345ddf800b9b45c71e842ac7ed1525144747ca737fd05256c39c2a"
+                + "349d6f6e45c0feca1cea47d9c51aee2513e2dd53597593a7cb7483192177d7854778d249"
+                + "b181ecd72cfccc0ddd98d1c50173d66966ead89987cac392387220ca93481f0be699e2bf"
+                + "139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1b2b918b4171d73759c65d75782955f2ba009c93fbaa8c12fcb81915c88946dc173c8d3d"
+                + "c60471c74346d547a4178f3386e4623167a672a1486e9876af87cf5d1b6e3c411807afec"
+                + "a3e7b0984c1bc41573b41c374afa07825a322aadbd66ce4a87dd29d8f9d6a7e16fd1a219"
+                + "f1df78f29665622e726f3fb37f8697a31b22722d97179730c371eb6d0a241ca407f3d505"
+                + "739902afbe5499d881fce748d0108ae5b6f19cb4199c54b2a980d6cdbb61faa61b797284"
+                + "610d1b8e06e326e3113873eb9b4313b696ee8d68154269c2e2fc51b3e3717d2bd4b91547"
+                + "2ace0cfe9a390f3f4db0a33967efe3221c63bab18399694ad8e2117b6472be24559eeaf7"
+                + "fe7ace99a7c51fb626f0dcf3191997c39040bec3050c9157b2dfc9cb187340bf12abb565"
+                + "00a84021a15998cee09e52c2ddacee04c82f7565cec229fc945c0590e5a534828da5542f"
+                + "f110177118071bd7fbd8f7dbae28210cb9be39da41a9f26c8e04c65e984ea5fc737ddc9c"
+                + "f638cf68f32a532177fa1e0d1738a3c9c1e275c824657259805759cbb2773018f546e67c"
+                + "136e7911bbdc7917e721c75fcf5e79d26ae31b3e12c952b3fae35d61e95757c4389ce547"
+                + "6fb04df9ae796d7963fc97e63aca9131c87bd7c58765c72e564519919161293061c2aa58"
+                + "90b1a2e43dc24ea5847f8a12b2fcff052dc33dab4df119ff7e08d22dc9c1c15d2795091d"
+                + "14b69a8e9c078c4dbe167a1c98ebffac10cff51bb7b74b59ffe216d925bfab8f0e7f8283"
+                + "7689d25d09acfdaa2388c9002e58c96d32c569f3b97e12c9da43ef2661183caf6d8baab3"
+                + "adac1c15387e481f5ea7c8d93bb8c09a70d662cb1aed7f2012bffa891748719b64f71883"
+                + "614ee80603db3e4b44d91971bb24ca7da8b808e3857143cc0e594ea2166aebf934994625"
+                + "bc3abf28b033769b02b2d1dff3be3654173d69f16a8774aaf71cc175bd33b53dd3d89984"
+                + "280297e32c4f8d5b68dac624eb8bca8d686bcb372b7459a2829a3d4953aa30abcaf27a57"
+                + "e752edaef88e7b684211eb49d2c024ccc2ed0604096f41e924a3e2767bf0d9c50b4eb8b0"
+                + "590a9dd312c54a362ac2584215fa7cd7b5a57aba251ebcf5e96eb328b368db9c90dc169a"
+                + "69649a9bd76e9b3bf717bc6a9b14778ab7be64ddbc696b8c78029c3fcff2971bbfbefb1d"
+                + "c850b14d29dbe563ef00337cee0fb49fb5ccbf90fc7b44f5ccd1f6c5701002a1c8904ef0"
+                + "e4d16fa0a67c73775856c2c8151ad16c123c1b8f293bd57d369ba47002b02c51438f6e22"
+                + "7870c45a73ddbecc0e77b10a5c3074a260226bbfd388ab2098a5d6434a54e9a284898272"
+                + "ef57347959bb208cef4146a6102bb3ebb6f1d7f65e6c2a7225731d13fcc3d712a7949633"
+                + "1bf0954db6aa47ef73e96819552ee743cf48bbddafab47536a2efca6aecdd14a9cc78079"
+                + "2daf6b9d642d8ca6e94bffee8a22ec57356954bc6dd7c79db3ee0fd1918d1746a5f63896"
+                + "760821a1f28697fbdefebc5aec52df53a080f7befa05fdc91a4d0ed245e7d88104f4f9b5"
+                + "fd27578249f1b1ff3bd065a420834e9288ec22eebbb606bb8c17e81866855a0a802bfc78"
+                + "61cddbb82aa895e4c0ea0fc5ced262d509db22e7844044b006bc0fa961127f58f157a8e4"
+                + "2e213c9d443423ff3ed2a7e185d89c2ee2c1272ec979fc5a681bb166a52471516a99d960"
+                + "f843fce245574bc837800e90e602c32c3d85eb12ec917d9760b1b9b0f82d356c7f7508b0"
+                + "8db62b42e4248ec0bd01191fd137908b763e76b6fed6de739e89731d2596db40929a4ac1"
+                + "0f36fbbac1f747077ceb61d6f02e7937e7d9fa1e5134394cee69641008fe2228493fbebd"
+                + "3b7b80d4867b5207a0a34d64daa6b7e1361285dc0394f6165f886db4c45c75ce04bf6c36"
+                + "66dd18c3b2af56d4f88aec604ec681ab08f0d6f67f1a46148735c165c34c8f64155ffb69"
+                + "5d030b4f4f25a403d8bb6c0b129679a719e35df213522910c1a285934b6a5701a17a0a3b"
+                + "35f828f1a5a7562108d5ef53005ab1d662a4fa4f3327a0c48509f6089732ec543e7ac8b8"
+                + "d782c9d59ca8b0f8d8eff8908b0f3dca1884e115ea799eb7aa4a95ad1c55d07350ceb411"
+                + "9ce578c217f5138b1d83500b7b102113c859410213f2bf10b28cbfe701d9ea3aacd25706"
+                + "6eaafdff54175bfc032dfbaba39e434f37a67108064f348d3d24c824ebcd77814ecbe608"
+                + "29d5843776a3e416442a9b7f17c46511d3951105c68315a44f753237d4a17cc63e0b4728"
+                + "9c08b5021ef09645aeb0904a9a680b6e689d27b13e70753cd6fb1db3f29d24fe60f30c26"
+                + "4c8c594b0af315d2a93dc6f6712a2e936e5de69d393fbf6225f272178fc9838c22fc6b68"
+                + "6e5282adecaac981b2f3aae4ab736179f27da92db0ea2034a08b55c7ccf5b14f34650177"
+                + "53908e2e768d1e9961a26377ab9683181251659f4bc8cb59cf5072adb581377579867d2e"
+                + "256f4fad201f57551d4f333129b7b01aa5f7dc29163f0b4871e0dfaf8908b63bb834c73f"
+                + "e6b458e6e46bde92b3c49a0543ec56ddac83ba7511e6b09f7c29307a2e9d7f2e5c9720a6"
+                + "3cbf7cc422213e7a9dc348d67b70ca6c31d87410f5b23005c22aaee9e4e887d474bb2fce"
+                + "c35792377eda3411246aefc49558b2861b49d3785cd596baf60ab5031371d0e546302604"
+                + "8a2371718527af7ff5ee855b9fb0799f970bdf13c8cbaff36b6898d7f536dbdfd30e2bdf"
+                + "ab680bfd5538b5bc758b9fc47a90ebff9275a01e9fa867c56cb3705b0d7cf56b0582992a"
+                + "45968847045b3f9efd6d69970b0c845eb134c0d4539fb717b36ca0f8164ce2d1a268b575"
+                + "2816052ee3a2c2585b7a3558ce41e5b251a81afcb148cf30a759e59a8cfac9f8752e2fe3"
+                + "2e7eb5a633629f2275d86d2d8a0bff6878df098a70de89957c0fbead36c1fac1bde02fc7"
+                + "7348efddcd75b48e22eba37f66d89642e2914a2594c3928f195629b3bf35a0bc95248d72"
+                + "89fec8a1625349b5fb9dbd2962e05dcef110c68ab5fefb0b0f969dbafdfa2675b6798b22"
+                + "cfcbebddd430ce95dbc278dcbbc264da0dd4ed042d2b08c41618a99e822905317f8822ab"
+                + "3492c6901907a225b13973e7336dc3c08ed13364742e3c49d378fde024816d4f2cf36f65"
+                + "50821d8f68fa8cf602a2ccaeef1aee2513e2dd53597593a7cb7483192177d7854778d249"
+                + "b181ecd72cfccc0ddd98d1c50173d66966ead89987cac392387220ca93481f0be699e2bf"
+                + "139c25fe3b25f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1c9688c985e26ded52ff556fa3b01a27a8fda7cc463d64b4f07a3fb90d83062439f40026"
+                + "51ce713e125ced8f7e16a1ea0519c20b64efb6d6cb83afbad1120ee226ef95bef242c1c0"
+                + "8a4280a708c583e8cbf7192912dd15fb93e64fab0467c5c6fc37ac9de1c69bcc5adcbfc1"
+                + "f178d9468a615535c321d21e4d9c9af61733c8f9536cb0003c507b42d497c58a6814bc08"
+                + "78b8a5bb3a7ba80b61d31ad8cb039b379d22fe7705b6de3ab750fb321ae7890ec95f021b"
+                + "d9338199cfd20de8f1498f8f9b3ef901cbaf582297721403d424837c7fb401d98ef4835e"
+                + "d5e936f8841b0f995acd973db8d991a5a987b642c6425df4161b1b2a401adb47987b76b5"
+                + "78dad70f5831d0d4331410b22ec32686a2dcace9115bef85e028b02fa1c15cf75868a21e"
+                + "0100b0e713a9a423ea92f59584bb140541be74742335861fb4f4bebbe503c6d8055c0fc3"
+                + "b3d6522e07ba137314e3faa8e100d8de68896761a89eb1b41cb2d2947c96d35d7bab3487"
+                + "6b1a74067b523218afdcfea9fc61b10d979ffcf25b001fce8c0918029b86521320a3ed17"
+                + "f5b6082c754ec6ead55712af81b160236d3c53c8174578309b298e3b6257b555497a7462"
+                + "632bff88bbd2bbe9019f807e883ac45ec0c33aaafbce7c7ca2f1c8f46577430406422477"
+                + "eb3c358a4a7c289428d08f5ca2cd7af9d007fba1ab06c958ca3a44fafd19c119d0f6e658"
+                + "3959b821e86e33f0bcbb3978672ed26341627e7af472c3abe6921ad36936ffaeb9fbaefd"
+                + "4a1bc0295e23dd9039ffc4fc142ba10811205cb84abad25273a7215ab590b5f050aee0b2"
+                + "7439e221ef847e881e99cb8e85407ac7e48d94b05b9ab62be64fc8c513578d88570a2c60"
+                + "efcd258e563ad654a818584d5ebc3d803b514175479d0db8c29cd97def4a97a6863c010a"
+                + "5c63a174da196a256d7cf87a34334091086213c5e9d6e1736820c10367b1c0d921f252ec"
+                + "47c0ed5901c0de1a39970e7a558d616d51db4e27f9c04e4508db3c57df03dd7dc2977c68"
+                + "cdfa84dfc6f617ffe56fa35a50c3b584606a89d084ff06cf6c5c83ee81a891ad5c3a1d5e"
+                + "fd2b46687ef253f941160a57a509a50b57ec8b3c1af2fc7fe5494f61cd3b06436dcada34"
+                + "5be059d3d906053b9e2380a0dd837f62f5955bc2d7341b5d65b0de40cfed159c74140d18"
+                + "10e88cb4bd51c0e9451f02349cf90da67a0a0a4e71b989ca9552a3d608f798c0abb40847"
+                + "792c9ab05c4479a1e61242124174b5b22b3a96c6967aec9126860505884854ed1878901e"
+                + "77f16603794a5d04ee7371623640e9ba85b281f3988582174183e57b6a7ee6c18e29fe3b"
+                + "41f31e12ecbd5ab3280ffbce4a9b7958f9968843d5b7062a52a373ba3ec4e4cab684034d"
+                + "4dabfc99c985ea059c7f3120dac79b927b0c87b9c0aa66459f138d5a667a721681dcfcf5"
+                + "3e7cb3583009cc17b6ec3955a68377e6794ccb2cd9773b265a8e2e46f8a198387085ad8a"
+                + "5a2643191e79bc9d4aff8377829ad58bfaf627b559752e08edfabc029ca3542b1b76971b"
+                + "685005b6407b9423b88177bf72c76a0e997ce7ad961b154cbc6feaec01ead8134e1c71f9"
+                + "33b0ba4b9e74001a425437461df854b5c3c53a291c6ed28ed651cc4de3734b2ae5a68fca"
+                + "693c744856adb7721e6ead389ff19344bce24584810ef52c516f19c311de335120e8ef1b"
+                + "e0018728c10cbbc4c0aa8cf751f966a6aaf7e6982c910d423fd8b8107c51eea68586161c"
+                + "f8737c44cc157c248b208b80b9c2d3565f08517abfff5f832d95a4b17d79f4ba2d0823a0"
+                + "aaeebecc4478113bfbd7954f8dd3772fa3d18440645c0447463ac2c3c8913a0ad3ccff05"
+                + "927e1f9481ca09451ddd85229f2a192b1a483f42498fb7160fb2815f362e698384b451a6"
+                + "45ccf87e8529af9a56cd5af76e75ff2d875ec024f53f4f3cff75505cbb1863775c3d13d0"
+                + "e23eb14cbb78d49ecf6a848d79bf43b482323c03ad84c21141e35124df31221e6150347d"
+                + "3b5277963f868ec19254569d0abed6e4f9b722d242ed87c5320ef3e68fe5ee0ae782d91b"
+                + "71e78f1a2278b366db912fe2d8db1767e25d971ee5d1b81d7f85b58336a942514325d3cc"
+                + "83756356e19fd872a26bf2d9f2112763908dcb4c73dbf3bbb2418a2efbce380e0425328c"
+                + "cb976b86bc16856af2eadc003a273ec247ebd7badcb64fd52d5eb669699dd5f058a0d161"
+                + "2889d06b4832b30eebfd67d14e467ec0fe1153b563fdae846cd7054c2452cd4e616ec77a"
+                + "b0f326bcee96849cdf8ae27af125e43e32d588c4032228eead9078b991a63f12321c5445"
+                + "20388dd7eeed58e4af45d6dcf12fda0b73d148e0229a34f3dc325786d9ad53a35f036fdb"
+                + "28f5928a36e458521e76e6bcea421ae024b9262387c51707bb350f9fe2cb8aae61dc56cd"
+                + "4eb5d3a6ced26d3e7a2c2c424e52a5243e438b3a686a88ca04e9feb6603f4f89bfd261b8"
+                + "1960fa2f429eb9ed291c5a76345bc83673c8c782c2ed18062ad3ec2c6c5c5afd68575708"
+                + "9c8249103c89ceb72a3534bcb0dce8f6b3f60b3f249658c897e7621bfb7916f74574cbb0"
+                + "70af6724f995d4b2a69f59ca8d785715fda61e3a6299df4dda2306f53360b8b8b8d87d4a"
+                + "5c5caefd7e593861ed6b72e14c8940d4cc986da45321bbdaed3bb0e86566a90ff4034d7a"
+                + "1cc626ff926d1ee7f9f9a2eadc23c8e07dd994ff84bee8ad71ce1311d6764032d2de9aff"
+                + "4e7db890b55f8bb3941f9493c0f69afd75798b697e228846a55993b5fd17f2ad5a36343c"
+                + "be79ae04d5fc3f04bb66d8dea940aea18945fc31a8f9a8b400d44168ce1398f5cf36714a"
+                + "fc0328663fb3e3f336d8d23f05a65273bbdd66a4cf566f3ab291f7f6bb966ea0116f2d37"
+                + "0517e933f7871e4dda9db394cce178409c662bd689e4d6e0f51cd309d1458c2c554f0c7a"
+                + "b3b74d7227f77a9ec911a1262692cfcc6e2c36dbcdeadf1935e1addfe9cb2b8f3e86cf50"
+                + "043b51dd2d04f9420506705302721d191035152a18e22b9ca28dd7e71f376d7bfbe44158"
+                + "7b4c62310933fb0e8d4e08fd7c622294777dccae6c4e6b2df88f68d4345f4f0a7c16c697"
+                + "f3c9139ce835e61488887b18fbf5a6921219128a51264bfefe4562209013a1ab67d86ffb"
+                + "81a1464c5c2f29f71b4a4d4f5fe39fc069a8280d5917f8b4c3c5402a692748121102993d"
+                + "d600efed27e7db5bdca9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01c"
+                + "f6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1dd47d0c6485aa0bcc09a5d4c0edf959b04a8b1c9169ebbc7edee5035b8ae40333766c50"
+                + "6a1327e776fb2cd16487a5517a309b72d1b5267c592b90a7efbd96b5ef0530c37f8a0684"
+                + "a8f0ffa04c14f5e839cd22396171e395d3467918d9ffb780eb304f12afdba17eceba96f4"
+                + "48b2d0791584465f35e1db8b1513bc824bc7ea7bdb1bfd2e12dd1d312b62a51381e64fec"
+                + "d684f83030962e682b89e4e5a4cf257b38b76f7c448e9350d0028f0b9916ec645f980c4e"
+                + "e12630fddd8251317b8276e1bf5ab6d32b215902e9049c9a8b260fdb5d20d7477776e208"
+                + "19721e09e7cad5e652be88a0cff32a287be534d796e1062abb93df619a0912f20bcb3988"
+                + "884bf9ea09cef7514fbcf025226d6e178bbe369e9eb093568b5346e8018edabeda909135"
+                + "d8add2af2130b5028994745c8737ef1695ad6f1ae9f572297afacf4a85c4c9908055fdf5"
+                + "198510827ea3f46bc3bb7d4769b523a367e5ed9b4dcf91da183f9d8486ae3a0282733271"
+                + "6d5650c60d3812ff12e63dc7207d3d60b39ab744327f9c3c7462f6e1db9da77a31796c7e"
+                + "b77205df0ffbc0ce1399889bc7394d9b8ebedd15a76643786470faa650a1e84f742292e6"
+                + "f064287ee8d32a201a60917c8e5a8f4c9ed35a012b89618a43d390fce82a7f960d306bf5"
+                + "512f2e7182e543f36113b7bcdbc2d895b96fd730012b77e4450100828ce7e7ba9cf30138"
+                + "6edd45f410e532b11ebd5df6f2e555b24e2a03fb6e4b54c87a03bf9b100b933b5eed42f2"
+                + "21b76434192fefd06a448b767a3192f768c827d85bf1f5d60747171c2340d85a694f45a1"
+                + "a25efa9e33d31b1008e823aac06b90ea1914706035c2f8d71f7716aa2965aaff54d02cbe"
+                + "44a85733506d7c03721b1c4874f89d2cc762e81271d1d52fcf99b008abf2bf885f398425"
+                + "fd80ceea997995f15f36a77063c491c46af1758b789a65d105741c021fce4ce4f2d6e17c"
+                + "9696ab023af079d4d6279428024d60a7cb08cf62688d83126cd88e280a813ad5287be0b4"
+                + "4e32cf7a8ea65f98f9e6a6d6e20469b977bdb2154351beaac6a6a4d17b5d6883311c3ec9"
+                + "a3a43265347a9f8febc467ff1368542a35b7e42bc69117adf013982dd0ee5c8920023a2d"
+                + "66bf1a22aeb1554550da4ca807174e060729e262b7fce5f1377aec232404633e535e3295"
+                + "472260e72b3d795043b2d3c494df16ec6bee7bee350469c25a486d7d5b40d298cdcf85ea"
+                + "37be74a57f49d8f808f868190937eeb82c57e4353ab978c753662c0afe72b993052b42dd"
+                + "085c94f921f77d4db8f8e61de611a6f2dd09a1541fbc9416c2a19dcef250a8b0ee570d5f"
+                + "a5f0feeee80fb4f0f3e03a0b39cbbeca9c1cab024ec5a582465e82a35ceb91464f2225b1"
+                + "a2cb802d1e82b23970071f267190c02d47c8c55654ce59896c2349cb601d383618531fa7"
+                + "728c1955bf3b2670af0a54042edad7d0447362e6e7ae5989a430f8055ecf1d4eae64069c"
+                + "179919600f4b5a99632a82fe1dfe61061630772657b1def4b97431b23b24cc91085dca3b"
+                + "9b675b131183f3a95a0ee1fd05b3faa1ff8ebe16b7f013c434ffeb31f4a90a4105a6b38a"
+                + "a7940aab83804163863ac9f147abfdc91d4303f88e14fecf3c56c273a02582a30a491243"
+                + "a2668304b023c6325049fd8d749592295624dc05514868fc2c3211b02bdc6bb1158f322a"
+                + "76038d55d85c7e37831d1e67a56435bdeada7cec00987c11e499526a35fb5d13c4444e9b"
+                + "5016e973d6c357801ba5af59360c48b1ded87fbf57fba8be1bd067e994f0f2a2f434fe03"
+                + "92daa4676edb00d50c21f27180ed48318c5236832275e79b9d7763f60ee79e69232cec39"
+                + "cc0047a9124f3a903302518894878d55a937787b6e1eb44467fc4b4e03f6a08a6dc7e992"
+                + "8ea020e1fb72ed5219a28c5d79d582841ed3fa23119105b7b8cb27c80c7abec5ea694079"
+                + "793bb2eadaf26fdcb7566b6c987629e6957392d038a05c87cb99c1741fc0dcf4c038b226"
+                + "6577a9fa21a672a0bcc14fd64aa8a6a0925039c8d75ac291e34a16dec33e7974470611d8"
+                + "55796664560065232c433f51e99762893b03a8364742a9b48156a519a8c61bcb3cf7a0dc"
+                + "573a1f6f277fc62cb50d53257de38a8da32865df428f71edc7b566ab11dfe5997ffaa300"
+                + "27c8d0daa42aa30a617b253eb5a3fc23f4eab76042915446502d962b98ecbd69423370c7"
+                + "b6a32f0382ca4b7dc0b2e28431bbdacee01f7a36bd6285f1c6b81d0c30ad368f402373c7"
+                + "0f6ee758d41c163ecb7a7c476b38d2a0a06413da78c83ea27a41cd80dcdd8124569e7ef4"
+                + "41c2f13d07adfc6b87f1eb69dcc47705e80fe88aa88110f64551015ca732453da4fe72d4"
+                + "dcbba759561acf96ab6cc48b0f96bbe0b8bbda54f6c9c7e93a951bcddadf1d4c984c7079"
+                + "2a715cf9013e4c640c1e61fbe5ba009ab3315cb9852a9288b13b8904a6bab441657f6234"
+                + "c81465ed880be61bb424d79a54c7cba39e839308c81ddc985666ee81280c179b82695a9c"
+                + "c93d9f8e21419012d4c879bab349afbc72d5d6fdfaaf8ca597685b9785f93b0776dc06b2"
+                + "e0fdb5254922e0366da4978173d63c213a9df4ffe1a71e7090e2670dc8403ce1a9841b81"
+                + "1e33a946a762dfa764083f84451b7061284cb86f5794e26688585ae5a05ae660b8085a3f"
+                + "cba7978a6054b7f0826f462b265f996a48a648a542d1271c6cb0aa9404f36970bbea185b"
+                + "82777f1958bb6c04fd367355c072b3865a744b6adca31e2a41fd06496809fe7589d5891b"
+                + "7b38049b2c4dff88ba06754d3415e31716e66aad3b967425161a8b47d252ae4b3d8cab4f"
+                + "386bccb17643fe1060b8df9c483fc02751a299f53e83134105952e361fc74d446829ce42"
+                + "0a0c19fbafd9e996109fd8b3b12e9403c97d08a89a55c7f6dae301de716b4790b93892b7"
+                + "f03dcf36355e75a9dcff3d90c3609ee5024f304a444f935f98fce6dee0954d6b7e79612b"
+                + "c371e5ef03c58032222a211092c5245e6c6ba8a269edd5b9f41963bed7d9bafea184c175"
+                + "e264fbb84f0c259a9b19acfa3cd6e07dfa7f4b73c8d2939bd46bca5bfb269ed6f1a963ed"
+                + "2d4a8565fb515baf38391e1fc99bd42c535fcf26d19691b52f03744de1a06f53fb561a94"
+                + "3dd7daf341ce64507191639ffbe39fc069a8280d5917f8b4c3c5402a692748121102993d"
+                + "d600efed27e7db5bdca9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01c"
+                + "f6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1e1629e7868df0508b5c5feb013e7e3eb75a0e6ce8dea42e10add7b106a6fc61f1821523"
+                + "d62c55c57e8a3ee2e02d07fc89781bf43f65b77e454199e3cead123d59b8abc721476e4c"
+                + "bbb13111e3bbcd5da757607b403ce507cb18a55736e6801cb264e571d668af57967f0313"
+                + "28acea75d90872b55d339a8b7fffdcf289c100605d10b24f0c2568c4ccc7649a192c7a17"
+                + "8799558ce4f14107e6ac84e647340f423e6931a003b8b80798085dafbdf9ccbb2cdd48ec"
+                + "28596e72bf5b1246c17a7b39d579d86b6606821bdf03af5d92410b4bc109f6cf938afad4"
+                + "a4b33e729f85d9d5efc64c261e5de70ef1c1987ad75928ed3e028c6e42233dc03635dca6"
+                + "caf890febbbcd0dc9807b29f9f437795df16cc165f25761843f5b6ac2688e07e1f6b43fc"
+                + "ddfd42d993416bb55afbfd9526c643409a02ff95dca4c645196a6446d02430fac0f8ecc8"
+                + "8ef2d9541d55a15b95f7536ced29b9e97d3fa1bb630193626c1ea6d35ebd8ffac60c1e43"
+                + "3ef9ce8e7df7a83f3b4daf0487414c310db829fc7c4286aaf822e0e9689507aba82c9c02"
+                + "09b8143974374910f7651aac5cc60791656d6b5f8b3cfecb34d20831ae7452b2d995a77e"
+                + "818d93615cff046a74b87f509eb46926eae38345f89d2b460b7d5f12b3fee6d4c1fec58b"
+                + "c13ee88852bc3c9a077724138962cf9238cf1c7fa428a99e9ead6a00375b3108d2087751"
+                + "0822fbb7c5ab04d0c653784cc355b43337c3f89704b0469564e0c40223587187dd220f66"
+                + "e5081a90268755e8d7f46d0d54c3c8aef3c5c49341fa4da879f866e4b98a4401f54b4da2"
+                + "dd04142ce58b3871e5e30aa61383de8161b0785e839abdfb27096ab95f7277b148ce539b"
+                + "57b98aa79c874e1bd4003c1a912333e47b3c41b50822715d83461b1dce5a712df97f5f42"
+                + "5dc6e399af15312ee21b283cfb9916d13ebf88b0b467b1c14dd600e36fe0fe0da545bbbd"
+                + "843101683dd4418a79b60f55aba747d873fff04e7c37b576f1dca7319f367dae50d131aa"
+                + "7e830e18002394b0d882b9467331be11dac00ab7a034487dad6f28c95a6a253f1200af1f"
+                + "ad0fb3bfe64c24dc710f19d52a88cac5c316a2dc8b82ff68f84321418164bd1d10f03236"
+                + "b5f6b334c01a66332e4e1c4f7f78129d61441e2b3ddf9eef1c1763706ce79607cdb9bb27"
+                + "0c84487b7822ad3f9275bd34fbf44b661c60dd2338577d657956db1a5b9d33484e8cb4c0"
+                + "87be62a47f22f01ccb5ac308d6f34276987eeb293265eb5ed6a9e20a33f6605b33eb22a8"
+                + "f384d0b03f7ab613daeb7dff38492a7e7857f3849c017bd21963b4598a534506068f4df1"
+                + "7cf48fa9671a9306d927d14df11fb277d8a56a28fbfa1c37a1122176513e3e5320c09539"
+                + "49a4901045e3f4a9fc70438bb2034c01844bb380558499ad97df77e0987373dc7076ac39"
+                + "496b218f9919b2c693c6d8b53a0cb66a860becba37a901660f30ee4265ccdf50d03d49a0"
+                + "aab69b3b0f8e34a732318d8b76bdd599ce580ab3cfc7abb946b62fb29a2391dd59222295"
+                + "35773fe41acd802ded42f5172dc552540ce81a673a9f22d021f9d689c977e0350e14db66"
+                + "a5ac025e8d5a43f1436af84a1fa57e03e6489bffec04d69d5db2d3e4d5cf8bca0b576fef"
+                + "cf2d88883be0333584f502f315d8c47de319ca62b9a310fc0ad0c76d9ab27b9df3f3e28b"
+                + "8e8edaf592755b56c2dbea6f50cee95056a3d3e8c82cd61abb76a1fb6914a872d90bf5dc"
+                + "5f57cae9fed263fdd4d85e65fde9141e80f397e427fbc1a5186ab1685a64180eb27dbff8"
+                + "79026ba3a1bacf2c2e1a559b854f7311f98aaccc1d65166260441201f28a380d13286810"
+                + "c1ed0c9919f7fe93bd35bd059dd57110f6b4a46b59ea30c3cae5ff19237735ce7a892af2"
+                + "97281f3056de704b6f93505ed24ff6f3f4a512563f9f2b3792a524bf5ca1be336bdd5605"
+                + "f3adaabd1231ec9df1dea585ebc087332c821097f96f1258cc86923e92d9178af779ac70"
+                + "7d7b3c6c025e7fabb70977bacfa25c4b2dacc90b533961ab56df226d7f8749d243b527b6"
+                + "0fa8550a8f0ca1c752ffb5bdcdeb6ee176872ebd4e6e7a4d7ec1af7cb76ebc9b308149f6"
+                + "97db0a72969003f1d6a4cadf796a76fb5f2cf0c6a40d730d557c33686cf6bfbddb7513a1"
+                + "02c63f2aa0a814b00ea966befd1527719ccb98a834d85196ed36ad9813ecc690911ce8dd"
+                + "7b08665b20028fed0f2d4f90c3d9620cc4413a876c515f2bc732c512e0a4f7a1aaf72f8d"
+                + "c3f7121f96ae96b30ce38b27732db600520b5549e507848e8813e50179389f6c9a5ba2f8"
+                + "776baf4b559ee3964edc2bc88170a8fd5733cdc9265110fa821727079f6df4385b5a239a"
+                + "3619b2182e0e622039a827bdd83c3ca8361104c078c5ccdc095b92052f445e3c1c291257"
+                + "15bd77da30dcbbee14318e1ea521ee50074edc20332753ece1e8a4ebc074f6ef1922d96a"
+                + "8856a7a9a9d9360bceefb3b0402b48d3e3b4095e10c25dd22580bcf465649770304049f3"
+                + "f9a1e7e63317527b4c03b1d10d8264d78e66c782c9b7b134c44a0c391c4eb63ab90c090c"
+                + "a108343660079cb7402f5c3ec12a6ce95809ee606f2d3d0625f0e9f3f926e9c8db97386b"
+                + "ccc188df159b330017825a1e9f5ea0b0a3b6dceeb3956c3f149582b6ce2263ecf0ed4e5e"
+                + "7a55f6dd95a12a5f5744877bc30704d953d6ea937acd9f3ac344decf67e4e937fcde3dac"
+                + "09f3d87cdbd0590f4e2b45f6865deec9a770f86bd3909d3f74effd2eef6d5a68d53833f1"
+                + "cfa9cf891c0a4fe101ab23a3ef9e752c44080d32aaf2d92b422568939b7a695d2c51489b"
+                + "55fa0a294ef1f8df0561186bae1b4ef49198464b0545f14ba86a32e89b919e77754bef20"
+                + "307639411aacdf49de6633659a5da450fae82f796ea590aa543b509928eb983cedcf4120"
+                + "e8c711e4058517f693ccdaad4cd0eafb0ac0ef04cea258e2e285952aba9f75ed5e585311"
+                + "768318e358b41a7c0061e21c69560f6602affd8864213c254ae4db4ae1f0b1a27ff14251"
+                + "8b8ce301471c235c32b5b19f4cd048ce99e4b40100e63c90d77fb9db0664ba1919853942"
+                + "855a4f14979d60fdcb2936d9b2ca49cbe1962b48fd93fd84faf8ee8952a52664c1992ccd"
+                + "51710edc2ec8c485c4932ea0cb4819093ea76a9606028cd2b9a60d20b1fb593d2b74056e"
+                + "bc71b79ecebd92b02da9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01c"
+                + "f6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "1f066969a289d440bf5f3b9a286fb7adee04c6efff57e00ee2797c7e18d27434e7664299"
+                + "daae2668304051cffa860a76a0558423fb114b48516087c19cecba67df642bb788deca20"
+                + "a7deead96ba181dff639b9128feab51785d567a1986c3d864d18b8d3e5d5a97e9a82dfe6"
+                + "331ebeed86cf31ecc554a8322e9a0d4fd70b0ec61c88dfd3185d46041f72f8b4ae7312a0"
+                + "83280ef4cae700aa9b0e094b413b9d67876a99cdb59b5bf0f1136f880917548537b028da"
+                + "6cf281db31064d83bd57f676f749d0ddec2e439486a1d4c9803a75486fad6e64ac4bd87b"
+                + "457624ca6506bb9f28a36bcaa47ea5c32e8562ac58b1f540226f652e7dedeec29d11dc9c"
+                + "9bf65358a544b6266d8b17e9128ac76b9960760cbb35a9463e21ce83b5e4d3123f6c8c9d"
+                + "88ea9945056f009407f40ad1cf33c24362ec7f210b6048c82680c2302f57e793e8c66333"
+                + "77374bc585199ba3e4ffcf31f1f22f86603655b3864301dced34f3567ec64bb419932e3e"
+                + "d73612feba7e0deadd9e92027872d7d67adce708ab151e7105a70b50defed32e2c789ffe"
+                + "304ccf198f3fbfb685aae71ebe28459d60fa51f49b645d122d1b80edf4d500eb6db8df96"
+                + "c2fa9af3c3b884332b26c1c5b3cfe02f714bab9b6eb5e6f6f2df23a886e6d41c7a33e7ef"
+                + "eff7d8123d9d5b9439ed31d3d2ba28a54209548d587795601ce5a005b33eeb4374355bf6"
+                + "9913432bc06412b398fac79614a1fb8ea1587aec0929a812ff8f42b86e8d4b48829cb6e3"
+                + "afca6d20b1d9e8e39aca72b44fad89df86d526bf82992d95358c05d9b0727f64d25a5782"
+                + "30854d976d6ffc4e0db1ae52c4775dfe1d3beb77e12fb7e26f09b0061efa6e3732f4e12c"
+                + "6dd945b91eeeda2e28257de0260a71bf0f1dcb7b705f4f630f1cf92777a182f574dec84a"
+                + "6c0df3bf8a4bd1f59cd8879e005d93a86489d1e4add2e1c34b1e7189718f99e93f72f537"
+                + "5f4f1b6c4e6c0bfc4b1acf715466aa3bb6168de261723c4f62b5e69275f06224d283e468"
+                + "ca3ec0c2ec77c6148548081b85ccb43c55dee73924c1da8e39b81496f66cb1d815c4ec6a"
+                + "1e34aaead70f77707602af27025504808464671556fe26dd9f760df45395cd3815d19b63"
+                + "548a4bb868625f76811365cfc61a646c518b24ea0fc7432f177ab918d5d5e824e01a4a95"
+                + "2b2f682a28d9d65f20d09c97c720a795b1028cf8e402f90df3a331949c59470a7f375a69"
+                + "1be2ded2469d54050fe52d9750dd80ba5a5e250039df62d051abcb9ca26c1ed30c413f86"
+                + "5493c033cc030c05ce300e3df778c8d40d30427caf3202448cb9a7508464d998bb0f50ab"
+                + "d7064bd5e8d947471b935d23d9e181f68a38068630c249caf0a1899c0b0c19735d2b8ab6"
+                + "e333f92297d816a2584038dec9cb87fa2d780a1eb24fdfabcd812483620260e5dd092af4"
+                + "ea2d7f3c308e5057fdb70a090140d8bdf7ee760faf670a1079d8556ecf74885eb54e47d8"
+                + "b3ba853e1f08b7d749114df5e2eead50a06d755691307eddd3c16bbd3ff4ac771d40e00e"
+                + "e0226e300221fca671b345c2d9ffd35ce3582858024828c8bf16189c8617a0d1849553c5"
+                + "4e2c26c12cece2f27f2343f8adc8653907f4d6b461b23671ec476cc3ed935fa07b5926e9"
+                + "a103a561d9fa1f5eba80311d7fc7e7d6745087886a13e7529c8525cdb3d46d011f689f1b"
+                + "26d0f2101780cf1fc2da4763ceff1234290056d573972f48e733f66f30386e793256157f"
+                + "f5dbc47facbbcd81c45604dc6684293f2011814d275dc317538f686c6ecf166583937b27"
+                + "6959f4e62b0bc2f7cb0c7e160eece99cf2254b5fee2518be59d5ce5dfa7959d5589bcb33"
+                + "776dc8a66c47db3b930361770f8cf5d42cace66fcaf0dbf263b7d9f3631963c18e460fd8"
+                + "47c70f29371983b811f4493d20fa0339280f0a2ac6cc1566e7672b658b6efdd6d0440f17"
+                + "e6bcb4b3f910c96544d73b71799f219798556d5c5f76d60036109dca3e3c879c888c1a88"
+                + "1da7668493ab5c1536362f812955006da85c6bbf262c44dd489c245c7e8e0918d624246b"
+                + "b6dacb905adccfb22e5ca1de871b649d7e66d2154052ad1e808e476679ef8d2bce71dadd"
+                + "976310f27832dfc524175a31491099652374cd3d4259605de42cfd9afdbc114652f4a89c"
+                + "7dd675c140cb56de3bc1900237d505770f5d748b17473daf14567bcd958151763ec99b45"
+                + "11e96a3f84ae718e3a2c5c38615258c1064cfd4b7ed8177da403e8bd480da010b8f7d3ad"
+                + "9041eff7aa2b9b6079c5958b5ace63f9e4e0f22d1f4f035e362e9e236c2d34d8401bf522"
+                + "5fbe95c70bf556fe2d21150b6d74d9dbba4216ed05aae3af7f23d51d69c0592f089450ef"
+                + "73f7a5a1bb28fb754dc77411c1eaaaa3abe834cac5a5d569fc4254b1fa45f3b086fa9a51"
+                + "fbf1edb3015ad0a29d76bf86c73a5486b35fd1e1bbe12f6fc7c0dbc783b67bf3c1738ea4"
+                + "993a79a1507835cf52586ce69e851888491c6935b74e38d9f1026cea973193e2119af0ed"
+                + "9072f16dabb9a7d16ea253357f48a1de81e605ef7f485404aca430b2fbc41328dbcecad8"
+                + "5b92c0ba4ef34055bcd9849b55868d6f75a8df14ae707b3e446a74783a11e76772507b2e"
+                + "7dcc38ad895a2eebdde58af7eb1f2b22567a2c19a08eb06f93cf0cbe14528fed332325bc"
+                + "479382375e48020df31f1de42c7677a788fe2f1296288526ce1438bde7a9c20a2310b73f"
+                + "99ed1c1ed824b89679b435afd0cdc79218949567a16c0e2e03389fc253186e7b31f437cd"
+                + "d05c0c5bc09852be381e8d0e614e765b9d97e684450f9d5c99e9bab9f4afaed205ebac39"
+                + "1a05893efbd6760689a4cb542fdb6c1cb62be6b9bebd21d2a1e9bc80e1a9a9139062d112"
+                + "d2abbb946392afea1554ee98ce41ee1694fad3e32143d250ee2b68ba1430d40b0a740c49"
+                + "1eda5a85dc72034037b9ab8d75c52049c0d47e886aac9df86dffce6a8236ab68f4d80b74"
+                + "653ca0dbde02437d9dfcaafca1c11dc919338a2c39bab7ea082187766c4c598a03192bfc"
+                + "c5cfa70963f31db89dcf212d60e1be9ccb55e5d02d07fac9b90ca508e0f6c897184fcd40"
+                + "d00e058de4c59eca0b4dcbf44b46926e2412d9e65010c44d508c3d07f84a149c7ced7429"
+                + "a050b5facbcb0e129aa17389f74819093ea76a9606028cd2b9a60d20b1fb593d2b74056e"
+                + "bc71b79ecebd92b02da9ba39c789c0a26f3a42a1dae1d6811a6ddff636748da69fe6e01c"
+                + "f6decdeff025f1debb766a079c2703e91009895d5fe90df1a8e36d4f10dd486d713ad164"
+                + "9a3ebcbc5931b22b1aca257648731ed7a4efb0d0466b6629f9e76dd81d2c556ad9d41027"
+                + "8a54beace99aa5ee5979279bbfddde31d581cdea96278dff17937bc7432b3e85a14946f4"
+                + "f1882eb111796a6f30de022d40fd200af0d5745a05001a7bd6dc827c4adbfcfd2df3a9d3"
+                + "e88b41732e16729c693c1a0f9d481e2e50fbede3fdda21d695531246165354f89d0108d0"
+                + "88a78da4687725f80c77988371144c7229400a3a2ea5e67c071cb0b904cab486674bcc21"
+                + "a8ebd7e25601958d78de76f6db7624c8f9a88ae6e432f978ba90b9a956be2a600df5bd26"
+                + "a8771e564e2dec9af2ac3030180398cd88664633f2150d5d6d2a6010ae55d3b8d478fd3f"
+                + "4faf333cc1f5cf0c10dbd0eeb59340e2cf7586246e774448d1ec525f4ed06a7fc026dcc2"
+                + "c1fe29ef62c05c02dea8a778f244d27d85a7634ba95a609fb2591feedd7f3c9b48822f9a"
+                + "725a2ee20616174d36c0d831ae67e18d838a9ffb9ca9ca7a19683dd041e5bb425803df99"
+                + "dbe9ebe9baa082cf3d1c6a126cdf642e42ed0d70bd8aba5af4039a01c6ce9d11c2752194"
+                + "67845bac3602e5eb9e461bfd61044f566650c5622e352eb8c1c7ba0f55748a3b802c2e4e"
+                + "e7b349766942d4bacea1ffc1c1179295b2e5ed38b1d8784c8457eceea725728a168466b9"
+                + "980dd0f67742d67ac9945f332a901abb0a3b22fa12023554bda2896e530f87334a1a6369"
+                + "384f8c0b1371f420958b629fdc9a645e3f7c9068d4a1b542b003a894654ad0841138c230"
+                + "a8e9f9ea4525481441f2936290729ab969182768a570bb7ae9283ec9ef00190dd9063df4"
+                + "5d4e4748b6000d5587cd931a554b9322a7f03dedfe4c931eeeac64bfbffa650034b0fd8e"
+                + "1b0869312936f1a85a104ac2fc2b121a9778471c2dd75bf4ecb2115431bc930a4fbac930"
+                + "137c0a69ae963bd6e4f0a123208c3616a555bd3c00a5c3212523378ac572186d11cc283d"
+                + "e165c8da14e4b9f7691243041f004f8c64558d3087e89307dc222d154037ea14132b2a99"
+                + "ffffa664d7642277f99ac4d3ed3463324aa31f82e5f07a3e2825fc90117b6eca0cbb64b8"
+                + "f19c474860e01e0153c628cede2a5b9013ff55e5fce193453673ce1690e09b3a04df2fa4"
+                + "dff313c90ecce32165b578db16fff29d5a61dc9ab3b4bebcfd048189e06dac530e0c724d"
+                + "1e8b7814ab19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2adc440bbdc"
+                + "d2ff6054c68d97d656e37cb211c48e3219235d192e7849b90b9ca803750c56b11fe0f12a"
+                + "696eb9d31526d006625782ba1c79ba26d54f1705cc3491e8a49d33288f0acb0afff1462f"
+                + "821fbd7830ded258e9cffd50764ea66178d0307e91817688d9368381cdede8d632a1fcc5"
+                + "713d039cc0034c701ec8f2ed8455201c24067fad2341bf48b022912617ddcc04af8ba427"
+                + "9d0d2a4f3578458a56ab697e1c3102f19a5baf90f1238801b1016efbb7d3c6e577a53911"
+                + "116ac306d65e5dbe82e0a217c8c4f5b7ef90d146d0c7078e13b51784a6ca9b02ece29a67"
+                + "6dc150127c1cabdf49168a9ad6a261e2af7293598e73decf9ad74e06258fc1deb06df697"
+                + "5fbd287851c9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711c25ec582"
+                + "cbe3d62aed3182fd25deb2f032b211738cf03c3c090468588221e8dcdc53d43c3eadd85a"
+                + "bebe565f43ae23a24e53826c620aeb71c6bbfa838dbdc4f6d17a9d15d43b65c56807c010"
+                + "01e734dc158b522d01ae541010229308a7069715b2c5c2d2739fcbd13794331e0afae203"
+                + "40514d4c1f8e3661a5491822e3013cf6c6656f262a7b2c4d85c3f508d73b3a1e7d9ffe96"
+                + "52a18b334b72e9ff5efee892e9754dec7ba2aa0539a1a333919a58ebb91daa89e2c6605d"
+                + "16a04bec8cec4868e4c6ed46631f2f33e3340c4436a259641b8faf7fea7c35c455aacbc3"
+                + "5f3bc3b8ea98e1e305c068479ff4a753a29ab12824a0ce914f789864fd92d9c4c6e81461"
+                + "1cee034ab78bc772ab1a289b94c2feb51c205dc1c9443fe5f53d99adb71970614670df9d"
+                + "807cdc99a01a13a7bbda52b73787b154ee3c389ee200aa7cb0a9f9478a02c127e5e75fc5"
+                + "18e3913ffa79e3a897a3772ccd096950c394e8a658c6725fa3e610cf2232fbcdb4f369eb"
+                + "b934ea53eab93f40c11e63ab51aba65c4386181dea6b09508f4747557ffc41579ef696dd"
+                + "bdf7a199bde2705d3d6ff13a67229862f2dc796da8ae0ec99ca0a310665d5a062f0fb910"
+                + "bab41d07229a339da8da6afed23bb93c8d9b88a3cbab6ad05e2295668355fc6aaa8caec4"
+                + "d880841651e440fd2b8c18e1acf6b953672e0f6f5f681192a14eb366acf4e36f9892841b"
+                + "65c4795c1daaad3a803bc71c059179e969a789879e0753575fb96fc1c4a6bc79e9d0bf3c"
+                + "50d51b844a7278bdd2a78d30d6899eca4002355833f80b09e9d307f752c6891b09ae906d"
+                + "561eb898cbbd912fb72bf86757a7c4333d2a0e3a26843ed43f65edd8138739bdb43e63d6"
+                + "fd5115185312c864aeee827234af3dfd7e1eef04a5c251a4017d77d1c976a8dd17ed2a22"
+                + "1c058b6b94f47c0fa0fa06aa764c9fd88d782889609a4b6b5f60658e4beb322bec08805b"
+                + "69c44eb8fae138f6e535e6517f1f510aec81793f04720271715a2f38ec751009fb9ebc85"
+                + "96cf49cb159b2bf3d33fdb61b88c1691ff0c6f2ec7d98f8776778bb094cc5271cd3960cd"
+                + "39163bf31c5aec1949491aa0c2d2ef2f12593db9b36cd2206616e9fd545a2ebe39998a4b"
+                + "2043afabc334c332f4a5407cb07bf467aaad82f077d969d2c2a052acb316a6c5fab0bc97"
+                + "a42f5f76165f52d1a2b312282e61d13aa84a44d4ba102346b01977b5190d938f533f31d3"
+                + "e60c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e1bd821a5"
+                + "57b2c39c584d173c1136b1043bf2912b079b7f42ee71a8d70858e0368676c0637b70399f"
+                + "c827c1a08301ddc0f2e0ccafdb15a18034120c48b38472209d75124912f7b4355ec14bff"
+                + "14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf8e38b0a9"
+                + "a089fac89496294a8542198d67836112a242f125c9f36beef4ef8e7e9264eef88f07ab26"
+                + "118fb08a7fcc497a31c96d1ecb",
+            "20a67e046163c0124c0880a830ab726995e81447dd7fae4a9179d94f4cf1f82c32786d58"
+                + "785d395a7146617477965e524f8f5b083f11c3a53bfce98e91d084e38efbf23ab541549c"
+                + "683732cac86994e679ae5b1a0b4df765648d4a6e630d8256a7ae4ca6c680bc350062d628"
+                + "289ac12f445743c9dd77f128696418808b3ab396377363a8791e63c28d01e335f6c608b4"
+                + "3fd96517fb7362c39d88a478ee9b9d06e6726a6ada9c1b647fe222592387997f426fabcb"
+                + "974e0841bed598e143eb976471747d7d5e0cc57898c5c89b97c14d49ad1a113f579690c2"
+                + "bc150c4148b2e5e52b1d21af88ed78fc2bf5ff523da8fea07e95223f440314697a388f11"
+                + "b7dda55eb83c1b78f416a86d455cc30842045db5d3c13c9295a45cf2c59d785923c457dd"
+                + "bc6494b3fa043fa1532a69b5ea8378f6eb08346c4c8928c295dbd2c42699e7988d994e7d"
+                + "79ee0da371ffa1ef18eefd8be8f4aafdeb7812db9605b38a5f21fb484b78c6acb40cb06c"
+                + "2b89d568ca8a7e84abba4c250c9859ec3621bf4a381452f5ee8d094aade480abd8e8856a"
+                + "a74dfbdc1a465bc3a2baeca8766b78b5e1a7398292a4156c980b7d4d13d7e6a287d481b8"
+                + "ada7ba93d65140cd860554e11812248a89c5b43a684fdaea1b682246b4c240f27828a40b"
+                + "2ef5091c32226e975384fc2d5b2584b347cc48165c64f394324dc31d17e46974c75ba1d1"
+                + "7a92361e6d7c611683687fd9590763a1838fcfa734d3df875dd116d6cfeb42bf442c0995"
+                + "7c351159c93a2876360982bdeb1eea942bf54a6c841d205609ef402026579d8e6dae8551"
+                + "b15d863fb90c6e39446867ddc37fa600ce563dd71a79c072fb54ac9ebe97113ae860276f"
+                + "2dc4adda26971e0620fc23bc0bb80a9f111a385852005dbf8b700206efb77e0383dc35d0"
+                + "f5e07b167e5479b4994aff1385f1677c6facbc723ed633780154249fea62124778cefff9"
+                + "cf3a597b0756b8bafd1299d8ceeb689a7e6fac891fe8c07e43269cbacf07d534dc0ffbf4"
+                + "bc422a4b88bd6fd4fd2e2426fc72532706e17b72ed6f8273f393d327c41fe6271692ed82"
+                + "58d4821855eb9c55723a293dafa0471a177598f65d88d116b407e60d4c50c6c3a7c716c9"
+                + "ebf63775f29290a57981ba88bad16535db2b7bd9e8279c08d9e330754656a5045602c376"
+                + "266028dac860a2851c9cb26ff97bb91a581c20c29829f74b234cbb5939fd0269d5972a47"
+                + "2910a15f622eeeda8d35f30d4f8120fb6bc43007aa9f6c460aa740c664426653b969356c"
+                + "64daadddd43716fa6bfa07066101c38a26c87e936c58fb86157875cb72a5597c9aba7485"
+                + "9ca200a037daeef702b52c7d56ce2546e52397eaed94a9fe071273b7c8113fe83af3aaf3"
+                + "f4e30d37c54b63e1377d89d62c6042324a466405066a077b81487997fd226ce2d74c6277"
+                + "f8cc2444122296015bd9e8d4af4c49b6deb43a2f84c5a01106f462ec269e5a5285073da8"
+                + "211f627715fd1845a9e0ff0cfd6bc0c37a5374358d632c90cab2c51c90fbfca63478fbb3"
+                + "544fbdfc20b4d0ccc8e690b82f29c99793fd87ada4e48acca9954373f1cfa81c9e450cd0"
+                + "db12688e9b760898e559f81ed6c316d6eb49b4932dad8c7b353e1415e10100432f72a84b"
+                + "a2586053b3fded33142dc1538bb790394c835723bb48fa8622b974f99a635d5f61dccd92"
+                + "bc9efb590020920d57a2b97d0d15200dfdc0e0930bd34affd8dafc80e32b6d0be1a39dea"
+                + "5d2945fff384e2dc5a35cfcb92eb5fbdebdf65408861c908f33c43df33633c6856e5e6e1"
+                + "c712eff6282211c8c266b3b00c7aebc935b178164ad43bb79c8f31e450c273e35622b3de"
+                + "39b32758a523464d3f4af869d4a1b2c495e24fc768a87782a610fc680599c3fa4e431824"
+                + "a665b067dd11d12eaea4e6eb2e105616985fe575f64cfaf6f117be2c586e4313d2b29918"
+                + "5d594765eab194447e320f8a92068bf032cdf3593977f4d25641f58422a07be3dad73313"
+                + "b83438a3a9ecc7dddefbe9f805601056ba301b3b27404a49e93bed6b650b5748e2e13a42"
+                + "288900318fafa587a6aea21f58f954eb1b969fa101c4e23fa1f6e6bf566e0eb7436e12b5"
+                + "28416b24fa5bce206b4ac219069273d6898a016385090e65497d8a6e08664753dffd57c4"
+                + "397abf3e24ce306d45257428d5117184537a98351d3cbe5927024a9f71466b3cf6413db2"
+                + "c0a99245a76f282ebafb1bc95be5a303901d3c6407b43a1a3a721ee20ca14dccb2f53d3d"
+                + "af9d60a6d555041cebc302cf4137cfc1a233195ace2a720bff25ac17f9f07ae0f4d3384f"
+                + "6c319f019b96d5f219ea8fe76c51ca352ea8e7366304e340303764ad0d07f668bcc40681"
+                + "dff8c7c72e7828fd779930346d25ee515cdff7ec505db8430e7eebda1bba37c405bc61f1"
+                + "41be0933d9a23a00f7ef0000b0f0f12f74e9e3a3110f611b70f02b25bffe5d9199ee61c0"
+                + "6d784d1e2a8f1bc1bc29bd9b2fd40b54fd288461bf18680c99f3180d7f061a512575a888"
+                + "fb04361e1f1852713a8c0b4e7679d782e2ddb9d5af65d0e2b64a9a36c15cd2c9b3108f7b"
+                + "eb360d0da0cfa2b7be56d4b3a69f904a388af2df126f2bb66750885da0be0ed1d5a8cd41"
+                + "5f1e490f750696a2dbe35c7e30db6f63b04a2b9ea5b917d703c398fa810f0ed552ce7f51"
+                + "2724bb60fde68081953e320a2a5e71534a7bf252f50c7d87a74e474297ca3d5454901ff8"
+                + "c73baaa72c4a2dcc58ecf3ae31f96349a0ebd29439bb172e838d96623d8a8caed81613a3"
+                + "4c3ab9eff73b6c793f5f49468be09d81931457bc7783e09fad47bfe9d0e10575a046251d"
+                + "9d7ddcb044407463c55be2002f5c3ea2f52bd0d09af9a392feb64a5c20aaf59b4cf4c2fd"
+                + "a17c244dc4a4c0ca2a46cc184ea541dd3f9c8b3a36145bdae9e746bd0b3bc86cb486b816"
+                + "bc60576e85651aa11cae0b92f55282700ea838db05f493f682d11b5e8a40299d52b10f82"
+                + "8506123ddba3b44cccdcb1c23032a561bbf2da50a3633b0905a1fb959556c7539a62d1dd"
+                + "43b6a9d3b9205b09a1addd0ce80e3fd79ab5f75fc6c44b371bcea64fad6844f21d33b0cc"
+                + "e0817bd0ba55eaf1ea319087239e56dc4f88c338317a9fad36b5e6af699cb9644de191c9"
+                + "cfc76273f8f8425c1cea9649fbfd16569879e67107be9223b0638e6a03d86660d878fe01"
+                + "6f36f76a21365bfe2885559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39"
+                + "f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "21eb9f2ed7230ead8fcf75de95f05f7ff9d14b5c45a43bbc71d2345674c6b0d3fa8ce619"
+                + "4baf890e09236a1a6a226a8c86831ec35e457fcb59a47bf151a18531f87625fbd5761889"
+                + "186012a02a72d03ea585724167a3070513cfb8605352d2e82cad502efa3134d75041c0cc"
+                + "71cd1d7ac6399ec1398bf633f4109d368afb179bdd7032faddf3d192560c1f53715e1f2e"
+                + "f6df71e1f24a85234d8628a107a6678363ecbe5e21a7cbebc04776e3e70a93c3e10e6667"
+                + "27a5bb981edcc0f89d1951d99ed4828c3ddf23d573c358cae7db77974e54e16c2af879fc"
+                + "a63372b539f84589b24aea34062b498a8a97b32fde039303daea1f00ee2143e484b145a2"
+                + "071f357356a76a0b22a60f5c084993ee4b51a56e13f0b35bc41360100aea1dcfb0b1ee00"
+                + "1efd5411c601d5b4fbc21e68ab03c39597a47a28fc93da988770f47cd0e342df01a3da4a"
+                + "26784ce9172ca3324fde330947a15d839ff0050f487ff04cde7f63d3c8e1fa344dadc387"
+                + "03f40f76b879b229b272c6d4b1ded01286868348c3d9d5049e8c581d114f6d671c75360a"
+                + "14b706dc809f0ee2499864b85cd3720cc9563a5e1f282e0bebcaa47b1131108df17bac52"
+                + "6aa57935c12c1ac792d423bad2817b8852fa746c6b17e9e9561312253e4df503ef8b12a1"
+                + "c5fb94a103a08798060dd945ea0e6519cfa9ec215cf3d34d30f0804da6a3e8faba9e9323"
+                + "410dbfac3814ec213e7f71ba9f8f574429fc2a5086e9587becfc304e1b45718c16860971"
+                + "9c9c6c3106814c242d93cf636d11477886a7701643c9233b814c1298d154b5e50f6946aa"
+                + "74a75f5978e6dd36e48bde5d3afa79447ddcdd98fb8396db095e99ae28168c8f12e3e727"
+                + "82f55e1ac04a2b44599876965140dee01fb472bf6d05f4e002c4601ee72ec8950b8fe09f"
+                + "2d34e0c308f6c4edf6ec64523af813d929cbdd91f794c8277c4d3b25df8e972cad022885"
+                + "e7bc1489614df154f9fe6537cb7dd350726659fdec3821a09d59b731b1c597264035a090"
+                + "87268a324cbf8136719500de1a1ab0d192a923e99902e8e889c2b5b064b80217e2e97c53"
+                + "ef228472f5208d88c9c06bc69a58967dd5399b47ced84c944e6473ea332527eca2bb874d"
+                + "b89d43a433f3cc1029c00ef955bfb2255793dddceef6a4969fca27e8c235b76d3586600b"
+                + "ebe781f9f59d3e69bd45f58b5a32d6f81831a753bd5a084e7b24274335b761d6aa3deb62"
+                + "93d4a46664d011ba6eb1055270bcfec70f645da0005fd1caebbdf183fa7182957885e45d"
+                + "8d2b4365ebb92eea2b5746f2cb3bc1070065d6e518b9b238b2a28726b68f6ceabd9b6061"
+                + "832fa114a6c6b9604426b7eb3fdf5164687ee16038a8f50b3780272b9cb19c99686737ca"
+                + "417684b55d39e99db3ea90c1aaf23bdb71576bcf24f2dd7e239c4ee82e3d0874cb03bf3e"
+                + "f362e17a6cd726650d3b2945d687457e880137a112aff4dcdbdebe2827e1d7d01035ced3"
+                + "3b4d1b2e4e9ceeeec4952686a718c534f6a20111220bf4d2f5e6ff7fbf080b0d85447cb9"
+                + "b5c1e052e0428939bf31d9a5a2ee84908fbf0ffee84adec5063670a7386c51d4a7f00ad1"
+                + "0622cc000f73f00549fbb1ec1d99b9fa4d80d3fcf9ad85b42d3eff1aea69b6fbef367db4"
+                + "42b00d158dcd6165fb9694e18a4aa77c783629cf32982a92182e6823ba04b2e6227080e9"
+                + "df4c7ada20c88ddb75104d56e0eb18e447fc3bc0e9322114c810ac2974c6da609d8863cf"
+                + "541da61c2d87c73f3335a5532a6c7a02aeb7cbe5750657a38b332efc88a3d246e4ff39f4"
+                + "9ff619c388103341a96f8667de37b69c80665ae8386a95e4da990bff87b2e59180e58592"
+                + "31068557e4b90ab3bcd1992c5ba738d361852c6fbbb18ccb29da5c4c23f0d4c12febb0db"
+                + "5516df2be56837929942706d0bdff1fc0609c9fcdc47e381b67f8c25630f30374f6cfffa"
+                + "0d188b7f374af14e56f70b83af8f3fc668dddeb028a2b241df6c74bfdef0e6102bfdb276"
+                + "22675d14334c841ffd689ae0badad31e25584c0be435738f4f71c32327db51a88237f979"
+                + "2594a0802622634875c17f1b554cc8be5d48afb5ce6a8c595b5aabb81f2b66eaf7f2e0dc"
+                + "411f5d04d8d92ae3664055cb9bbf42e8790773a41acbb37920f5a9e798dbbd4e1cbd7174"
+                + "cf38ea83059e25fea5793a8231843a82d17b181d4c5e514f611cbdbdea7ba5658f386e43"
+                + "7838b90a5ac236ff89bf523fb374d3c99b5af4ea01cd9ce487b278925f2ebde33147f04e"
+                + "0e8e5be0a2c6fa8379f306a56da8581249dbd3fb2353275d94fcd6357c7a8f9e64bdfb64"
+                + "46af8421147069d7dddae83b7030415f79ef596b4e61e30739e4c4233a1e7db942be32d0"
+                + "80c309d6ff011ce503f56f6d17d72dc36c203c0d072267f97d85f5a491a5208423db7967"
+                + "0b5747ff7b75cab0573bcc40c77f56f8d3e291141d63160e0da5fbf68afabdd1c06b93d1"
+                + "93035f4fbf3cca82e3aae27ca96bdd6e03872732e456f7e95b1963c61fea0f785f4ee970"
+                + "fbeea443560b9415b24c97e747ef28b6b7d56b8c006bcc6b4c080846e2b93d4d458334d9"
+                + "2628eae7dcbdcf78379d953b4bb09118d3fbef0c33ba72c99b2778aae182bfda7bf7fe17"
+                + "fc3569ac6b172b52a6f183e83650215a1eb92ac5f7fc3653ac58498777c4784c8a0e830b"
+                + "de345180f88fca2ceb4dc6346db1bc40ce66fc9ff84f3b31ac222389f95c71db212e7aef"
+                + "adfe458fb787ad65491ea72c1ce797430da23dc3ffc3a682ab0cb4fae8963bbb51814a38"
+                + "1fc36713856fa8f4b4d7f70ae0c91864e51e17d60133719ed0efb4d143e3ceb05a2e8c0c"
+                + "1c80e0091bc4748496bcab653ad84634ea2998bafbebaa3c61c2c4b646b62caafb5206cf"
+                + "a2429bab20dcc858ec7cb5da3a1b8b335b503cfcc809193a781cc8f6def3234f2358e5a1"
+                + "99d7d855eef9f7284657a47c9fa8d20a690a614981544c54764378e94ae25bcda1e7b99d"
+                + "a7415d977ecbf3fbd104052deb9bed64a7ec0d97d4c0cd56ada74ec954477410c45116fc"
+                + "b7916c9b2c8eee79470181ef8ec402d0360598262fbb8b87686ca134700408b7e1b0df02"
+                + "7cb57dde058ab2d28f0ae85c6eda55adb1a6f5b5552d0d571abf7a4a989119da61e9175d"
+                + "357c78a6c29ade27869e9c0aa1fd16569879e67107be9223b0638e6a03d86660d878fe01"
+                + "6f36f76a21365bfe2885559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39"
+                + "f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "229abe8cbc651af0bc92fb2f812ddf8812f79935e8fe27c2d64e9a6ca072dfaf70825a3f"
+                + "a162ac44504c1f18cb849851e930af1c1ee4d202ee5d24783ab8cd16237caee308bbfa7b"
+                + "9f2f187a46b2f41bc5f6936c264eadfc455d401b8face01ecc000b602ee6c9708f3ebd20"
+                + "9145e8b83939a15aac858c0a5079281cbb14d8c4c318a047f813f4a72b24aa00fad27408"
+                + "52c8a07085da8bfef31f3c1cd622d725bb471e9a67252b76d90caff85ab401a6cfab6e62"
+                + "80039059331b889da600e552422dfaed31067e8aa61729f7dffde62e7e5dc60658d50cf7"
+                + "d2c54e18af4f2ff284eaa1f31dcda001b3581c0a16b17743052148ce97dbca4b81ee6adb"
+                + "5c20303007cda5baf1dc3821a5a311818659c802e997fb1c6f1590220160611957739302"
+                + "16a933c563c00d0b3e02d51638b0e49f0b5d8d119285553e0d3fc34811a8a28d8062558d"
+                + "37380a6db0a2cac2cc0c434fcf57a1218194725f8ebd9b3edb7c20b0d13c6a19ccf69a95"
+                + "471901b70c400ba25139b1de0abfe7cc5972c188b44c53781d9b03b65dacf29964564cec"
+                + "50176bd0861c38be323b626eec673fdba9a483b56192ed14c256ea2cd081d98ee58f5feb"
+                + "04b0aea16b1c644f61ebf81419910c06b356fd7ecd3fc5b785676a2e9f8c674d42f02b48"
+                + "29244367dc4a6b1c8c9405e584a2d4a68e96e80e6ed2db045f59b3b8dc0d92f7f83e2357"
+                + "45d6a16e26af8d03131c5f418eaffb7c608f14732037f0de18350bf0334c101070243fdd"
+                + "ee7c9d64cfb178f2341cc8f85b5714b8c9a9008e3ae1fa3308b95f5d4390e7d5346ba2b6"
+                + "ababac55d3b558128ed833b6dfc691aefe873e1637b3e7b75db63aeb315452bb5438e6ab"
+                + "5b492cb4735c1f2fead844455718c4b7d4d4d2330879ab3163c4f56556c0c14eac745533"
+                + "2a9962e6b4df7a79b60ea3e2c63a0649a52b22713d25700d784f6858d87e1d2746368251"
+                + "413499101eda13fbd5257887a7e45abcc997cf7401949dfe83cc0108257a9076bad42170"
+                + "753f71829f8993b9a08e7c4a03d447470b76a7f494308c5cd051d129d239294699692887"
+                + "dd112740f200992fc8be845424ec831f57ebc32cba152808483076cc74688cc686f7198e"
+                + "31e259180778f4f227a0887e6a84531446c8065962a3b3a3fc662f87cba991f0fa5cd1c8"
+                + "ce1d24c948735324b7623834cff51c896093e25e4acbe3d375c5dbc168a9e985847cd15b"
+                + "2aa09fa8d73321a64a8f52874550b8739e344ec8e94c5722d68e8cd0b821d5f1322bf359"
+                + "2a83ae3fa605ce5cd232a149c51a1d88abc682403f7d843b93196cdd92ed403183905c94"
+                + "72a8981a836e7e9adcac64dc9d131a5b4fad91960e1046533b5064c151d7cb95a074871c"
+                + "46900037e7190ec97190010496982245f540f1ea7575e38343618d5996900172da9f2ef2"
+                + "b56aba7a34892bf8339f89ef2911761459bafc703b057ba700f0ef00d454d4d2300b3f9d"
+                + "ce61279561a6836ca7a28eb8139fd4683a517345862960aa92dec51bd4232c87e31aad3e"
+                + "89beeaf6c0eb7f603741db9c4e7261d5ae6425474bddc24787bdadf5a7a5cc4837e6782b"
+                + "9602ff797b501fee984ae91d1a6cf4e1da4d7501495a9cc423c29a1e7ea520ed0f9d6793"
+                + "60b903ddf6bf36571b59f04e61c486e8443be6254f73a2a68a51f6cde4c9845d46df7496"
+                + "cb80372a3f82fa7f3cdea330aba4af06e28df020f24ad3be8ea67ab382867e2320b7e0e4"
+                + "cc5264b3175f4c6724006d584fd6a9e47fc298f4ee0537908c0e0a8f0b9a6815b28c8de8"
+                + "d0fc6244bb39114c769b614ae7d7ffa1d07dee38f7a5bfd9725bd94cbae22f72d246aa06"
+                + "2e28bf2bcd98b3c8ae0f6b90b676a9d63fa29dba82cad1d41ead2d4bd2e41dfaab342174"
+                + "936bdf84e09bcd25447a46b42fdb8559c0533bd858617d267428bbb0297351a343e90b68"
+                + "761aa841237fce15327da87bd818b40ec3453b83a598fbf4ce1c87f8c22f9100f8e42236"
+                + "602a928ce69ecbfbeb5e2b595bae50e3386ff453c6bd63281c8aba06f4345f8c833ae53b"
+                + "89d124ba89b3adb33838b94f2db7f897a47e1cbea2f98e7d2f97717325d3745dec7a53c2"
+                + "61be24c58d3da2220b0ddb55e55dbbdbebd599494cfbed180687dd3fa4b7306e30949f48"
+                + "9b307fb1649e8a930c3b9ae14e63883b64ed46f13545165a1e18efcff6a3c032e33cb145"
+                + "113a7aff7b891b7bc2747030b10255d61241b049473198c8eec0e4f5abbbcac6dea28e99"
+                + "4d022f7ebbd7a023b52b2c611cc3b5e1baa55e9df23b5f426622b6546944681328f7308f"
+                + "4672cb8f18870590623c08a378163aebd33987e0163ae06a787f192e6dcf695faf2ef037"
+                + "75250590d0fe5aff8fe5f1b029d545b6c19836e55b358512945d12ba68e57fbcdc7e3acf"
+                + "afc9c6d2a5aeec28744ee16a6354e9eed0714b234707adc2b2429238941513d226cd2268"
+                + "e6c50c9c0376ea8826941ef8591f55630baf7f2c09a6347295df96cd7ffc058257a82783"
+                + "3968fd6e6872d9f7955a15f875dc5a28b7c8c3dffe76f7312fda0d958aca4e4cada3b379"
+                + "6bfee90d5a4b912926c630182b1ca26cab9ada935756c748f92aa8146fd8df58d8a99c67"
+                + "4f62af1da4c06feb1db5f3dee0a5cc286355e80918608305952f9cd1717bc53a5dcc2c4e"
+                + "d8241cea86a4ade03dab2cf21857e02007bdeb47820a0a01d4f99c1b4395aa337f698e30"
+                + "5dbc0ec483c9ad46bc32cc621ed7aee830cc07549d9ddcf9925e300f7344003d268d7a7a"
+                + "53e691db7050d4f9176191bfafa1a3d6e0478afc3623b0e84104b0277ef6f5063ea18075"
+                + "05a6601469a7a8bc6a3733cc0d4b897b48b8d282ff473f63b824011a6c57c837ea493c6b"
+                + "ece3197dbced0efd8ef1ae79ff8af965a66c023f6b9eb6e7f77b4919f525a81727cf0887"
+                + "27ca6878a03ab558d996ab4edf6020e11e266e7deab0de96c22610617254005f02e85a58"
+                + "93cd120a2ad94812ae1f46ddf5bc1d48e1da2f9b61684d39945dc9093252ae2600e385f4"
+                + "e95012184bdb9abd782c396ea283bd56fee00c9d96579b465746671013cf23537dd5dfe5"
+                + "046168b7262567060cbe00689a709493c0f84152d25ef350fdec045994210043ffcde66e"
+                + "9bea0eb9c150a96e94b684bcf63bd0ccdf13acc0251763b8fe83b4f1861830aade0c337d"
+                + "3b4ca6bf46b51f3ba485559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39"
+                + "f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "23a2267c7d68930fcd162fb7b743e94c520197ce16e57ffcb0af6790245d85cb0563e06f"
+                + "ca2249e821a8b102ee771c4e38d797bac3a79eb26092deba93d6cd9a698cdeaabf442d79"
+                + "2fe01ff325f998f85c34398ed4856f070eae3a2e71e44dd427a34bd6a721102b0ef32c33"
+                + "b49db2d8d6f43e024f5cca67819829a650268408ca49c9a013770a979f6a42f39f7d044a"
+                + "878f7a46f8433f506b426b16163ca8d5808e21f9dfb39e339812c452d68e4b63d1f1059d"
+                + "7bd9b5ce9ffe38d687ea6b9cc6371e8d0d5c2536420a3ddaf845cca2fd488acef90f5536"
+                + "4398ad3d1d7d592a23c11a1f0c368201af728b81302f1b43e7e4158b909089153cc5c4d1"
+                + "941b7df2a309c44e13d83de34201f36b1bf487883e04e2acfab55e91bdb56ce54e0d61df"
+                + "000465847f9f90a908f800f6df66aa100e2af0d513cb720e2b119cd454f93395aa216558"
+                + "c4815a3b62c0829dcb1ff49f51194c8e376b8bf273ace333c60af6ac0f79e997d059dbe8"
+                + "76290efad863d81a7a31ade170e0b4aba20f529ba6e1f92847baba3b7a62b5abb2b4d3f4"
+                + "77e077581775180611977aedc9b65db157549202ce291c7c9ddf32fe6f97c09e36d3db1e"
+                + "7da7a364b415b41ac0a894ee2650955de6077f12ad30d3d750aab5a376783c41eab41d75"
+                + "9875695d61f491d7c445184337c806f4709bb052cd4d99834134c1efbf00c68d9828c595"
+                + "633a9b4565016d2062f1b863f28c12788f0f123324dd89daac5b90b34350817671b47bff"
+                + "831934c6d60d30523f6e4b903a0c56d1a7c558ddabe2f9b80063aa5036a888d98713e627"
+                + "ae8d654261e31aae7c0f631d18085c82d9ecc8f1548eef4ee3d89955597a056a6593b6fc"
+                + "77ff7b0b958387bedac460637e0b3f8963e80e84da162332c20652e0343be9f26c81dde3"
+                + "08b33c5170f1547433f2292ea696db9b024181dc6b0e9b7c979dc78fb4a93bd8b262dcd9"
+                + "4f05eabfb2981c83b8fe14fa9b2a4983246a506e66bd05d64368d31a60034226c7ed2d35"
+                + "e3eead5b7a719cf7b5074a16ba7c7149fb2ed242ae3cbd0a3f84d37fccff2bc432a26648"
+                + "b883aab754281fc0cf00556ff74efb73f17a4e2e9c8c862cb5ee0efd886a7a80ee52f853"
+                + "3f82d57bacd5ac1a2318b5cffc9569bbca5b27d9b329fc68b56ae8561ee188705653c618"
+                + "e38ffed8acf01cbda08efa0cf6cda609c9447dda0d74fc383f17b9d81a309dbdf5c954f6"
+                + "a322ea6eadc41bfd3681cebcba78d440e779f61bfdd203b05069231b33d146a44b34582f"
+                + "cf63f1e2037988f14d29d6dd067b8236fa613485a6a48ea37c71d58019b6dcce04fb77f2"
+                + "1d23ecc6b9f734f996ec571ab3615cb40997c0acb18ef3e70ec8a804b70388049b20f64e"
+                + "fb4935e4d3c62c132112297793776720d8494c7921f547116a4980086a9320062dd0c916"
+                + "113b242ec234e3940114ce49de4e9640bbede2ab573725aee1d32f6ffe91d38093e4d983"
+                + "0b15bc6444fbe75023e26d348724a434c25259c2abca46dd6b4762ae3b886221a81401dc"
+                + "4d841161ef7cff07c243ed9685cf2d560f66735231f7fd297e0ede53559ca3b035d6f2f8"
+                + "950e1411c29a2a01a589793b4fd7d0be45b105fb9ea9f494ea5dcda6f7656ebd3d4271de"
+                + "db62605527591e21f2e45d44e21aa03ad8794579c2d93737ab092741ac153a56252abba8"
+                + "f95ae2557447028a60a592f3118f348b42057c69f4ffc8b022fe5827e40964d5c4cacef8"
+                + "5c2a32b36fde4b2bcc3973942fd67fb147fd3f1b26b1d88cd83f6871c424eacbfc418b19"
+                + "804cc8fe9b48f85121b375a8489680ab5f602731e71af1832dd734eb921e1fc6458957fe"
+                + "106f5df96d2fa0920997cfc56c1c7ba4c13af81c90eff571050269a9e6e572e46389a015"
+                + "d48a1372aa0c4e7e18d98efcbc2c2d2be7766eed99f7bd7807f3683d4311028d1ef6b0bb"
+                + "dd556caf554eb4d7c1922c16fee10a4cc7789266fa21f3e1aceabe0cd1bed8dfac4e790a"
+                + "22cd032ef7971874ebad19690198319cdc4c4ccfc8171d7cca7e7b1035870af3ac0b0958"
+                + "53cdf7f9054c461b3f95c8ab4c9923f35b097fc9e7e76952025f95faee965d534d4f8f0a"
+                + "51e8977331b00160e4ae5897defcf8556afa12230920ac7881daf5ca9590c523f230f2dc"
+                + "9f504c1316a5f851e507b5e5698f9f8209e66f0d91ad287b434f49a99bf159b961906897"
+                + "32a6878a64c890807ca862fb71f91f5b8ddfd58ab1d4c63773c32989aaef8f2987469dea"
+                + "29547e7238c19f999b7b3e9e8b5627545b9881bc0af6665a1c730205413be5529c41cb02"
+                + "a7d65d7f1d4697bcb7cab8aa9af608108bde1e31e7275027bd73e3ea60c10e0f9d6f4169"
+                + "39d3848db7a8fc3bb47141ce105388441aff4bd20fb28bb46a68e3f4b614d4a78e4d5847"
+                + "a03c400665dd785371b73085a895e9795ccafc72e0575f771603c9603c4ddb5f701edf64"
+                + "83f2039d52bf8360d946c205560c9388398f023023aa83b3e641b39e1333a9d9fb69ce17"
+                + "81402ef0be8aa2c5c880e5ef4b03e0d2f54f74c3f54ca4d8a82d2471744b69e54e883f92"
+                + "1de4418df44165b1ee650c987f9541c4774da12608526661398f2b4d5dec9d767ff154e6"
+                + "69149e765d5e4055f0fc981987c40d9b033ed56efb91c8f547ddb33542951ec0f9586209"
+                + "ef2f801f0c33881de24efbb82076c2cba55bc3402c2a6eecf34fa37b7a2c18b04b72b37f"
+                + "1927f3a8fb9d906cb08da0a6a41a311f204f4bb958f1c4447d7040e9708920dbaaa054c6"
+                + "90683a197723f8e07ff4c62c6328ed66e0f8fbef365013f8346ffdad9b42dcd747de6358"
+                + "4450c10a6ff0ade8c7970388191adfea8db6af91870e07bc3f0dea103c0cb0a0f1c3dadc"
+                + "445859373c47acf20e5694c23922f4e10f17d2b5def9c298a5ab5b73e8b6702670d17f91"
+                + "617ee8ecbf11221f8c25975699ed9b76f2895a3c2c9a16f1ea96b5c893039eb6be0177f9"
+                + "a5f2dc65c5725abe6eeb60158879a5ac732153f9436353e72e8ee1244045305dea94ab66"
+                + "450f1cf78f1d9ae05636998bd1f29717f2acc7f01695fd9ff9da1fc5d22fd5b4e3630a90"
+                + "520918a84c31e3e3cc4e93c624e75747aaceba351707d39c56962ed12cbca38859dabbc1"
+                + "4ccd23af96e5f99a6557cfba7a3bd0ccdf13acc0251763b8fe83b4f1861830aade0c337d"
+                + "3b4ca6bf46b51f3ba485559ea9f3c020901e0e18b6de5fceb6044c2e59debe07f1f5df39"
+                + "f6ea85f4e4e7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "24a6c9d337c58d1908420f010a3df1fa396bcf04e3778ebb42a27a4084c17ace078f5d1d"
+                + "36f24ec0a32aa45ef4aaa51d44fa71689cd6dcae06e6f5a8f5b0145a241b9993d1c4719c"
+                + "07ed2f571abf0852f6cc3841c1b7aec3f8bbba9a53bf378b288880d0df18517cc8dc13b2"
+                + "5376fdd4dfeb88ed0254a8f902e5dc5a31af4e42547dec66873ef0803bd6e377168fb6cb"
+                + "55985525f0b71198cc8352e6aafce7fa80f3383973e22b4f66ed5a9239d108baab9bee4f"
+                + "d73bafacc376a6fc2746210a00facca815e05c36a6468cd0aa7a22ba61a8a38ba0bea83c"
+                + "e589749b028123aac322f964efd0c26da3244423a7db19f3db2142bc1d64c3b8730bca7b"
+                + "5accf58d07a720625ea494513299dca973fce6ddf262be932edfeb04001a5cfd2012f03d"
+                + "7310383fb6ec85f76dee656148b0a1a844000aadc8ce76f8127bf74bc1238f15a03698cb"
+                + "013a42a8a1f9308d41aa106e3417e8630e62c452b4e8e17be0f3d23fa07e6442518c4ea3"
+                + "f11ed25c1ded6e905b5696e1ddb9e54b9b38e1b45f5fd8df5351680d71abd851788c82e1"
+                + "5ffb4cdaf9d17f32957be30581c39cedae1bde0c48c31e79ff5bcc5c8418a59efd867f5e"
+                + "b3a2d0559d3dbff1712b9228d45980f4f8ed3e0bd714daa79e23bdae2d08f008188646f7"
+                + "6747ede5014792d59cf21fea6dabbd3586ecef23446ac7ccffc01bea2cc393300d6125a7"
+                + "e7d21a60503ee653513db94eca370ea3c55bdcd5b726ee3b3162a6561ec2ba7102caa325"
+                + "f660587b298ba33bf832d150a3be950b55e0240259440daa2252a9408f72496b60bb23f6"
+                + "a574fab99a8cc7e069f22bb055689130bfda18787d80a20b01dd1ee46505987ed1d54574"
+                + "1909539fcd42793084b6cc4b5c49b4f685c41834c97223b8d0fd81023670c1ebf3987fc5"
+                + "89506fd7f97fcd5d7f83762d73fa97dc321c3d632669770815ecb501acdf7cad03dc91aa"
+                + "9859ac710683a4389fd84f3a78d5784b8d4c497d8dd69c7747fa98825c01f48d81cb7998"
+                + "60b478faae2bc065849e948dda865e0c71678eacc87f9391dc6ac2ecb82c0f47baa472ab"
+                + "8d6f6f040708e7c998f879bf3658ffb9a975cb14d3074c44ad709b43796b5b5e8710cfb9"
+                + "12c5c2c56169c6376309d518184b1851ac8c2a8f49c08651e4ecdb59f078e93c647288dd"
+                + "e9aab5bd42da6fc24a301294ea8d9121c0cce01b17da3754fc424882a696b85d7f7a8fcd"
+                + "85c5bd49c735b3b707e21d8c58732d56291b751f484ee1dd77d35ece16138aa89ec68b3b"
+                + "a08856f61772d6b86d1d4b86568de014638c1da03111bf1b5337c47b1c44f88215487b66"
+                + "f6ce39aebb7d84164d97ddb095d15942dec9793a79f3967c78cbf696d6b572a6143a00d5"
+                + "7fb5c7185af26c2084423720c47e7a9352615008bf2808d1251363f45a26507460a54795"
+                + "394832d82ab898b9aee8fa262c21edfb2e3478c78f61d5da683406557037b3b796d4fa57"
+                + "e4a817a21810b01422dc0b8f1bdb68113f5f50108a27905b646fd62a97072513c2ec632f"
+                + "2185bade6fefc0b2c0836380d5231b8e43739e4f937cac153330d263ba42447b633549ab"
+                + "00d6c898cb7cbabb86c0a28323cc47f2b0093652de36ecefa60f87f3de13a41cf7eebfe6"
+                + "85515c07a5f893da49a3918ae3ea4f51df0d9e4cdb3d311fbcb717d4e48e39620e25302e"
+                + "e43d505fdf8ec97607e198e8ffe7d09c864f9328b57f5b0596981812b07c4d74b62594e8"
+                + "36125696565a262d88e47f7b7cefbec6a94f67fc7429402b7fa4f49fa8d0e3b88a937cd3"
+                + "720196615b1a48d0788a7fb527fda3ea5bed71962ed519447676b2701c357e8f3abfcf7c"
+                + "dd39d7d5c758cfa4a034e51f57fdc8388a86faa94dc4c9f0c8fbe1fde47e9231cebdf83a"
+                + "512dd95abbee28eda1736ff6173f589f8b99620fa705f127e44fd217607498d89c3efbf0"
+                + "8107804958093c1687bc91fd1c7c27b85943b01b7c3cd93bed1f317c552f4e149e91a962"
+                + "d16a0c3b3b4c5e9fbc026a8a1b527f99f445f312e7e585803790291c9dce225772490c35"
+                + "280a1928336d4eebaf558b76490a286d36a6e123fe9026f7ad1276cd829f81b8ed8415f9"
+                + "5cc8fcb5533137348d609b8cefad35a9dcbb1947f8346bdc7f5d7bdb3699340a734c2798"
+                + "51e085730cce150fd336597c570a53567e03efb985dc3032ac95e711d87ad3a0357c60a6"
+                + "7779a02c141605e24cc2d68d065a95672c069690184563c889e103fc00c5d96093b3675a"
+                + "dcc439fb1464efd60905891217b299debdf1df34c93000214f0a2629d2cfd9d770c0a4ef"
+                + "2205699d2219c1c00675089156da99d9625c485e4fc36a970ae2deddae5a2963f5ec76bd"
+                + "d02508d858b54e8a65842fd3953f3dbb9816a6f7403e6798dce1b455efb3fcf2fa169f55"
+                + "46adb82abd6301b06ae084cee5db07415e2fc9e0a29abdc316ca95a310544fff3210117d"
+                + "805644e46cc5604e8ae9415c34375c251a5169c2cd0f1e474a1a73da0a307e9026d22e5e"
+                + "c40d2a8ad52e7ad1409bfc97c1310e3e69c96673a9b63c22ac2ac1ad682aab9738273c4a"
+                + "11504d5e106084c2afb7321dfb200022ac7ea0c7e018761cd627455e4de0ebdc86f75c7c"
+                + "54b1641200f8daf65274b332bbb0f949b6ddb619aa434b350f980f7683564e6f7c9c611a"
+                + "bf325af905fbfba3f611c6f1365dd55fd328ec13fd1f5d0f6931a8e6c607e22a4706ca4d"
+                + "f0ce18cc21c0bf4afc24e0391573323dc3ab7a090710ba77a7e9e49c0c012c91aa4d8bba"
+                + "1a3016ce6bca023da54ca722f060d984ee2187105caa7a38108a644ff24b2414000a1baf"
+                + "66bbdf59d81a55761f4992af45104d06ac0205bd1868801a76b9e1f0cfc3798f7121f6dd"
+                + "0efd765232e71275d2af100f8c56b449e3d3fb1d50796f6b5c9c420844581c536508d0a0"
+                + "134b1cd4800b4fa2ef99fc22e9b03ab345a4fe68f8075cc4405bd6489a7e887439c04270"
+                + "e4afa11f29b73fd59c4afe884e70729d5d2ee673d3930c503501acabe2891c08697a7013"
+                + "fb3d07d091946c61314729fed05122a4812eb9298d8ea9ff31b5b6bbfdc5dd230889454e"
+                + "c5aa1a8d2831269c7824d5ae5c828085fcc90592ccd17e602ef38da81e87b7716669909c"
+                + "7b8788687bf86d69dea7fd514426f5aca5713d2d3fc976247402cb805da7e28f525cf9ef"
+                + "0646a64d9f9200ac85c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f"
+                + "8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "25bc9978864ca6448a943b11fbd92a84d65662eb7b0157d1f4ca732fdb3830a745f9c9fc"
+                + "03db135321ad591e2e1ecd8829d7370fa4c6aea2ef0da638b328af9b9c56caee4c87a54a"
+                + "2d229b8b92c90f8e46720ddea820c65a3adf256cc2d003e0db9018245411561967487aec"
+                + "8cc16374870a4dc332c23d25645b5774a39ae6a7569f3ba1a2d478c9a8a506cbd38ba154"
+                + "3b339324cb0246a38f5398eacd44c6c7233b0024e336be86b1df3cb70d6cb208368f3e44"
+                + "f78db80604a68f92ad8e2af27e09ef43e1c7322b84cabf0ff6d4d575b57dffaf0a6bf552"
+                + "094f31857c755bf1ab4550a79456806cd1d4672e579781b24b45734317b2258d5f6dafc8"
+                + "e4dcf4a4dabe2bc562acba272387c39eb5a0b0576583378582e81930eefab9d8444689af"
+                + "9d8c1b48095110c2b2dfa047a0c1d01f89ae4f7254fd3251b2da6e9c2962318c4bb2cb75"
+                + "5a05b8d1e0c337a745a813e1e61bc8dafe542cd082ffeac50e263e24812fe07f99db1adf"
+                + "b2a47dc1a24c0dd4928d6e6f2d3513d10c4235b952db87fa9684daaa72b7e112dc7d3bcc"
+                + "4a301363a19ed1074fd97d80b4b79292b8dcdcd7da0fd016103fe56e81c5751010c91c6b"
+                + "9bd3389f79dc65f5fba4c94ce0a10a95c9918bf85aa635f22effcf3ea795d6d317b7fa50"
+                + "86fedb79bae6b160a34a7738edaaa0316fd0fbc0edae6c73432e2f89c2b2b495ba90de4c"
+                + "ba7d3ca9ad05a32b4d8ae47020e6446e6a90c8ed552751c880a88c689d254a8f5557e428"
+                + "5a2c6dc7ec608f2cc35a29262b8b3d5d0cb2bbd6a0e446435abfc8379cb843706777269e"
+                + "f111421a3e06d318bd64cce886493f99cd9a835da82d4e750d9b4c47c1c131c1168c2b37"
+                + "43301a4ca9dd56f51e1ac56e177f9306565ff64354af74abf41418441c999cd5a02a86cc"
+                + "7990deaa9b1ec97709124c4f0965fe2ab9a03b52663eab3b7638fe6e04852096cf63c3e2"
+                + "937ab402a1572b24a8b888e1ab438fd03f288e20ccf77889da53046c142a59cc4672188a"
+                + "e881f0557ecaf4deb5dec582ecc2a7774c3b4442dcf7648adbc237cc9a7c80d6a2a07bd6"
+                + "6cc71e5ffa865e19483e4b10a47d06e5254aedcab1114f620d4d7d1133018a9ff5839d29"
+                + "41cc919ce7a059f058bbd935a6c67e19d9961c42793e08e5256311274b22031a67bd1381"
+                + "b5c88bdf969b8c3bd0f3eedde337a18472fdec1daf465775da3341679b814a33c6ff9355"
+                + "1c029b1464f91937ca4c955e664bec9a51eb382cf41bc628b2fa88438847c88a6b6e606f"
+                + "9e47b52f17ba463026ddc83fcf622cf7d92b10af7055140da03af2fb3736fc992da4078b"
+                + "f12bd7f1639d191bad4e5909ffe5cee6295e3f9ce76aeb254722d47bf3d7139e35d65553"
+                + "aa66c9675202e86f9023bd509de53052c5dc96d5be43035f2660862ce704e987e3935a9f"
+                + "71cb08f83d649f535f10c4597c6661ba5565df3aea46a6818b5c4a32eafbe2f4fd8c980c"
+                + "a25e55fcb333b6ac0fd7b05643d0248abafc29fd0abbf170ad451167d491e18d75320f95"
+                + "68e486fce49e977b1f525e7a90752c6f699731b9bf1f1fb515d580719b4afad0c4c104f9"
+                + "89b56f91f86b3b9b19e04704537bac918c2198cce83d4ea1e80f9a0cff443ffba78ba815"
+                + "4e7007ea07e442e47f0454aeb7e011e962fac88a094c7aa99c5bc134cc19eae2b3c601ed"
+                + "389d9a26e065225cde17621097ea54c611f7b17189cda0b030768eaf155ad69b29dc115f"
+                + "4ec3f98797cbfc61d8e48a8def905ee044334d6ed003eb3515f0c013a90705e05005d854"
+                + "91a107bb4164932cad16fc225c36711e7649a23b57187cbfc3542271aed653acc7aaeb45"
+                + "8d93ed03e025b0397caf9aa1a02aa7b2d7e2f79a55068184d6ed4ba2662c2219c02b58b1"
+                + "cd5730cc3c3580cbbab4e648aa2f3a4b0f669f08b7f772163157e98ae6117f9033a56179"
+                + "cf1a8e6face3ff9c179713340eef6b6b077348ac3fd77501e4548d1e7d064643c66f954e"
+                + "568482d4abc5b531852206093a9b774ae8a06795c7b7af2d1f6bdec69187095e83f78c5e"
+                + "3f437d87244c1eddd1e3282513e690b71cb8c9171f384c0f1c586d7af9f54b9072893d51"
+                + "cb64ee22934e88aa7910949108dde72dbb2401e245b24a4f85957eef16e71fe822f3a1d9"
+                + "b3549484f8ec5cc2292287cfe7adfdace67c91deadee2978138b2f0736cadd71b98d55ab"
+                + "9e7d0fb7b49a09475ae85ae88b50474f6808e78a253ebd76a8d62e53bc459024614f7f2e"
+                + "f6b3cd660a44d1aa629fe5a1e193bebc5a6872545a3dc3a5cc5dc393571860621566f122"
+                + "c37f945888e7b3a1f1843c7afe5764f08a4e27c8e11eb9e9ab65c5aaf436ef3943ae2bb1"
+                + "f508ee4c2940557b1626e40a96578cc987d4be305e817cb789a26dc8fd8721d83be2f02c"
+                + "71b841feefad09273ba81eb066e4633c4cafbc988def2544c9ce88fe016b225451961dbd"
+                + "a821a95a45f5024f8b517dec14b8dbaf36d9fbf5eda95360270e91839c7bb13a5954e5f5"
+                + "d19de8f9ee69e6c91a1f48b1040cdb8d3b5b0ad559bd4aeeb3441ada040012fa9d571c0f"
+                + "4f5296cee83b828af80558521b2e48da78b82739204a2f53057ce8bb868944c8358e69c9"
+                + "259f6b7ff77ca54988c72344756a34ed807c9be718dd3a3e4e489fb94909681646f97f50"
+                + "e0553f63ee9b3a9e8d05d106744f8dcde86f7e59718e9bf5af840ba7ba942e5635e4697e"
+                + "34d8867106eadaa88e941fb300056f16cb3dc12538869887663376bbbfe9072bc6c70fb4"
+                + "4f8b06f860a23ae140935bd7e851306d1ebcc5eed8fcf5948bc66ddc71bc01d7231aa871"
+                + "c3933ded30ca5efcf2d7cbf52913854268344d5688c67fd73fd4e50eb3fb86775c7c2e59"
+                + "e22bc2b98c4bc6634141ff0ed3c773d6545ecd560347992f385111e57633159c9201233c"
+                + "c9423f283dedc13f4aeb941cf5d28c44d46fb187ae7528745780fffff1d6f44b18601757"
+                + "fe3057cc59412622871b572c227512e355fec914fa8a7911c691fc2ff7f28050670ce26d"
+                + "198ed79b6d67a8361a7dcc28cb89e75d138ca7a32b253ca9fb4afce3918cfc9082203a45"
+                + "e9af91c8676debbfe6c2481a184c21170b28e273afdc5e8e36ece68c2e89df8a3f8f600e"
+                + "1bc5f06ef191c3b27ab3582e8626f5aca5713d2d3fc976247402cb805da7e28f525cf9ef"
+                + "0646a64d9f9200ac85c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f"
+                + "8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "26dec253be7ea1b6571336de8917b8b0a4d24f128a37bfacfa0557ed1fcd72bc3f8ed7d8"
+                + "3417e7544a8234a6a3ef50c96bd3e3e671fa2975b1642e13e8a6b9c80fa17b46cc77d902"
+                + "391a2863c85b9b5b5e4e73962aa90d9a322d29457bbdb18b92e844b7a1d4030e933fd748"
+                + "cb2da3bda8d97042ef8059efb433c9cdcc71c4a5362ac20167c299bc373fb4c16cc38724"
+                + "eb8e1c133773013f821bff92aec0ba65ba8687830edbf67817e501c56db9d00bd85288c0"
+                + "de09bd0a0e86a46f63e4e508dc04a352f383c9074f41051c79cebb7504cf10176ad2abe7"
+                + "78481c08b4e5dc503f82d29cef99b6e6a2b321339c1b12b1791dd1621215600f85ff8190"
+                + "e93fbe1cdfce14186b12f0361eab462e9885b32d2a551f1922964b7db20ca60e25ff6fa6"
+                + "5a53e971531aad3172e11ab7503135eb97303a34b31a5d9decdf0c891993e76ace5f3667"
+                + "1311d2f2cdd07bc1bcc1aed0b7e2aa26b50c3244a7860290aea8794471918e02a2eaccfc"
+                + "329285880a85bd43853455d248bc05f2345cb5abf19a0c0784170b11df8751e0a716263a"
+                + "7a37143f8e5d71847c7762ab69ca6e9ef6852f84a0bdb56d194d1289787a1fd84373d66a"
+                + "6629f634f17b63b91e42a2f2ae7669846d9d747669829fa4d2061847c483295fa8647cbf"
+                + "3e7b0cc393ee095763639442eca202e710d7038984486bbb9d6d8d6792c1921293e51405"
+                + "97c2271c06b26e5125a3359dffcbf307b4e706934cfb986b83de4d288b0862aebeac408c"
+                + "229a4726674a8a37c1ecee1fc00806c8336e69ac2a6a03e987801d05abdc00b9a77a7f63"
+                + "cc130a6c952387ec3cb49c12f0fe8049296fe9b1ec3548827c6c729bb8c1dae67fddac8e"
+                + "a477bb45b5cd321fdeefc7f24fc105501a43364c6fcbaa646508963f8c0848c17c182ee2"
+                + "95d99d482624d2712b7a872adba3792b5822a39444d8051db8185b02c4d3042b9011a96f"
+                + "91cc98fbfb6630ce6032a63395b5ed566ec6b7abd39718f6c37140e555db7bf75b5808ab"
+                + "f866a4f55f428d866109a668d3eb0d1ebc7c02e3a49b1103e73d59d16693b1389f8b878e"
+                + "b47898afa16d3e392691aef932591f5d16b2b0cc38d56ae8f6df19490e200bfdff185d86"
+                + "30cb2e720b2823adfbe3134fac6ac012e7242c22605f193629b32ae3a9785a0fcb45208d"
+                + "cf6bf81aa123135a9b87e3cbdcf21069efc95f132d6c21c7e768152c1328d669973d9940"
+                + "bf162bf1e45f23db1fdd5a44424e3f22172e3575ce5bf0a791bb32ba274f72da601a2030"
+                + "9bc15f18847ac625f514049635fa4bce766b7b692147f829eef763ac52fc93d3a09d8fc1"
+                + "0b7515c97e2f666a9e372706f6b3e2d3fdf69dae11b98266ebacbc9cde2bce799e9184d0"
+                + "c1ac77dbca2576b1a177a870d205f78df26e3e02ac3cc50ba05fd69dbda7f0a2a1198f8b"
+                + "90b6975e42cc26db94fb4d79a028d4c5079570c538e396110a969837326c12cf6e1811af"
+                + "9a9c73083e3ca4cdab8bcfa5a952f59589853e73194d1187fc7d104e8fba55def86014f3"
+                + "c3d4973802fbf894ee4c39c7967328115878aa4e8268e705d5c248087de89197d1948d06"
+                + "0a56f1d9bf022ca31e7aeacc30057006b04b61f9e092cf0a5ee38e5b83cfa46157793c46"
+                + "170d7cd02db1215b91c9e73920af9bb7de5d09123bc93ff719f8c8a2e0559121897b12a1"
+                + "101a8ae9b8a908282cbd9d526b3ad23b0a7a0fce05eb567ddfd52b5fec693b3ae5d5ba07"
+                + "176160eed13eb1d01aa6ac91f15c48dd0358c7a3d7d02173e2eef5e06a21f9f0505153f3"
+                + "e84587062101f922b3cb6caedef38372106e454f33a66e64a3056d443dcf9b3c1b628e6e"
+                + "cac2939c511c196885ea48f4a20cbf47ccb0ebd247387c8a34402e66885ab4fe9027101c"
+                + "79957e8026396d2dd7ea4ea5267dd3fcd0bda0017f9a8a9b4f0d0100936ac65cd9373920"
+                + "514af897bcae920c81540c1b966f22eeef25b08cfbb552b9fcde5509035d4063e2824d9f"
+                + "5abc014420626e24ae59eb995f453caaf0885b8068c3f23b8267a9142c25df3a1d731b72"
+                + "a9f8e3c77a9d5581cc181f35ef03a219679c11b5437660859efb89020dcb65ea3f1a3dbc"
+                + "97af4848f946caafe150ca8198808d4007f8f3121c26a33dc6492a855171b809b8709e37"
+                + "d090cac44e02c44a3e540bace6fd1cffaf93d653778a47d5360e7e8f30e7c11a9441d552"
+                + "a486afbf0f0a6053fd16b739b9cc2fc36c789cbc34722c68d4c7075bc23ddf45223d3169"
+                + "1dae46daf3abb6a9c3c196e0421797870f813e29931384d069e6227c873c81d74a8488ee"
+                + "cbfdd6ff198dc0afa84eb2a3e6c4c6c89c8cbc172f7c8e2c252992c693424a9bbe9e25dd"
+                + "1a34d5d396ccb649049ea73843eb2b09143d2a6380f6114a1a3d7439adaf3b9d26cfed71"
+                + "eb2e1ff902b0d7cfa09200db5805cba1343d61b89430defbfe7b2b8de584eff8d448f69f"
+                + "f77faa6101819f8fbefdf279b233071d6857e5ca55f35150188b5be235311b3eb632f7a9"
+                + "e8bcbe6793e1fae4fec9bbdcaa899c8169b745feb48ddc2163fd6b356e2475aa7ccab3ba"
+                + "0112a885905fb1bcbfd7e2904263b7a0263573d3e289f5aeb69017ef1718cab3806adc44"
+                + "b84627a7880c671f97a2559e11a344399d67143dbdf2f43992582b06d9cf5e1b7a1c7329"
+                + "3b12f5da236dca67b83140b570bf511897da0bffd8b098626bf76d1c5ec744854d983529"
+                + "fe80b6718756338599e83a1eb8e4915d60329e1a6d4ae845771330ac40c2fcdb32859cff"
+                + "4f0d0564a653a9d33ec8a650e8419068f57ea36d36369da36c70f53f54b158862f0cb2dc"
+                + "7b96259475a340b0f844c0ce596662c8fc9444928b5d3b4b213679c3545fa78b87bf78f2"
+                + "be27f73357c6d1552b05b17dd6bb541bb090972daa83262ee8cdadbbd6985385dd6daef5"
+                + "a23c21673a3da91631650dd0e08a6ebc8dbb24eebb8c839e47b3299a197f182353494e38"
+                + "7d7f51a8f8792a09f57b08fe33ca019f01267a97028af7f39150e11513830078e6e2f7b5"
+                + "1876f7f602c02b1d07ddd2208088323e806f58ae5750074f4d45afef7cb652133279b140"
+                + "a214c3e294b4d07560cbc4876ed10648f5e3b21ecfa2064197ba30bb056e2405cb818bc0"
+                + "485e7cb3f01c98a69e763ff979d290fe0b3e583882e5e3fa0dc92868707539d89c205fc0"
+                + "9d86e964c764070361c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f"
+                + "8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "27896d88eaf84d8fcc3610f36b89b94d85da6b773f457af8d87a576277376414ceefd4b0"
+                + "5482f1f207a1ee7116e3cc4fdeab134faba2b86d865c65f133efa89fa73c236031619400"
+                + "95e5c42debfdfba0fb45ef3a59c3ee132de58960da06894753232d1cbdb0efb7500b5ed0"
+                + "817a119cb165f1b0b14cfc057d158069128926253730e5b31386e239f5fa240354d81895"
+                + "d0d8c105c4e0650a16c50a246d82f36ddffbfac557d062818fd1cb990c2847d9870db034"
+                + "6b9df82820f421377dd8682d5ff3f7622e61e70a829626facc4199a44beafe4e8adfc461"
+                + "71d8e33dcc7a9135242ea336b64b1604916af8f3d2f49c6d6ef73621914553b7b8efd22f"
+                + "cc2da1d6dae332e1b3a5b770e1dbb0f3025e666d5a0ce78c63f9e509d8464921a7e397d5"
+                + "82a9e9fa7770e5065bd3232945de15606ac0d5b60abeda1b2055019c46065f74ef1b0d1a"
+                + "e174423cfd1000c07d3173fbedb2130e527ae0250ea85550efe42db9c25bfd82de6569b3"
+                + "e82b4f6bd9ff0405d79376dae0e762a0387f6ae2bb066ebe2e20bcc63dbf3789f0b8fa5c"
+                + "271cd92a3d33ff0612f6812d47e454414de205ff6ffca96d46d2b9d2300d2dc4568f429a"
+                + "13f7c887a55497188dc46c6bd5f556ce103af84e16b187ac8db9707028bef1acfba12d98"
+                + "d295c6533b54314d1868f337ce08c8c2111ba845e2f026950d083a925f9b84afed1a077c"
+                + "a956b3d73f0298452ebe32be8e488e262da9e3992d154fa0b89526bc66b1ef3af36ad21a"
+                + "6f785981997c55c2d67a61aa063e76acc173af8da8a983bcfea9ed849e1f6b281c8b04a8"
+                + "6351d0ecec00b42c3b69052ab1808faeeccd7438f930210e94ddaad6860325b72a0e8402"
+                + "6a134bcc61c959610496d094245fc944053ddc5eff10ad14c4f93b1e0773dbcbbd1cb348"
+                + "5d4263605e4fb3026974989551ee5a5569a91c3e146fb125e5206723316923e453dd8180"
+                + "f9f0f5c05bec1b6c462a45b16edb7d8040a6ace59ca5ccb329f4dbcd73e5bb556542653e"
+                + "17b1293814a3fece1c9725115f1b70f7d8f5c99a9cc86166c2c064fa7c19cc5c3870c250"
+                + "437191b00eefddb31b97f193f7c4ed12341694f47f01015f50c6f1eba41386bbaa21baa3"
+                + "0bd510b501fef2e440b6c0b2e298561696fd70691778c430620e9a0da8fed2817dc2c19f"
+                + "d2589f3936a6597f00650670ca87c22d1478f0945868ea3846900661ca44192e51515896"
+                + "0008f14d54ff2fe85e5d1ed562e4862d6f4e7f45414dda58ffac0e8a723e3013013bd72b"
+                + "d99092f05e003fdc0ec7c9bd1f9421c765d07f2cf342ad85eddd6c976a57f883ff9a4980"
+                + "cf32a7cc79ad4dbd6869a73df53dde7898ad8ccd7b4064a9117f4b00f807c133c374a069"
+                + "da2eb86b2ae6a165f43e5e7fe04938045a7f52d58fd897e5ca7b0ea913844b1fabfb6fb0"
+                + "5013822fdff60b17d852aff7ae28886fdc2670b9f23d07e1320aee3c10ee2ab2779d2897"
+                + "ad7f90e4d6998846e494728c0d94124b77cc9190d36bc7835f8dd8cf1781ade3ef3a9356"
+                + "9ad9a768cad18fd1c3a658ef71b7bd257d569c020408da1164293bef004668cec618dc31"
+                + "ea70ff6d2bd8700bf77780cd3b5c3534a8f8754565d6d44b82d497a680c8138bbed767dd"
+                + "a8086d67c264b2568f7829e473e35602ebc5cbab38a94a2be8807fa396ae2fb8da9a82d0"
+                + "2a6a9c461760709a62479531f08b9bfcf6d0ceb15fffffe56d6c6674dd7fddf8467cc697"
+                + "2d404965f253cfca82ca7d49a79dde0ebc38ba00ad7390ba7ec1d6a91b0ece73735022cc"
+                + "d84552ff5dba064f247dd2e859104d5eb2077b961f61762279a7c289031c083edb3119b4"
+                + "cf3e6cfa0169eaf575ea3a38a26e7cff6e559bd30fec423c7b4052073c900e0303192714"
+                + "4bc25a27baedb07a476e15f27082f97e509b229b8ff829dbf8bb07bd1a0d31bfb0922f3f"
+                + "86bd54683c5d35600337726eea57b17a5e98faad765012e21522c80c7d90b6b0a5e0da5d"
+                + "b701a464b1a2e85ba50162b5c3b5863ab63a619620e6397b13a7d7654fea3d601684f917"
+                + "40de4a0f19ff671cd58a91022c68f4895b179a709e85637b5197ee7232f8862918bb9766"
+                + "06e1e491b86fedf453c047b2582a5258e6fd76eec1eedd74c7e53c777f73f222004965e8"
+                + "5df5658079e21579b758ee5d24a2f68fabff1692d0ab7d359ee5618e5bc4d249f8ad1de7"
+                + "4a7b33a5d9e6b5961ce7937f9de4fe1fdaf136929625a56d734c4898665e55b29a5ae90c"
+                + "2d85d92e77c8dc866008bf375932d3c42980da4544ef7db288ded83dfd871430b8d7cf4b"
+                + "8185184242586ea3a0029598226c9b614c18da61453e390de05becc7097988ca1ab912f9"
+                + "60a705d024092a6c35fde00eb05744cbbf7852d4874db683c6f6bfce2f2df3aa89f3e6ca"
+                + "dba90f4acd63c42824c78942fca3d5dd5b969158ebbe0e401f7c3eee1eda2945e2db57bc"
+                + "883083865474fba7ad3ccc1831639a1669c0421989b7eae327aca2366d99767f48517743"
+                + "169ea17ef92957ea49e3a5a1fc1b77b727933651ce48eef10c5bb15687cc96ee64ed4322"
+                + "44070eb18f7f6852f5be55200842b50bcba50afd03db33e4dbbfab7813614cdffcbed89b"
+                + "ee7800a9e10a9697df80a07616372bf6126c7b20387ad2a9ee39b1662d3c294dc177134a"
+                + "b8607c63e2d5f123e39c8784cf640b13ba019ab5e87369181711900acb140b749f841c7a"
+                + "66785c23ef38709bd4ae612c11ac915431bce4e6e4c0a05485a06b70e4926b04c95e410e"
+                + "62cb13eff48ad7252360c8eff8e740769990ed552aea1fc83eeac71711668ebaea2e404e"
+                + "ed74b1bafa297415d010577a8112bb2de93cd48d225a2613b03084929d306c59fa970252"
+                + "47a49ea36c9d10de8fbc4d06ebdaeb4894eeffc386d47d8116b5cf7bcdea0afdae378d96"
+                + "28a1fecce0015c1ffd617a9e6e0254161957cad458261aded95f3c432a9220c1ee88f952"
+                + "3787cf81dd8872a6c6c32896a558fd5407f25df779bf78ec8645eea6cbc7b2df8b6f680e"
+                + "3195e1a8c84dd6c4fec740f66d874efed2e1030c5511db3422ac951976709b37d864a040"
+                + "e7b022a8e0030bb6d34ef443a00b1f8adfe232d5a30417d9ef8858afac1bb7999ae39b1c"
+                + "1264de0b82410b34b5cc55ccdbd290fe0b3e583882e5e3fa0dc92868707539d89c205fc0"
+                + "9d86e964c764070361c01e6210828c2bba7416664772c146ecf90e55f25f6717b6f48e0f"
+                + "8d623d07dee7ee7ded3ff07f9ad0d538acfe847551dec86ae87a65344698c7be512a9bf7"
+                + "f67419bced642902ff3fe520aba6602d4b81e7cc008a8509848d43f11aeaac5aaaa1d40a"
+                + "a64895b8206096fc36daf38bd7b90834b8e687b2cb62ae9e3b4fef298d5a1bcf41275c99"
+                + "fe72e3857721ae68d5374d9234e73a44c3498e444a8508398ef0412697b538f6bfce5471"
+                + "8a7424b9859edb92eeaf98b32b5f93354387950ccd4a2a1f27fad59608e3d4bf880a11af"
+                + "701252bbb28a4a4714d99f98445698ae8ef8eaf2f36bcd2f0a6eabc49f6d3d6ea89812fe"
+                + "3a3aa05b9c197f64d2a0120d60bf86ce938742d9bd6952fbb9355037214505a67ac89626"
+                + "bd4e2fc778268b3c9db4ee28cce5c13081fbe5397b46fe6539076cce644a8f423ccb9469"
+                + "dec1d841685865bc32b454ed5e25a5b379e64eea38307cf151d83c4da8319f0c5a85d3e1"
+                + "0e9b4c1d3e416284c4a5d665741df5abdafaa9e0e2949c396033ff7c0711d4ab9086bdbf"
+                + "7748556ec43f467216f688d7ad62d0451ea6347e257bf35ac9ea4fae491354f8d90871ac"
+                + "0d15fd145ffdf6bb4495f491149f1ff3de661094066d4ddf8f20cd13d192b92e5efa2fd7"
+                + "5df80283a36ff02f45c25c45d40a65ca1a1181dc8f81aee7248854e814f8b894c5b4eb08"
+                + "213743724193bc0afedaab9dbb71298cfb490adb347a8ff001d504e9204f968ca51cfaa0"
+                + "9965651b6001141af59c81d48fc6e544b42746e835750e1315229a88e7e6d754bee5cf31"
+                + "e89f1e8451884b85a5148bf85f9ebdef5a876929a51b54f0f9c352091ca8b98015f6d74b"
+                + "1ec1aaba96d247391b90fce011e1698a2215e7b4cd7da7e90fc8537af306b7b9f8e48b99"
+                + "8cc8557da6d695c0fd7354fc056294108840c6fc6a6752cc7a3386d1f97b66df6c36a5ef"
+                + "a8f06673980d506cbcc73ba11c3848d2a83cd34f6b944d5ab5bab103c6793d994da5bb39"
+                + "ce20fe26dbfe3c515bf02cb8ec297ea06c35bcacb6525046d9d22d81849bfc31793bf675"
+                + "15049e3a82778dc7e8147241efde4e269579e5ff9b7ee219b2b993882fa3abace415b600"
+                + "bc1bc0457834c150d2748b537950b9c4c2213fcf245d7d8d17cf2c32db06a761a1c02afc"
+                + "0bd491feacb49f79dfc91ae35f5319ba3da01a264525c72b367a225fd041ccade751e75b"
+                + "cf456d3934e7c69bfd246db51f43baf476bb2566e30c95eac1f846f4f52750b3812944b1"
+                + "8973f29293d7db961756cff813eff9591e328b240b2a53140a4e6e46e8b627b085d2a9a0"
+                + "54d233c419c619ad3d546595e5b675ecc8d9218536f88174c7df4de3a32ca84760e9d37b"
+                + "0aecbf6216ebf0d778ddad11671da9d8369ef318ca79f9112d2895707bcab71e57891c27"
+                + "6ab603d5f4d8ea08fae2a5cec939dca56eaab8a1293962fbc4bf9a23aae328e1939329d7"
+                + "f7d29dfdd2b0b424fd67f212eed1b6d2e7570c5377404633580dfa67f28a0102372b7724"
+                + "bfad3e5b99a6e04aa9da9c17da278a19199e8f9a95737c6d8eaf17374bd30a207cf18a67"
+                + "6b5cdc81887d26c0f4f406d3ba0d4a5df4cd17ed494b8e4e75a488db6d3d5743c06c888b"
+                + "1d178827e18227fd1e05a78b7fb0f4c17d3e061222d988b8cb27b148cfa9d7ff18c04902"
+                + "341627cd63ce9f877b548a3b643bee9e1a3dbcca7f1b8562fef75c941e089e2ae24b2866"
+                + "f5126b57c0b31d7424adcc94cf1b15004263f537cb935b4b7a5a4b8fc7834034b395519f"
+                + "4642fa0083c0e372f05447450c5dbf3cd97597865715da0040d37422a6e47c1645a165e8"
+                + "46cff368013c593edc2fd448a057732431e9cb0957234b02cbba211f41b6deb09799a146"
+                + "d9a207956058cbea9707e5f821f5a6ca6a4816aee8cc5424266ea118c7f3a0191a46705a"
+                + "0f78aae59a4084dd2873edb8ec644caf70d29228c1e7481e9dbbf78321394f4ada79aa61"
+                + "653122e27dd616083f073b04eb0942989c74e2aa975713775b294d6311a928dde41d1516"
+                + "8cdd0d0cd8b7b87cba2b263b9698a7251af0f3d37a76fdfd97f7871db5bbc82a293a520c"
+                + "29e9c7fa3c8f9ac38013a40d701a72cd118ba5dc756d25c9c6e7823a8c1a2b146d3dfa74"
+                + "da3d5bde5c4846b355a99f574060306f22f841109e610fc8ee59853266111542593af6d7"
+                + "369e5238c0721b92dbe258873714151787de5e1ec7de836d248bee8b30212bdb3cf648da"
+                + "cf0743d6c9912e5224bded3d222edd61023529b7ff1b00fb8ef427ff2f5f3ff189bcff9f"
+                + "75507d90421af29315f239cd4e7ba1f0540836d7a538035d12a34a7958a5fcaaa1d48910"
+                + "bd5501b3afb86a1b355b1f2d9df7ad7cd332021ff15ccd8ef6e6420b2b1874e90969ecbd"
+                + "00071c81387f50b5f2a086dcd5f5310bb9d6e4a1b74d9fcd8ce1113c115df5fec08f73a3"
+                + "574709c5a97d15658e22e14a4e54e754b14f7521df95ba7007c7f3e97e0f6ce1de7f145c"
+                + "6249b57e1d7c0a539d906816f1abf1a5d53795b421af91915e47bd33efc6510636957ce2"
+                + "24d85f21f5926dcc50895d1224868fe8d78ef7e1bf67d1b5b8ff412e66e5bf3d9d311041"
+                + "dfac409274f886100dd1812cdfaaf2736f9f9f6809e18dfec6fd93e8a0dac2b104e46ca7"
+                + "0dac73280098d107cffbd8350e434eca915d089480c38f2cd340f7d092319a9892887fbe"
+                + "706b1a238034704a4b4c6dac71ea32e1cfcd4bd0d99526e72ee53a734cb8b1e4e976f991"
+                + "70e7c6dbcf84188928ee457fb132ac9462f1ac83a700bda7883052d7599f77c2bd3b38a9"
+                + "e43dc4f5850222c0eda600f8eed330dfedfe095ab0a4ceedaecdecd9f535c583a50b4566"
+                + "b8fcfbc68338b2bad8eb97267c3a8211b6c88233c249e260149ecaa6de3f71cbc49e3d8a"
+                + "ae4a439a9c31c961b8bcee2c30979d28cad77edfe2d9ed6703c19b942ec237aa9afb7040"
+                + "a15696463bcf8ad68357b1137c25fe5e3ad497d80d2b095fef588d3228bc697e3b1b275f"
+                + "377da513ec87d028ed548bbc2b8aafc51b528ba94c0107ed1f1956567afbbda4692632e5"
+                + "901d0603082819cec51cc757d2e05e9331fca9ba66e691d5f127912559229644f98a634d"
+                + "07bbc63ff081074afe428e2042672b0cfd239e42187e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "28f614afa0c4a5b3087844c15e82b74d0bff64ef28d6e4a761ccae96a01b15b578838099"
+                + "feaedaac93320f35f6588319c44caeadebd9ae7463e217b77d8cf4fc025be780e037da76"
+                + "abec3af5c412e9ff86285bb088ac566ed26ae1862a1c49359f557d8255db34394b1fc3ad"
+                + "8cb7ee491282aa19765b028baec5ad47a64808a5ca768fb2f0ebe306e015440d2bca571a"
+                + "a8175e3a3518751a50ed41d3a096fe6fb3abb34448722c9dd5bb306585957381288ac3c1"
+                + "304944c5c7fab04f673cf0086d9507527e374bbf77ebb9cc926e7da8203434a74aef4f0e"
+                + "656cd55505291dd3e488c6c9460a134a417174d739c2801968e831abefd502e72e232e3e"
+                + "eb4768588456121ed8471cb8aa09c54a7b2a07cae0b608b8f1002c9a347680f4cea314c5"
+                + "b60fe880520fe5c9644a02792e089476b45f3758d64d05513b1e98a62360c26df59d47c6"
+                + "788ad9133e386b92a185e8e8a701da6e0bda5ceceae0561ec6983ab5a369d27320d73f3a"
+                + "6ab6f3e068dd694da60764abf4f5fc9417fbb398b74b1eaee458f2b25ea219814c52d889"
+                + "8fd5af90d2f4693b44c2101ad493f1cf44f130f3a09ea215771afa4b7a8603bbae25a4b6"
+                + "be9db958f929fec62746036fba2ce6e32cc689439d87cf15cf9e5bfb66470951ae4ac3eb"
+                + "c66eb9c946a9044a6dbc543fb97029161c8d588cc6725e5027d443234f63313d9b8a492b"
+                + "851d8fda68d44ab61aa15e9a220ac9bbce6d67d78e44216b8ca813a1ddbe3d22bd93f976"
+                + "c8371f5f75781d0881dab889a8b025fb34e8935b326d4ea655c0af31c78d96aa56caa27b"
+                + "053427934e23bca4dcf79e84f2dbdb40b068a79b541f92a5a6f1d49f7c3ea6d29f1d0825"
+                + "e5d6a1dab897c7b619467f19033b8b78f91a21a066634827f825e5d3e8db826253151605"
+                + "2c123b47d28f4c016a06dc8099549f4c8865dc7c65652cdcc965704fae5ff55107f44e5b"
+                + "e67e95b3b875d5d6f824050fa3f811a7a68a2f59becbdbebbbb0a06917e5fe7bf25c76b2"
+                + "1e9068deb8d67960e0d903c354b7b6248d3cad4db0ed732b82efc012ea84a7aa82dc857a"
+                + "e000b989462020353ef6c7a8ec0c25aa6a804b33128499683a4209c83db87e5b5e9abe87"
+                + "3ff81408f9fc7ae93f1345800ee097d6a61079d53e551a2652d6d9a102e761fdbe420b97"
+                + "08f604d9ba79c8172efd9b6fe8f2d948c6bebc53cd34b977a5262e2f3377550a17b70761"
+                + "c1f6dff57eed1e3f884c8685b9d5783b7c0a2102387ca027a215433baa249538b92a65e4"
+                + "9fb3e5a6db27fc3c2c6149bb62b0559ed4558c0b52de3e2710c1bf7a0910800818634444"
+                + "72bc7d7d9764665a0fa9cb97c9587420c5d5e704a254c897e0d5c7c77a8c4b680d54d184"
+                + "93a5d7b5583c7fe6531774965a09d16ea379b2ec371d06de939b867516dd46bc7d08cf7d"
+                + "8fb23ea733dc2ae68b4485be6ec26ae9788a3bc7083ad7efe0410f3636b9f83b06c6165f"
+                + "cf224d793688db04b2348956f76ef2ceb09781ee2c9b0f56de6180af828e69c718f5c5eb"
+                + "3f3ff269e11c8fbd5c75a8561e8fc24b230a0d7e0499b28f5ec8d5c2e668730dc16dd349"
+                + "d20f090b16d4f88fafa6364a31f768c1f721b45b75f2638c7e5a747079ae05aa48d52fd9"
+                + "991b5213d59c128db177adf42c49cad07d5bc4dc8f28bea9e45cdb4ce466647ac19b79b8"
+                + "f727c55e889bbc1e3612fe0d65de2a04e47070336a1b16f9ebaf3f6aaeea519822236d38"
+                + "0ab62192659a518a76f4923b5380e7924436d1785884d2e9249b149340cc2b6279de7fc2"
+                + "115fde0347e7fd9a4825a6996b7d2023cb343178adb38b2c4564813850a6c63c612c1853"
+                + "f7464f64bfd92093c615e8117299e2eb788824f3fe231e34812baccd34c96ceb91c21e89"
+                + "cb1fbe62673c60be2c85e89a7805462097a781c008f8d424adb6b55c53f06979388c3297"
+                + "927c6c21253dd0085c45e497a550f8b6e56a2ee4fb392a313a22d00a83b7c7a95a060390"
+                + "40699b1820db1fc30b69643ebf76035bdaa8bd31258054db6d12f01e5fdc06b1414315b4"
+                + "da16d4eff65945df02ed524971bc07d54937a0372fbf3407217d5e73a34fb84b6e4b1279"
+                + "3e25739251d3ae6bcacfca804acf0c4f257a9123343d0a98fb672eed3aff551a3d6deddf"
+                + "206787704a910ff31090d6e3eaf90b3b7c92ee708eca6e0e91cd9104cc4c9ed0c9b41a3c"
+                + "adcafe0b1b6fa6e54f65a32726be11d4b80019a5575175002e93a162ae931a1eafa5c3d4"
+                + "034a544f1a41f8404c0768682241755df7fb2a0b413318eee9bb25d0092b2b2ea48a9940"
+                + "a4543250309566eeb4a4a214ca05b1675a5020c1bef77ecb73614ec781c79c18d9c78016"
+                + "dd90816c9c953b9a00ce97048e8228fdc31de8a356356744eb106e23a04fb730337855bb"
+                + "52a70a11ba91a4fda9b9d32beee354972a477b57aa95529727c4981d4cb7b480c2a76d78"
+                + "802c380e78a4f73cd51f88e8ad8595df5afb5e2bbe49eff97cca50b02b265f4fd80cb749"
+                + "b38c4591051c7526618d8c8485c1854a150d070c43f140c11f2280dabf937203b9ce6b3b"
+                + "3f6893f1bf889dbd51c132f65dbde9945ec9152fe246fe8596669f3706ce89788970292f"
+                + "11ec149b781b57e04bf8d79073a0ae66e71d5877f040ef52f28640979050d3bcf943cace"
+                + "bd61ceda0414fc4a876c987eee793cded762bcff517e53e505e7da0f4e06f38066bc3d31"
+                + "a646bb3470d5e07bb8b964b3ef2719b16bd54bf8ae7564ca0409ae47cb799561c87f05ab"
+                + "1d05a7747d1f01c7f18a3acbd5e6bb46809c10f387136a628b3507577c11d1b878caba42"
+                + "64a10538663b55f56c7e5c16df59a9ccb98ed46b961c6bdd0e59f97e11b9e58cc71a2383"
+                + "7b3d3d7e7cdeaa1bbaf2546f631bd85a66cf45ddaca0e17a3ec8bf2ca003ff1f32fe41e1"
+                + "27414d5729be295e5a3aa1065ef7bbbdaa4e6ba1249809991955d43a3f4759617418df5b"
+                + "55d3c3d83094400710d0495d7fa48d8e90987898b0dd9f6905091016633026103fa2c644"
+                + "e0f3aa680e546d5b439ed6a6770b3598384bf8681ce59cf02ea63656a31afa309a490e03"
+                + "2d7c7a978f6bf722cd473a9fff34ab28697e6cdead9091b3ea7c789bfa93e20d7bb43a9e"
+                + "28b454ccfa676c7fc50bebeaece5c34830cebdd739d4d24bfeb876b74e3c8deedb3981cd"
+                + "cb91305caf56734a42dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2"
+                + "528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "29f0d042cf382e23c516767502496d166ed9a17e81a731faf1fc47a00cea19323d49688c"
+                + "a8e7f30a6c0c2321a1bf0f033d2cba1e46cdba5af5af42a554a962e4b5257b7938958415"
+                + "771299401c194dc04b8fe43dc464421de90659708ce19cff38ec7de41719f1893d38a26e"
+                + "b2073b6e90da4aa000cf4568591d0d13ea2483e186be9627ec32c697b23a3405384cec5f"
+                + "5284700bb5b0ef84339b5df55bfb61b76201c2bfa857450ee984dc2f006485bee0602e40"
+                + "74b5aed91c76033862bfe5db796e2da13064c65914071c0f17aa59dba9dd7a72c795c868"
+                + "88cc6ab7331f8c64c72304c06e6a694556d2cf8ce8fed1a318bd99b51866888f1ea372d4"
+                + "97509b5aff1a5e3222d864879b9c393d4308b738de1359994b29cfd2657a808ced006d43"
+                + "4cfa205adc51568264e125becba025baed15789b2c237a01ac3d0051d61d8e507d5960be"
+                + "40bd95dd41723cda68435e583b4d5238a650c9d22652902830837ef98f5212231552b1c8"
+                + "2b87ee0b6b0b87fc091d21f1ddb10a3b1b8d0725366649841c5337e08db29108b8fb9ca5"
+                + "a21ebf696fe53dd800c80ffd2b6411a6ec499e417f21c48211c66f4afecc4f4b98aa2ca0"
+                + "8622ffe0ad103dc3cf456c6dacf1238da3bc2f5bd8bc4b30a7d5c074d26dae992f3ae50d"
+                + "9c6653c376981d8f767e7e64b0a6b1442a840150c2fa4479c4e6a8c5a17021519aa5690c"
+                + "2a0351688714d0e6f62d5826df5a9715b4f1d2e0ed41ef17386f63fdbed99fc208d48ba0"
+                + "b72efd423e7afad70dc39b9ec06c024f17b16316571570b58283bf374590c2e03106660a"
+                + "dd77bd5ae124e714d36c3bccbb64ed81b2a94cd0d7907550ec86a642e33a4278be937f6e"
+                + "f8e717aeeeac8989e354cb7447fe9494271822eea7978e31212d7286527480d98d10bc7e"
+                + "49b85884089162b650e0de0ee5477ee48c59a4af8a786dbe4f07c8f68e737d6ff6886e32"
+                + "1c39cdd7525b2f81050947d00e4df3206c32224048fc3fecd59dbe7416ea4a95cc8e4cfa"
+                + "cf5387f3c3d31cc5b7bcdb8b3aca033dc6d1416d69a9e19800d3603bde59ccf70afa44da"
+                + "6a33545591685953ceb91c589b7d15988b62a42b1ce798ef204a548b037c1786da93fb24"
+                + "f206801e892444bc4a7976938c61517e1584f74435f6ac0baf814686859077b60ed259b1"
+                + "4e5b173e73aceb7a6d1d1416e1208108264d6e83aa9889b2d70e477715231bef262a1cc1"
+                + "fb5155841738805095cd22eb240419d2de6612b0de92dda7dfb76d062c7719228f15dbd7"
+                + "412b949cdeb4ce170ebf8a2c6c09de11a4b8e7142d95f52d8f511261761cef86d91f46e5"
+                + "b8319345f6c774a1e9c99c7567800e70c2107a09c12d1be41f1f88a944d6007d1bfa9315"
+                + "5a12d08c05dc223961b35a29882fbe295a60992aa6a4d8d403ac3f2ca52a425a084e3358"
+                + "d7b9b08cb9becba6f13b57118f97442e4716fe770774751ff703fadf90c39ead77b1ab5e"
+                + "d9efeb3c3254e7d0abb84c164b0ef9f3c64c91530aa2cbdff8e6fa2a9b1a2971f296d168"
+                + "c7b0f0729de63ed50e44035f6b55901d3a5dd95e6425385b963d0136100c94a58cd1796c"
+                + "f82bcd95a91c3932aa277c50cbe7f57386119bbd6ad5adbf4f2bcb10fdb8eab08bcebb8d"
+                + "6f42f4d18ddd6e98e62ac65bc1309378b92b0bf317689f01a29f9a81156537183acf982d"
+                + "bb25acb4adb9d52dcac3e36e69319d988f2d61b82c05a5b6636dfef4430181213e7ba7c2"
+                + "a27197c61924e5b96b37caf5f1a3c528adcf63a150e9c924e5a189ec2e1fe74e0dca98c3"
+                + "49a90e675aec475cf17e7d52aac6451b37779be64492fcbb9b86fe3b309356489a7f6560"
+                + "c334f388fe67fd0d41789b80cdb335cff0924a2d95b2f4e3b08d08c432ac510f2692ab30"
+                + "8de59f5b8dca50f902fd194035a8ecb00249f2b9bfb02bb94047f850086d802154b74c87"
+                + "a07c5489c92fbf0fb73a63e82baa3edc7955bbf400c01664e05c09225f79d9caa05fb0c0"
+                + "338e209d5dc7e4ba45bc53aa628de76996f4ab4d9a006169c8d572670c0834938b7eb342"
+                + "ce952b3eef277875900b2f6ef16a23100464497decce0050bf15def0adf5b56404ee94da"
+                + "39cce349082162e5ff4c32884264957b3c1335829e29a30a9db021c29067becf1cc26602"
+                + "307c6ba78b92b8018f9b0b531d13ca343a3ea1c7f3d777e23b2c9cd565b632702dab9d1c"
+                + "a59094c99bed6a1e068f6b72f04951ad79249146819ba138f95a070c5b113c75fa91afc8"
+                + "97017d5c27480aebfe92af5f2971435b937d2c13e61033b4f2da6e485465521f52f5092e"
+                + "aa757cb8fd2fd0df9bf0d7a3666a43fec9f6086ee06ed593f5d4caa1322797173549e084"
+                + "ff1e2f19ba1ab810f5730ef4a9fdd0ac72ae50ee315f86daf7e41f4489b7f0ab09eff3db"
+                + "4a51533c36d96850468ce4195d676498ea2080502cd5d9106a87eb7a173f783604167ed4"
+                + "e3b284bfe2d60c54664a658cea446f8a46a70c239be742e8fd17d29b1fa9a366c18eca26"
+                + "062049223aa88a27810827c89f5ef96dac2a80b86239b9c4cec7abcd6891d8f87813c443"
+                + "d20393028fad67b8e28165c5514ccf02efba80dc4a1cb9e960dde0f84943ed546cfdd416"
+                + "231886c1798aab9446603800ea463363dfcdfc06ec11266c4fa02148fb1e982df1c44216"
+                + "5fe329c670901560fb13295827762a6169c0f914476fc7d62d9a2493aab61523bc72fa21"
+                + "b79b13bd998c179dc45918eb3d47dca449f009888038e6b00e641c70732131635bdaeb58"
+                + "dbc65cdb62605e2f5b0de90438a3bc9c35075cab86ef37727f447c1e40998d8f13b26812"
+                + "64515f35088fa3ab956b3686513546a29ec30831b9874b01d38c2b363439bc65ea9042f4"
+                + "f5e84f1588eb6e7f3338d0cb1c94bf6285b8125b74de6b2f0f185b734237441918245e1b"
+                + "ffee55d22ce5bf15905dc12685acdef155a2dcbb48fcb6f1825a73541e89d840a940226e"
+                + "1a6d92ce58a1664f58e68c812a27e68c6d357fb078b48aa4e3f75df149b6d2bddc370933"
+                + "275a55450f502315b066bdfb7b9ae752a06e7c8df2de0beb96ac46cb4f78cff847669463"
+                + "94453b6e2905d1f80a24658684e2325798ca417832d9c4bfce5e3263fe0f2e8273f302a1"
+                + "2282dea597d85a8ac67e98e03ee5c34830cebdd739d4d24bfeb876b74e3c8deedb3981cd"
+                + "cb91305caf56734a42dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2"
+                + "528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2ad8d36352d84d652b73759aa716ccc320c0ee46d12f8cdcb844729ba3576def05d76529"
+                + "4b8ef5d97ee07dce30ab9a3f4e84dd3cac9c6639e63bf85ecc4367bfccb8c3b33016c4bd"
+                + "92ce370201b0db1791b00f6ee2891cfa299b8f89b4714069bfd06a3812906683efa178c8"
+                + "ccd0f06b3564cfc0c1b4acd70e88ea1b77298c447cc43e7371e567c01c8d723a615b446e"
+                + "5aae9511aa8a4a994318c7e460415011f6459a1c3e9894b45951621f01f3f2da2e1b27e3"
+                + "093dda70b830d80bc90ea759536725b3fe18cb7bb8716bf944362dfcc81919d559125087"
+                + "aa4e401a6bb03831ecdcf4bfcfca85625643fe6edc18ffdd73113d8800b614037804be90"
+                + "e527e3a4a387964d923f057d2b56e4d9c3ae4530ccdd5bcb3ed791523de6dd3470dfb5fb"
+                + "20cec162d410ea85bc00aff738f6d6617e41f239fb82e9a90f942f90e71b763c15844693"
+                + "9cf133df1b6e65ce768af3076bc77052d96a2e439f174d961a649dbff53f63e8dfee7ff5"
+                + "0b62dcaa44d9d2b5b5242d70acd996aae16c127fe57aeb55babe5138a67c7dd9c8fe97fc"
+                + "7747fd1dd8e20689811e0937407c133a38ca7ca1dfb4a2e3e3f97980274e8317132159ce"
+                + "f36cd08125330d1df3796081a8ef3c8f355461b5cbc7eb65d8a1a748cd8a4c8437ad017d"
+                + "a281c6f7acd30275d18f08be8366b6593b5e44365960ef219c01f04d2605bbe6ea21c097"
+                + "e86e6f7ab6fc17592281229017d8d3e23ca75685cb44b99c0c204dcbc6b9519101c19724"
+                + "565675cb15cbcff83a1877ccae3409615373e7509ff959684be9d43862381c63585e53f2"
+                + "25fd895648f237afe638ef6bc2452dc4bf7258e695e3af11a51321d65a9b655310c31b34"
+                + "7f274ff83edddc17b7fa957109022911afa5794810fd8f7985b6c070d81b920505db9307"
+                + "5cc1a392cf6af2433a4d8a1f2f3050ccf9be48a60f5cb8d82600f15c0995de49675a553b"
+                + "41534c9eda7b78018c65aa53dd0cc52ea9d4ccb67fb0dbe07431b4392cf1409871ed302c"
+                + "b3e270ea635d81145bb28be8fbaf1bbb3eae47f3406d42a56b172c9912b936712c5c5061"
+                + "e7e1c2fcb6d9d2ffbcd1afb2b377586faaf2c8519cc0f8bc81581bb64762369811cd6cdb"
+                + "a40421a21c66d20aab11aa99fabc94673e6c26b0107932e2c9220170412ff0e5a758b3eb"
+                + "50e5ce0e80117254b4139d4eb9fb0bbd9e2f58436fe6a79e14d7b13ec634a1e67c2d8037"
+                + "85e2a5839a5a389be0cd50112bfda94ee4a213b6beb8604fe1f4a4a1f3c4384d6a417a5c"
+                + "aab90b14ebb5936b037d2d7bae0e7e45ad8381644bb089a0ba0a8cd492e24fa2847bd73d"
+                + "f9c5c7d534f64cec2f0dccbbb6550f163963a360e0eef7c8fc3edc0f61a7ea314c39563a"
+                + "5861705f6011bdea87b163d3bce94c1d1f5d416f8d675c128e86a5186f87d3fd3fede2c4"
+                + "517919a6e089ba5efc0651723f70e42fc0ea0305fd1be2b368696595f32cd0b16de87b44"
+                + "c7c72cb42b64906a997a5e2b6d4e340c51178513cac57fcb40a82e1fb7260f2f512b00c7"
+                + "c032fbb3eb58c5133876a6f6ff25221652c9d34b61556d1bf3661c58f38078bef7842f8d"
+                + "d825fb815f0d37c8e2d8df21a4e4614e1708a40bb5c2fe6bec4a3b593039a114258aafdf"
+                + "669be738db8b3f3d0298440e0bd9b4b90e085fbae0737a9734f79bbfe9225fb4aa350b7e"
+                + "90a82ea7391b32be8749048d55e8735361d2c79e1649ff3af909829054a9883a8de52f16"
+                + "f5f884a99c28f769b353e7679f9ba147a18a1d2fa12690034239d8043c1bf0f37955ac64"
+                + "4936a99a70ba6d02d96283223bfd4e7a5ff0e6ae1a24680a67545450830b6ef4865a914b"
+                + "3afb295dab712529e4b194dd3295ad9b18c7f412de792e3f07a25ad30a55bc45bafdb023"
+                + "4dd702f3b0e186968d9f190cb1edcbedc0cc0ff6cc10ad10c8be092b396d450f2502871e"
+                + "c6d69869ea303953bcefcb782fbba9e6f905df7f43d01c77667953a321deeb72e41650ca"
+                + "f960552859d1ad6ab68159c5a200b308b3bcd8c8b8d44176779447f853b07f730090fe14"
+                + "b6c804411b7516946f87b65aeec6c2b5ba6beb4f135ea1d82fa4f3ecde1c7a8beb8cf32a"
+                + "725c96f5c0de502137eab3f083427437d3bae5694178f92ff0da401d29367ad86852cd24"
+                + "6ead40b1e669696f39d5265ce19d1968fe23deb81f53a99e5806bbca13ce9c50d313e677"
+                + "89b82c6886f7dd0ee88eee9043af3feb19b931ea6d7d394e1c8bb0c4305d39d4ac4be7f3"
+                + "8f0f442814045c568e1b7a684f0de2ec24deed0907ceaaebf122d20c61a739d61810df0c"
+                + "fca063e3646f4cecd7eac1e1244d2fd3bce1c04af45862dd0affc1c569dd255bdb282045"
+                + "e5b53f5635f43b8c326555131dc8ad4bbee1ed78f391ac36914dbb4ef5795eec30f58a71"
+                + "48006010bccd94191875236a12c70f0328213ec66e20add51b7622e6757f3641e6eaab27"
+                + "217ff5ae298cb35716889e9dfbafa07622d4001416a1ed8c3f3328e930e229f5d52c4807"
+                + "6a56cc813bf8937adf4bd5e3a96c764daaf2bdffc927a1ee03052488934aaeae2de9583e"
+                + "207dcf0967e6eae4471a8991921f9a0c621f7bb4e69edb3c7d15d5d49ee4d0e8fe9f01f4"
+                + "dd96cf0ce1770250c195c5f2b7c60525f1fd8ebd57c81883ff3ac5e1ead1d25fd4b1c28e"
+                + "4cae1f4f0fb77057c5df4a6290c1a97ed9ad44eeb1a323bbf4121a15017ee33fdba05fa3"
+                + "c714b9ef9ec8c824a0fb225b01019135f03443b094c8475fb3893f60deb50668b77f13a2"
+                + "68e389fddad5939f38c0b556b99c0577c1713a55e68754843b67c740920dee90bb901987"
+                + "36a110a448e64e73f53053d31ac8a0fa2acc04a8fc921213bfca3e7ba12cc75af6a99348"
+                + "4f486889ab5e28722ff744466b79a72cd93288b889e89db460e6350287610ccbd34f8aec"
+                + "4ffe4765e5eaf4ce1caa0ed8226537a7d2c6fa51f6a45f95b35c25f0d537a133a33d6e65"
+                + "d913ca92cba6060082ce95e3d5adb72105d30aea51a6d0c3adf9b72f53b0f56c7d5d510e"
+                + "7eea287c2ee01cbae259ade57fbf4ba66d75beb68dc466515096d6e2ad123589c1522ad8"
+                + "a35b6e7a10e8856288f211c6fc71376c60994b2440aeb4dffd8c3d3dcfac0d07481770be"
+                + "ffd462dc7101d1924d878bd950e456a454a3fc23c07e5a2c526967f21e61f01ec768939f"
+                + "a0ba0ddb69efd43144dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2"
+                + "528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2beca89f874df15e8d42c25754e84604d14579cf8cfa0561cc0438778d0bebd805515daf"
+                + "36ddad53f3b5fd4216778d2c9d7957c73509f43d4f1fdf7e981186cf1517284a2dcab4b6"
+                + "f658ac0334baa34268adcb084cbc7829affc78cb0fd9c9bb5dce848441e1b2582f30b4b3"
+                + "9bfbcf08efb24a406af97c5ed51be2c057ce0c250e8175fba83b39b47c1c690bd13cc3c9"
+                + "e51c397496759f74579bf6406f1643396ade6aa5cd22e9e3f6449ce8f143c1dcf03303ca"
+                + "ed31c392add1bc6e49ba4597d84ff697c789d134a818479cfdf25514c7f16e282d887043"
+                + "44921a6213a0c88f8108fa62f934690b22094e7c745827721a5d8b3ab334568e4fd0f036"
+                + "1c73b673ea3dcedc93d73d3e2767f4ba510715b25d90399cf8b0de92a650d2b55d224377"
+                + "16a9be719f17b8a3267735f2837b86a4cbf9e412f392f337ec0bfe1b6b29bf1eb65aff1b"
+                + "07cad4f7d1bbdd614ec844bcecc7b35962b631670a8bc5297bde9d5ac9b33523730e37e4"
+                + "e83dbc9ffd367c4f7cf3f143279e4d43d4eee8257dfe72accf65e34c15b8bbba4179c52b"
+                + "38cae85c01262dac2408ef238031ddca9f2719c8b4dbe9891ecd9a3f47a7ef91fe894de9"
+                + "50f771df97ee52c1df1e3aabbd41a050fdc7a10d2653aa81f0463bd7ab9c2441a8fc6a0d"
+                + "3e14006038c3f2cd01219e25b9f98841e25cccceb78b67ad986cd89aeaa4548ecf11ff09"
+                + "83bc2e1b02f62393138c29b9f0c18538b3f8453edcbbeb44d309cfe714223455c69b84b2"
+                + "04b74204d1a0ef46d30b36feb3ef409976ce56ddadb84346e90d387d5fd8910ec407d3ed"
+                + "1c632fbfdb58e85fb797419d49cbc5b902bd3806c483994033817fe9ed6015b4dc4ef5b3"
+                + "5e14ae5ef1667844cb570a2b2f344dbab4c9f5a7a33a334e704d3a6fedc690088ff331b3"
+                + "71b53d2fb0e8f8570fcd4250db8340ff677479e81b92deb5484627ef3c10c210db151eb6"
+                + "67c20c52ba4d9ea83f8f109d9846a9dbf51a061b42ee6e98fb26986f87de70ccb22d7ad5"
+                + "151c9f978882be28e2a9dce608cacf73e15c08e8f5eecee0e45ba428730e619bb25ac943"
+                + "d93de46d122fb2e52d992ffd54fbb7d4ea67a6cabbd175dc0146c82ab5f5c248f51660bd"
+                + "44d24dd9f6e40ab28145003e6c1925fb0a0152dc2dba0d92f20562a9adfe26eeb82ad642"
+                + "4c805d95fb494797cf882d4e5a11438a35d2e29ee22b08c62980d656176e6049def1759b"
+                + "ae024a55e24a01f3dadf1953759017f48f30328cc3c1972dad13ebc58ad36975a7bffb3c"
+                + "1a0f08d84ea7c8a6cbe0c0644fbc095291551f232623431282cd0a5ff1e07ef997f78170"
+                + "d1e61c6ed4995a02d6416f5090f8c254107b6babcc0d0a4c9b8973228e35a4fbfe1c3117"
+                + "e0ad3fb7dddbba229386e21ba7e0f4c22584cd09839ecf3e432a9ea3d5fe0bce55e410ab"
+                + "eadd51242484c48c464a64e3aa4c440e5e0b0bdda97f5b55cf175f2bebdcad0dcc95633c"
+                + "58037974fe5621561e2a72b4d821a816c2ef87544eca26646ad262efe6d8715a1594c12f"
+                + "9ab1b5682c3cedacc2cb0a8f2ce7bd822ba6c09f7acec55155eb045f18abba40071cd3d9"
+                + "670e64eaed9a3ded5e50fd09d2a2f19463358bad2e0206f23c683aa36d07bf73f45dbeb5"
+                + "65e35d04e2429552b5d6b0ee3e5eae0b05fad9ab47897c275938b7c1d4cbe27cd2a8869e"
+                + "ca252a35c64a92d1915957053063567adf1fe1e7c505728e80b5b75232d5468e703bebe8"
+                + "1d8f8e4af4956e31fd908ad454dc3852120e14b73d43d09022096a67b0ceb33929d71bfc"
+                + "521f06434e799ac52795a99eeb6c37179b3c477e52aea42b3389ae6c10f88f0b86fa4f15"
+                + "d6ffec4392fa3e15cedbb14ca9bf20a0e8b4428942b182c9e7ff55034d3c00647b6f4838"
+                + "758f255cd788407f2927df0bec4ebb097e297bcd641c85eccfce36e238da7e5659a2c86c"
+                + "2997d9ce43af5028b97a93d4dc879b879e88329853c2f828f451d1f9aaf09bd250c78a97"
+                + "2ae65f927e9fc0d42b5d41652fd8f5981b6a539a05bf183a2a9aafab420940839727b3e1"
+                + "abd10607dcb357bbb9a42873cec7b94e07d89e62431cc9359a6b21b1b0d188c7475dfb24"
+                + "12412abcebf7362e9237ce23ad96175c494cc612bba81db7b1e25269403cc7c7f9d567eb"
+                + "5cec3468c1bc4bb2909fe0402e7cf6ae55890cdcaf20925be73ccd7f5c4a901fbf5dca81"
+                + "fe4addb08f8792dfe7f3401d0b773508decbdd7a68a6c65b0406e5ced5a8d600bb2e0dba"
+                + "6c62c81c91b2c104babb238e40a2501eec80eb2ca5926d15a3dce16969075dcd4578bfd1"
+                + "0798bba7deb1185d54b9de44f2467f01d10f1434d160c70414dbf8f470ce8055188dd378"
+                + "77b76483654249d0130a3e2dff9566c8c4c0e4e7a49c3c9566a3514230ebb3c259d023a8"
+                + "62d6ead2b6c039bf3d625de70a0f949aeba7d5ee3e36188d03ec4f73b7320a4a14917432"
+                + "a2bc8badacfdc44d3e27a67143d803f3b2cde15c4c3fc0c44d7217db4263079328beafa9"
+                + "08e58f2cc50cf3ce8ea72e3afa2116e1029e4272a778d856bb554d301c51c612a50b1493"
+                + "fffee9e1af8661957542ec9d0e752fde6ea1a779629c8c420a65791479af9d0bb3c6fda2"
+                + "62f123fa31caf73269c5e2b225f29affbf643baea8f9476d374f196fcf9f84c002fd94cb"
+                + "7b962e7571693acb3218bad4d84b7a5059b514521ff73bd8fb8b3a56ae26c9d887c487dc"
+                + "3d0a7efb976313fbd463b8155e8b0b9a4adc4dd047bb801c4fbfdcf40a36e34c514f0a30"
+                + "575a249560021679514c73bd9f8ab4573361fc154a8c4652e2bfd3321cc07f8414149ec2"
+                + "fe2577ed2ed555bb185152cdba7d261888ea25255d9f6cf03cb6e070982d9c96df7d1eb0"
+                + "4a827d3b8363db91862511325650c883a0b5b7b859582abefef6e53a40fd44e0796e77b1"
+                + "f153335ab5ce46134c70250079ca2203cf669c46c83c98069eb250384eb0c2562d637173"
+                + "b62bc7a36f45cf8d6c30db080c4064405475aa40e56286c379abc104fcced45bffeab6f0"
+                + "cff7affc6cdb11b5059f58c29b9aff8fa109dab6aae812f98dcdd30d697ae85b80fc660d"
+                + "4230ec4e4387a0e782d0015f69fb3748359e26c6f63c5448bbd2a42bfe51b09d791bbff0"
+                + "b2f01af9929ade978d4e5c4447e456a454a3fc23c07e5a2c526967f21e61f01ec768939f"
+                + "a0ba0ddb69efd43144dcf105980c3b333a340b09c24f400cf3bd83714207c8e2102487b2"
+                + "528fc9d26dc6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2cf2f2b4dca49dd5439887c17619ad19af1875314966a1bff213fcabcd840e57f9758a2f"
+                + "491ee30814a1200ff2ec4e995050854e19095567497e1b347531064a0134a1a78df4bd64"
+                + "942e69aa9229b50734b7de1bb27a357231c7967e7e34180cc2eea4806698a1797663a60f"
+                + "d7f31312f95d1196a8cc9ff3afd0a7f9f2413a7b57ad277e722c9579ef0f1e5052e1a8c9"
+                + "e14487489cc973d39db6a737c22ccfca16e73f7d9995b0cada5f12ada2f363eb60f631e9"
+                + "cbabbf60c54506228d05244f77b31516d13d544e93e7c09c9a278c7e20d08c827e7b5d6c"
+                + "f9045867af2bbdf67351a062d9b92cd0bd0e25b3117f0226a94a5e702534a42dfc7540b0"
+                + "5823107ae3e862f0313c4eebd51c2a7ea2529ee3b772c0b5f0481d599d9877f7fe2a17c9"
+                + "e68d863f7008303283a662d858b23b1ed7eff18b4e6700da4385f3b63a50afb69f91699d"
+                + "67d1d29de92067d74fe3802701b7c1992cbb79d4bc93c060ed7853e21f52ff6038b7f782"
+                + "d5abc83b2338e6673a4d279155aa0ecf1b413bf5a40350efc125d5306d370c0098a4cdb0"
+                + "f90c8a56ab83eb788de80ba3f0f72594bac39e72c9db5fdd6a5ce756360b26e02e44582e"
+                + "c37aaf2f6b62ce1e310e610ae837f156fea983e3b98b3f423a05e921fe128fcd5b77c8a9"
+                + "b6e83bc1e7a3da672fbb8e9608f73d90c3131a3105fd5e7a28e55c36bcba0ae9cd81f3b8"
+                + "85b3097922d3bc38c0a07c9b8d5cae72735ec42b7773736a49742a1a874798cc413a8ef6"
+                + "99c93721d00fe839ea658629ea0d095220ef8be586b3ba3b559b3b830af465bae074e6c6"
+                + "35dbe792a00dc34c6b063df28e7b590334de1a1168e231803d24e20a7847ff07d84cbd92"
+                + "edf06748d9e4ed5f220c6a29768aa0868ab913c18ba8644e9231ff6543f0e8040a293b02"
+                + "1bab9aaf3c6ce2d0e9a00247bc599c039b8e7f5258a32d4266506d8982ff6477d2b33f86"
+                + "a1b49d6f764565a247b55b99b3bcc495f6229fca3c25ccd0fc0b80ec869676bdae256088"
+                + "88bbba32c0b3091e4dc7bca1e075621b844ac68a721e8e38f8509920dc9d794859ca6a55"
+                + "197a43682948aa36e24c7ffe5d332c37f30038a1eeddc1bf7714153b2de07f0270a45d86"
+                + "f6fa4f7455abd53b835ec2e6d7a3a2701b7cbda1dbcff9efe8661cf979ba6020c6ce1348"
+                + "d917235b97733dc7e1719c4d4edaa3ce565f734cbc4bc9c15d62710940b5466fa3670c58"
+                + "f8e379e7e1bd907f341d98057d9b2d9d96699a761a4e1967080de5cc9063cd226cc32a2e"
+                + "cb35c65d649b308f78947dc60c5be1c41a86f39f57cc97c5a1c0a3a1ae24d9340020fed8"
+                + "c950467208a8153d25d57627636a3b658dc33a08afa1289b8b995b868bd078a07a04f74a"
+                + "fa3a2cbcb31f83a923f73631ead4fa1bcbca7cf6d0da667da13bbb4e81639b60b66d6fe7"
+                + "de1d4fb4b43a47ff5fe0428bd83d81d25b500d5cd45e8423b3edfdb6bb0ad1b0aa179e69"
+                + "fc9e7761bf1d3ad9bd6b61cd45ae349b41f80ad5a44b5e016d4edbcef1ccb2aec3724798"
+                + "54569927fa0763df1d79d64c64344b1d3548c79ae18ccc1c9e8260c19442097c7526aecb"
+                + "2ca88c90ce83c6fb20351251a26765da8693bfe6e19c6fc597d06ae8392051cba13b8ba3"
+                + "40bcd82c5a618a0bcfb1b323da718ed39c8f23bcb5ca9399f6f553aa64ddc8c54e36521b"
+                + "63713571cbddec9c9c8ad0ec0816931e508c510453c61cd6b1e4015d11a56c6fc51ed4f1"
+                + "09a2020923f39dc973e36563a8623b2141390c83a9d5f862657547b2c03ed44e414421be"
+                + "6310e6c30a56a537712534ad0d1d6a0fcaef476de1547947217bb3784b7eac2153190048"
+                + "3163a346a98d129b384ef76b8e5635eae20a8caada944ba3a3c8f9314e72364e8036e65d"
+                + "d45f805da66eaf2d103077530e51c8fc24cb5aa80e71064d839e51e7181faf4aedefda99"
+                + "ada1c4e6d983b4725f06585737fec97c0f1305e8d9ab6ffc9e668814a0b724adc2d32ce7"
+                + "5922f31352b06ab88aeca21f74b1e395e7ae23f0f5ca522d9aa891269b066d6e26d42a88"
+                + "88a6c2d5652c66bee84182e04f301bbebeea210e906c2b4aaca884b59e05e49d4676358d"
+                + "31a2bdf1b1df8c1f1a64a798ec4164387a46f867dea6eee27f55f8d7cd7db706b8728f80"
+                + "ec8727bb911e0f40956cc5c64b627d8d2756d3f2f034b9beec2ca8e020161886c6b2da8d"
+                + "dedf295d203db75e3556137b7b06bc3b184c47c67c1d01673dd9944803b5a94c00dac3b6"
+                + "906df70452a63a17d34fc4ecb6cf728a8af98b2101ba360dde85861ab5f4ea8da4bdc21d"
+                + "1150a0920b90c6c4de2b3820100e257f26251b71f1b8f626b624125f30fe09e67cf25102"
+                + "4e7e38872588362d4a2e0654946153c010913c268be47a5e813e4addb8476477bf9872c0"
+                + "407670bc0a7552cd12d6f455315d63fa9ff564cdfc91d2d5bd44223c79b82532b39f1c2a"
+                + "64122422665181bbed5a87d144b621afc2c75fd07f92e44576b076ac2568fa2836ac51f9"
+                + "2fabd5cd74380df0e8fcff0fdcfd3de1aa1ace9e448dc1ac7953cb4bd9daf932247a6679"
+                + "3d2906f5cd80c87bd1504c12fa9343efd941921829b5e5905bcc031bf4d0d25d99c8c68d"
+                + "87ff350f1a6b782b6522b2cde82fe50782536b36050f8bde173f4d75f9c44ea13bf9eb2f"
+                + "97ee31deef5a2f4f888401095d3d7b9193b6f6cf7c38691d84389cabef5fab1b00b0fe2d"
+                + "a794e65cc6303874c157e7977ee723d832cf09d24b296fd7dd4cdb568865562a55224204"
+                + "b3793b94a4ac073ff112279787d17954bc1c1f20c87614dbea1b9590f826df22d39c2f92"
+                + "26c7c3e66de14faf33ece2e0c7825d72b137e1e507e9f7dc08a1fd37febccd3da7e4aaba"
+                + "3729dc4357bc6130592452c04e26634c9597e2045ffec98d064ece0b6257c3fe89a1fcc8"
+                + "4389c477d2910067b4165385584833264b9581dab39ea7e53fe69ca5107f27c1ed6b9f91"
+                + "71f85d5bb463f634b561a079de9f27201931cab50b5c81c77c6ad22d23757ba532ab133d"
+                + "217307d8963a97e7900d90b5dd749886234172753e7afc9a9ef72500dbbd6caf4e86a586"
+                + "92631739c853a3fd07e2e1f8a7d6356f8e292f350f2d8707521323d56a9f9549808d6ddb"
+                + "aa1a1c12848a9430818fcbcece279b35d934b705ea39d108c82a1950f093f9c7b6f8de80"
+                + "f5c3cd043c2ffb5ed3f8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525"
+                + "e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2db3bf4734a965fa4770d979ebf4fdb3d34f4be95b71eb847b49617f135949949f7cc0a0"
+                + "3bf7dc34ff6f252a1ba282de8a5e9ab00ca0360287c3f6f0b6aafee8b8c85cab6a95e4e9"
+                + "8184f3274c225fe09652529bcebf94e875b3f865b90731a09ea961957cc64f5cc8f8327a"
+                + "f89d7aabdfe674b4ced15e518d2858e56e75c1390929f78543a9f92fb0e0f8673a6b1754"
+                + "777f8255e18d3ff0910152f408b01618866f7dc543c774ab7ce2519f2a6f79d36dc016e2"
+                + "857ba62fe184fba6a3f67b6b227970d66d8e353ce4fe8ef2ccf1287709843626a0daff30"
+                + "8bcd43da83e97b361bf2e8def2653d09e55959c4c869a07330d4402b8916389098729839"
+                + "38c70b0642f8abdb60ab0a7ffd8ed4b1b8f70a46e928a03d0e39b13fccfbb6ccef36ae87"
+                + "c574ce1c0c8457984d11a57b440be6e9c4b59cb6f1cfc744de6b6550204859f4e4067b4d"
+                + "68689acab08c9ab6992011d33b9fc151a6e27919b919577a91e832fb2be49da4e5338f1e"
+                + "dd826ca2c3053893dfdc1d5793056d25e086214ffe5dec5aa1f714455d217bfcd6eb15f9"
+                + "71aef90918a3df6a5e2446b79a0403583986856d6ddea69c19ab9b63fc14c5735f170935"
+                + "f69463b00ebb2bfb532170b532aef338450f92dab4a6f137b3cb0622b3a0c8a4788fb6f3"
+                + "cb9ecca94f854ace5240ad1d94450e890a87de8c68b1bbf6ef25c27fb4a048a964074b66"
+                + "bd701e371697f6e980443e7b73f5472a70c2a90b37391ac097e7babbbe638eb48f65c657"
+                + "5a4d17593662228e90befe04756bad819e57a023686751eb682239a476a171148068a0d1"
+                + "ad6be1a41393cb99b7418c344bc5d746a0040053630ee07187481d42b1a280dc7e0d6279"
+                + "73217f834e5ee4fd39077a1513cfeb50f43a3e9755bc76f77c322b41fac2ca343a0ce83b"
+                + "53d817590035c1ec93807637647e95b9c6278e1ddfc190d379a9ff08a0a76f99fc39a90d"
+                + "38c6fafc30e93b2674f7aea3abf5837f1ed1669e2644b20645bc90af5410166a9f408ed8"
+                + "7726bcb97680a492e2dd8932979c902f548e9d6c08fe9ac5624246bdd1c8c447bd3dc300"
+                + "01f41306ec3a4376f5fe84948fb47293794aac5c7e232277a666c9e6b89164f34e3c7daa"
+                + "330769aa450e4c8e49b42ad6a89567133b15c1dba1e098bed5f8e11219c6a924bb451884"
+                + "b9f913c7e4e6520d979637ff2f041c6d509e9ef9a556211fbd87637f9631e96e7423edbe"
+                + "6d5f159e6d764604ac6bb9d2f817ffc2d972cc8833caf06c3d82fe85547d0d616904535c"
+                + "01d0b33428ffdbe55dc75a2e02b5fb475265cc05252b50339afaa24107157a59e6d019c1"
+                + "f97a5e501ab70e1a990bea653a3a81a53b2ff363c86eb0f2faad2b6710d39350baf21ed7"
+                + "e37c7b2a641622bd81fcac28fe4dd94ce46573a747e5fa5a8598dc75250e3d163ea9a5d6"
+                + "d69b641ae137f226fd79c5dd5ba5d80051464e07ab54320881f2528bbaab9e0cca2920f2"
+                + "f226ffca4f3226fe6e0e69e6ea9e29f7a22e5a438080366bcad562b4bc55066d95597e8f"
+                + "ae2df05eb1f4ebf31959483ccd0844cf8e38e71d4d7adc5ffd7785b9184c7c11e9c801b8"
+                + "399155864aa0415204202aabb04d0d2f67ae6c749a94181c31b7977e67ad2727ae6827c4"
+                + "83192b0f012838a51c99900d4a46d28d391ee156b9bcda6de602a234b3cc7dc64ceb5988"
+                + "a03b66cd8d45d3680dc75fa49647a104fc786d87b97defdc85e0cd02480dcfcbcc2608c4"
+                + "83b3026c6121623ec37a21d75043854b9b0df1da09e3af9f9138e9b2c9a7265bf955ca0f"
+                + "2bcf06848ac369aa9afc6df9eb0389ac1e496772dadf04bfcb935a14f21bfe7768729f10"
+                + "0e3f34ef640b8db629c49ed3d1075a232f0a3826bb8df7086b06ca3cfa953d506d613166"
+                + "f3f951ee7370ed35f8ad9b8379c38c0dd884d282060454a2d6bb1ca945c2b80d0c2f45b2"
+                + "43773aae5773d11a0af3e8d43b79f11f0fe02a49ab580b0ac2a43a875a07a0c16307096a"
+                + "96adcf528abc00ce33897b1727cb14021d78841e831a302abe431adf4996a448889ae893"
+                + "6a7cb6f76ad44f27e601ab6b4ea43fc40889a4079359f3cedce6da4c7b9bd9085ceac4aa"
+                + "b78a0ac3582daa5450cea6fb3c9054b052694db1959e75873ced1a02ec2d0c982e0b6119"
+                + "9a6f410869672a869ff299154f8ebd3bab0015ca265ae7cdfc58b056bb5d3720bf8aee6b"
+                + "a64233b4539ef1327ce171d98862e833381574d7863c495898c78e475d17d7e10e38c209"
+                + "10d685a41484d66d70b19776e411749cf8ef100afd72cb18c34d5e2ee8a10757a108d844"
+                + "67b3226159cf2d91a5df24dc98989ed9d10d796dbe8d4cddc898dfa3893ec2da267f1870"
+                + "53f8a22d70e883172f3767baf3be2b5246d3f3d955bf63e49acebf7f44d69de9ca3bba48"
+                + "2766f04c76477019e928008143deff1e464c63845cfc693f0149b365c109fc496d5b4527"
+                + "42b97bc8c45e5a65e5726f468526472fc59eb3585a9796ef0cb295bf561f6c46770c7ce9"
+                + "486437318852516828026b421d2895609bbe830d2bedabe22053804dfae54e1db7985166"
+                + "c9389010033a8663b2c98b726c67171f2c10d36d05cdb68d1164428992218cf3ef2d2bfd"
+                + "fe4a5d92bbd21a718f9c2bd649bd12af3868c257ae6ece7b6e5b17c9bcd0807f724c80f7"
+                + "f030bc0738488a31ab47693188a0864cb8019cf8e3de831ee512d2bce9e73b33a1b06596"
+                + "26a66eef790ea7e7763bac3aa65ee3ff12fde470209eb8b4add5081c88181b64355aeaba"
+                + "f9e2857aa97666cd0d89c7267c57cb162c74de4814c7c5844df620cdecfa65f96d6dd5ff"
+                + "3ec01016ae57737c293270a3c02b360c5953adb5799a622810df0db8570a7c68d5a31be0"
+                + "956e0859d967c3a12a9a2e8cea8847f6e08f9574d9b1dc275c28379af23ae9a583beab9e"
+                + "6cbad0d7dfa52a92ea7e50cd147e6d2a92eb6c1ea75ef42e57a8ecb89156c812aebb3807"
+                + "38dba335696c98ec4a343b4e24e7aac3ca13162d970d8c71c3a3ebb76ea8571be7678e03"
+                + "ad52fe131c357025f8183f04555ef4f846b09e00dfd486498cbc4465abe4360e05dc3809"
+                + "25cc32cb62315208f2b8400b5b3d7b09826dc523eec6637ac88fdbb3ab1ee394c1dad030"
+                + "99690a12ec692cc215c46a650d279b35d934b705ea39d108c82a1950f093f9c7b6f8de80"
+                + "f5c3cd043c2ffb5ed3f8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525"
+                + "e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2e9f1b2d971eddcded8bc3076ab9c3f0031ee792a50ec50af9d35e295521d775eb347682"
+                + "2cd1421c3490a34c4b0d6fdfcdaedd0e3a991743a3bb783993f5548e593d11a5ae5ffdb1"
+                + "197dd6ffe6bbceb726f7abdd1a44eb544535dbba01f5ed986d03782a404780b43e5914b7"
+                + "de443d989f31a87986ae5a30daba9fe9e8b3888930467b9d29583345bd1be8f400812311"
+                + "95acf2941ce2bd867ea15ee4689532a226965a1e4bbc70ce44d506194dea555e86409a46"
+                + "2f2f88a449266d7d07d3bbf0e0e39ebe9c3cc34e9f94a494ff00ecd96d1b4d8b1885eaa9"
+                + "5fcb9b4bd93871b7e26011a863168c94b98d883d278aae9a8512ed55c009bb8173494123"
+                + "ef610ae533550df03a089a0559174df73556477a53177030e5c436b03637fd759b82cd7c"
+                + "8e7928b72a0224684312469284f82c1f5bf38c83c0d3574c4d44fea4153958c514cdfdd0"
+                + "0382b4a3d210abc66c7a1f3d6ebaca64842964fd85b8681970fc1e369b3b6a319efbb09a"
+                + "d888d92d12ae4789ba8015f278cd815975d0f2eb57c621766547b8a532d3fd8b7851df30"
+                + "b491f1a408b69c0c6e56077e55dbc3003183b9e991428323a7ed00c2aa31adec9238e7f6"
+                + "332663d6d31106fd9d253e6351f25681cb81934485373e525d4bbdd0fda723b556f3f7fb"
+                + "c097ad8aef777b9bb230e5a354c872aee55a9696baf88604133b7c876cd54e1327b9a924"
+                + "294e4c6b37c26d6da633ece6fccc6aa3116ada930b16931b87f3125be81f277d3ab0bf7e"
+                + "7c9f38e5ba0a1934e36841ef3ce31e5aef293ea2eff2bd520225633740b167ca5992b225"
+                + "0f914d0190865190c36b54acdf4969b60e0027f740fa27b1deebf832f5fdf9b41ca410c9"
+                + "f1ed521a7dbd8a94b000945bf515bc137785bd44fd54156e2c97a7599b3a9581412f3659"
+                + "9b771abeecd73bc94d63f74e38dd524be43cfa3491e9b8e5fb8b88ae117a10bb0b14b21c"
+                + "326a99b6ac6656a8cdca2364be8605bf580376215685215757b7ff8e3c23a59d33ef22f0"
+                + "6f42cda43bdb13a8de132b46d36ed91acb3f8e6275746519b2729a3f22660713471defac"
+                + "3b6a55d3cad759ce250aef3f19c07c69e445d880283b4ee7a14ac7f2521bf059e13f7d10"
+                + "b889f98054216fa283a9f0ac22a32ab33f389282b3081eec97e3d3111d2c82712a28eb37"
+                + "d49ed36799980c5c7ffb610fe79ae13acdff81a9f82ebb8b8328e01367c9fd889a1a31e7"
+                + "02afcd5676368b8222a5b8601767b698f2ba702c6a43965db7c22ab7407c475d4f72d791"
+                + "61b7d2ddc44ec73ff6c7e9e09be8f7c2bf9d84e9e689f572f3073ccf6e52c92a0bf83e09"
+                + "cff8e434e5807d9728a5e8e8be785d95c2d9a2d73084cbb226007e70c46a4728d58e939b"
+                + "77fa65dfab5f757646b6721cace23795072d35813661890e743a806107a40bb23c907e70"
+                + "918efd8562b56e8e83088505fafba9a18c5615c93f98cb0380b19a3795c7c02928c64822"
+                + "0e84c114a1f70d7869f9d3f91bb72a860f2e124a10b47a4f01274fe26e0d2d5629a1d869"
+                + "3d79352e9cb2ff7b1dc63219ea3ef709e0e63a2e1f0f465e6af54e138635da7185c7595b"
+                + "bd148a419129d91b1cb3a6651de9924786103f5f6d57968a2df5bb075e1a03bc9b536e42"
+                + "baf093d02eff946cd81174160ebe1b7d223d2cae0e32018551b31ef77b05ae19041fec60"
+                + "06915239e7f793d033692f02b85eae37cfa56e1d808462103e2eb1ec002eac45c9134c27"
+                + "b0969b9eaa3d4842a28d44935c2c76f811860898fbfc2e2e9865bf764829c8790c0438ba"
+                + "4757a585f84cde8d336956ce99a44a65d5b328c847a1713b0bfbf43cc65c8da9520bdf62"
+                + "5c31f026b802e571a879cd0709ce71d28ec4df44d6c1edac5c35c03dfe7411e60ae1aca7"
+                + "3741d7507d4db28fe59df2975af6f5c10e0e10997fe683ea7d30616c416282f4cc40dddc"
+                + "a0537c4fb20b11fbaa0dcb479b18d79007fa1ad2bc2b16f11dea199c0542f026e34f136d"
+                + "a017874f58ad3e1ba48ad5c173b97a70b0a5242f4643416d0b4afba13d0c0ec334e76138"
+                + "7a787718c35db2c635d30c7b475e28e33e3afee773c599636d76b08c13707ffe390c2ad1"
+                + "ccff152f6dfa9176cb1d4dfb7effc7b98488e664e52c0217ad34ac9f18dce1a19a61f6e5"
+                + "31b378155f7b036882bc0bb338050bb8d57e124ef3d70b88ba4d82d1b70b7597462588d7"
+                + "edb5a94d19a39ee73bc4adea9f269008526b1b2945137dc660303cca7d38f40c4aef3e48"
+                + "c509a7e4149324832e09e9b943ec43f9b18facf6b91f9bc292496b0a88baae59e94eca7b"
+                + "560111d80be1eb0e279402db9847965db6a790522d605e0dcbc0bdc5908e6b8f68015582"
+                + "713d9e5ba75fcd2c926e1c76bd9e929d72615be75d044f55de47c604ca8a2929c635b23a"
+                + "e361264ad9e92e701b4133e4a4e3de9892066c699df6df836e1daea7fe1618261764d28d"
+                + "d96037347c4b1573354b78f8a5e315f7ea4b0c388dc3c25e286d5613a83616548e847cc9"
+                + "d356178f432b348eced18dfa43f7a29d03997e4d5a03bdc454a66d901bba01bbf99faef5"
+                + "a0d10f55fc24d4608e92150a5cc6f1fc94239c3781cac1fedfd490609b9017d1712ca97a"
+                + "0c31afdf00722333fd8dbfd197b4fb2fc521f4ca8f27e3d27d138bd880a0d52c62b24204"
+                + "aca927e5a5b03d523889ab9af5ea87d633d641715104d8e925a61250e07d2e102c52ba80"
+                + "c0d0642ae21ac66d00c6591dddf2335704b10f331aa3c3c488900285cb2abf93f4553ce4"
+                + "e88d9ffde0afec35663f03e2779a8ff5ab23807f8da6354b6edf5134a605398068a071c6"
+                + "3b4ee493a23105c3f6d9fceb477198ddaecfc4ea69b07b32ddc7ebddf910de9bdf8b6c9e"
+                + "c249d8c1ac193b2bc5f11787bfb7b2bdddc32086ffc61d5b30c9df5c0afcdceac1171a9c"
+                + "1104141f57ed63483a9c37f69d97b562cfe20c377671bb5399e63946578b059898d93f12"
+                + "ac02e6457d215aee32de1e299eef16980b908ac956e909291c26b96f0fc30b7cad1659b4"
+                + "5eb9a96e61c1228f3830451c46495b49557a6c2c84defaf3aff36d5f83f45d903893b059"
+                + "6739776e2f619644f65ecade5eba2d47c0f4d6ff83cb37516285a6367f93ff7f51199db0"
+                + "9f814e363edd1a0c499b61b07a1ac9cdf31321bfb6444a82b0ca889596a233822d164678"
+                + "60aaef0287eb6cb30cf8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525"
+                + "e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "2f535cf82959171786811979ba0f1de0c7083d6e9ec6b12847b9e1a5680b8a89f84dfd4f"
+                + "f5f72c06b1cc9272fb69ddc7f13a646303f5c69a54e45cbc50c0f14fc2784b045292a241"
+                + "e50702cd1a8f3f3e4c16f8ba2e1473baeee96494eb59cf4e9759cf91821f86373f185f00"
+                + "4fcc4d3e1c7e4ff037ccdfb54bef431f5a59d1f039e2a6987345c29c71e07c648fde68c5"
+                + "e76376378b7c315c1fcc7cbb3b82fbf9c47c58700729110a3e25f47539dd51250e2194b6"
+                + "dce4b2d5252abc3cce279effb0af45ca5da69a5077234afb1a9c8d1d53e6b9f187b6e945"
+                + "b5ccd270b59c06ae33d1973b2019db39ebe80ae603b83f3197900631b9e0fa43cc6f0f58"
+                + "0b0c78448291fb82c0aff788cb2202b1d343d30cbba61c27ecb06b8e76272138c0480fb7"
+                + "973e9aae8a25992883955c2c9ccdf3c1a77f8f842ddd1494a4c5c1f6633a1388b04ad1e4"
+                + "aec668d41aef4fc6915fb996855e399636c4ac111d1e72ce2016989d35f22df7357e36bd"
+                + "d3dcadb6658aea1e9114c94f2094f95ced96db9df9c1d19607c102f489cc5f02abd3769b"
+                + "29449ba792eb010c7473a1faf6783e68b83595c66edd8802eb7f7cf1a6243a94649588f3"
+                + "90f037ecdbed1c74c2e88ddc14f75c454640e50ea2f409bf617ed96eacd44e6d43339cf9"
+                + "bab4cb8789b4dc551ebcab52524b5ea03a4e4cb2a28ecf34b3e75dcd7561e279fd4a3e37"
+                + "499ab29e753e86468d00f2268842e8dce21b4588d56f78d87a97d189b05553a80060dc75"
+                + "3d86a0dc448915e764921f337325b5f7fa0e98b81c0d65d97c6857ec0cc75387cd9e8236"
+                + "d53bc91c3db275ef75d79f49cde5b209a96d48ca44635f38022628a307daf7d9c4cc16cb"
+                + "ea074da62e9fc97ce2a337feed8d25c5e5125512d467af34c98d0e32f8842fcbb3acbe1f"
+                + "be8713786f731efdeec117aef42643bedc5b9bde2f3b288a4937217c05463253069d3301"
+                + "be4ca7a2883f6b985e77e25de40f46f4714883fdc41481ef55124789538ee10d5c1e7c90"
+                + "294dc6317337f14b0a15a493b82d2f918cdb09e2015ec560e3e1d5c9ae39faaff8815e83"
+                + "8171edad66684026a46c1417362ed89eb5ee6594b87eebc522aec52e81856a72be8f99f1"
+                + "4ed3d2365cc5954f4b35a181d7c0dd251f2aa3cd611d2bca7f0045a9d44d9152a849c3c6"
+                + "cbff2a0b2110efff9b4d335fce3e1026023b31909d2b22ac9ccc2b47a52608ba942369b5"
+                + "189b0c3936c23a1b10ff0e4b3c7d67fe8c94386c1c43d2b49b623b561680ff9ae963acfb"
+                + "28ca64aa22ecfe37afb4f30bcd3ff1e1422077794ed989864e36e2ae686cbb1c30154d40"
+                + "07ae500ff8aaf32e722ee266841222a00cce8f98585f196ad63102476fed8aff61af5e5f"
+                + "2cd7d8bdaedf45e0cf20b340ce5cc9af6f3b5b85d0446a110d1894d2ff0eb48a3d8a8258"
+                + "f18f9f2283940f695de56fe14692eb40a28a1e0067e81d67b9e247bacd73f76f88f702b3"
+                + "e313e26c2b76136a0c02bba2eb80f060aeeba71753c37f08e66d4a348fa4a91a8ee7bf48"
+                + "893e7c14801adebb11bfe445afb9c2f27dabb62f2ab77841a3f3bc1ecf0ccc73cfdae400"
+                + "5a4cd8419aab19470a64508d9726ab8adde326f009853d4d986572ada479937611784c55"
+                + "5b8ce68fbca8af09a476e0231d50d8237437426891a630b0ce9391aaff2bd905d46106d4"
+                + "2e64db9ecc551c63c34c3803783766bea2a9c6cdf91c65203fa87d11329d1dde05b1c16d"
+                + "59610e842712ade6692d796fd7a861d37af6e097f8fa0f291a1b94cec92e5c6267d91cca"
+                + "29ac31460f45decba1ca21c8d81b3d2d473d3856675b932de5545751317189905e703bc3"
+                + "69ecdf83e01785dbc463f5b296af31e935146b36a76faf15d6c194e0f11ce13731965dd6"
+                + "052828602cb1ba8cc340fa8b6cabfabb819aab0481fc0fa7b1be844c9738f6a16e0ad2e5"
+                + "1609fdcfdc412ddec4e8ab1d10e8ea4984d13e44c60bde906936f55735925b27dae549b1"
+                + "0604c5b119ae1f2fc66fd72b87e5bb2af592c1238908585e021b97a5d48e5592d9369cee"
+                + "6b6a86820961e177dd0e78ea5e73e789a8d26e2cfba0b0cab1d1964011dc3c39cf7d35a6"
+                + "c451a1f90e0ccc257909509fb4eaf8ff82d2b1946a3c6ef054e094b0c9dbf8e6b836f4a0"
+                + "c2103407eaaf8e8ad1b7e16098ba80c1bff2276b8800814f23ac346d63696c0247978fab"
+                + "01f358168d0fdc718d068193a2708745682ab0f29fbd1c35c38bdb8924fe625c54f9e1c3"
+                + "9b08f0178a1476679c59fdb36a81267c1929f915157353b5b01bedba1eecaa447e93ab72"
+                + "63d24ed67e4a94934fe4dd63a6ec41730b1e42f7950f3750a62cc94bf6dcb72add306161"
+                + "a188c48db97a410035d28c42a24bf6137a56a1b6d04c02d9952f8ac6008ef890ea5f432b"
+                + "67ac498954d220d2bd5e930c8c6de9bf830b4e067b1e932c881275dbf683f6b32e0be3ec"
+                + "59d582961a8d13d16a9b8da27b634037624ae948177eb704239cd18a366e27a99c22663a"
+                + "31342d255ad3b51062ea14c5803941cad125bffb5a70dcd82f0f9c723882b27ffd16937e"
+                + "24a75087de4cd4eecc494e88d1388a3291f5b2fc69434bf54acb8f7397d461ffda0a83c3"
+                + "66e3a22ce0be8168daefc0a5dd6c58a56b63572b7a780dfbae8232959439448afc38a56a"
+                + "44f295c692fe5ebe5405263dc617f636f07acebaaeba3290272f83a907eadc203b16f405"
+                + "b6b95070ba42679af1d848b361c3c6a61043776a3524f4e758c8bed6770811e67adda167"
+                + "e1d33b7acae6c2fcbfc2633c1f502cca061f23466463bd9e81edb12ab347cafc0040e923"
+                + "5302d1f8e65a4f20f3d61d1626dc419c9f55533882ab1c38e357fad85b24c6a52c73b880"
+                + "53afa5fea377ba6da57c8c7fc2f9777bdaee7d43ba4f5f02f5ff5016c27e526de7620bf2"
+                + "13fa679dd56351a8cbff81a23e6ee30376f69280b005540a375dfefe0ec78e9994112d65"
+                + "c25346b1b239708f62e48babe0b856b2e939abde9f46919d11a9f3daf978b55fae2384e5"
+                + "fc9ba1eb835750c947166880bdf85c15834b25194892220248657036d6fb2897b70ec185"
+                + "c85cbacdfcaa6243507156166c50508c2269043ddaddbd2952390536e85590901cc5e8e7"
+                + "824025b42a20c3dcff4ffb9bd11ac9cdf31321bfb6444a82b0ca889596a233822d164678"
+                + "60aaef0287eb6cb30cf8d8ade0750edca071ef611ef59cfa5ed322c6401bebe646e92525"
+                + "e04c677a2ac6e4bcca8649d98f4cdc4049cba4189fdb4f6d3b302bae2509c24fb1dfd023"
+                + "66a4f3b0ae9ed3929d5ff53cd2a52f79fa5f760def0c3fabb7ad29015630a1db7c94902a"
+                + "355f9253b04b27b37d4032641359b8a2af4873a00935af24b3581eb876dd5b5b77d33362"
+                + "4965d9f111a418c6d52d27ec077bbb6e4af47c30c1af88de18e78bd12ddec4ac95441d6d"
+                + "250beda3d00c5749297a9d9eacfceeba57c702b40dd689c97f814d875d7801467a116c18"
+                + "fca3f87d29aaf7d93df441326e915dc4fbfb998a09210194b83c0f5c026abde5ddd84fe6"
+                + "fc0dab52f913aa95e18a17b57076a45c43d462618801a2a491188b8669456b13b5cca4a8"
+                + "af9743edded05454a6f2daeb25f427b0e869f93b930598cca12cbbc0c76934a7ca189086"
+                + "447bc98efe77fdc451ceda5daa59f46163978f066cd201264cb07e5a4b060932e908a3f3"
+                + "f50dad32997802ba09d260e5d34ad736b0fa071b56e903171e0b6ef7a315d3f707e75813"
+                + "de262e9fece391ecc13c75b8633fe450535d8f376f047f3cfb95d8236110acf7f634570b"
+                + "adf7fca12fc67597993efb142f8cc1b4155a12e589b8ef4751ebc79ac76dc3081ba98b39"
+                + "ea862d0acc43377608b85234ca37518f77b5f95f4f0c9df443f29241bd37e0c5b2de872c"
+                + "4820e080cefa55222c4bde0b97064f25f1a1bb22ca5e6fe46ef570e5967717acb5022ef3"
+                + "a16dc33eba4e9fc073307af3d5121721b04958c15015ba717ba0ab7cc9f8d09478cc1ffd"
+                + "8a95ef7588cf0913d4962e19c20bb3eef063df0f8742316ef2e716fc1a07cdccde9f65c7"
+                + "f96ba4905c10f1be509feed93ca9c87fa25e69fdc9bd9a65645ca88959fb9f192d9a051d"
+                + "257762670019ebf83a822061770261e5d853366480f9e78d916bcfad408296040036d8ca"
+                + "110f1aec995b3a51e1cee2c091c42d822bb58e5030ea544ba3e654635a3f6852f10ab8b2"
+                + "0261abd29728386b7afa1c90b22f501c2a56791290c7b4cfa45f00e6e5cf9290615f5662"
+                + "fa14721f4bc39b5f1d8cd26e48eb315bdddc0ded1761ac2c35fd19e6c6b5b09934203001"
+                + "0c359c602d884d5bcd1fef0b767f48007466fc5de7e75edfbb0d0bf5283580806a1206ae"
+                + "56ceb33462d73f7963d59ea39a1573b68077b374c78c7f9c2e30702a702de89eac5dcd30"
+                + "7e4c15b5fcda71999717c4a62ce39d5c8f0d64b04432db805e6fb1eea5bd80f24cc16ada"
+                + "944460f6dc7b87388cd35d95b31c556ae2fb3e38adc976bc0b0cc57195bcc5716da47184"
+                + "3765cf56463c5dc60545980c3e875bf34b0161d151289eb40ce4ffeb622289f875e6c772"
+                + "e36bbbf4adb0219ae5a57d3e538c99aed9dfe25996933982b16edcdda63b2224ee328ddd"
+                + "7c553973dd179fb103214f8b7a2ef64be5cb4902af5d1ac34143789c1b22334a94cd62f0"
+                + "c564540b622a4fe64f351e9f0bdc38cf67cba390a89b4f31122c2af687621b8e73564fdc"
+                + "55b1f47d726456a5d680da5d190a6964b75d22cfd2a1811e122da4b13d418a23d8516c00"
+                + "ab43258ad1a167c4c646086a393453a68a7d4ed728390c3668fbd5a3249f3abfea61fcce"
+                + "739dba13d7f54452710ba93abc3fe59521e6b51f75c6c86eee2b802eb91906a2012ea2c6"
+                + "10cb6ccce1426ca387cfeaf5adc58f8efca2be5c879c4ad7dd5743cddf055d2ba7d5b21c"
+                + "1bdf99b0764db277c009f4a74551c2439c3077a622cdf71cca3c50c7566672dada194a80"
+                + "b87b61fc9a9703516d05416bfbbf8efe8b851274b4aac21ddebe08b59cfc27ddd09e62dd"
+                + "2c204ccd41e7e70439f14c04c301ee12510829915cca471281aada72bce316db43b300e0"
+                + "db02f2de5c5b983a0865ef040e07bda1abeef8aec9dd35dd1f0d682c74143e4975a8ad2d"
+                + "47388bb11ac712b0d497333a2f7d8a1f8c592fed73cfeb44608395c961534b7553071d5f"
+                + "5a8c5ff577fb9a256e3730d523eac6802c2b3ac51dbb3d99e9ef82e9a665574284f20e03"
+                + "eeae9bca77304aa9bbdbd0e5a8918079110cab6f360b2cac2bfec486f0f4fe243103d76e"
+                + "4edcdb6a45bc26d1fb1ef7b27f8fd186a51ec05afc9293e7dceb7115e3f5d704c2e2a97a"
+                + "41a4d0e020dabf71c5eb383e48afa92d92ea6ce6a0a49bc03b51bf16054d0edd8949a989"
+                + "774021e2a33c6ff3bfe2d27c30af475af7824feffea8504c344fc53ca7be12486a14bf29"
+                + "a1ee7411e5fa9c30d1e7087b3de8d66cef6961f0a8aec5d9b2028fa7b616a67cecdad5e8"
+                + "7c336692707a977667c466dafd5ca9fbd4d82a95b8fbb290427012cade518643d4bf23a3"
+                + "bd3bd03fb6764aea2d67712e6d9037865a73b30c39d73ad646c20bc7ed12d3665d579dea"
+                + "7fe535ac2a793ad000b5f24bc620262757120cb5a5327b9528bffce4a1e4424cd6d7f476"
+                + "3d32f39994fc1354a951403c4c9661652204c54339b86b9e0a3502061f46c49ed270640c"
+                + "0592bfeb3be8e7ce46a88794ba25e60472824ac99b1811cd45cbd5d318fe142909d8a650"
+                + "697d17fd9d9562ab9490a0505be9bcfe7ff076512e9a59e82d0158a5651718d3cbfc6aa1"
+                + "350ee9f5a3dc92fdb3c47f455b837ede08ff887a13932b99caae20cf3454a93c9552d2b5"
+                + "76090a3732fa060b79114a25b7a76dca543a2dc6196c036c3fab400ecae7f60db7445495"
+                + "1470a9115458bbf66249435ffb072c1337590f216c66fac68a4ca8447b74f0369ed495f0"
+                + "8cb47f7e2a35c9f3488e986df8515f6862ed088cd1664aee5e795243b5fbd97a0e9b02b4"
+                + "a717b6a465dece97bf94eb051f005c4c6bcac464cc2eff1ef76d0354445e3e6c73e265a0"
+                + "d8678b9d03c7d7297ecbe05df1b6c1976f0149d3fefd281be6e7b2ff4417807f98a915fa"
+                + "2f547ac1e0754e4aed029f537ceb73ced97dfb64d72310c1aad1c6813309cb7a5ba9d9e0"
+                + "a680d03b485deef17e6bd6b37d68b735729a4c2b6cd9171247b9e83f34ae2bdb9b8281d4"
+                + "4b19a3a16bd435de67d279cd409b49be9c9cabef9c33369e276a1fa877aeb617c158b5a8"
+                + "1efb72a2560d0d5d52f242429aba494341ff666d029a81b73851a3a67bd472ac45e24650"
+                + "5501f6ef3d96fb4d79bb7db21c736f931bf8d587d67e52ce0d006146130b3e8b420eff80"
+                + "04345d8e4c6aa225f93eab381eb96b6d98329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "309fee5c1e2cc2cef7b5a4797fbe6a3da2be901a4ac73af521ed419ffadc5d10ace0de7d"
+                + "20d680a4cc3b930d021cd107f900dd335897c24827e47e31bc8ec9d0628e3beabf160bee"
+                + "e542a20a1c6eef06b31c0ce63341a32281ec045befb784b6e24db918778d44138e54e3e2"
+                + "c33b8c350b387118880d0fa20bae46ecfd177740d577d95d6271d76bb68fe0205badaa73"
+                + "d4ac4a0e3f75e61da1da2e1e37a133f378e6c4f1ec03460788140b3fdf8de15e2cdbaf65"
+                + "c7ee820a5e6766815c6c3f0e558e1484b000a88cd2331684b717f824494693f8dc188568"
+                + "119f1516834912d83c084b091733c8dc449846a48973e8f44137c99374d2792b2bd74241"
+                + "80c56cd33cb815b9217b37752adee64dbeb204e542709ea6e15e2df867a368e6d5ac5ac0"
+                + "a48511164db33e1717bb626b01cd2d2ae5b0940dc3870be595a8732daaf3962441b778cf"
+                + "7f4e876c8fc32e87769b230d59826306ece603ceaa4ca25747c46ee41b0c69dc8cdd69ca"
+                + "c23d463a9c4341d3b5e6a7718299c0fdfd72746c46d8f2fb2acb1db7f8de60a124ff7cf5"
+                + "3f006ffaa8f0f2b4926e9455a47bc371a11e19399f956126ad4d7eb007dc9f5a4429aebc"
+                + "7da515236f0311f40cf7d0bc5a570047e9610c078de3cadc301c0665a59bd290ade578cf"
+                + "400142f7d6b8e447783a0f96cad5809db3226a80b09e9e448fb91caf4d91a8f955b2e923"
+                + "3df80017ecddbc570b63dc75473c421f09bc49a37a08b8c72380828981976a1a93aafa30"
+                + "073a7dd068332aad496241fbeb9d73c3279fed29a8fb527eb5ffa762b63aeccba8af5972"
+                + "3bb01aab70a48c478898d21be15e64ae3bf1c706d63a6251ea2c9f20cd111f2d923743bd"
+                + "a51cda858bb50ead68722ba46ba52905a84e5a04957dddd9009674705e6aed7d886fe038"
+                + "fc5f227db6bbeef59aa06741c73ab27131c04708b57477a8c303dc68a08f0f1e1b05cc25"
+                + "821f49c4d6defa831d90923da9206a1cae4873f0651d6aa09881c8d1b53bb0acd24dd763"
+                + "bbf48014d178c73e38a3a77ef89be6c5b5121bada30cba16cbb0074cb4080cf2670fdead"
+                + "f774b6f862a047122b9140c6c4281fede1159a3bb7ef6eec308605cce104b1acba2c865b"
+                + "cf05b7f2b2b5ba403d9a81c5fa3cd93cba798d6541a7987930c515213f45bb16c25dd356"
+                + "b8b906e606d02a4d71bf99847f1c7765435f05e7b6746ff56c583b0691bdf434ebb896d4"
+                + "2abacf853ff5dc78583b8d799284c869d373cb9be85f989ddfaae821ccdc650088a5e303"
+                + "3bae2893148473be9ed329969e5bad3352991e74faf15a4062f7de8d248375a49b626edb"
+                + "51ac9196d91ff330c17fb4a32cceefddb31e6076f272a82e584be63c33dd8105391bc4ae"
+                + "816d4bd284f62e053c63867011e55f4f514125e255910b902a3f767eca6f01f4fb077a08"
+                + "6f6a4be2f15313f6c38faf6ab1b9687c031161758fe67cd5dbd559f603cdcc74043d3458"
+                + "f9a3c5d5e110892d99490d265a21a5101a3a406825fac120b523df9e1347feeb911d988c"
+                + "af96e0b21e3789fc86b5ef3f8e797cd76d5f4cecfa51fde91b2d2c3073a20716b969f4df"
+                + "25cc1a8d6640f0283111623b3262e62b11cb9345907775dd3a0db27cfc3d4e4446410cbb"
+                + "1e42be95f592f698f4dca34b4dab324248cff0c71f69c83a69f02112c07585ae38afec6d"
+                + "83b5e49945affc207d127683875fdf252daece5dd7dcd6e97181df7771cd8ef89d029165"
+                + "0fc4d4f1568d15c155f7c960ada659a58d58f7442fd85abafa2cbb3077f9f507889cec33"
+                + "37da4a58dd3da450339c1f072d6439b9a908a5e3e1f947faf567d2562bc44e95b2a7ab5e"
+                + "38cf83638cfbd3528222ace115e2456708c9bce986acfbf03e8b18c81e0b62b3ab1bd11c"
+                + "29b487f2a305a5fa36e0b2d8c003ee093eccedde0ac2572c8ddcbcaa60d05250ee4144b5"
+                + "22ab6bf8c4970bb7a828d7dd46f4335baf14f17c80a82f628affa3e45d10c0e02328570f"
+                + "adca943ecdabb9b41155e65ed71712c38d77a80ad5c1d5b52fbbca45f7e0889f6b118ba9"
+                + "e3bc769d9bc5950ac8fa469801ab5b16cedbf02ef4621cebbfe0f4ef31939334f8bd87f5"
+                + "0d9704ba447be0f283e9b59f665f2aff2d03dbddcdf9742ce4e48b91c8112dafb0c2cc57"
+                + "f130f8ca25db59d32618ac39b2412ff2cad3028079db762f9b4cfd70d6e94725b7ad33bd"
+                + "09baba332845073401007ec1dda4ec2bba97dae55471902067dfa8a904c2977750113258"
+                + "b873b10a467aa4a551c4fa286e3c0e425419403471a06392646381df3155e418588fc1b2"
+                + "c4f1df5099dc590ed4a5a606552b4e269ebdcdb1986a469a5111ca6a63384ae67c4604f5"
+                + "6765984ff3b4d4e9c5a3675f2fb0c3239d7b5ded049915c35d7fa293e0459a5399e7afbc"
+                + "1430e73b937098f714739b372f62610ceaad184e4f40ab2d7f48b7883f1d7b8e7d92be6c"
+                + "7c05ee3cec6ae37be9c934ae01be26939c673c10b38007640271db5e5e0b862a0babff66"
+                + "c120064edca4939f9a3724f43a8fa2e37d9c86836a43893c7486b1561a680e5f21dad8f9"
+                + "676535562af70d15b02ab5e38f974fb1d053f22fc138b335fc8630e905374e5290db8bca"
+                + "0e8a0925e309337a1753f12b0d8d7c087df190fcc7b42edb0dc2afbd614abc4b105218b0"
+                + "c3e7bbdc6318370633da5e45293d24a9eae6229fa098f51b714fee2a6c1e1e71aba828cf"
+                + "47d332e6bbdd51819a80ddee8613ba200c3e20f290e70e6532f44d1a81376940664a8341"
+                + "98e85d08b33b958510dfe7e0bc77a354dfa5560e1b289fbc9f5af73487db7f4c29a454ad"
+                + "5325612b10ff32b4f1626229fe25bdfac0cb8ba6462f16f633fcf11c2ae575b2c48ae242"
+                + "90df29b4de509280cf34ec200c2e140e4d436e9520c9f410ab5fb2cfe9a61ed0d3931b65"
+                + "b65ee6424236bf8e7b2724d36050e823bdba0db911cb80e7c6cf297b2c38a9df6a0c4ee2"
+                + "c3bf8c48a0911b872b9b34be40dc2e77c2de49fcf0c11f45f16523adac32ea7c3e8ae4ca"
+                + "b419e28762e448434359f0298e7ab23da3e24b1bbe8279409df5470c1a97b6e80d5c292f"
+                + "c7c62841f1eabb8e9656078589ade0bccc129cb899fb6e8cd4a6a76908c4d8ad3218d485"
+                + "d8c22e48c081b8adf29a59ef0ec3eaecf757df6b2eaf13ab35b5cba745e2f82af27c34fe"
+                + "075d1cc54041f664f3ffc6dc3c298007dccd60e431b9670c10124290c714e7dd079da828"
+                + "01b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "314df91d9c76620d31fd9df274da9d6ab40fea389a0438c32ffd095a689008251f5e7800"
+                + "bf56cffbe31cd8ca089951a2d081f534a98f3e4f347336b30366e8f4c993bea94c0a78fb"
+                + "06c87a5ebfab7fb1b4463325ecad94d4add155300dce75e77b783a7c67bff5d6616ed696"
+                + "7a43e2b63052000f66007dfbbae8106879cb41580e616518ea83626825a6f3f76a282131"
+                + "5a1e4c0c3861ccfbfb19f8fa1724e6f2fa673d6389820a2e6d8c555d847a541de6af0483"
+                + "6a6705146a025e56121d51c9c7faca2526e83ed683226caa6cf8a75b7ad0b4a05ae0ceea"
+                + "d879d05b2692042b41e1183ef95707eb4c86a9141eb9399e48488fef1528e0a8a8fd476f"
+                + "f07c290758c1dbc06407b6707c4a908421ff6ce07e3d2fb14777beb13d6b2277585c7c4e"
+                + "6bc478f5e372d816181f40236c1b5b71a56d742b0aee603ca5da3c341d571edd3ee45521"
+                + "da43f2941d1ef089dcb71b293bb6df38b5595b789f53eed87359b8899baf2937f5bdb0f0"
+                + "959e1bb6b36769166dc01c115983e7113c53ee17cdc8d9d2b680f1531994b3c23e0d6355"
+                + "da0a11792853da5bd5d1136f51efcb75757bcb67aec78e1dc393c5cff04ff523a4c03037"
+                + "515975acfd42b95f3f8d58f49952e5b549cfacd37b8e7c10ed4e1fc52c26b4ecc9a9ca17"
+                + "be2465a990ba5c0544af9938bf9c7e874952c2ad3326f938f4f088042467441b39a1781b"
+                + "fbb8d2eea743c7d8ea367e5a0ca7ebc3234c78402637cfc5e2d3c97e964bdef6a56fd397"
+                + "9aad16cf5672eb3b1a505846d452c2ca0faddac75ba464763f279cd81451165f4c7e7f8e"
+                + "b749f14f8455c88eca24b22f55459f9d396e77d162274f8c3e0ee57576f08ae3d5dca76f"
+                + "2827a45e91a82cd04002e5f3d72be948d6db1046865968e6ba438bcd58dfc88989607ba5"
+                + "bae3fb62197faf7cef67fcd4367c63771c1ccde0d896bf056aa7c86a1f81c4137d57aa43"
+                + "7267e039f6f49a3bf92fb835e82fdb7460577d122774149ecd86b0b34d6d5ed19bd02d50"
+                + "fb7ae4f455136396477cd4356a011ae9a414cbe0f3de4f975c19047a246ea1e8605e43df"
+                + "7f5dc0380ca7e77bce9b2e3a5f7ab382dcd83e12b244bba20d90aa8ebf4c471e3e3b7473"
+                + "a60811042de7504b4518df4372e801eba9e7e80240320f4ab4c1595fce18860f9f0e926b"
+                + "11f5790f77b84c9cdfbba1b1f72ad666c214686c13399e005a709e9e50ce143d8e413de9"
+                + "62c5b6c3cdfc8b78aa93b303ee4676aafa9ab5daa87cc612ba2277af8333d0c64d69e875"
+                + "7972814c3ac90c4e3286f733c947135438d17b63e50ff91dd8f026c96fe33640e313b3fe"
+                + "fa7e1340419b2c57561c41ab828acc2373aacd7ca5d8ac5babed9dfc58204f43a6c5062f"
+                + "e710614dfc0524d0054edab65dcfcb43b3ba0591f5ff60df99eed5d116b8df350c4fb282"
+                + "9757a47aaef623796c6f2cc2e05cbbb8371d1bd3fa10a25403e943b302d9d72cde3a575c"
+                + "99bf1e73234d03b258f70297b83235f9ad8f79b6afdf4333ce650817c510bb243c0d6dad"
+                + "0d37c309bd6feffb8fd1dea6170c57ac2c76677045e822057199dfd3a8d8dfdfc17e5e72"
+                + "ca740ba2c3d0a17440774e1ecf8398a4d08cd7252da99cb660165a6b5bcaa64f6bf552cb"
+                + "38f89637eb300473b3364a2febb6ae3c52e4ec62b01374368d0f70dd4355ee819ae60f1e"
+                + "3990bb001f0b457fa33781fb6bf60acdac26ed021c528902fc9a5faddf0fa3ebce4083cf"
+                + "98070a864f7d299847a63b1cd6b023b8fc4b5e1514d7cf0115f3b77420de6e940ab7b592"
+                + "994010e4ecdf69a39cdd6ad79dd31d7a3b92a3749b9397c7a334dbf86663d31519e20b9a"
+                + "b9bceff43e4a1bb7ca14c90877120d7f331f01daf955868390ef915498f49ce81d04d86d"
+                + "ebf1af753c3844bb27cb2f18b27abfdd95606409da48e0672102f1e60fbce631903c5222"
+                + "b78152aed15707b68216cc7825a7f91565537e5c4150e64cef5b8e1b92c68d981c8a11fc"
+                + "3a4fcec354f094f8ffd80eca71d59418f3ebef6769e9457b142d524b5aebbd38c8123210"
+                + "405ec4354a7c7e89ec33152c25ce03bc0b1bcef29802875b8fd737b55e29a5a63f1510f2"
+                + "e58faace726edb54a27ba267156865551b16b971febec03c9042bdc9688513b640cdb16b"
+                + "eed1de56609564f55d7c2ce748874cff5affe8d167b4d908351b0182796fb95dded6ca81"
+                + "6ed75971e1d6f7a3ff841c48ccb886baeba8de6e573e5d63c871e81e6d22380bbe754f32"
+                + "546391a5a03fdb0123c8f03b5dec6f6840a9a2121a212df13b079ea331362850ca39bff9"
+                + "2806f6e6622aaf1c5af4dc045d750861bc5d3bb6351fe889e03b0d1f6ee362c32b9add1a"
+                + "4aebfaaee4a23aee7f7555b2959892bef1657c6a2a1059a17559447520062177074f990a"
+                + "3ce356c62db77f689931ded997f2f2033b634807bfef598f7e1152dd10958e18b4f7fb90"
+                + "b203e2063c6a84620b19f02593410c2aface54e0c5c0a29e70e938714a02ea907c85f6b8"
+                + "219db9c98495cb614e617e6ca17ea20effa3f25f8abe0291a657fdbb7fa7dba2068784d2"
+                + "8e38139c4f9f830386309e33e0d205719ae8a3c2763af493373430dfe1a57e3c91064204"
+                + "a095d3bbe421ca21985ced88c7793cbef38a54f8c93289183674a3aca4cae7f0c8148bc8"
+                + "0882d86fa0eb2cfa45031854f649814dd067bce19a5099d2bcfa83d3eaafe2dfc41dee91"
+                + "7290d20a4ecd2fa7d9ca71b80d7e446f6c4f0c89b8b390ca667c7f7985efe7241fa37c93"
+                + "18c03e246cf9af729e8a1dfcc83c85eb72abc6b31592f1e71afceb0c2c9aa5f6ae3fb3f3"
+                + "eb0bdd49d8313cdd54453d24557e21b4b7803803493f7489ba3a64d9ddd6edb6cf82e347"
+                + "8533b56bc7904c14046530a6656417b1f22202a32d31899f9473e152855ce0ccdc19552b"
+                + "c6355117752c3e8d915be991ca16591b3bcf921d8270522ab0ac93a29ae688d3ad105bbf"
+                + "f03412eccaed81e7e99de7bb90be6100404faf570d7f8a0514623df64f168d849abdc707"
+                + "ff27ab5ea5c1ffed66e7c3af713aa6eaf525ba9a4edf2e1b63408507a1bd933d43a617d4"
+                + "4b68a319704cc69a0b9c372e6e6c816363443aff799acaddd65a7b7dc17dfbdb83276765"
+                + "6565cf5682ddf409891025569bc3eaecf757df6b2eaf13ab35b5cba745e2f82af27c34fe"
+                + "075d1cc54041f664f3ffc6dc3c298007dccd60e431b9670c10124290c714e7dd079da828"
+                + "01b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "320874fcb8dc9994f4df4a2aa37998a47a205b077d37bf1883db5884e8f90d7341cd254e"
+                + "777b0ee7241ce0ae3eee042c6c7fc7d9a28fd15d05844470767b3972ec05e9987bcabab8"
+                + "b31dcf2b6b9901e458aec7470c7804642bdd474a577736f8880e8a1bc1cb69fc40505247"
+                + "afd2ff5e2f593212521a9853900225abe6d962ecafdb8c3fdc97545de2ea474c290ff766"
+                + "b31d5883d51158cd7dd49a6e00e1a3aadaacde913a70cea960a4ed2658874f08793f08a8"
+                + "a476d3fa5c19af8395ffd525c0635418bb6f4a6294676e4efa561de44bc684484d03a3dd"
+                + "1204442b8b90df192e2cacd8a41fe5fe4835eafc607965b61de75bf9f9a4ef7708ff2cfa"
+                + "a6f30af90340adf57eea5ff9094f21c9dfc6d5f06c42039e8dc61d9d35915286effe7ab3"
+                + "db9c375bc024772798e13836e744159514c83acbbc56de22ccb2bcaf733f6f0691d2d07a"
+                + "290de613ebc5fa0db39078584a6f506e9fcc5f45a3132fdcbf3906e32abf064a113bf9b7"
+                + "5109d34418aeb70d8c2ec1e08eaa559f3afac55f49faea78e37b4bbc6163b7f9392b2488"
+                + "de06d493c495e9baa5cdbe662b78e50996f946278748864c9f36784d8c726323226cb0c0"
+                + "a38db3d8f8d036775ac026b8bf04688fbf1e0b520ce96df7450bb3e6b17b196ef079ba8e"
+                + "24e9bf6231b9d9b8e84d1b62252fc76bb3611e750dfc96d5007624efebcde1ab4b0326ef"
+                + "a4835067d62c133d141bb002ad7d270f160081b80191fa17da3ef8d0af809776849989e5"
+                + "d64a5679be20c6027cfd0159451f0d3abea77046e5407cb7019b682b6b5be39f624e7e98"
+                + "a8513701994e8d4a31552d8569d78ac35f35305a7ae327a78acbccdb784a2bde1ed81ab1"
+                + "3c6f6aa8762d0da7cb4f142768abf4eebb0b4be340220175cf2724fecbda8b5b31033773"
+                + "733c73089e46787a0610b1554681066779ba0897bbba48cadab026d1e06c636de3b864a8"
+                + "f6aab94e08ac5dd6dbaf500fa33783254f7704bd83272cf361fdf5f55ddecbb2ff085196"
+                + "d15a833dedca487de51ec3508dec5d34e2bc633af681a9be23fbf7f6e29e4e693fbbea80"
+                + "b3ff5ad7d8cabc4f025ad1b0f3630ad866be06e716c059af034b4e1723f6d9e4860ec711"
+                + "ee749299c5d83da242928c99fd66adfafbf3fc71eefeb313d3a0cedee00e44ea79ee4896"
+                + "bf0c4ed046ef86cbf543f62b995ce14c3ffada875efd1f5d3f00b341a44cb73a4e83d9a7"
+                + "e21074616ee9adf9f194ffeb7c826c4e0770733d5cda58ab58322180d767ed9798c0ca9f"
+                + "0f78cb0d6b872d0966c5befc874080e46cc3717bffb7d24551889d75fcfe0e7489b42280"
+                + "6abfc4322a26ffa58921b796ac583c09dca4a9d647dda7edb9f6725b0317989c7d76ba47"
+                + "dcb471dd796bad6d1e18e7b50c774298ee343de23cb4e186d26d8f3c2d5755674e814dc2"
+                + "3d81a892d55af912e8d29e9463f5a7ff0d526715c69fe2ace6ea24c3e77a01eb253f54bc"
+                + "87480afd3cf3b2ebaf55e52b5675f0799caba63a3934804a5b86b1cd8b8962e0d70d0932"
+                + "8f64a7b49eb63863695c098ee411843747e703820485cd65a814939452ff29b6aa080750"
+                + "e3a9765b4930c1123883d4f8655be28732dd1e3e65ea7878706b1e397e79f0a345e5c3df"
+                + "2df0630df76d913aeb05b335e9296c5cd0c2d9433e599050466b12af93ab07b5fec067a4"
+                + "a124ebe8664a7227a151c1a7257a24b2d34399a881328e1ca13ddacaf5c9aa3cf2e6a565"
+                + "d4a98e8b730c8163d2d0ff4e9d918754022456eb41a8d3613659c1d0e39facbef92ded92"
+                + "d973f031922f8fa8f70d172796ed153f9bb0d653484c9672c01264b2d92f6bdde727d518"
+                + "ab66bc331fdd7dbf9c34b2442283d60123e2931a4868c961aebb001784f58716b37db82a"
+                + "158609751bf6d39404fbc457acad24ec84b85a26e490eaef47e2825128e36bc8e031660d"
+                + "74118be8c0718f5b78a35f530e941d46a83eada2c8a5b4e0fa4ef4336f866744248246e4"
+                + "b2c535ad8f0ebf36f71bb5d458189bbb1066d532ce9a9e84dbe33ea8e814df2fd53b3ddd"
+                + "460b594d54a80cd1cecc1dcf2a48ab00cdb8b371923e667d625c8ab0eabed2d83ca51b9b"
+                + "19909dea6eb15efb51ef1db32089b07d2395834ed5bca43f8bfe21941f57362376ee081a"
+                + "a6a5a043db0a308b5b4b905c65c837ce508e9fe314ecd532970e0056d00fa76ee13ec50f"
+                + "d20731cb66a286a674fb7ddbe61ec14fd45a88437aa033a9b2123ec8d7ea00447c86bbc0"
+                + "53f553d9750c518a0310e5013256ec640ae8353597c54a054f079b24c73428546bd23ed2"
+                + "97bffaeb55d09ae2e9e02cce16d4dc5a386dc6072b701233d7a565dc9d2118db3b718ffd"
+                + "a5fcd236ffcb5f58a0d864e484ee7d72c02c9ce3ddc6d5aa8c61d5026d659cadfb5a9781"
+                + "84e7c47d9c4607a6ab0d8ffe48f91805ba3a9ccb4e7ca8971864f01eca98e7b5f43a0942"
+                + "6d60c5cc87800afb9968f1094b496d15e92e3e559db5a3f90593eb9bf9c19332bfc5e328"
+                + "ab950d080c96358aabc623f683c491eef281ff790d84c080c75f5939a0084228666e9f1d"
+                + "2ad7613523b2530302c72d4916c2d183e44a3314be025a0cb1d9b857ceb988b58bd39055"
+                + "e03dfb3ef07502a2dff9c1d3f40f2a3cc9c7c9a6d7bac943c2c990e45d58f87e1c2f9c32"
+                + "af6356134f5d610670488c9b43e334b11c2a7f987503a71cf8c3bad740a470d3751d11c8"
+                + "f1b60f0aba7f994b7a6e75c0027c3b5f2b0d733f17d08db11ac4a690a7237241ce4d8f3b"
+                + "71d743b6a71baf7921dcbd1e41d4387dbab201d77c0e256b178f104c5ab0568a66eee000"
+                + "1da790953acef406544ba8c22b3783059bc780f35b25c21304e41f63a4c7661696059eca"
+                + "432aa5142fceed8bd2c368093b14110deefb8e051d5b02448de35b29180a7b14558423ad"
+                + "d3dfbbca1d619220eaf90f06bda0e9a0b47d63e6bf6e3d3a4fdd059a4435103b43186bd5"
+                + "69622be3791f6a5b3f6b85577fb80333f563ac75ca5f61f4e695a47b0deed8c6ef6ac0b4"
+                + "80c0b339d57ba46cfbe85dfd29733462db7908327a5d7ce129ab5afc4590cb3ce0edfc53"
+                + "42564b1de184dd54a0a4375545ac54a3639362ce704d0d5724ec8cac0888608f6309c7b8"
+                + "5658e6cb4e454fe265a0d29ce320446b5d8d9347689d8aa1458e3c9ee89cf73cb635e843"
+                + "57b371064fe2c4532effc6dc3c298007dccd60e431b9670c10124290c714e7dd079da828"
+                + "01b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "331fe9494ced34e060473f28fefa01b5cb02e18445e8372df391c1227f4b12ff0996cc86"
+                + "c44faf7e17bbe36bd38f0ab3a7f469ea25688812a991c7fbd270bbed607f88e3bda40e30"
+                + "30290f6e3b97aa02d0d3d576c098c79b0e863d02bbd48225636d5a41eaf4d0af02f1d0f8"
+                + "0a62e619fd159f4cd3da90f45d60bbe4a53b7b0b5f6bfafdd8b6749d0c17572ffa54245e"
+                + "e02b41cda56a1824fd3e8066be91ef3dcdaf77e8fe9ee96416f0e1f04e331390882b6ed8"
+                + "9ab64471e112a9e6ea0c32ad988ed2684f7e1541fee1c9a6f486186fe8a424e2dd724dd8"
+                + "dd1d8fd7af219ff865b0cb59085db025caf33a3af717f3f68c97f61ff6187896ba2e34c4"
+                + "54dc0f36ce327aa6cb4fb503520065094e52b13bea3fb1d7ad251b5ca9a1279ce43503c9"
+                + "84d92db5cb78a40359fe3cd33c509df30aac3887b46bd2ca57754149a98ac703addaf4cb"
+                + "efb20c834d37e9c24d4bffb19c802f01ea263880ff85c927bcbab4dc67fbda82cd05b83b"
+                + "1e4e0fb81dcaf52eabde2cb94371ae05b22bb603c3f7984a79a705a2df8745db3f577304"
+                + "051b99d591c1652604edab44a15fc5bc56671c5b4b02935e65e952581f2927c8b08d14de"
+                + "1052c7aeed467850a49a2143f1e66305e4e392f09c1768433d3e914fa4c77355b49eef92"
+                + "1d7cfffcea13406bcbcc5cf2cf711bd6c9fe37ac7ee1c617614ee6550a90eaa5d21d44fd"
+                + "139fe6018d18379a75551ea2b9cdfdf33c922f22de13a67e740bb02ebdb0dbbfc13741d5"
+                + "d420b83826023b3ccfc233ecc9808d933339a8e6e4978d42a8b317780c76bb11c4c56887"
+                + "449c629ee20acd95a737416fcc0faa4309d124820c22f1203a3214fba8fb819379d85216"
+                + "f174f370e2c7479542baf3e3e4f4fa6ccf31ac2dfe53686d7e9f80dc5dac4cd9fcf475d3"
+                + "f6b1dbb7b0e3f9b49a08a69da25eb7ea74bb58bd550f2485161b3c4c52d87dbd84744f36"
+                + "c6af3648c26baa1d11a2bfac111a8e7a400f8f8a7abe9777ea71b3ba1acef13975da13be"
+                + "d9d89cfc69ecfeab08e851e30018057b62241b1c614dcdb2e78529e91c65c36feed296bd"
+                + "cc12e740d44a9d31784e2613195043643d5e9dde4fae17279cdfa164b1d00b928a8e3f85"
+                + "b1e8f74ed124bf9bedade0dc606c0fab03e6a421a7f4666bb58612fe5620ff9723b40f75"
+                + "1507978402f2cd4526057552d6d48498c3d5e2f584b68f71cda408c2a158536e3c19f7bf"
+                + "198148541bbc6648c0cc39afaaf3a9519ced58563e4b56a1adefcc4810a6fd810ff3a9b8"
+                + "83974eb20fa0b08ce0948aa06f45fbc6aafcb61aa59dc45281c115c2a10c3ff542ce805a"
+                + "dd318790f8d803e20676b9216f92c0ae78a69f17656a2be2e69d565ea2349696c59937cf"
+                + "e8be1415367f17d6d9685cfbde200b37de94fab635d3a94d6961421816c893fc1a60bc9b"
+                + "7f90b9c5211bea89790006e9b0a87563fef956a088539dac633ef7125d51dc650d39d490"
+                + "c40bda681c48f4402bf2ddb6277b3b6ed788d64fb47324c8fb7c6768548da233fe2f7d09"
+                + "74b8b6b310a5d37bfee558a3d725710a90b319ce4047e35c2a1727e6bc81b3ea7efc657f"
+                + "f9439f9ee30d335c20dfa6ef7a1f50bd478d99f162432494441448a27b7b80cf89d1a347"
+                + "917526e5e0fa319f3093043ff2d27cd38ffd0fcd321cd6687615698573af3ac75a7e66df"
+                + "f93ee7713d3dbb195dfe5e038dcaf8bf27709afb0692d814e768cb08c92ec4fb386518ec"
+                + "c082fba4773bd58289e3549396984340f1224743826f46b2953d1c60102df9d8a110a450"
+                + "5c06b1f352301bc9eba14ba6a74ff0cb145a653915a413b73ae719c7e0f070e1234ceb07"
+                + "d00417440c38cd90d3a10107f88ac0a3af5294d45568f8795cb98039865347fa2689d335"
+                + "2c4a4e7162eead6e2b00a86c23d08308a0f3ebed0f9d1a40264a05b08ea15883c5335e38"
+                + "eb363f8dfa949bbf010d38cc1f1aeda6c3433ea5fe5b7f46b646eca7505813428b562d11"
+                + "0a770a0a4427357011484f277ec9efa1cfe23ce439704b72f7963ffd290422670c6e7dc8"
+                + "0d4625badf29e1f85405b96847eae6efab02fe156ae06f69145c5b6ba49ddcee66ad9998"
+                + "ed271243bb62686f1637c5fb90ad2b20346b809e811ab221142ef81b262365865503d0d6"
+                + "84188c998dda9532eac6d64a081f9d1eb007a7e3c005d998ee24e794dac515233471b796"
+                + "f0de1b92b54d2d36f0d9e569f518c6c06814f538b2e46e3b1104faea2443797fca067e74"
+                + "dd54a82f2652556114262e58f5142100810431b120a2e12272b8a112962c5b0cc23bd6bc"
+                + "213aaa12cf9983a2b58fb30250dd92e144ebdec0e6ecc4cf9e04aa9b7ec937bc84113c8c"
+                + "2dcdbaeb837b9fc22e7f5486203e8868cfc6c95b16f9a62a87e6319ff4a0580592566e1f"
+                + "e62901c822ca5d331a7e99c8febf9f12c6e99cdbea28fd663482135d13a22b8d76f22053"
+                + "bf5c9b34f6642772f7f145cd940506dbf30605e001824f473f38c4a493d1d69f06fdc81f"
+                + "12a219d7b3e2697b2de85fe3adc9f822139ed427e40b3c29f94a50b02c9cd2ea9b447801"
+                + "4f1247d851f1f41d148c3fa823a36115fd5120e1c47a285f4c2c8820ad04453a619842a2"
+                + "791dfeb8b58d158e155a6e6b8866ef0284e277692a95687383f5007cacf8f22e178aef07"
+                + "63e2ee7f1d7dd7e94e27b15b8141ee3de32dc9dbde52a453997e426d22f85667bb96c530"
+                + "ccf047e2efcc503d78b27d45caf6f8700858ba3c8141136001e3de867dd72f0c0a3bc413"
+                + "f54628f0486fe550773edce53b9b4c0ae94b9cfb256de489b40dbe73ceff8f696b43ace2"
+                + "1636e40a189cfc51436bd809dea3060f9df9fbed205b2d06aa3d6cba0192a6825a50f089"
+                + "583de59f7deb8e93432914f1c07115cba8c137f98684d568bc0bb436fe2e67b21d684b59"
+                + "738efbe8f112d7c522cab01d5351fe2f9af1f1d88917ed97ca4772a5ff8dc7182fb8cbb2"
+                + "e4fe106baa59bb16c2712cef7b641f3d2f49542ebde1ad02787a0c597de245182f4f1222"
+                + "5eeae4579eede1451c06692e6c84c9662fa562bd2450c5b27d928e95535203f80715be89"
+                + "adf11ff516ff6dda65d692beb555ccef92a5cc4ed87ed878a6eb3f0dd6268111bc50586c"
+                + "3fe0d2aefe7e2350366984ddac20446b5d8d9347689d8aa1458e3c9ee89cf73cb635e843"
+                + "57b371064fe2c4532effc6dc3c298007dccd60e431b9670c10124290c714e7dd079da828"
+                + "01b8db983df3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "347a023354bdaa851c37d97e0eac48aee083649ff74a675a622ba3446bb0dfb1144046f4"
+                + "6682e7c156846289f1d823513c585800861380ac890aa8e4a39233f46893b696c00cabfc"
+                + "7dbeb88e58afedf2e328277a30adf716c0640b19ba4fe3b3846d8b2c060ba8692feec9c1"
+                + "f9535f95eb17161667435d52918607927290a8b947c439dccd990b897b49bf3ca5725f91"
+                + "fd75e164914ef60e4962acf0360b897e1af9b9258aebb3c00ec447a070e39fffea7b6f63"
+                + "ba86532621a7f0b9680e5293e92f82ee3e4fd312c91513fc114e79e06607fe29e88c6b8d"
+                + "3a30f28697c32fc2cf8c1f926de58d78618bd17ad06a7479a17c59c4f7c38342fce17180"
+                + "a3bf60a738a1c343857d113cfc6e5818b8ed5bb77e5b2c87e0471d07aecac1f254f73389"
+                + "515e0358149b4a379ace7e93a349a03b3faabf52a5589a64215a168059e1ec7f87f88aee"
+                + "71ad873c85c9cb0934922a1ff7cc5c64d2fad5bbb390022fc7a79f3033ca765d266f050e"
+                + "8ec86248d2d59f4873e0c803d5bdd45a4a0a3ea3bc72a9277b951eca9cd440b97e3216c3"
+                + "046a22739770158a0e09d12a77037d86d4034c26dcb4162522f0e3d0dba3f2e4040c2307"
+                + "4bb333fc8319a509bfb98c6d92be011c1e78fa57219de8a0b3e813f682e97f12020d89b7"
+                + "972f80149eec95a6c0b830a9cc05a7d5289a891b06257c7f2a3b20fdac93263cbe636831"
+                + "94d49d38e131241fc25a20ecab58a89f9d7896341a7f536140b3ec5caf7f276be6e0a899"
+                + "0e322fcb5fd5b3fc5ab547358f04e0a42bceef264feb6dce6e837494502f00457840dc46"
+                + "1b52aeb7ccc0415584f7319a224f623f23630ba04d5c52bdcd882acfc3c0a2de37b86823"
+                + "b77a569f2330c1dc07259adff7d5ca8a1926861ca6f46a68877bb0ea9bebb680b1cc3383"
+                + "92deb6423ff01bff5fc6f4cd311dc2402ffdc0a0596eadda53b18fc8d58bcc1b7e6a3921"
+                + "820b332e5c89800099d02bd15019e8db4e93c2e0bd2e10729f26d9fb419eab99745ad596"
+                + "286714568480c94b78badf2de4bf773ffd10a037e0c6305dc55e1593d0c0e1a25f82012c"
+                + "491af7a14c5ef983070fadc4e2be4a94da2112d27662cf2b88db58ed107fbc54d93ca8cb"
+                + "047503b9abe4d62f5feacf960514491a1e3636d96e34e1c6daedc9c6961f4a4d749fe0e1"
+                + "0d824d94f143a0fe9d784584040301beaff72945a1398c5c8c76cede10080bd45ee40000"
+                + "bfb45f0258ecd46560c74344d11854da6af601512fa0012b01424345a67153fd7962e93f"
+                + "39216d247f1cb7a834cbf0b390fee3f8927d3f68257d0f4c651a01d184f532f423081e9d"
+                + "86f108399039e7826368d3c4a02c0ee2aa7d94dce4c5e46a49aea464aff0b1b472aed28a"
+                + "676af227015843433b0bb41e7e9f6c470d347b22bf7c684e4743b200a148a26e9a4f678d"
+                + "a3d197fd3e173dbb95be3bbc4e90c62cbd308a7ad745d92132999fdec824b670f4c0a641"
+                + "a923abe4433867cabda18a26c376cf4c69d680558484f14b273683421685454d4d7a8afc"
+                + "11d4dcc16af943eb14bf432ad94085c8040527bc859989fbf526905ab52d25e643911c81"
+                + "eb049008965d85310994e2adfabf83379e6d9c9854eaaf4219108f7b97e13447ad789c29"
+                + "e81b3ad403c14286a539d41b54f7494382d566dcd32b0772e73a0002cb546a599f9dc1ef"
+                + "1d7b653ec45d927cff09a5221e60823e282485ead7aa8143e290e72a474b21eeb9638fff"
+                + "c2320729fa3f6543a5fe472e85f6aa93e3f22b5edcc3d8ebd5ebb977733466f27e23da82"
+                + "e9f8594b90fd55fb7ae86db06341b21403c5abf8e30cce009a3f76b613f00669addf77a5"
+                + "31775116a3ba5d7392c9cab6a37d39d9c26363a987a94fffe4f23cd2d37f78535f65f504"
+                + "b7979852cba2857241cfbc433da9d623a73292acc75f627a3f7bc04699e3c20aa430354f"
+                + "7292804a6092672b2bc1d6cc7c8ceca571f7b86545741e8c8f9e4b0bb1915582385879db"
+                + "65566f2e6efd470cf01670f32a2efdc662f0935360bd7f70ed01d6c53f62212d1ad912af"
+                + "40ee2924c7b3390cb3a673208efdfefc45063866e59e6f05159e66f07c405edeb3b6abad"
+                + "09c5b4c338de9692965dcdbdb0ad038bd3896ef936aa4ebeca0b6f8d258d18b0948ce6a4"
+                + "a6b40cbeb1048d9bce6ed8b5a1fb64fdd908ead8a70f5d96c61e62dd21d07a067a86fcbe"
+                + "7825d79ef9587502bcaacccc93bc7dded6bfab474da4a2bbd4730a7820772462076af552"
+                + "4a7ee6af75924fe994c877866814a447d53cb96ad1c78815cbeddc09a48792374c0166e7"
+                + "93ccffdfba494db96c94ec2080ddd88619747f0044c15e40883fda988ddedcad089f72af"
+                + "47f1eafa6b8b08e0d9e21e621528598e2ea1a031c463372bfcb537870a061436333ea392"
+                + "6687a33d2e1173ab48a840f026094614785274f406e249a27a76a5ffcfc950d2032fe974"
+                + "a426bca632971c059d02f9baf0360dd6dc6dec76d0d296dab14d4623ff123af284e32bbc"
+                + "3c7ed30d9c94ff5edf1176868171afb341e22f6b774b9db6897c3e6b5b1eb31af465c438"
+                + "02460a39c0567dda3b83b48f27aba336025e07b276a49463312309c8f3a5690c1f1e5095"
+                + "93dc0ded8c96037bc59e8c7125d88fa206b0ccd279a83cb9169488bee7518333b529b31b"
+                + "d726e0a105b55ab1372fb550bce50daa46be89e555f29799103e2ea92814c80a6ef7faee"
+                + "a0090ed054f5665119ce7f6e5d8830e3f8efce6aa5ff9079d9c9dae1a29bbeafae135a4d"
+                + "bc882f45044c4bea402e91ffd37600d78aab7f69cc8314b5819584797cda67ca097f1e5c"
+                + "d47ec508ffd15cf3019f62ee4ec5027c856e779ee549c0f7f21d5c2085d3c5c2894d3f35"
+                + "bac944918531ed678f7dae724cdbf9720cd7191e3330efd56e45b767fc303ce2729329dd"
+                + "ad4adfb094ace50bee7d7102739eac1f43cd79a9dd76599e135faa76ac7cc24530b22303"
+                + "98a77c140d0a4b820ef3a4cc6f04335c292051cbb19fdcaea3e8f0e7c9789ddf4779e0c6"
+                + "566a3b6028e7bdc0349a622c7ffad3fcac17a9c69cd9ef798e90a838ed67c1d110ce040b"
+                + "9633e97240fc33a95f056a8211574581eec69f33034523767e9cace448c213d265fadaa5"
+                + "ba1216bbd57225a3ca6cb3c48bfa5c28194f04121855f7ad7f60e901cf9867f858b716c5"
+                + "ee75956f646dfd2bd49f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8f"
+                + "a7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "350e70a98dedc81b5021d82b3eeee8819b65a5d26d495b3587604e16fa81c191aa8c2a6d"
+                + "6616a4f7e2aa79404b54e90c5dcc4e9f36583b98849176c37c81837c6c66cad549093203"
+                + "3afdbe6f938875445809dc21af838858319f44d7adfa6699002f4dff8eedccd120a86596"
+                + "c6e22e137009195be5727806b263a59e99379c40d36dc302ec7cbf6629f6387b7bbd249e"
+                + "89de86a1347d64c58468197c808c446cb790ee82dd38ca35b0dcf38f2c523d36ff79d2db"
+                + "3b3696bab3dbcd9df2fd0cb2dda70e1edfdabfcb7656c981f56799ebb799d337d05d5598"
+                + "45d6d39cd3f82d75af9029ec9331cc9393bc214b6209829a340f1127506556f6b533b5bc"
+                + "4cd124281567080393ef624e25ad69ab0f49ea2508563b413c0fa15c4b3264d2a39e4852"
+                + "51d387a5f92f43b4b992a99c625cab324b2e2c84de5982f61eec40186209341cc5587266"
+                + "8be2c533f94caf3bb19b186657aeeeab0ac9fa1af9d65cee92dedda88a48fbaa707dd30e"
+                + "630c1d79d5dd56f692b5f8ceabb1f09316f280c52c7f58d02012b5f9b18408172269c146"
+                + "6b966460b8ed16e6cee281cb26f9fbe499e4245e13d21a226f7be2a19a4b67513eaf212a"
+                + "2b587b64ab16ff8380645eb82d4b23f5a5cab01e126b2fdbdc55c4b0e2d1dcfbb3e3f253"
+                + "137a067a4d579921424401d7bd0da45c1747ab9f6895ea8b5521d9059aa9c2b9f9990afa"
+                + "f0179e736490662bfd10343976b0fed72b0bd9731d147aa4a0a8a41cdceed9d608636f06"
+                + "a3bbe3d4b7a654855715b0f053233a3f82ff3e2076faa5e1b747c59628af3f6df02ef50d"
+                + "dcc5d9271a7af4ac7133b0b6f607aeb0435ec62f0ae7855e248f47fa94a66ac0232950c8"
+                + "776df2ca149bea61fa98a5cf2e4c7cca0a6087b991c0cca59a16067653e4f6c9a3e2394e"
+                + "bdfc59143c83c596baf7823c1c8b5af03da69922ad11e6e4b63f3222b3befea14c9a5442"
+                + "270a74661a1a946c877abe988b59f92a27dbf8fbe1dbcf89f82da90c40531926eb626ad2"
+                + "9752fba00f93749719bdaabff32cc9d577d58b5ba874bf44948908da85c308d91fe85aaa"
+                + "f2f6e9776858c6c29a6febb9117efd187890af5b36e5f545a3ed0e904240701b1a5e2534"
+                + "4936abad9fd87e644ac10aed2ce607980e85ab3caa1d7239432ea50f29b1644a22b261c1"
+                + "de47922264595de355eeeb028e2ed1b650ac02fcc4c3c1fbff39706498dd5f1542b6a3ad"
+                + "a02aa063221688846f0bcb9ebc538a2d576cc31e3d172ee53490327727e095866ef87fcd"
+                + "a773fb0949a8ca78d38bbed89278c0365f13446453798e8633b31895c0afdde5536009bf"
+                + "eb60e785301fbf8fe2b1e2b8ce38c9c8ef2760860f26beaf7a5c102fb94a5b3774dd00a4"
+                + "e7d827433fee3dea117ac9d4705ce757cc20992c6ddf503169230b0de8d53e978e392aad"
+                + "37b73a11ef9fafff808579386a6035dfcfe5372de95234e4358be3a5aba209eeaa91ab84"
+                + "8ec0ebea4d44c4909d234989ea41ce83d69b67d5d2436450632c5d615edb1bfcbaa98761"
+                + "5d851a90d898bb5d200f90bcaac876d9e7b41935f502a35b74816858626e092b0c304595"
+                + "e84a73495d2e772d33b415e224e4b48fddd51aba6454b7c955ac6bee3c8ee091fa6a3b7b"
+                + "75edeee9ddb4496d2ad7e69bbe0c24a03310d2788983528d552cb511e4ae881fcc2bfd78"
+                + "b1e105776e9e9a23d86f16cd943a395a10a279c0b07499017e2e0b8b6bd04d4a7b41b51b"
+                + "6036d608e552710401cf90dc347a6cf758fa3f7ca9b23b92c1387e80613cff3b713f8ac4"
+                + "1d055da4e52c6d326b96e502fc8b1d80e9fb7d42f7775cd2e2ff88c25c1764b061b45bc7"
+                + "f090567c70b87287e9ab7709b71d29be9fd8ecc7988bec715f9ea6157689fbc90c76e23b"
+                + "c9f556acd19dcb6b5d4a3ce208565881e65813cf66e96ad5b2423594bb6956d15c30b8f6"
+                + "b467930a95df238690c973aa95661d8c3cc9a231decf1c00ff0751cc0e1ca4ea99203062"
+                + "927de304c870a8fa40198c58a12661a9ca6526b41b6640c5e4833b65d55a9f4d959cbe0a"
+                + "5d0191fa71ac803b13b8c58208037ce70058acffad8f5f0161ef335282461bdfbe15be7f"
+                + "6b7fe3747eb78eb401087ba30956d8e8d6b9ad6695406a5b12706878b41cbf7275eef0ac"
+                + "512af69c9ee03f6b25e56563e8448e0ab42968d6454ba298baa3f2e3695fcf7c2035c64f"
+                + "4f2869f4d5953aa61076394dc366d03ee5b0b12974254a3ef29553c50bc6d770387963d8"
+                + "ce6b4510e6572371c74e79c3ceca57d99d623cb85d05d9e0bbe2361fb69d987c060064c6"
+                + "b0dd58e7fc3cce979ad82c4c0c80fbc38d868be5044e69cedf2f6fb2cbf6576b52913f40"
+                + "a1118801818f215106e67bc398861a0c9182dabb4dc468a032ac2407a113dbe31c73af51"
+                + "c99e63620927f1a0edf5adb8570f14a1634bb33327d968c7a2cb54d76fc59da7981ea145"
+                + "bc5860be9c7d8c380cc540c739ba0c507ae511b51d86644b5b09ee52c1f50162c77fd319"
+                + "709d06ae01c3a5785e47f59f730ab363ba3666b009b5142944ebcd78b8d1fb0a71ad853d"
+                + "b97099b9250562464190683d42d83090df8e9d35851aca7a3d71df9b8de19009dd6f2b85"
+                + "9665675922b9b769d8f22840f4084b7c888e280a59745d20bd0dd9fdd69f4ece06dedb8b"
+                + "ef37bc0b8ba358c39eddae4d9946999d6989d30915af788cc937c1d067a6edc240b37221"
+                + "ef78e63fcbec1858c437b99424a53df8aa077e11663315d83f786d611f87b742e359bcfe"
+                + "fb5563987cdaf10604b5fb0ba0d3d94b75656c81d526fef110dc8417f661d075af789829"
+                + "6afb55639fa3dd4e9d19638953c67ea7ff42df9f85b048cdc41406f7c1dcd591326870b4"
+                + "e5728c8e1cadfe4c0ece72dd79bd7db5ccfdba4d74e8f70b0917ebd2840ee09a82e8e456"
+                + "9ed3e341c0c5e5ee9465552ba122b25e3a61cac494afa6c28f4af2687b51600523e572aa"
+                + "86c2c0fbf1fc51a3729fdd01d0b3732e8ed37778ac9421db6e8cbb062e6caef2e6d5de02"
+                + "6de22fde2427bf458ea6322d1a979bdaed5b9b61b3a44af020cb494d4e2badb68e8a820d"
+                + "1225df50e4124de5ddea4b335a46b92baa44e4b2115f99086ad91f6b504a9ac356a008da"
+                + "67333bbcea71095dff905fb442fa5c28194f04121855f7ad7f60e901cf9867f858b716c5"
+                + "ee75956f646dfd2bd49f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8f"
+                + "a7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "36ed2bc0acf613c41413dd1e783ab698f8edfcc3ec9fff7c40fd4ef85c7901dd4b4023ba"
+                + "8c8a24d4d7c9a430726dc6fc3fc6d5b3833ad49f7674d281aee5c88eb1842f95eb4e9735"
+                + "2ef14f491bb1b2d42250cfb97a9e29e5513528ba2419422b890d9eaea67e559c237a03a1"
+                + "33a3f3c4f81bf56865f9db1dec717332be0a779422de955e939ac750dd2e2a355a36d56a"
+                + "2525693938965b7104bebddc1ec889f392f3f4262bdaccf9db79e41e4993eb9f4d5551dc"
+                + "c4fca2d9cf4cc5ea08af7c38506ea5da4b4edee033ef10c9088384f9bf8c3982aa083e3e"
+                + "94b8479ff139b52bae838113957c63bb125666a5df6b32b53021e225e0a32f281812f79b"
+                + "1595a4733d946cba1cb3eda1ef756a341699d23d9e1dbd8c577a07e6b5838ea0c2511b38"
+                + "2933f0e6fbe969514823f4df5dde093768971e1438367148731b278d119d944853e1eea7"
+                + "d1d487c3b11fc4be5bdc612da29ecb161eb0bbe05befa9b2c4906bfe56eb5049e1589ed1"
+                + "946fc87533a1f5ea92c97dd91db479eba93a423d1aa98fe7ee7c2338eb0ffd5b09144e64"
+                + "d791ce3b65e71f5a26b87789df79a510931997b28f92ba335b40bb1b23c5c63400bbdedb"
+                + "841d6571a90e4ebe878499d8e47ea956f527d62ac823a51ddca502ace8847ec79ca7f21f"
+                + "95ef68936f4141dd4fe204a4b284d7c8db0cf49193a549b2258d11ca0be5624cc6a7dbc8"
+                + "71cffdd98673e72672f7ab6d23158013eb4bbd390f24d2b213a03f8baceefc65fcefb408"
+                + "c862824c8971223847552681fea3877b350fd275adf1c5394e9089256699542a543ff794"
+                + "1045faa11029ff0bc731f0c43d7348786068943de5ebade0484b82f71ef61428af4cd43e"
+                + "cbb2d70abd9be7134149c95e51fc7443d553a49019b8a078b56134efe45e02bc8fa492fd"
+                + "a8527e6b80f9a080ebee65ec78a9fa114a542c843de794719189aaa7320a7e6ac1e2e63e"
+                + "b12fde40193c76bbf7362f8246679ec03e97b538de7aa7859713d002075585d25d81bc22"
+                + "6998d69d87909a58128065ca98be14714c47e8a80e39d261771f0065250d24294339d01b"
+                + "977cdcf33b72e78286511f8d1c2ba7ae91ae6debf73c7aa8d4710ff0c32f84ebb5cf08f7"
+                + "688eb7f59b823a8fd82e038bc2f82d0cf4527ee6df7a25779bb3ab7cd77c46d1b70a2862"
+                + "a865b714717ec5ef1d9cc03c9786abd6f3162ce710c2b01d9cc38c5b0c5c36d6d1bbe8a2"
+                + "12e2a1e7bc04925c8d6ed3deb0c76914f75690f88a0140fcb05ef4d8708bdc2758719faa"
+                + "efd3633f470c4557a165c6ac5ce087dca8eb4af0d30068dbf45df4481be08bd7be125129"
+                + "7cdff338aa036762f318cd60498cc6eed61afeb6e7f74c14064ae6706452762b9d06dd81"
+                + "58a3ef4c7a5bbe6ccfce722482a578642c8e61f582f4b60e01919c334644507338a6b98c"
+                + "f14df6de512a4a66e3c67f38f63499e1b4e156f5c071a44da4df91f66b096996f4245d76"
+                + "d4391b4f50ac343d67004fec4e2798c2010a6a4159e259efaea66f5b37b00dd3ab1e5e43"
+                + "c476e439c1d905ea841a902a2dc4e02a524e7378a3132d03b883c899f07c7df0a9b6a6e0"
+                + "701a9586453653eed97a4b15ab983092d2dd49392d7ba674bdcb8a5a34d9f08dcec821d3"
+                + "2b8998ce20a3b1e66ffe3a7baa55ddf7b32ec9c5f14ef8de72eeac9b80158b5becfa0b31"
+                + "f9865a93b8318cf0da327801fa46d658f7a0a8c22a2ab121dc2a3cc5f6368def8b0f93c3"
+                + "b4c1c0e0a14e6c9b53a0e542b7ff54e85b00cf0f4ff68f5508be62558503a3c9ca679274"
+                + "03dde0e1a1a842d31a731d4d1cad8e7106f3e32bd9e42051f2cd53efb4a1638bedfa7924"
+                + "51db7980f9c199237971a153916ea40e118edf5a3d7ee59bed0623c5aefa87d729948354"
+                + "910447f55c393c9343af03fb918431daa15de1343c0c813dd9c6159ef2744516a05f72b7"
+                + "7ad31cca3d9c60a4f19b2c243406f6ff69bf41fa0ada05b07771e30f36507751ac412520"
+                + "3feb3ec4ed8f7d961afbdab5a6d3217ab74cb401551b28621c8c975556c24d3d6deaf4e3"
+                + "00bbea75dbc7f048fcc667c9daf6f74749e5052fc8d026e1f8e1b62f1aebfc58c1161b45"
+                + "ba0cb7f69173219164d1bcfd1754a10d8d1ef42bac2b61766f12067ba1de99c51422f6c1"
+                + "4c6676285c6629232c4a694398bd935767568ecf5f94d60bab325a41b794d4502c48e25b"
+                + "62f893a68d94c4a03130e30a13214e6eaf69923203a83b6ceae544b70b61c3938c64bf52"
+                + "9e60cd1243c6c126ad3ed3e934f2cc762e86cb0b52ea22f2e9ee4b0aee233a4caee4c916"
+                + "971c6ab0c8216e7edc410414a56b9092773379fb008e4aa66f4c2562978576858be544dc"
+                + "d4e7d25d9d13dac7cfa6a4d79d13e312234fde7521aca571f4862477e00a28b510d4fc4f"
+                + "7301ca56fec77277633a81e682c205653ce650a0e2314e6ad561a78aed98c5b4250a147f"
+                + "786d0dd8715397149b2f9220fca8dba19d77b1b0deb7434974d9152dcea158108cd39f7c"
+                + "500d5d9176ba414c7bd6b055140eebe229962f56d3654b167dc2ea8e26821678f76c9324"
+                + "e1c0ca538e09a80f2ee6985b6da9177ecf677a1e4c7abba1eb3e6193385fa7119ac584aa"
+                + "1ff077c4f3f8324dab3bbfdb5e475de20ad66a96aa296c0bc302474827cf44e8163ce83f"
+                + "1c71a0f54340e6815cc0b10432f84ccc0a79571f16abe50ddfcfbcf9651cad5f09ce62c4"
+                + "b1c73d94ac57aae66f41abac602b496b4d9b35e66efbc69938a8a05474a7d5f32aa872b1"
+                + "130a914d8b4fec85270c6d3be5ecec8144b979599f58bfa221af6042f791cc49f0fd4df3"
+                + "936c51362e1cc977e271f51ff8958878f27696a651cb1b5308a4d12b2e99d7a45b2c8a1b"
+                + "420302078c962e2218abfdc5ab2678567a88f0b36e02a47275911dde75693bbcb62e44c0"
+                + "5543866d1d10c2dba7f4bc112e2ec3baab01db1c4ad549ab4af11c71a14853b744f91872"
+                + "09905aff3acf686b02212bf48b277f6f5c6fb979c3fa1662814162f51c663ffdf7dffb1f"
+                + "ba987ddf05bf8c21591fa9e6f3811b1bbc19bb29a6ce58d322fcb8269f532067ce4b16ce"
+                + "a3c8f7aeaa56171566a5c99d9bb1dc7aca36f181bbb4b03fe7204ecec98ba4f29dc58b3a"
+                + "ba213fc63e28e96a2a6a8cfce407ed417873ae4e136f2da4d973f466e5af7caaf930bb0c"
+                + "88981ff76a037043789f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8f"
+                + "a7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "37a48a8a9954787f1a81b61f1e9c5bcc59923990c9c84c573552923aaa625e2e1a384e3c"
+                + "516072471a1161241f32de43e46f4eefcf47fb1171a21330e0fe6a2d57b0caace60c2b1a"
+                + "4c2dc4ff194fbddeb08ec103576195ab8de5c9ede57c34ae5ca05f5635d5913e1cd75936"
+                + "57635773ff82c228fcdc69dc7d13014d8b23a013fcdfae8c2d24e8a03529fade94344611"
+                + "d094121d9d75e0d5ffb5c044bffab36e1260401e652d0651a6ef0079c68e20229252eb59"
+                + "1859c7c14b281eec17a53ac706c3413308d4bd514882010b823a40835bd21ac9adc3b881"
+                + "fd5014215bd58487824b2473fc52b67eda1f875709c88b54163d0e2cbe6bcb8b80f2c923"
+                + "4e45ac17c614c21d319aad3d290c920569b677aeade1146b1b721a4046beee62a2277a80"
+                + "8c3c4f41dac0b01d6d4736ad6f560effd07894500b9bd4ad115c57c91e63c8e637785a32"
+                + "1578f15e8809c6b3d9bba2e1982d63e8e1f27969338e65ad8b63d57c34f10dc0c5742bf3"
+                + "9c90aaba2c0c47cd7e4f53b826bb4f636647a43e88c9539c641ff544caca870aec61c154"
+                + "29c7f124457658194448e6327c6e02aedc08ce7e9903150f943b1fa9474fdaa5079f327f"
+                + "1a720599be73f7abfafba15964793ef54fe81eab3e203de5ecff682e443767f896de1d7e"
+                + "0a04b1e973bdd4a6fe640a28f142081b6909630e1cde17c34229424e6328944459ce70ca"
+                + "1c17a886acbf4acc0be23e3480bf8df821fe41a5d715b9400f60427add5135672c85ed59"
+                + "94ae1ae3f1a4af8b193c48e40ffa37827ffe87cb7932f385164d5e8a21cadac4f67cd016"
+                + "df022db9483d9f865564f4428133794ad4e5bae96f9bdbe55eae220107f574ec9f12a72a"
+                + "f278d15cd0acc60474e717f83ae5d012b08aeae4c3021e0a9f855dbe9496a7c728900129"
+                + "6d726618dc82b3c891f94b33abcb282e76c4d1652868d0f6ce0d0fc6af7e7f265d5f0570"
+                + "16256b504cebfb0c675f9a25df344bd9a15504793aa9c8100f3e43667cb5830f1afef829"
+                + "39e014b6d0900129910c94d9d5ac1ca1058d2cdb4b027d5de5983dfa5dd9e623b5bef745"
+                + "4a1efdde0708b1b2447b909413e774f64efd0da8cf5a8e1706483c62ddcdcbe2909a6f59"
+                + "41336b851a8e1eb23d0cd81291cd28f2073df840bb77a07c0e1f4e702597c1aca0366da3"
+                + "db5664aaaea319c70792312c18f58f4d71f78da25ff72b9bdfc7bfcf500e09b3c947175f"
+                + "2f2db565b7ab32df569ad50a4a9ca84763fb9c1f3a0e994a4f81470ab01b32061ec6984c"
+                + "9dc4dc6859e0636318dc597a3b062153cd50116bfccf2c0d98af3c6b63a87b6502a0a71a"
+                + "6f30720e7b3a63cbbaabb658105088c9c72bacb69744ebe63af8ca3529ba7a17147279a9"
+                + "5220171aa671c60b1f758fc9c06f513637d5a07d7d1ac7c024309d1a07b5f7b64eb93bac"
+                + "675fdb93087c3aa84c4ff5f7552bc53faebddffa8cbfd5b953364015afc6d3caed38b5f0"
+                + "17045ddd2eb03f575a39beab4bf9ed77ac05e5d760b5d3d211fc11c3536c5b0ef60d4b9e"
+                + "55d9530d099e8a8ae615c33ba0dd933bd71426276d05f06373b8f413ac3750cb4aec5bd2"
+                + "b4fff3fbd20fa2c063cc3ad7fbf2aa4fab7560aa34c887cbdd9c6cae42a34ad8f0791ac7"
+                + "95194266ae927656ec62bb8a55baa9b5716c207290cd3df2d8d09a8e21a1cf3e0f8f0d48"
+                + "209df96f801d87e05cb5113a4613816398d02433924b81717b593ef3a9ae1e8bd58579cb"
+                + "62de7cde3e788828d03b4afc80360c99c04879c6505b02e5d7c5bf924b17a473062659d0"
+                + "3cf3c6e8018159fbbd0059cda6922257d9f033aa2bf98998366ddf2add6dbcfdc147bef8"
+                + "1af2d87f09ca613e55c3d171791aaeedc6da951bf7c9e3a0872db533bfe78ae2d5ef43cd"
+                + "b50d1c1855c907b43b7410d82f6b5d99648a9b6bede46d847816c0da41ea630b084142f5"
+                + "09bd407b13373505244d5ccd6162dcda183ee0a2792d8f81eabf38dd97aa96a9b1d65f40"
+                + "be5488a0acf3699142cf6375f33dc60986f4b5cec2cffa853bbc1080384dc829d418cde1"
+                + "ec40942f6916b4418c882208b2fb158c28a8a806e0767c134976a74ade423a914bbf6837"
+                + "5d15ec3ac9e90380e59254952ae443186293a14eac84a0e06183b2892818ee51348feb2b"
+                + "c1f87886b3dc8b06b93f1db27399b943ce05aacd04bd6f36377a9be3f035d82e02da8fe4"
+                + "cd38e80b9dcb1eb4b0aac9c7b54d4818f729b239b865a1be96e6c421fed296ca8f3e257a"
+                + "554fed520ef3cd1e019a167e1511a281306b995c010e57671725dcce14b70a0222910098"
+                + "483bf2a8ab87fed47cfb4a20749d66f5d90b1afc68d8624a7fdcbba594c7839d019386db"
+                + "a72863e305582cbca848ebb045a87a387f535aed00fa8f93a1847ba39ec4a616efac860b"
+                + "43e8c7ff77cc17d85eda912a4be999e4ce86a6bf84512edf8ce6581b155994b8953fabc4"
+                + "57af3060c749d5ceb26b6ae21dfed753dd7f25430a6042112104c67641d0731bf43501a0"
+                + "911b7428a5ca375eac981642c2b5cb22d5dfeccaa39e1c9b8fa811bdf2c8f760ccfc4ae5"
+                + "7e99cb9e92fa9d13452fff8b12316b66eb66154afe46af9c8efe7393cd13abd01f7e9741"
+                + "80f235f4fd1eb12c4b7c2832579b4cab47e19cb21c895acead8551e1c2faca290d4522f9"
+                + "f0ae42197cbb3b5b46c5d970356a20ff452a080a19d1a9db6c8a9fe8a9b83c56786a49c8"
+                + "9663c48bf1e02540d96d8e24f16ab887f3f7855d35adc4ed9fb18d7a78dd13d046f7c736"
+                + "929dcbbbe07c725f500d93a8edbc3be8d261f27b6f6fde9ae6484d8b008b7abd42e29e2b"
+                + "9031b27bead901418ced3a03aaf3caf792cf39f956f247918006ef874b67f2d33a180bfa"
+                + "5d4e33b13c8d0c65862683607d93c3dc476acfce20952a259b395eed13bd542f48936992"
+                + "df61de8e1437fa7739fd5423b43f8ed900e09b5cce1b6edde567cd046983fe80be36a5cc"
+                + "0f0ecc4e2ba43eef2bb4c5a4a19822fe3ee8c487f410abc8367403f5536be677a79c2552"
+                + "25aa930207c43b1e7419651e3c600c77fcf6728a429a63de4f6d006ffaab687452d65801"
+                + "ed53cd68aee6cc7c4373a06bcd4579035276c43cd3869a6691aac4c019e4bb433e518bee"
+                + "d443bcbbf9bdd60a1245df257e07ed417873ae4e136f2da4d973f466e5af7caaf930bb0c"
+                + "88981ff76a037043789f76a64615e28b5f0b67c5677c713128f5ab683a59362e3ba37b8f"
+                + "a7920d8146f3f15497dbf3bf7279d360d2fff58170aaabc0e099206cc02c8ec699807d62"
+                + "4917501961868a52c98378648cebdf92f51d21dd8a1ec89925ef0e571f9e464e4994f1d0"
+                + "284562e005e2a53f2a64c78538e3d82a7a1c9f228c5b08b2e0fb2ad9707d0f1ac41cf90d"
+                + "aa704c96906eafce30002407d5f37a5c34f96e7dd9f92b1477d29e8df7030a62224de68a"
+                + "9320db6653722d6fdbdcc0fbfbda053f9c8f3b2c41765f2916c598035e4c1fd26d1c5063"
+                + "b7d80701e15f1b460987c55e4da30cb04f96ba6b5abb934e884cd489c4994b94de237ac9"
+                + "601c415159e5b4bf1c75b11c47de604f7af7b467c1849459f1ca2f22db77e2975ea99583"
+                + "3d2e395267727d0f8f77ddeefaf3df3ef12f0fdffc46f3d53017b8b0d6dc676d95a5d932"
+                + "7d35e6a7cad33cef87a620b27d72c92bffa26a0d7e8babad5d84a305455f97339f756494"
+                + "ac27670f37a703280d3761b7ea0ae37567bc8894cb1e5d3f3849652a3fc4a5c413f5d0d4"
+                + "2be847c87dd8b7bfbac6d77de51f00bfc81ddcfd418192bdee13843e03a699c52760191e"
+                + "0916523b13682807127e5142198686cb2dd8d0a03559383305acb12b899851d32d5a6af4"
+                + "d87368e8f7da281221cecf66ec2937e34df4f8ebacadc0717fe69b015ce4fa6ba053f912"
+                + "f1491daae582333c0c2c4d18d7f19d926d487380bc19db28d7d0899511858ebd00cc637b"
+                + "f56ded344517c9fe8e14723adcaab57d90d2958901edd47e8b4645b232c9a2c9dac8d286"
+                + "91b7be0179090354d13cff2a01dc9690656f85d0618558487b7c8fa738dbd17fd99bab9d"
+                + "8b21748e6b9b216f7d056788336bedf64449209445d5d103488b847d5ffc06ecf9548824"
+                + "e3c0cf5a84d5646dd55ca3e190cfbd7940e9b3201ce8d7911e14c1de47acdbfd10f02f2b"
+                + "1efd860e08e8cdb658b8e6c1c5a40349b720a6b267aa719df4cc6c7596e6897208881988"
+                + "f189c7806095275e8735bb2f62d19ebca995783d0eb936483b0e68930c31071a43f67900"
+                + "67f119336739ed17a2bfe8fa41607833d6194b5cc1900bf8d19601ba12be8473f836dc58"
+                + "83ff06e885f4eaeaaf05d59970713d3ffb8a972b29391d3bc31808bbced4c5af495f8286"
+                + "156a0c03aea18db2a60069bfc4d14df1238390e4dd816a6edf28ea1ca71cc2ae5d0a26fb"
+                + "b86a3271faf6cc8000765ea310ae979fbe377a1ff044f724cd6a389aa7c24e2f0aabc4b8"
+                + "d408ebfd92b2a40f16125fc4b7a5ff17615c0a50a3c83aa54de3ce8526e215a6de45a16e"
+                + "9d3d358b039325ad378c0abc2448a3bb8089e842fa848defa7ce4f2fab5e966b38830b9e"
+                + "bea02ab0a8225621e9c8ee8908e620ad020c058d7ab25003899c225d77aafd3c492622b9"
+                + "e8ae9f98d512667340b7972b4c840a7afcf7a75a4e176d8867f9fcc26491d74a7827762b"
+                + "3faa51599bcdb2c1bd2a0ca8b407f4b5525f144ac9ae9f7f5349d2832a6ff0bc3749b328"
+                + "8054837e16a910ba8d21ddfb547014b0820a971937087907e7b96f661caac4384fd44355"
+                + "ffecdfebb42c44c244bbd6a5729a194c241dafa0803d67dfbf34b81c6f55ce5fc07da966"
+                + "a688f304eec8f6d47d39c0eb0350c728a0b481999b9984f53d0b542c0654aec61b49b3f9"
+                + "d942d3644c596892c7f0e20c569b63fc2bb9fd2cc737d70892f3a73ccb7f306cb67fa027"
+                + "d162b87d23ecd73b5d0d53926ac0ba21e1bb25f63dfd22c32f71c1a7dc0ad6f3d354a260"
+                + "c139a88d386407b478ae8476e846aef4df07809680ec3d0ebe5382f905501fcbe5af86ad"
+                + "584e737cc018920c19acc39fd5e067279d72c19ecf80b1a6049715be779f730efe9d2cf2"
+                + "1692abf138b4482263b6db65fa00efe7fc1f92877cbf89a7d77315c06ff3fdf149e7f458"
+                + "0131fc88ed7a401e32f87e52c354fa100f343e5520f74d52c86e6e364499aa5b609608b3"
+                + "a4b2936a817f12bed11540f5561d1ee105543ee8ec48adcf4a78504478017f2cf110ca12"
+                + "7d689c5cbc0f80801b98e9db87c18c3dbeff208fa676706e989ec8472f184874cd72e8f1"
+                + "ab7e461f20bf8a9420b9892b4aa70f80450e5b0aa4df5335e4905e6e60608bf180cb2e3c"
+                + "06edcbc705f6707a4f9658e7d22c259202bcbed6947d4631a2064bb44d958e9d3e423580"
+                + "5e148f38f495102c17a50ed44193777d221e4201098882408cf2d5c39c558c8ad2e1af20"
+                + "349784f6c48064d485f951b5f3b2455ea10e4b4c49ae98eed3e0754420922643659f7209"
+                + "3d18647d2919b2e933202c9083c81872d4b12af41013d07cb6fe084581ff90d2c21fba51"
+                + "f20b5225b3c67521e85ef5b56cb4d1255cf117e78d154d9dc8a5a7ce1e076a03d6ed91b5"
+                + "ad76dddf5f98b760b505abbeb77a73d183b1baf431e95e7afe7e8a72e54dc42941f7734f"
+                + "f8569a59246245ad643ba8141aa293275e6b323d5d43f7edb6426fbc0c15a6186c5bb6fc"
+                + "7db088fdcaa79b81c076d95ffdbd011fcdc2d95c928a30781f253783e3bc116db85e3edf"
+                + "e69a227f799384e9f505226a4c02e941d26dd8d941bd4d2a215a9519ade4da4f770d374c"
+                + "368dcc6cbc7b4d11287bd2c7ec92ff844076c40e1c9c770e681bf1e09d1752d8625d3333"
+                + "6500a98ee375f774828445e7f88ed8b13bc7508e7c6cdf19b70381ae582dfcd2376ea637"
+                + "b89f1db62aab7154c79e59f104bbac1329ebb7f97d65185e388abd2e9239ae5b30358a08"
+                + "1c9ebdb9b35766fdeed7457079c4a86e7a4e3d809b0193ef03b5b637ea46b881d2abad23"
+                + "cb1b97a5460da4293ae41b46868a5aeb728ab8152882b75d5339d44278c75ef35e6514eb"
+                + "3670b237c6389c1f6b8f8e8eaf7a6988a299d4fc26a89bdd930a7f785844c9e8545e6dad"
+                + "410eeff9f09a68dce5fc13dc331f7450ee38dd9b30941e2c40358991ed394ea3c674042f"
+                + "0f048cb8c200e9b123adba886353e8e52220a4ec753e4e4b4d5648630c16d538c64ef308"
+                + "7f034f7fef5fcb18e183468429ce5e479c7d106149a07560a5a620ec4f5d378110ae30c6"
+                + "b010c42ce3c93edb0f2263c20de6bfa92bdea7206cfd1d981ba87e8221028ec449d5a218"
+                + "7509a1710d4f5023868c5ae4c0defda5183b7f4a289b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "38f70027d489f630a20da8764d4d2c3ae1e2aec8835f19de51b48cc01c20beb5213e6871"
+                + "603738088e0969fa4b87e18a027e037c0ec0602258943ae437031061dc273656a2b14c29"
+                + "c90a568f465b87f8f3ec305e78c1bbc996fee42c85ae3779edf6a02aaa7f6ba65dac45ca"
+                + "b3b1d5796bd8219ac8034d419524d6485903a6d66ab0ec43577b5376808c10e99516db99"
+                + "08fd743c3f2132e28378278470fa852137679ac5f25f9fada9bc596c126fe2217196e9fb"
+                + "90b9fba8b2b1a924aaed580840030f89a4a782517a4ac33f4b46ef52bc39ca88cc76b096"
+                + "5a7797554fab30dd887c305c48545ee7677213af71461dbe7a9cf9e0db1a4740e5ab030d"
+                + "dcee14ed49694b6dd5eb6103996ea68803a417825821b42be08a18645484ff2042804dcb"
+                + "d2386b167fddb429f401079e5cb5a1522c8c278ede64289e9d97289f428f7824868a2217"
+                + "0b0dcb5851a1beefef86d9bf3c18e9c59a9ba884a738f242452a768616cf1ae5db937a58"
+                + "a89ce213fcdc544c2525df3d1c89717064f132584c0fd86e58ee21f5d06bbd36a62877cb"
+                + "8d7e41972d1e65bec1ce1246ef00cf1aa5878713110a89fa18a3cba679664985be0580c4"
+                + "81b7b1694340f8a1f39ffbf07ea3ec489e2847d1de83b885f9a74f27b112088aefb856d8"
+                + "fa98aac7197a3dc8797f82c2ab2ae2a0e0e286478aff121012ac6865f2a6f3fbda2fec61"
+                + "62cfce60f3b53cd04cc0636fd890b7afecb59092d5a4752533cc3a4302394399978be0bd"
+                + "6dc441d2b6544e5791511dba63eb2b935dc45395ca656befe64118cdf2acdfd180c5e1e8"
+                + "d5991b64bd4b59d06522908de0b7dec283163a0911a86fdaba0cd4653fe44507a480b1ad"
+                + "878ac9b5f7422696a0e01de1b84b0db8dbe630dcb1d6cf6538e546ecaa3c8670b049324f"
+                + "8ecf2f75cd2aff73a0ceac672e1d6477d13dd5f127a564d1b8e64464d24dd0682901a21a"
+                + "85386ff561f7b2f735f95355cc90d76cc828804da20ee52bb5b273033a70ca66819086ae"
+                + "2373efdab77c67b430104ccfdcd5bacf87cfd6d058aa6ef00cf53b9ca42f6c11d0f5853e"
+                + "ec1266a82477a4a8b0034feffe3d24d2a6fbc396f085832ea59863bf18e32408894e5685"
+                + "4479ad24f2bbbb2916df0c2688aa14661bead8f3d245a7b80faea3fa52c7c5ecffc4cb9c"
+                + "b0d7f2a0d787c1df3b5ba3bdde55c433ee51f835952ba14ed5c991dc52a4ecf3d1b32960"
+                + "c7c7968f0e0bb710e306dbaa63ac4d0e049897d362d08bc7b6b2f19332c24b26f500c1ce"
+                + "c58a312944a02580bbab54e751559de1e168aa31a594684ee9fe1e8e982677d8108ef54c"
+                + "ef267372e76233fb6be4efd59da9c8392bfcdc8f889347cfcd0b84fdb95c8935115529dc"
+                + "de567ddcc5c7779209f6aa7ab19f383c54ed5ef0667d6e2979963c66babe0053f3a17c41"
+                + "b417dd6fe86a03b6e12a3514267f9d2712f85a1c70c20dc83b08eb3997055b9a17b01f1a"
+                + "9a84460aac5f3f5dcbd17cbe965331e30eceb8d15b9aeefe009ddabeff50ee673ecbf2f5"
+                + "0f3b87c08597cbe604e88624d3d98af06fdafd476ea7ce546215025d12c5a303d4f743d2"
+                + "910e9097748ca1c5252cbad7099a4850f4183a5fe07be119826fb71c580fbdb1c112b76b"
+                + "b205e51b1ddb270c9597e52e573172286add0cda49ffb089cce8fa0e8cad1659d623a395"
+                + "11b17936660332a1ad8af60f04d02f9096ee965144824f8ab92f2b31ed17ec89b16d93a2"
+                + "a7e468450497a27ee4d4be6e4bc469f5f9aabefe9c8b41e91212c05c6904cc33e71e33e8"
+                + "fe296ba0e920fc8aecb20203c804ffde759b1bbcc48d07ddbbd36094e4d2f919b8f90ce7"
+                + "a928d279600a11891649d5014132c3c094994bf6d9522716364299c00f7a28e017ff8a61"
+                + "4ae8a80a2a79757543332beb4cc2b980494ad13b9c0de125d34e2322e9e03e3c2b5c1434"
+                + "26cbb6e52a322107ff13b39c662703537c8bf93293464c428aca016738bb1aee0db06b52"
+                + "9112e697b6529e5c62d8aecaed87bdb4cb98d26ccc042efda10fd2bbadc1838e828d37cb"
+                + "c978326fc83593f76100de20903c42658130c7d71002c112389d792383a1d457c838bd95"
+                + "11703776b7367052c97b5a64edc62eb255c69ff9ff6506510c065672e52f23f495321d4a"
+                + "7709a45e94960e2d4e5eeaf70bb9ccc65f9942bf2f6618f24935b02843d773d04afde33f"
+                + "cc4cc8cc271470010e30aa0006cc48adb69c33ded04e56e7fcbda14641a81772524ecb21"
+                + "1b85b50d2589f47ddc6cd5eb7f0442690f244674cb4e053e4ec57df643f83654295807a4"
+                + "1b1a237d98470e34da114d3a86a02af2aef9a62dd5542548160b2c8d327428a19a9d5df3"
+                + "e10f8294632fa9df68ae61458ded056233039e125b58152fbf23f9acbf8c01fc829b5c62"
+                + "234b9a29be37658204eb9622efa647b6c76b0c8ec6368664a35e5c2daaefa25153633ca3"
+                + "79237441aa9fe88127f232bac07dcb10c152dc4dd0cc9f58349063f4e554b6941d69da2d"
+                + "d26e76308e7fd413acea557f9c12c8d5530ddf1bd2714aa0c89cc4ec00157d08125f22f2"
+                + "d08a1fc2224f4ea9ce714ccad43f562d5c82b66394f5fff4ac7bd2eb96386061f175e1dc"
+                + "919f78a969ece6ac955b6a37c8dc9c03e654d84993c4266b56fbdc3f71a55c241c061562"
+                + "3cb403e59f8395ef761a3112d7673731f4431bac19adc6912b298f4903bfd0664cfd5ffe"
+                + "7927459a47d73bae35c090a5efccff98a65219ed3cff613fb43460c9b031c5d449a7d4d5"
+                + "0c970d5dd358d896677c91e40f3afd1189cd752514f7bcec89126648979f17836ba2116d"
+                + "9036d608f6f83dcc945f859283f27374d6ece760d847611e8706cc1f6cfd92992f7a746e"
+                + "d02d609942c2a3168040ad66f84440c1ab9f1035511db2ec72dcd86667c28bf4d9397cf8"
+                + "77d0b6c1f6be25aad63e1f227c1a185801ba6db815f7b8b6b8a0163188271993ab0e9665"
+                + "6c3cabee65450b7ed4fe62c831103343fa1c5f54a7ce6ea2dbbf70acf501032140de08cc"
+                + "eeb883dd337747351c36b6b5d6449a1df48f9df9ff16a2842607e1b08be8ae3d69cb1ad7"
+                + "30e01a3f03267946b3d07a915ba014b63da7ac44f6082252e619a7ac9fccf585dbf15b2a"
+                + "c57eb660602a077e9c32f425d71d15f449bbc588283454d94eecc36b06287d874fb97472"
+                + "77065aa8f7c0c3630d7a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd476"
+                + "36b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "39d50598bf0157950c6f626fb8cd21ccfa55e82a86e8fa45ae8eddc18767b1b0a4a5d508"
+                + "0511c32a0cee5631804458861a86fa4b4c73f2db345b05b2dd67861046d4415e87d0b0c0"
+                + "a639f74c9fa3d4fd8bdd56e55034ea341787b565b07c76f2bd49e5cb57d10660f79dd06c"
+                + "d4f604f051d01b3fba10f9285d436ee7a8d8d61791e006eccc98a2222495ea3016365bbf"
+                + "e73b94ff1202fea5cea6aa20b5f82b2497e066e6a803533fa6ce70317bed8e652f470202"
+                + "c9b8c0cc72e2573251a0f5f70e89c121993eae1cb6740088010e0753b174ca4f6fcf236b"
+                + "1eb55915de61b079c9c2d05a34b274fc1915fb44ba6681bae28a7644f49fc8493a4df168"
+                + "e690eb994642ce1fd175951338e54cddd103223b8824c68804dc62ec754137132e3779b8"
+                + "2ed74b123fc52943cad0d0bb06119ac8fb9b82fe302f7641af849775bb1db27d01e0c531"
+                + "1b9d03a6bba9719be72e337cea2209b3b9a0e400f6dae6dde69e618c865aa3af16ef1ba4"
+                + "d9b48ef5594f7670e7c472e2be49a6ef06efd2ade1665e8182bce220eb9f0bcee0ab1dc6"
+                + "7a1f3657764fb8b2ca62e212f20ecdea55d7384a38907d7b757d2a03130c08abf65b56d9"
+                + "c6ae54e1d9acd994e62453fae9c43630079502b0cf33d22dcfd516b8d2bccde2fe70d74f"
+                + "d4ae24ab5444e6ea299e13c5448eed5249da2600012fb95b4addc28b3a4c15689cb416c1"
+                + "19d73a65f0fc35c7c7a2b0fc365f8fe1915956d4d70e418ff62612364a9650bc0d485ee4"
+                + "9373fef0226c97d2a6232984fa03d4be23251c677a8022cf8a9212d0de901e9d06187753"
+                + "f3e22995aef87284f0ccb40eb92b1af7f64294ff3b2bca09456933785c06f5edb0648c2d"
+                + "86fa5c2bba934cd2c4c4d0149e7f3b9f0632406d1724bf1674d4053a42d88de9ebaef134"
+                + "adfef817b1d6b026816830dd87e2ddf31717801b93d59f2148c28c678f17a15349283121"
+                + "94942b6e9f89eb274bbe78347f5dba939206081df701fa0c05ffca40c298e8e4d3edf4e6"
+                + "5a25a0a813b8baee8d82923fb544755f144b49dfb812a58c0bacdfd62bb9aef1a09bcee7"
+                + "25f34fa2b8378bb45c428915fa52d2c165c9edabe3881dd97dc63dd7e3a907c1ad3cd2bf"
+                + "3ad41f3b52a3ca21d3ae072d981b84162a02e6556afaa22b593cc56dd8569a18cfed408c"
+                + "0f843e97276f98bd492e7c7f6277fe1ccbbe779b1dad3ab9ea2eb23868933fedd64ae9ab"
+                + "90ffac1e5b4e7ec86eebc685d56abc25a61ea63ff2bf5b5abeeb8fcdb861cffaf00cc9bb"
+                + "cd55ef8b23cafa0669f61f30851a0fd08bffaf7b1cdce86a403c5979fc5a979b25727089"
+                + "109b566aadf5829119af553b5df31c298c2860e6ebf236483f4b7875b314b909324ae34f"
+                + "03a4c308730131a8a6aafa85ef05da56897dda189568062be7e791b66f62c38bf7363069"
+                + "ba583d7caad16787ea288e04bad498cc581381155b98c477f1008f484c72bf1843c3e86c"
+                + "f9daf9a1d45d8d9d6cd55d95d541fd855209417ce91875ca5ccc92903ef906a34470ea81"
+                + "8f4561cede77117da52245cab83844f696730ef096f3d46a3abf5cd2888700e2d38ac8b6"
+                + "614d8da0fd2a05c934625af8522fff875d907dc69545173732a2f8030071577b13508f6a"
+                + "ea3174628a22621d32da0e45e1bb9612637654a9f4809528514877d5742a6d4cd8ed6f64"
+                + "41908789b4ec320c6045f7f88488be698b4e4225c794ceee3dc1027e37010182c62e5a8f"
+                + "5b5461a50ccf2cdca8b5e6aa72cc29fe848de1437377685712783eb0cc215881a1792207"
+                + "bc2081b9247fcbad21dc6edae817f4df57e77e3dc67eaa015e661e264f939d7dae79662b"
+                + "373aa526c229df7ff15db854db59e91feda24fd3070364e1e9f34d5dded17ffc68a4c226"
+                + "e4586c8e85d1f20f7cd36a99a521c0156afe65c65f95cfb0aa1c6e6f8ced73a726c79a25"
+                + "be125dbfbad7683704f54778d14aa5beafb745bb9c4815a273581a2c627b1a6758f1c09e"
+                + "46336d10dfcc31a832eca9b8fe61e2c6f97923a7734d8620416cafc788cb7c53cf79131b"
+                + "b048b97f3fa372a5e0ec01e677c41496e67e6a585eda533ddd76ac06b05a36dc541b2019"
+                + "ac62d32ef03967516e86c09eaf8a042e61213e4169690d8d2caad79389c31c97ba9efa2c"
+                + "b8e4a396b197d41e394392f9e2cea24293ddd12f62c75ef9f172a08e359bbdb969cf07b0"
+                + "7eada3daa5226070522435dfc897f0ca6c25baadc22444228f54056a87d111b16778212f"
+                + "32ad49686bc6d7f315519dba787d7913b46a85187765b177be0eaeb422570089ca9a3ce2"
+                + "adbf4d4dfa35fd990f00886729bbb70a025feb80b9bc5aa2570523b84ae8e20bb009ad9c"
+                + "61c42c409ad74f6ec88395e49c1859ac7ba6868ea786ab9a2f435b9a9b800d012c87db61"
+                + "ac9ed69c3c743e2ef542744f1104258d470ac16b20fbd255ba3988344d33f24ff3d1679e"
+                + "5c4957596d03c6dbc50f68253c2af3d7291a0048a19148669192fdfdc668035aafce52bd"
+                + "f1ee47a7f957e5153c5093d1d77fb54b1f19398c28e20ebfd8095c072bf1f23c9b637936"
+                + "836bfcd0786714502aebaacca8fe85c49f9ea3c53e0781648d7839820f7d1b0ee07f0546"
+                + "228a688941d3b438ca4597e80c481107b6d74ef11aedeefbb3a3008dc50edc88ee44abdc"
+                + "645aaeb2ec9f645dcf63b4d15bffa3898a07dbe708f9d0e3e5cf2744bc1159f663a7048a"
+                + "e8d23e8c9a1b9eaa0e7c7a8bda197ec90fb6e3614507880770710f01634bbe64687a6cba"
+                + "b73279b15ca5e1011ba29152e4bdb1eb8996b6d66b5799c4d46f2552dfece1e0bed9a140"
+                + "776dbbcec0b03aff2243cb13b81a16881414160c920ffcf5938ef2390b44eb8b84f277b5"
+                + "256ed0ab7324ee6ef20b0789b8dd7cf42ecedd24f099a978fe22dcb604fa3430078e7f0c"
+                + "ef3045cb8c2e0a647af6967086e2f505fbe1fd796b00495901a39c5c3ac36e390917723f"
+                + "c9296beb0a1d13b0c768f1a08ce67ce6013bacf95a4c8fa36cb2f46f022b758fc5409163"
+                + "e412022e82260778e62bd256ec7f29a3694a548cb0822e0b387df2ede2bce510ad2185fc"
+                + "289af1e0dd71fc5308ded413cc1e51fc39197cf57ca965cc6ddacdaa83cc2b25e3bad563"
+                + "0fdf6ef3974b27690703d8809c1d15f449bbc588283454d94eecc36b06287d874fb97472"
+                + "77065aa8f7c0c3630d7a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd476"
+                + "36b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3ae79b4aa31f135f434366c323eb977388c1bc0b4e05a8092e35993d6d16df215d75ec66"
+                + "f68033ad6a5f5bae6414a4b4b221436a64a451476acf6ef3963e5760f67a8033ce9d73ad"
+                + "802f451f5425278368afb81acfb61e3d09810be734d88faa0bb5eca1387f9c6b0bb323ac"
+                + "cd504d3dac50e7ef0dcfd25e5f395bb80b7b0702544b9e603a35bdd362f280d9b3f80504"
+                + "0ed0ddb35180bb102c63f400de1dd8986def669ff9e2dbd16471b86f45ddf9e42a06be90"
+                + "fc6b950a2051af2ab5d6e7beb57a87f29b82d0b8b6939ecd3ff10160df6077e50c4302d2"
+                + "df4103c53b88ac48462527223784dcca47598ff252f12b57de026d4baeb0e5e66942824c"
+                + "2b47f0ea0f42d6a26cfa353ef8ec908458a93ef834f77868007ab41e12ddba62692a2ca8"
+                + "9d9c5bc66cf98871740bc35154f73e614199f6cf833536859bf8f8e0f374016f6b84d8fb"
+                + "7856417bdde9070f12b5aa42c78657cb7f837670a403201d68c920876382038200ba14ee"
+                + "49820cc77a43cf95f1624beb5d5c9c38acdc65e8de02e8802cf3b21326e941220599f101"
+                + "7b4230889a169a6de74bb100bb71527c1ffb70f0fcdd98ef9436eff6c91701042fa77eac"
+                + "afee9daa8bc453719ff9999a803f27a388995f2fbf92f8605af9a92bef46185765d4883b"
+                + "8bf9d75417d3e2c549d98951420512867fa006046c5c78ab10f4eb707abe84cd20491386"
+                + "d09be4a4515e633b978617255c90bc0664ca6ea08528b8b9979d1281721f54fd52920312"
+                + "9d42a0a5f56826b02d3a2106e5429cbc107f470ceb14085e1f568d4313b95556f4b2adbf"
+                + "670c1bfa37184a9e5896b57630545213c23ce2de0144051891db66384aa8bf8867f3abfc"
+                + "fd300695b8537b41cf4ef24a86628747dedd51ab8061e612faa1f1043dffe7d60a56859d"
+                + "96c63e6aa61e2a2c0e6ae4442239216532ebce1eee98f174ffd1c316f0d41bcd674cb0d3"
+                + "65e75f85000d47f20cf22fc4689aa1679bcae25f8716fa6ab2c72d041574c9a699abc9c7"
+                + "2a4cbd568ffe8d00e73102538dc4536f7f93f377d2de03d0ce9e52bcf51a7047c5e07c7a"
+                + "fdfc34f567b9355c190404a7b89c5a7b2de7d7205c8850f3621053d7542d83a66b925314"
+                + "210272b30c889370bf19ea42feef2a7f5231a7b5a4e87411039379faf42294807196903a"
+                + "501492372edc91d61f0403adcb843b9c80d805899cf5383c638c439f9ff096519e0b55a3"
+                + "004327a983e0073daca1b4affba6dafce68da9e39ac000a2260db89abdc0e467ebc072e8"
+                + "e48db515c95674cdc67273afbf2acad7ea9a8f829207abd7b2caa7fa8467fc20317304b7"
+                + "18a2275e9c3fe31f7639e5be5d71d2f4b641ecd0a4ebc9004753fb809715b11c827ec967"
+                + "eed4251649ea8a03c0a57aa5d7fd2dfd1db1d55ef4871c1431e7cb4e3d96ed9acdf20eb6"
+                + "a027b6fc23642a66b85276c7c21ea9bbb1c85edff31d0b33a011d608990050d92695f799"
+                + "454424d0c989d61fd4f3506523c50d4bf75ce54f160f3c78e9e9bda923e294ca5ebcdf3a"
+                + "02b89ab56b22e000a8dbc5838a19690bd2f8ea8eb0e8e7245aeadb7dbddaef4a492c5654"
+                + "4ba30a1faf8884ad284e6df0c24f21c55fa2edfaeb8355458fa2a48b93cd096d4546aca6"
+                + "49e0f09b3f64a1709ab334edc92a1148edf8f673efe7489d01b90eac25b26fee62317f8d"
+                + "c4071ef834e507f47c41573748a2f4f26b2dc06fd26fc12fbc5bbd4fc401e263b28e8664"
+                + "72258d09dad986faf05683f75116e98b19d51d779ed5f05ada29dd0349dcbd3ebea78e7a"
+                + "9b55827f0fb61dcc55a2f1b9648c4fecbadd0dcc30e9d3dc5135200f17cb1e7881de0239"
+                + "8c90dd6b5d202f802e79dd5e851a898a89d862cb870fdc32279d3819f5c92f12a9343acb"
+                + "9c1a8277ce862b12e50ae828f0e375b131affface7b0b96fc6fadcd9dc7192be5d60b2eb"
+                + "7879e52638947d00eabbdab6a846d32d2124d6be534b8a0b86440bb72ddf1693b19945ab"
+                + "5e60b71b68dae0965e2db31e4cb1bdc271f95d6736efb2f7a14ed34c7ae2ca3d13beed7b"
+                + "8d9801e0daaa09c89225ac30d6032c1bc5bdb2088e8522a410b8dfbc38ba93f22700a22e"
+                + "3a80803000b68a737d4ab684dc1f96e0e6f447da1a89f224f81cf780f169c25757cb8092"
+                + "aa1f5660c5481b60a2489ca486f55c632de4d427d46bf3d93b3c652c8b8e298052f6f9f0"
+                + "e5c969205f36b8431ff454d4bad6349cee13fd32c97f01f5c821602e9196bf37f1036b0e"
+                + "85dfe906526cf6b7f4ba73b4497db11210aa3d4fa9403e3fa1a1b4bd9e18cf0b0f74e4c2"
+                + "1cb6b016809a3d3652601b7fb3dd0e40a63fb94321c2536115e93ebd1ca98bd939531654"
+                + "d4d26909fcf4002980d78e924956629a0055d552773087b99fbc9fed7129882be26754ad"
+                + "527483c43cf1a7d3a7149d47b399bcb0afd7bc71f4db394157931700ff516c684ae45e55"
+                + "e51bd8ab3176235afd12432ab602c846c3fadd9afb7a49225d7ccb34acb792ffc32b9060"
+                + "879cf56ce75384485985d2597b2fa96d5415d7cbca80abb71c496168c64c17775af8c25a"
+                + "f659d3c2e9e4ed01d886db90d906eaad6bd5ddd8487960e404154ae68eab74c1662ea8d3"
+                + "24f36ec16869685f6713cfcc03d6c20b142f284608f998295cbd2fb7d612993afc4b62b7"
+                + "7ef901a93592e33b761fe9b5d782aeefd89d4dc214d42dc645a1b5a358c608adc1b27df7"
+                + "6fe09a905a4e232c4f2b39a53300a682619ae7462162011eabccc1067e493bb46ad1102a"
+                + "5d7aa6312ccd3a20593dd2ae8db98af243f778960db08aaeb7e44b78275924510661ea33"
+                + "e6d5f41a4e25f35a3eac895a05af1d9ed3de5fd14c33604610a333060b72b259bfe63650"
+                + "ccd7800a168eb9437071223821b556460f8c2e9d06d5c0d147b164aaae7fff7c8905ebc8"
+                + "79b5ee35d8524b02de79e7243a95217de63a06c680ab3120eceb35391787d28ecb379270"
+                + "fe13180b99eca4f6bc8ca3f98c7871e6ebaf8ff7c13062b45cb6301e9bbcc84fb0499dab"
+                + "af5c580f89d0af3ebf5e28b42fadaf0bc2f67e3871342c96eeb35351e46705f79a2ed2db"
+                + "b459924116943a1da7423333bdfd083e3d1e997b74eb0999a28d304c1e2568a6a141b066"
+                + "e7a726d7934a131eb6cb5550eef75d8ddb0b6246b0f0d97240694ef176fed93e932017a5"
+                + "b1e933a870ac08d2797a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd476"
+                + "36b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3bcca66dc56e4d69e2dc766efd99815e13ecc8b908b208ddcc0831d97766e2fcaf81b915"
+                + "8b31d9df71a587e0cc7d7ab4df3346f28528bb654e6f75e8dda12c1b7e2442975b62429a"
+                + "a3c3f2f9325979a627100a5cb2a355301d015d35dab75903806a65ad852e6ba669af1a16"
+                + "879f887ad220af632f20a40969e7f2483da74bf65edfbeaef9936ff398aaed04d990da13"
+                + "47df039d19d14e64a524d0e3f5608797052bb076ac3abf87e26b70b81ed1e7754384034f"
+                + "22b68ca8e4779c2154abdb316e77db52db58bb386780d11938e7738f1bc84ea01e76d01d"
+                + "00f38402d66df7680923f6add5705cc0d768fd8d7623157b28bd6a4798b8811038b8c276"
+                + "46e2d9bdaa09fbedf92ecbc4d741fac0816f3aface039491ae7da421f5025cbf75ffec08"
+                + "97b1dee6606448f86914d5c34fcf8f127947fadb239f1884efa189291c7cde897db67d09"
+                + "66858671416e4d8d595148eb4162ca8b2c1369d564bba4cd328c236195dd02a82ff26c04"
+                + "ca55c21dcd9ab5605e5a7c900ad7c75dd82102f7a38a4ddf4cfb034036c15343af617748"
+                + "ea64e999000659fd33e98530ed27937a68326060cfa2c4d8e797a47b93dea937d453d5c8"
+                + "c42725aa5b93b66b45ac37563f8a2cc85dd1aebbd21978156c757b255ffcdbb08315b15c"
+                + "57379fbe6834ff7446b20c063fe7e74ddecfb28ac34baf32f696a83e286f87c648421ed9"
+                + "aa150a0fd3d3fe4fb8bae7326313f11425510bf53a50721b7f69cfaa50be2059fe57b182"
+                + "5040505b174674efc41dfc160e32e9e9be6cc7ef36835f32328decd6526203d890631850"
+                + "e13d50eb9b20f660c66f4ab69e4b525049cd45c0c5e69afb60ef861fd42fe311add049af"
+                + "6ed681297c19175d97b94cec700b87550974f4d431b295dbb62068c9532ea5c13e8aa9de"
+                + "0fb824425155418af01307c0bd234ddc1025bdfd2067be6180b4a3494446bfb9d2cd1750"
+                + "1616618816e571ef74644629671d57425a3b1ed6a1c7f426d3d50da4658b25c3aea5b4d9"
+                + "630e35e5f1b91ebc26c8b76e0f35b7bfcbc5126bfc8dcc56f2ed98f286dd73f9d8da7bbc"
+                + "35e3d98b4a183dba3960a3a6f0b1bc41c25fd4747098294aeaebaa4b4363ab81b11b75a1"
+                + "3e7d4f600cb567dd9ade91d1d547e33f9652434f0c12318b9f3d7b18466de939ef8fc1c5"
+                + "9d06fcf98f6cf8c657a4950e5b8aa1a27c6fe08383bb7da6305d7662675cba75c34e1297"
+                + "15a501b434d467612451008295d23ac48434b746e51998ec85acac5936e404fe5cac4f22"
+                + "bae2d1fb12539480541e94c571b490399f20f49b0b897d1efba81a06d31dbb6f35c5d123"
+                + "2c2093e59285850fc53f6c82056e07de5131594dcab7b13f76749abcdfeffe7290c771b9"
+                + "452050a9e57d38da52ca78308f933294cad6a4ea1645f8f6146e9f675a8720beb7a8e388"
+                + "0c69dce9199e0342d8b636c77f28069988d979b490377db0cf68e4c92b77474900972988"
+                + "d3adb9bc783ad42f7fe9b96d8862700fd94b142b1e1160bf39eef164bdf42f62c9397a21"
+                + "7d1dfc147b7f9730e1b2febca0b25f020dda4f4c148bc1d75404817833174f5de76d3866"
+                + "6caa7da8317788cc9bc78159d64063f2c02b9149728add0a8461ca3f11d3effe1941d9a7"
+                + "0dc080de74e830bccd2ba4c231f48d9e1481e62d221d2a2e6f405c2f0ae346fede6db3c5"
+                + "199587e2a19f9890031db9763e9eb40a1f1c71412dec94dfe674b65da797a0aa59a4ecf9"
+                + "abed5f70fc51145501a38d161a6f47e211322906279ca7da3589a3ed1af38490968630c6"
+                + "d1f98ebfe523f7a357effac1f5856d522c2f27938b981099f01af267d4242eda979aa3ab"
+                + "80aad7a61f406f8bfa4a02061f6726786cf52464ae690c5e8a9f6e84e28ab01e4a26a543"
+                + "3120fc843ee13efdac9dbb854c31c75cd405c840f8ef0d32577c14f2e8098d9ab1a05e50"
+                + "f8c8921a0ad256b771be4611bed6071eb130db0615f6e1bde04ed0d452b9508b42ad970d"
+                + "ea429b6cb8a705d15197904e496b59e84eb7ed5e600230735e4193a3b89c0aa6957ba228"
+                + "566ae6c2099a156857845d0738b7572c82795c54ecbc3f1815c76fd019e2a281608df529"
+                + "d4857f6a8faf22726aa221afef6f3a9e9209928fd5abf5c04913e30843c9f0db336881d4"
+                + "8fd4c471dd71d1983a582f921f4acb9a9c8b267e06b7f9d030f7066a8de8450ab9ba6e23"
+                + "770e60903204054b75c2f7e3de9bad06e15fb67cc3b233d96caeeacf58b7a342d96ef00b"
+                + "c96b28e4cc2a3f9736d290e1d69cbf2bdb70dc0484be1972a37fed3b03246eda3d08bef7"
+                + "e9d6e617d82db9e478252a140699d8d3d60f3fe6c67ddccbb741bb735483a2c79e5201bb"
+                + "44eb552e64cff06ee1aafc56b382fe6338923a7db762a35ecfaedcb2af74ffe79e586df5"
+                + "cbc08ac0135ee8b2db8a97da083b24c09109e545753ba604df4c48528f9bc633054b56a7"
+                + "e6b49bdae64aad461091c70ac9e9fcc517c30c9112248a7ef5bcb2424b97243413e25916"
+                + "a2f0627fb48d2df1b3f7d2f7122a96743f19552c65f74d6464f73369c6007dcb8926f8ae"
+                + "35b1c30ccb66352b50328e3d501a67751d5dbab5794b60bbd2fa3f20292e66399d7a00b6"
+                + "1996134b6dcdef5869c4842de92d4a66bf4448e7a0ce0d05532bdf7e9d27fb19209596d3"
+                + "08d7a6bffdbf6f4e839d61e2ac8b47c396b4b38d08d3d0bfb3d27060af247a36bfe817cb"
+                + "5228b7204e332b09ab4e6b25ab6cfe30711c9291f9df937f4a45a4f423e3c9271ed0c3fd"
+                + "a502f1bb379c1a458ba4f537910d29b14e25047abf14bc5d1ca5fc0b56d6773b7a6225f6"
+                + "5530c403d6629c2e815068ec8a3af54d717d62a90ca3a46a1db12b9e8674bc8623724923"
+                + "2aa5f9c5d72c173b0c252df316181a7133d2f6a89382273fdc90cf3bd61c07d1362dedfc"
+                + "2c7b1a8ced88789ffbc487f986c9ba9a793c517381e6af3e815179e0c735fd6f118fceeb"
+                + "dc257b9ec7bd28100f4d025dbef36eed8b420603f9c7232f4efaf27a5ee591335c35aa67"
+                + "963add6781421a23be4a82ee188ea5752c2783c37399478490cb656506d7b848af42d4bd"
+                + "fa21f2cf3f7eae88070d2db67002b900570ee4ef34702974c8ba1f9999f9e1f4b9ab6ea9"
+                + "06b166f50cbdca94dbaae5eb51f75d8ddb0b6246b0f0d97240694ef176fed93e932017a5"
+                + "b1e933a870ac08d2797a7a380e89959a95f3c25eca41f846e7a398c51906f7375fccd476"
+                + "36b1609009eedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3cd85230cc10db3d4466abb719a3875cb638e81ed02fe0a71e3b20067dcaef30d29f0c35"
+                + "a511264910a10500349b382e6b5ae5a642c9dd272514f3038ca463ebd948a3fbfe80ae34"
+                + "ae4174f5170d175f28a3548040bf0f0fec06c6e9a84f3e98d9916cda47d55565f26d5ca5"
+                + "b199af689e506a2573ccc4b0f9be50ab503689c11ed7293a0d6a1389a1100539afcb21b4"
+                + "b25fd855801e9a8293ecb1516a788bd15cacc07f7cb8fd78973b7b372d2452f0e5e0236d"
+                + "c11a99c501b773888b57d230aaf3694621e3df176b7140dd426cfa406303d3bd8cf6b32f"
+                + "1a3e09f82a109055dad9e72ed39b8685fe4b7b14d0528440c95c3b5bdc71a94b57d68d79"
+                + "bcdf1d73a08601f65cacacb4380f74a3d3f2af0b3c01a177327692bdce417c05c5d82f0a"
+                + "4344147af0f10c34b5b63fea2b83d87f83f80d2baaccce34f210d50b5b4bec594bfea6f2"
+                + "302d1688372b611a91f173a3cfdf09dcf6818274b6e95327c15946cb44aea7ab55c007bf"
+                + "66db1f7be7f3822ea4661c65734a20bdd8e7616fc8dd93647a62567cdb4645a003f33de6"
+                + "8d9a8288848800688debfde91323333010b7d393b6cf1964c4149adf494066e85cc96bfd"
+                + "e2fc9e85381e7ffaa840f460ab9b8ca8aedebf1a394ea3f524aedb251b57f9488c9bff6c"
+                + "8c9177fcf7c87a57c0e7c7ec4af6b259ac2ccf04db888c12775af9f29d9c626c2fc1d4f1"
+                + "df52ef33cc4996435b1331ef12b3c4775aa648231681676e0a020a5fa55679e90ab02b8f"
+                + "42de8aa65a35aebdb9c5b99797fa04eb9865d38ea9937d6d6a9ba4d693daae850d66812d"
+                + "3c66cd227fb3f2599f4c0e289993dc04b23fd2fab7dff749434a6ae7bbed0c12f1069d62"
+                + "03c35537191a7c97f0ff8a7383b838c3b947d1750a35d1764acabbc92952a82addc1a0b0"
+                + "4c0408f408a50b148211f0a729caa55b64a4d81247164f2f39fbcd521e60708f1a6ae1f7"
+                + "14b2aeec1e4d1bde130ecb7fba85ae122906923344e54c649f7e9fa358fc67f1baa59e88"
+                + "7391b4471f9b10e3c77f68b47d44911d2af5d9a5fedd9d54f8db689fcf8f657df60aa16f"
+                + "d59b7d59f165a393481cc1a7a1f5cef1655f489b9ef8afbf87eb2039ebb3d9a2c1ac4166"
+                + "87bcffbd518908076abed309368c3b131107cb2e9e3cc177eddacce799515572cc75fee1"
+                + "42a69b6f98894cd9ebbf6598922b989a37506d9ebe39bb5aad9f7fca1b4173b9ee5b04ba"
+                + "7380a675a1956e30cba3b918ec5e2d54468f9bf7d9e3903477b3da996059555fb969cc72"
+                + "1966fce8ca58f2808b82721f66b4350ea59b11b1e799cab2fadf768aa0b8ab623fdf6403"
+                + "d8dd9968301573f0655b86d2f147d8f41697101b12d1ce69b6012ecfbe4ba99f3bba199b"
+                + "d3e1594918c21ce8be8385489bcaa168065af2150581b5dc32455be124e52af9fa84bd95"
+                + "a9cfe82048d3cee501193c24b51435983ae5de14ffa694d96dd3fb326c7d43a137e39794"
+                + "e7fd97639b7858b875f136d36734ac1c71300dc35acb201d00c282ab0407f4a253dc91f0"
+                + "2c9930e6d087b9aa8562a07eb5862886818f4bdd7beafb6575a9c8a87b4eddd78e67d400"
+                + "bca1579ef60fd480f9cecc32416a8cf88cacb67c7ff84746558670d0c5ff10da07479b74"
+                + "ce0c8ff2fffa9358c36e5d1b9bf9196acca0063cdc38610417e7379f22efac9eb76fdb24"
+                + "ee8388270eac02df2d96b8b4e4047b64a433b1250c7cc8f491540c22b6903704c6638478"
+                + "15e84bd53257934665e73cdd010f9d461123657abece1c6c6bf7b0655de7405dcb0977b8"
+                + "4361b85206866972f90f1883b325a781324b160499010079069ec84c33170088d275a11f"
+                + "34fe480efffc917a4fd3e40d12ab2408052776884e8e273cea029fa08767bf473ef6a49f"
+                + "4642179252ff17b62098d8acfb7ec28d6430b6f88a59e4f68e9187f3dda9d0c19f67c283"
+                + "9eba3bb633f136da22818662538c904b8989cbc631e682c767aee49088b6c626ce204011"
+                + "cf2813e6f7b09a5147e5728548a6c29de164930b49608db0933ce2c6b379018a8b115336"
+                + "e70ae9368d053457ec13758216692032ec4d404c5b2592c8871ab16f39eb70a0bfbb82fa"
+                + "d5a8a15af3964bb3a5cb8c9712a57def48d2eb5b0174bbe1ec7893c2a78afb05ba31ba39"
+                + "8e8589590050aeca8e8c8dce0b85ea218d3deb9a51ad05c6cc99dfae70d5bcb4d265796a"
+                + "a6f9398d36d324692e7ab89fef4054b74c6c82dc2306490040ab338cfd71b817ce35d1e6"
+                + "44cec7621cafd31afd54368805f9cecd34cb96d1b4e9024da1a0e9cc4bca60ddcd685be2"
+                + "d862e9ca32f69f6a1821a05280fcfdfc6a54b3b69b29ae2652e8d26bf30fc84f0455c489"
+                + "4bc795eb6d5c569cec9ff6295626878de95dd9cec9b383364fa1c34ab5722fc23308c4b2"
+                + "f8f3de2115665447695407a7248d002c1509f1ed64326fa992c600226db40d205a8a472d"
+                + "c178c8a39c853f61942275d1bfcc31fbb24d53f86eb8abd2690876cda8be3ca24e972a93"
+                + "34b9952fddf73572cebb122cad90eb394667d067add30d54008409fe19f8c97375aa2dce"
+                + "65fa602b805d798e70dbcec16bb96136922b5ce09cec1dd27cfe43df0d6457242e6cc4b1"
+                + "6992fbf3b4befc32ba334d36e9ed79645445d40cdd6976e5ddd1aa68c9b9d4633ef2de20"
+                + "ceb67dbc5d994eac71c0608387561211bf7b2ff3dd3fd794f5aa3e16d984a1d9d660af05"
+                + "b177d894392ed7e7c9df1c99c713c4715fe05eaa1bd054f7ad37df92f67a2800927a9a9b"
+                + "28739e5f9a0112dd2a3da29692a8bc47a9d169ac769fdadb139f17dd9cafa31dcd1ec90e"
+                + "83e1f6f088a389c318c18b8aa9e902c70f035084514cbc5673b57ce3682d5b32906ba8f9"
+                + "0f55d47c1498242f54b2e3717a8e64baecdeb05b087007b51542bc384a04b5df27ce33b2"
+                + "4ea7c4792419664c667d76ee9e3c17ebd483f5c9c24168c325bdeb90d648d2263d97173a"
+                + "c074a4be9079e351f42ba91c3e4efe294daaaa81de0147b1964388805ccc89b346e44ee0"
+                + "4ae9ae1240ebee397f7ab93ad1d15d888428fb5bcd2706678acf5843eebc1a5ba9dc47a1"
+                + "2aafb61dcab34ed6e2aacca70fda9618cc6388f6566eba9dda4c0b225abe20cb0ea4b0b3"
+                + "b7623d6d4a6a1481002aa40f25a3190c49a1b9514f9708280f6f1f3b4d9cd2b0579cfec4"
+                + "38b2409433d71c88ef8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23"
+                + "ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3d457fe03664d78c3e2612bd4b90cf24e06ee2a508965eba3a93eb2feba43bb260a297f2"
+                + "c91a4a679226d94ca7eef815b0ae0530b2d91cbe839bb0ec45293544573e79cc9a0d567c"
+                + "c7916e99febdadada1ccd6a6d76118696dfc0c1cf25fe101692bbfab466f2f39cb2ff4d9"
+                + "23b356d91896c39a1ab0370816af05a6abcf4110fe7f4cb03a0a28b2fbb68a14af8c9570"
+                + "c6aee9f11ffe1d6c1dc5ee9451ce99c09f93d337f3a9520d42fac43d94e0bc971723c931"
+                + "e834edacc08247574c3e7fe90c2cc2af443bf5b64616617e0f031064aee320a7886e4f6e"
+                + "692234fa88d06afe075a0a8aabcba73c083150c9701b057e44ea7245b41e4e9076fd747e"
+                + "a5cc40e33a6f49ccf4ccefb84d6794362dbdabb2814fe12b4bd1bf93dcf643825a70e57d"
+                + "aae1fc94c588f18a8bda7688259228f8d7c7a923567fcb2b93c24ddb2c311d02cf4b7f9f"
+                + "ff0d0ad14aeed8cb2d77bcfd5d068a2baa0e5228e38b909106fc70110f9040f7d3f74103"
+                + "da5cd23eac1c0a3ec0f218e9cfea7ced995b79377bce8b9c18c2da819c6b0821afd325f0"
+                + "c6293dfe9de85d17489b0f2730854212dffb1daa62e8aca1eac2bbead63e7ea4ec9ee231"
+                + "b7e626281f6298cbc67abcad1b6e8af0055235a31d8e09b87c11adccb914ee4e82843d9c"
+                + "a4a6b500cac2d8c6631d58960f511dcf053b518d040461401e4f51f10ffbdfb47559b987"
+                + "bbbdff8199e8af80e61aad95a2fd36467b107c0a61929d2777f3431a815e7a7deabdf466"
+                + "5a91e3af0c17b0f42a4350890e56998f47bc825d02bb5351d480f394561c3d70f99e5ee3"
+                + "172ccbe9a0f7121c1067d925889e4bcdf00e5ddae09ced2184c0ae6e565e546886d41465"
+                + "aa9e32dfdea7a14526c07dc7812e68d128df53e534e361a041c57ab051edb47a666e4d7b"
+                + "73f4fb49abb51c508256cd8a64641f34d13d9b99d5860e70868c83e796144fe9aa6426b1"
+                + "37f16eeaa402d69ad8803602f83b81c88230f2db65f1b6f613a3775560cf299c8a607542"
+                + "410e346f25a0d8c3a8875a46c5aa6f102f98b46e2f016d6433a6c7022ab0e8a651158cca"
+                + "07cf64c37b010f87fa957391a0be18640e583ebf71067a5ef8b58135a7228065e0999632"
+                + "d829a012429dd90ce506ae548cf14d82950d78ece1d2f0a1c820c53555e108d1430bc71b"
+                + "6e88242bfd1d3cb6f48961e9dd164b9de15ef17ca2785a8d68bb89cbb42f0fa6f0128d6b"
+                + "8e714a8b31cd6e84da4ab56fc999d7f1cfb83923c7c7c3a05d446cca5879ab94ee61452a"
+                + "bae21c3aa0f962c3b18cdd0cca4c899e2bffa4f9cc999d0e3e39f87cc2cad999361fbb09"
+                + "29f807b681331dcbcdad5eb877920fdccde9d380207b4456711dffa26948784bb612db77"
+                + "6285fd3a9534e1fad9e4fd692b2a0e08004d41c151ff4987b016a235d57257d8f6b15c3c"
+                + "ac1f03f00546b826a8ed31c90f09005916853f3284be341033332469de315bee7040c106"
+                + "826d00f0105044fdbfd67ed40b40c0a3fd49dfed38aaea90886f045196cc1a9f19cf6a25"
+                + "a6e804c1c7c39691f1f1c24c650f45b2b1f1a7d7792467f6b089e56d56883f0f50f66021"
+                + "6e4ddf42cfbf99ebcba6016c85343375f604bd9231754a8216e92ebc17c0778f319b2c77"
+                + "b2135b2adcfaac33f01736fd0a88e1c3dba46bc5a4de6ad1cd6987c8342851cb837a7485"
+                + "9ded09d4756dfbe167b0fc58d101120a82104a39619f4a2385be909a9d519768d17b1260"
+                + "9724457dbb7b5f6fe902541bfa3c56947c00631fce01070311bcca5bd957dbbb7f8ac1ba"
+                + "1e42be603512dce7e49195bdfeb9e132db3cc6d5e9bdaf77bc26aa01fa37547a598d796a"
+                + "626c0e86bbce1e2775d5b68436995b9cc791b3f191aa60122ee7d5ab4e362d9ca23e09ce"
+                + "c8a198f6225d53bffbb57ea2c522b3d255020f9ec437bb65ccda9ad44ac86d19bb7ec4ab"
+                + "b8ce3c0014be5b0f2bfdf7106409971578057d355dfbde8f1454b685a22de1d8bf4a1251"
+                + "50aa6307868029b672bb65778b88b156448addb5e2982e6b175020fe7116d2df25cbcab9"
+                + "a0e5315cdb362c81d76756295a78063615b5bef6f2f5fddf0633dfa0acf1047669507aec"
+                + "90101cce6443ce92a452db42948bc8191cdfc744cd1d3e792984813ced7d45bc3cc1e889"
+                + "e9d7fef41fd20678727cbf0e31373c2d62425a06af35f0c8735014dfd1115eadd4a3f156"
+                + "66295c604a9a4a13395a7715c6f2394ef34bcdd20994a9a8950aa4abe544d9d7c294c1a4"
+                + "c77dd447042d3de6c3b65d73a2eb89e5cd3eb8dcf2d21feca7a54cc6f3d97f9bdb078ba1"
+                + "c13f6797b1f305f6e9bced0c760a525c819faea7ab65b9ba6bc929d69382c99085110907"
+                + "d5662a5fc533359b5cd2dd9831e4acaaa00b826b379ee1d52325b4ed14365aa9ae1a0340"
+                + "9fe32ec9affe033a00d42f034b3708b49805849f2bc83921f26e60806eaa0b8ed6060f51"
+                + "9086e5776f1c33c0a8033d01341662c3dd6011031062e55647d2497bf9fa24ea0977ea8f"
+                + "3a035dce97f66c3c09a1fed34d2b1de6976f0a8adbef90b61e68919cfbbdbf824e532b52"
+                + "e90f0eab396bba5e467245111e717845c894746c139b3b0dffe9894f055d7e8660892d2a"
+                + "65bb868567a31399379c5929051f82d6a448f979103c8646168f97bd9c43cd073dca6b75"
+                + "9d1b71243cedc028209492b624a70239b7c0363032cb19cb0d05bcddefecb41356473520"
+                + "68e0cfcf98fe8d68b461fbf2e6bfcf565e53cfe201dc147fab49af5112dd4979bb64bf1b"
+                + "e7a7f01253f5fe175fcec61c4a3e00e7f64b6bbefdf7945a1aa947ca8848dcb3b61e62be"
+                + "7e8214c99f09d3365d64e6750d06294be65af13c9e897aa3350a6c1a990d7cf6047d0b07"
+                + "8e5ecadcab7afb0a096963707f8fe10a28296a5169a805840af75d1cf2033f2d54a0d22a"
+                + "884d583d12e33ebac282e700b130877ed0a295c02c1729915a733c05c6ee6a1a5d80b8d1"
+                + "854f65d1a89ac8eaad19ebfa163bfb641a139c5441a3e8e7c3f50569b13bb696e132c40b"
+                + "032cd9738451f6bc9dc8cc0050e48600018d688443dc33a5cfa90225904989befbe701fd"
+                + "d71f9b35a3bdd5b60f8026ca5f2840fcb3f6d65bf9dcaa18065f82172f97801e81ed561b"
+                + "54c439a8360eb0b9aa493e5c13a3190c49a1b9514f9708280f6f1f3b4d9cd2b0579cfec4"
+                + "38b2409433d71c88ef8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23"
+                + "ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3e0235196e6f140740d20384df775060f0b670ffdad5ae27c7f1e01aa7eb9f41e18341b8"
+                + "c8f4047c0af138b15fa1907cd2734f8911618eda1e43e15c7d8ff73e454cb10724e8fd1f"
+                + "eb90ce35bb254681ae792500e83d86034e53eed6e3a716fb1992d3af9aa0c3487e0f9ac2"
+                + "d67ee8bd9b2366e6ceb1c0d280c743a6c21e24fc0aa45cb7c318d0790146506dbc31cad4"
+                + "019c07b5518d5c0644c0196776cc5b26c38c63b25a86a1adcc156fe5790e2ecf55f458c2"
+                + "31daf5429e237978ce3dede327cc4fd41fa67c0c2ad222a059aeab1f6c4983a431c0d885"
+                + "4277b68862ad12f228e9d080ff335b724d98274aa65a4c6e1054c844e9507161eb6a6334"
+                + "d9a289af4a74482537bae5363582c446cfa05b2cebc4a0dd81410c529054fa95031c196a"
+                + "892930ed831758a4aae6cf1878ffb7461f8da70ac5edea73cda28ee683b774d2d3e8802c"
+                + "3121a05b2ad71d8ef8e46514ac924ca33f20053adb86e369c1dfff36b9892aa976e8686a"
+                + "4c8572da45aac199ac1447f3c10371d4e294fee1ead3852b0bc97a8851c2a2caa3d367ea"
+                + "fec272f37f26650ce70e318503e0ee741449524fe9d290d03186769c9076c2118a772bcb"
+                + "b0c3774cb3dbe75d6c06abfe22f17d14dab2d8d113330f9c9b441f8bc52503b3b6e253c5"
+                + "5d9327dfce18f34346630bcff7461f82b2f221188afea86ae5883d374303558818847bdb"
+                + "295f9e27d3b7ad3eec3c12c072cfcf58c228655aa844459e050bea31e892350f1a8bf1da"
+                + "f3aa87ebf6b1beee2be98fc33b86de2f3b4b25d95803f8706b4ae1443bd4ab3c92505dbc"
+                + "84ede3670ec3b99b7471b7917359782281c32641737db3a177057c13f1b1e56501c9d65a"
+                + "5fafd1c2691ac2620cf95c7a61a45509b071e6ae415ef3be0a24b6154d8f6706d56b0b45"
+                + "c9721eff7ebe5eb8acc9f241fe19c437af494251c2261b5846b88297c0176c081c55b5c6"
+                + "27dbe07befcaa6af5f2e406c767dbf2eb0c74cac95f548837a88a183f21a4a3d2de1e785"
+                + "5d56a46aa73e6a84234c7cafce84e23dbc24a2778551341efee584445537330b06f61c9d"
+                + "f96c1e2678fc1d5bd0a067aed3e0a4ff62fa2266f9e988b4841126728ab81f63c0dfa952"
+                + "8168046829806bd608f8c497878fb1e4138be7235a9b6282cdc71e3eb7c7477d969b1170"
+                + "103ca1008c0b56cf9e7549ffffa42c6a399e40331dcb942055a49487e08ce3f5bb52bbf4"
+                + "7fd69ba9d4e81e1bffcc6771ad32d3c0bc62c5365fa5319bd719fdbd2592a8df6ab9509a"
+                + "59c277994e4fa0c84375227a9c44c897d13ee8f660bc3d204af198475a0c4879ba4cfc35"
+                + "1d4909ab86ed68fda690df2df1ccc4e243718c4af111d0e93c20191188fde22f84851d5f"
+                + "c8b8c2f077293406ee1939161855c5e434689ca0a2d8b5fbf9a740e6eaa7f3a6ae694265"
+                + "606a32e89189c37e5b09a7cdbf682cf68c848b361284eeae0df5b80e8912706c89472dc8"
+                + "60c4b7bbd349ccad2b4e1ceb9dd2ec5312fe905c9c697a4b87eaa7f9152a173278b24f36"
+                + "a8c5430805bfcc416bf563b6260ed3599559060d05ecee5e0f078b86691e36c52818d307"
+                + "73403625a547541fb73bca0cad366cf3429aa41f9f11b0d0f739a889aa26d9aac663cd92"
+                + "99a17eaed69a4b9cda5f3cc5fa3bbfb30e0694bf55129955470e44503185c32020ba2434"
+                + "98e03dce9ea5e84ea83c9a67c2b9e12e2c3d21c858dd675c28a89e91d1ecf3586a35e7b0"
+                + "7dfc8565fa82253ef1f5ddf6dc032471a35330e67520d81b8482ff23bb1977317fbc5fd6"
+                + "bd8fa234dec9404ac26f5482c5707c92468c6bf03f757f8c0706c664adfdc8ccdbca14e1"
+                + "d325b07aba52ed3ed78307c7ca448e7940b5171808d1e93b0d55e13e607b05b3eb1493df"
+                + "ead1faf8acd5ec4c5d4a69df860f109c8ab0acc3cf861de49a59d279fb316cf7eb92701b"
+                + "e08e2dea72f4413bca856e6ad55ef53327290f499e531e9c217c68d4cf89fc47046a8b11"
+                + "f5f5ccfa9e276a8227a99e1678f1abe26fd14f892ee5b57789799da3538da453b436de11"
+                + "fea542a7687fe0b0bf776beba1afa024ea3307d274d7f19fa24a82962a6ae4d4c021ddd8"
+                + "4f9cf1151cab0a9ec8c968f7e2ef3a9e528ebd3e983188ed52b2c05c30b91afa2e370e37"
+                + "d11523286209bbb7197efeb2e37ce222afc3be3b94ef40770feb8d88c5c09c119cb3b384"
+                + "3ec94c074c258bd822af0f3b023c924f3347515c83a0b98e672c5ad005cce80c38390c06"
+                + "7529bb66a360c1fc43538300625b95b808de3bd9a07fbb4795223a2465f107ce4582bdf8"
+                + "438a5846d4f41ed9eab855d011de761aa4b6cd3a34cafab4a433839473e3d55aa1f10592"
+                + "7ec1c621450b49698ad8cac67333cbcd853314770fb93551c7ff095d3b24eaf7ca4e19e8"
+                + "052e5b1caddd9d75fc76cb7e8ad0a2c3f456dcc9b63b76006d4ee0655cb2364147f4cb37"
+                + "0a202fe447700f32483dffd8a46f33fd2126e98e0c0c16f6bffec18553a8d0fb14e1d3ac"
+                + "216fb56919b508918a68b8724bdd54a1a76ed6cd6be093d5d63103e6b5a12ed4986a79ea"
+                + "20358320e0481c0a43f1eebd4b3ecaae048c62a9195126d055fa693980b1fba8c6db7712"
+                + "161aa4de92434ab1b23d887fa570283974e05cf6566b58bc60365e981ed216bd1953a118"
+                + "4a66e7c1de25282b594ea2a0defba13cfe505aa27a8f3cf5c896032d9555bf5bf287db04"
+                + "8225cf09d664cdba2a00f68d27ece641bb5197dbcc670bb10be61677f41a709abb12bb2e"
+                + "7dc0ddb073491650993a2469b24735a35f4ade6a03cbf720050c3332a708c919fe69bbf9"
+                + "98c267ee498b4348b12bc666afe87faade80a495bc30ddf9b45faf5cf2a9ad119646f4af"
+                + "e519c97f3a2d174e802a63b9d7f947f8fb99e77bd58b97ea55ed588eca27a337bbad054d"
+                + "b58be4a02181024ab2d638427d806ef50dd70d53ee5250f82dd931589f2fb7fafe9e8084"
+                + "c8946ebdcaec2c08963bbffd65e86338332444ec414a4c8f56b832f868fa0c14d6485786"
+                + "cd339d077adc4db997c38530b4604dbb003596c5e867873225c4581e66fec82747cf6823"
+                + "4eecaa5682110a1fec712571f261c60d706e423179f9f0050bfefe53f353dc306916ff02"
+                + "ae1fdd98e5e2565f050853bc3cc3f0772c331a214b9cb61afe06072ffac2b86109369dde"
+                + "cb321f78c9a814b7bc8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23"
+                + "ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+            "3fef199d1e8f4b5ec30f289f0e11643811de32d694bdbab50ffb020e02ff6adb4a68e5ee"
+                + "7ec242301d1862c6c9de81673797fc08a73f006d46407454fa8f88c05b8aabfe5397541c"
+                + "ab33d93e9ad585d4fc580f0ea5cbb8d9517b075149c68278f1aa76f25ad625b54fe72466"
+                + "5df891bbed9cb52af8d1768315fa3a44956c3531d8fe4631b6e575bf332f6b50ac27744d"
+                + "4d4036375f1fa6c6d341193e823cd8d41ee035d08f77d317dbaab2480f3d780840311a94"
+                + "a733bb7235cb021167ce047d7f9271f7c3a1ee7e83879ef7fd08e313b88fdcb68aa2afa4"
+                + "3f051ba6a86ec6fbbb5d4645f487cdf01bdea7150a6e5c78758f249b482ce71c4f422301"
+                + "0e6b70a6261452dc9037e4826902b768e326e979bd1f01bb1c90160975f3ec123c037881"
+                + "24ee4246ec64293ce913b7288e760c556dd693bc89a2a93ce8e920bb2678c8fd8f7e7fea"
+                + "68f3c8544ed3873dd39bc7152f9b4ef7c472d77bb60722ff65638ab4584e9c728486c008"
+                + "f998627da2ee446866d5db13e32fb7b5a30bf907bcea2eefcabaa7f53e9a7c94df11ece8"
+                + "02c62d376ab8dbc396d7109ce9ce232207b7cb8cd3a6b127698b4fa8c1291a09b0f11e0a"
+                + "9cdfc1b19aa4f6c7a002028445edfcef35f1a68a51bcd3d9012c486abb8da48eb3498fab"
+                + "f8ae2d21afd0cc52a654869e67cb078ea887f68ad5263a57475a1ed1f7b5e551fdb2d967"
+                + "dd082da4d30028d946277e5c406d07a29e011bafbf14e5e51da7867ff76b4705ae059de4"
+                + "6f3b9ac922c53db63189c154320652d6066963f10342f4f20992271c24625e8286e918b0"
+                + "5e37c287cc0502375786fa930cb526bd6f3413da7e01aff07adf15232a8513fdaf7ac4e5"
+                + "a73f4e4a540ae008e4e52d44adbb1984c08b85540440d56fd63415ae56d978cd145001f8"
+                + "ed780665f273ade06f72364b5f4d180637a06156b60e75247c81d0a37e9a5fde226cdb59"
+                + "5c3161e3fa78ef49afeb1edb2f2bb250fc5af575fada6c11c137b8595fbf57c4c8df3832"
+                + "0c29787bde8f4608c3964beefc2b9f04585bdc6072720f014e5c7cbafba340a5e35ecf2d"
+                + "fb0eea4f1cc49f532eb8ff4e2be928de92336ddb0b3234b5ff46c48f49b2dd0a4f58ee33"
+                + "91353e295843d8d7461deba00102e2964009099804f0660ea37026abcdd7d4389460d69d"
+                + "51a85895f7dfbf4a0605a4d3fc6dc5c83d6e15c4f04ecce15856a1912599d331632bf1a0"
+                + "4f39310db0405555fc62c59518fa8810ded2ddef7c3163ad13b9b788a163fc6cc2d26f25"
+                + "358562f95263acac6b56c81b8c39376162a73591b1f6a2626451cb2f425bef9455fae7e2"
+                + "2c250f14299ee90d9aa9d548a0d1196949d8e72376f1cdc4f56b2db1786d77e8c23ba5fa"
+                + "8a32fe9209c7b621758ec11b4c0b784bd9b106aed25dddfaae73e501f92d6100ec158dd3"
+                + "a981e5311b8340d5445d24df4765d9e6496700124bb21721c63022b3546d31130c88ad2a"
+                + "e927700f3c2912dba4d84ad136c9c70342bd42a458cf09aa3fcc333de09db3ccd5f43b33"
+                + "17630e598b45717e7491582cad253942e5fa36464cad205bcbe71504d2567fb9e1afdf31"
+                + "c423833fe63d05cdcfd368f4ec6d69d599825fc48d901d69d1d9f192062aa6aae0cbde1f"
+                + "1b94a3b88869e92cf52bdb0f0734f2e2298747823dc4059873c0784be9285786b4cce5fa"
+                + "81854c2e19f6804ea3638b69efd10d7f1918d9c18e50360be16f4e968771d537c9898129"
+                + "48475b4218a0ba4e889618cbef4c0c3f50bf63d9402b451c2cd3202b41720965094ad5cc"
+                + "6938c9a9e9716fa599416410d434fcdda0340f2f021b45553856a4126b03f932d242629e"
+                + "585993c58d71bc6fd95e11848a6092bcdc7fbcb706ece63c89548413238e3d7be9c3e345"
+                + "886af0b081254274622e10913937492368d29c790e8e338d7053b33cd6c1654ddad73476"
+                + "b901dfcdced535fc174b06bc16f183a2f67545de2f325aad3c07d4856eeb5876896c8867"
+                + "7683ae72f8851cce00b8472703d88305950925c70deae29d726c5e2258edc84b71f436f7"
+                + "bdb339d1e9f132ded0de3c0aac035f2b93e8d19027b758e7f337311e2a8556fc50a0608d"
+                + "f5d93d45e7bdbea1b8d42d5654d515361804d79e88e1b607ef3309f610f605b9d18bcdae"
+                + "8a7597a6e334e5eb66d7fddec12f07df0bee57a873945320a5e88bf751a4299eb324e071"
+                + "06e6ef26224dc3388ea127930d649276db54f5dd705e3cc94d0d56447aa9798dd62804d4"
+                + "93dd1674e15ecb95fb8a169b6d74c2e21c3b84928591585f26773451edf872063c06aeb3"
+                + "defbe7a6f57d36a195cf16dec3068ab8447808a7c49306f7d8a6b7dae9fafff813ad8267"
+                + "756cbb87d4cd5b8c398e2f3aee3933fdd651a0d52d5ecfb9dc7758d5d4813a67ae61545e"
+                + "9d7498c1f6b1713cbf5fd72c8d81ce1acaeefb67ffa0104b8728f50ccf5bdabec070d653"
+                + "a3eb9a97f5bafa581aa47924fae03fe59d7fd76d6c370ac35c5471ff830ad655b3a619fe"
+                + "9890a62577d73ae56cbf1ba3a17a2094304eb9560bd68033cb18d8824ede87878b04d98e"
+                + "a715241999aa9c78a12682e4e4d2ecc11457af4534d0e21c07781bd3381b2dcc3c7875e7"
+                + "88eb960251dc7cfc6d7380ce3b419a39ac545c5752ff83c298b264539b1550055d9bf70b"
+                + "56e94cf8fd2bc69a70a0da71d0c6b1d9c77a9bb858db24c1597f9ea000465f1d6c4329b9"
+                + "e1b72f3d97c8e86630d84d1bb76f211c6254d97713207b984a3cb98bddb7cf53a834fc34"
+                + "d2c7e6fc7eb41544859899e4ca5f326f3383c44f509ba8139cd3af00d47e6049dfdf820b"
+                + "076af9af90340f6c0675a6136f7d00c4c060167758050915eb64023c04f733695f8e4fa4"
+                + "2f7f965dcf6b8ff1bd1c15c5450410a556b23b6e78337624634b691397b02c66b83cf3d3"
+                + "58e2750de25b7e289b4e9d9774993977c3dc0c39161dc684718ea7c68900457b06f8c4c0"
+                + "d0da7ffa4373478fd9c8b9862890d025565e06be5802c383f82a161b3efa482975d46773"
+                + "33bbaa5c05edca512b059a8ca718bf244dfc2f573a1d448a9329ebb3eb9e278abd23842c"
+                + "320a944b8a86a1fa5b39ba08fec3de7b70b10c9be17406c09772b270f82348ec9b0ab262"
+                + "c7a314d37a15d68338806b48ecc3f0772c331a214b9cb61afe06072ffac2b86109369dde"
+                + "cb321f78c9a814b7bc8df852bada0eaae459efe34872e3a21bb28ea963437c9260cadb23"
+                + "ac6ad5176eeedc517b616c5fea4b9946b83cb72104b5e160d84300f8cee9cae72a659834"
+                + "0569fc1b97aa3083dec862bf5d781b5f15b9d0e10cf9264f9eb2ae99448dff2c9a89c9e9"
+                + "6e176b212b7637a1511733e309447ab932d87f8069f694b42bbd456a503b5c2b5c7876e4"
+                + "60f26138aa68850deae1de2ade824f27721ca0a089d2edb7607a58b0a6a40b10788fc3ea"
+                + "170427fe5b8c0de92797890bd44c9e9c4512a299ce47ce3bdffda2b54cca10bb0656d036"
+                + "1fe1fa31590f01c8902ae4d99e416d0805b416da8c2fce263be00823bfc5b703364eb8d2"
+                + "9af02353840927a369ce6060ad09e517ebb90dd1b9572133e8ce12b2cc1464ef645ad2ba"
+                + "04fd179d27b052fc5042d1ffbd9ebe2ed6c671095e677d54b2b6d304d7e4a929ac011183"
+                + "d7cb0c2ebcb94eaadeade0912e17b0bac06e092358d911ada7b1f6cc25d333976e8740e9"
+                + "266e1af2045fe5ef361d4b624d8b58ffd71ee6113c79e221405a8e9d244b84f4ee0aded6"
+                + "0ef160a937ef34a4f12d91f92f84997432fa484c1eee881ca5f7384c9cf1a93fcd48b4ba"
+                + "669335d68a2745bdf315fc70e3870b7f2ed8d72a5ecec9858dab33e4b8a0f81600300a20"
+                + "7af29c6a4ed63dc40f00fb08bed452dccaab699cd8e31b805118482fbd3db5b10662a194"
+                + "da222e5f0cbb485baded120a3d64a42b17852b1ed150858de3e42050886afbaf608b08a5"
+                + "ab6be149853ae37c0307aa06a0a47ce2f06695ef55c9d051a1faec488e604d0687be84d7"
+                + "9dc639ccae00c438882fea4b737505f75de56ee099c110ccc05e6153da121cae4ee913da"
+                + "84f0e31c9f394f7798f01dc211e2a2d0228ae69e701f8d03fc081917ff83a20c42957615"
+                + "def47fb46cb33a4f2bf94febed7970e10425e9d389b2b080a060c89d0b78f4e0bd6595ea"
+                + "e225acd01d0c89b3e4bafc85d43e19d28c579c31bea7197a394eb68204483855b47298c2"
+                + "a6bfeca80c290cbfe4d4fb9d097639e83c7af928f872804af5cdf2c70f0cd80b6cbef7d2"
+                + "1bcb34ca8f85eb0e779ea8d0a3edd90708bda16f55738e2e4f671adb7fc8820fc72ecef5"
+                + "6df3632406cb884a6f575bbbc93724d9fd01dc5a89bbfd62c2cc838039b37a977a27dcce"
+                + "08b379007f3f2b8f06fd0960a4994b93c5c85b08f14cd66ca5d90e53cccea433fe7e9836"
+                + "734b3f833b00913eb80c1bbd9e90f88d714bfd0ab0b7928be35881d6f58e7a72fd13d18a"
+                + "a587134f8b5a7985fad32bdbe347b7700d8ea5fcb317e054c24ec132a27018d273230921"
+                + "fcaafac0c7deab9a77e7bec0308af72b3ad1192edbeda237b788228a515b17585346a1bf"
+                + "fdebfafd62ff24d6877bfc6b15aedc6b638aa87bdd7ee2e23aa607a16e4f66065b808f0a"
+                + "a4dcc355d57db0e76a66fc42a39575624bb4acb9da91e67e101e0ef341128dfbdde7f4a0"
+                + "f20d79d739c4a3e11bb1b90b7b8e3e7ba7f3c10841ed2e99777af8e0a64d20a540df6555"
+                + "0dd6a089426f82fa1bf597bc03e46f41f9be6147b867643795b7e93e7b34568e210ff4d8"
+                + "2d46e834f011e93972099978889fe98793736ae88dc35d5aa99034fda93a22490b685cbf"
+                + "97264bd719df5d54c3fc23322ab5cfd8d3e7063e1fba9542c662e269e93289d71e1966ac"
+                + "94706e74f9702aed3053f40bff842e4c02ed4753ca69768bfff599edd089011d9752a476"
+                + "888fff2d8734f4ff882b73281e5fcd7e30f0f2633f53b58f13392e11903a93cfa23ccb8c"
+                + "49c00ef344fcf249dafa38d321714633c129498df70e0e8370db620c2490bcd99be38e67"
+                + "dc3d18911d87845313be28e17e2ee3bbe0ed7ab8e5a98d68c1c5e07f169db41e111f98ee"
+                + "858fc7346a5d4e7ed184b2b60ab441b1e3b186c58b2ac33ffa547473767276efd912303b"
+                + "440a58fe95091efde1360e6513a9e5390906e18e302173005028325d34f70a8a85f48bda"
+                + "bae0f15c33ec997d3d4acb6d3bcbd2434b0a83a1fbecbd33fcdf31e4a4044e201257836e"
+                + "cfe7b4c84a181827536eb95fb340da741d3827095350caf3964cbfb2eefe4e8ebb257ffb"
+                + "0904187df2eac9afc6e5e35caaf6e6a855d43b6e445fd91419bbceff8d30832b5e75b2ca"
+                + "5561ad64c306b09ad0d2ca3d17ea230bab0e3b6c1566eeab4693431e683057f07fa77099"
+                + "227a5c3f05308c8c3fb63e94a847003e0f0a78bd936d59b589c3e70ff70b9c688d43eb78"
+                + "35366a6574b0701e8804be965d7a2d2ec408faf31e6a68522b0b8da3cdba52c76cef757f"
+                + "a5fa466b542d5d57ab1504500a07037e1353b244a4424082f66d35aa99bd5fbb9a702362"
+                + "d81812771aac607695050c62cb589f00d614695b163af1e86bb3e6c1505609032576dbb1"
+                + "4d42b421e72561482737faadb6fd823dec0e1310a99c9936a22017b9149ce23cbbdf77f3"
+                + "4a57050b5f3b414028ca939c31e95655e909c630ad9dfe3f3f09a250e578030f4c3e9099"
+                + "dbb0088d58e1b4046b97b2f61d9a2be4cca9bc0240f06075850045ee17e19ec504e4ae27"
+                + "3934909cc4840fea6766af2592fd6c12f3a82ab05856a646786184c68ac2020d2a8fc782"
+                + "8717cb2f89cc2d42046d8b618dfe4fa9e90b15d5c08ac3bac5c197eac5cee915e3fa0c6c"
+                + "4730110f2da5e66c0a124c26a62ede1e2de4357b275ee84d020410668a3efa1e828cba53"
+                + "c62150bf2691afa904762f2aa0309bcf67d023cd62d1b7c37aea8b1d69e36e07f5b81828"
+                + "85d3b526b02b6ba5b3647364ceeb601dd90c33d466779fd06fcad909db0fe5f376108786"
+                + "a82d336cc1f79fd23264e5455938ba924fce91d6f94e0ad00275b5e5c063ace5ffcf48f8"
+                + "4e370c90b51c1327ff6e5105699795201784d8479d695cb70567f8f06698386e751a552f"
+                + "e72bfbd773226a10eacd32fa2cebea1403e796c2e1c21accc9f76ed21f05549fa17f10e9"
+                + "7bb8736cf345d70b2fcee0a2c0044d02b5615c2e3f5075667942e2e39a40a3630fceec36"
+                + "53450b84307bf027a3bc78033ddcc5841ef3462e34da3778c496bc3f29d55ca47b64db23"
+                + "ce488170a5a0498a3a0cd0065b6ac1a8676ef1dd5bd8b14f298390a29c1df1403db94993"
+                + "b9537b3e018d82875623b1ac6ef0b549cd2b9676cb9b00159139a3161253ffe47e77ccfb"
+                + "3ef8ee1119ba56b51e5c626de41f5ba197329939971b534ef9c82ccbdaccede15e6a945f"
+                + "a603cdbac2ebf385846864fd30",
+        };
+        int height = 6;
+        int layers = 2;
+        XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        for (int i = 0; i < (1 << height); i++)
+        {
+            byte[] signature = xmssMT.sign(new byte[1024]);
+            assertEquals(signatures[i], Hex.toHexString(signature));
+        }
+        try
+        {
+            xmssMT.sign(new byte[1024]);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+    public void testSignSHA256Complete2()
+    {
+        final String[] signatures = {
+            "006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf8030347a"
+                + "dd4d942530446cc44c26dcd215193c168c0b255a0527f7ed94b7df5bfd28f26560243926"
+                + "420d87b78f3d7388af1e85204c7cb389a9d33db5e913d3a4e26044c3f4d8296c4e744bca"
+                + "233a84b42f0524ee3ccf68eca45b66de7dfc123cac45a2aa2369c746b540b4573813c6eb"
+                + "f40344f7cd5ac7222d55d812dc3fd6f66a81b19f27562e8c3ee26a04db098db46b4695e6"
+                + "be887a3db47cda06c80ac1989523f0f3241aedd852c7de1bd4d80091c443c25c0b5567a5"
+                + "94584163ba01b609ccee1fc8c2e4ec4705957094fdd5a5d62ab4cd1149303522c06ac7d9"
+                + "c461cee06d36db642565a964f96ee40be0a75ab3ade6a952305655a8ba8b11ceca31bc37"
+                + "c5ec4650e67a2a8010cb348b81283aa265659ad68e4f45a219b5f104e84afcccbc759120"
+                + "64561f3d07fd243b6840fc3a9c02ebb9bb92c0077fe7084d7f81c64dc7ea1ce1efc000b2"
+                + "0ea0f854f68f9ee1a03c414902bcdf648eb0a6b3512f710e2ccb6bd46d58c78322efe0c9"
+                + "4ca8be6a35ccbb75fa14b913965ee02792b021c99d790367633318edd0c73a377db1f03c"
+                + "47071860b8a6c9764cf75e01a0d01921803286d635afe6977b30a653b2792aa8d8ce6597"
+                + "c87e046c603f44355201981a03827e7d1ad02d7754385e708bb697b5ac2c6d8284800991"
+                + "1acef13095cf1eb1724e239c94dd6bd0123d13e44e5bdd2344a019c5ea46e947c81cf911"
+                + "a8f18eaf687a5a4d395072d53e6edcce71e849785e2d097dbdf82eaf62efaf51897de4b1"
+                + "7997c72fee51d5d822d75acf9944db04e5d1cbaf50f83550c6fd4a360cf30621ff1b507d"
+                + "47a07065f091af9bd30d24a5ea7aafeef7537d05d896192e18422792b71d7d725c157d47"
+                + "ca2d9c33b8fe747f73cb344e51a19bdda74141a2ad62406a1dd41d4046a30994f50ee6bf"
+                + "b7b2450934db86c9cba3c4ce79e6348e18aaa693819d39d64aa6ee4e1ac5ad39cb71e35f"
+                + "e127533efb0344450aa55d61031f01ab160beba7667deae619ef4ed46074c899fc38dd18"
+                + "94896c314cb4adda6f7911f2ed6e1e498fdec6d365fb5858c171583a92fb73fb66dd4142"
+                + "a6aafacc5407aa52f78cc82a284f52d6b3816b708c04d32c63b3ef37a08a07926f521c45"
+                + "29e59f6cd74495c5ff49993a5ae898bec172babd64b398403e7207a5c703d9f04c88a975"
+                + "14c9440fcd90ca009421adc9fc22da94f35ad77f42031927c097a37a87d1a3ae35ff0774"
+                + "102aa52deb21275da6481905660f2b5149e275a3de718e211da518d0ec346007f9bb7180"
+                + "0d128f6da23ecc6babed12bad95b3694f19921f7135a37ebead795ecfc7c9afa6893f762"
+                + "54d4667df608bde6e126109676ba1137eea67ea1d25a818c8af76eb64d338da019803d84"
+                + "1efed57c6df63ad47da40f585121148de8c4a3c10a0197875b05063900b3101ac10f155d"
+                + "88101807032b66d9d9843e7960cc395658025c47e45a5ec746fc695c51a657622456fd09"
+                + "e38c2faf409cdd53f106ba7626cc2313805ac36a534d8b21af77bca84a5d7fbb750f6c93"
+                + "f06225e4452dee39b615b0f2feccb0fe2db1331633ddb90b31e4af49aec2de88dc59a68d"
+                + "aba6a485396abf56a5de1bda251c096832ecf8127de7403025f127b5f3308dbf04033f92"
+                + "8b2d0ec74349cc1c737ede88510e9ad62cfe1d5ca9c22b8a09b81a094924d3fb069998b2"
+                + "1b401a0c1bc1ae2850f1e2a45cbf3c436b9e4723432d009cdf9fcb20926ad7c909688855"
+                + "b2c7af757e64e4b3603c3af4e67809f9abc5688f82f608ef06c531e95ffe92b0fcc0c5e1"
+                + "0b03b29d5f3cf466b562ce441e4fac44df6036c07228a5196ed9b7b17e3f78dffb5d8333"
+                + "09402b82977478da2227ce1857bd66c566e34bad421bcc51558e39dde6d086083a40e392"
+                + "ac0e1a6205b421598ebd717388dd7b65a3edada491ab6e9dc572caf564e4c3295c98216a"
+                + "005cf0e018c4ce5881819c3e6fe65e50ab7a71b629b5fac0ad19b556704d8da30ca1a66a"
+                + "4c1c037dfe7fad1e7f9087d4f5d4c7986aea352e862a06f49842247c296683c0c1cd4c4c"
+                + "84ed8efbb4ec60489a57d2bda9476f158ed7f3887447244bc1a8915efda8ba53301d080e"
+                + "8fb7fca806ffd7064c82adbaaa0e90ccbd808c08d07758866952e25d86cea977c431a8d8"
+                + "8f56aa24d82b936343029070f2fe38d1ac3be1783de3bfe1db3d79ca9a986c8c6d31b530"
+                + "84a4b0f5b88c979baff4c991da692a45b583829ee442cb8d5fc143695d94d1fa4228f096"
+                + "f62e13143d4cfba7a9c929c5cd196a37d5cd3236594343d20b5d7880627995d5c809b74a"
+                + "6916965364e508ce32358169e7880fd1fb6d48c450de04642024f5268e7bc091d2fd3658"
+                + "ed7d2e924aed7e7a03fd9451d63459345112f3cda244cbc4a95c1d5e445b928f98195fab"
+                + "9c2b7160791bb2cd40f7462e6115bd4eab7b3bd711054a5dff71efbcb5e9f9e1fad83e33"
+                + "df5e46a182b843530472bc9a8f0eacf75bf8987e58813efe3c1633963202dac839b645d5"
+                + "253713b7e7c655664e642c1591794863bd65e69ec860be45ad6dc9b4da0ac90eb053be59"
+                + "6690bf75e5876af16321389c929577f571124adc3aa125c3ef8146365d3d5b256dd13e4f"
+                + "615a64ef54c1b14800ab9e68d6d5e63088196dc626091be77018a00c23f48b53a282dcc5"
+                + "839b522507207f0a6b16b168f7fb4c6ce52bb03010a4e579c00b35415f1fcd4f47b08cf4"
+                + "47d11140bc911787b61058ac55776bf3b989c79c735170c3932d617eece741ecded3211e"
+                + "d345e8bd9beb375380d8255b8b04df1e0a1795b3798a24a0182d9a969a4a554b4ecf9396"
+                + "b09bc2af3f79bf6a753da9f79a5294947c07a2caddf3d6a0d4a8a3289ac1e6fc5b867205"
+                + "90a50d2beb7f922c6b0d55c93a5af80169346014021caeed3cad3b2d83609c7a99189675"
+                + "c5e67460c3e20da7c2ccf55c8e447fcd46650ed01008502543594c29baa89a3b29abf183"
+                + "2de35d9186e6a6fd9034628127c2b1842d377fac6818689ac8eda4ad6c32ebf74e9e7e55"
+                + "9ff0652bf58b3ba2ee9f5a5e5ea1bc1bf3e0e81477f50e2b8719d1d9a3c1a55d5460711b"
+                + "88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d"
+                + "467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde"
+                + "4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a761530"
+                + "38370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311"
+                + "e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e"
+                + "693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa2446"
+                + "04dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b0"
+                + "52c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404d"
+                + "f9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a8989"
+                + "3a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a"
+                + "6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad"
+                + "13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c"
+                + "8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8"
+                + "523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e2"
+                + "1fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf423542467"
+                + "8d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e154"
+                + "57ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b"
+                + "1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d"
+                + "99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa9"
+                + "2466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c"
+                + "27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cb"
+                + "abf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062"
+                + "264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa2"
+                + "5cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e"
+                + "2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d"
+                + "4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8"
+                + "abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c7"
+                + "35e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d"
+                + "1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9"
+                + "b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce"
+                + "15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf"
+                + "03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ec"
+                + "dfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f37"
+                + "33f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b"
+                + "03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee"
+                + "5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e3"
+                + "50273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489"
+                + "a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3c"
+                + "f9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bcc"
+                + "dbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9c"
+                + "b583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5"
+                + "c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b"
+                + "71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b7"
+                + "7625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3"
+                + "e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae"
+                + "5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af84683"
+                + "3f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ce"
+                + "af360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c8"
+                + "49cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed"
+                + "52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463b"
+                + "fe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9"
+                + "126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3f"
+                + "fc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4"
+                + "c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254"
+                + "f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba"
+                + "7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9"
+                + "f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d"
+                + "33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc"
+                + "0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c"
+                + "0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4"
+                + "cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd"
+                + "0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "01994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c2b946cc"
+                + "55e90d1e91702a152efb6cf47c250f7034ed776f2f948a656ea362f4c9d50ceab45fa2ec"
+                + "26ae272a2a3797b580ac874bda3345cf56e069b6540ff8f603131f3aa0c35895e898165c"
+                + "df763a6d5c88d4b2b88efcc10dde3281ab6653b0af9ea36dd569ca57b3ffead498f1081f"
+                + "fb38cfae75b368721f67ec1d9b5d7f8e29a2381c180584d9033536c5c77bd55d03577deb"
+                + "b4ec171bf0728172858b21c6005d22e542f7867f2452e3ce5c1b3938eff1d79a74338478"
+                + "adb790a374934973c2b8597058dd84957d3d2685d8a7be23ab230b3d83b724799c0f235c"
+                + "df3d0d2e0bc9d961759ea19ea0f6562e5f553b13a060b6f597c7f49c837a0456929ff01c"
+                + "c547d3a28f6a62cd9ca5b1965975d10a10667603c64900695dacb0ff43fecd4225577ab1"
+                + "c2867a6dd3b0b6ffb90d413bcaaabfc37f829aac0fca5e66647854deba07b0b93b3c48a1"
+                + "47e02075456ace562178131bad047d9deb64f4c13205cee916cd0248378eaf715bac2b82"
+                + "f5540111150ef1cae360832ddac01ea6ad1d9019d3c66c55962241bcf85221586ee2a390"
+                + "e6220367c9cefccbd66d4c3ce2cf38bbca2d0ffaee10b04afe2088d835281d51b6e57894"
+                + "abf2ce971bd7372fe3e7521ce0a7163c431519be7717f43f0b4d85a277afd75b91fdd075"
+                + "c4d6d303110535305b9dd514ec9c46e0cd84f5a9e4c0a838036915b0fc3ace08b991ab44"
+                + "d44d567eb737148aa51f62e3909f9b6b1f690e5464f51ad4bc8e2fa38e85fecba6db7921"
+                + "d3cf0339ff2f3b01174abaa95e32fa01078e541db5479553eeffbd97d56ac15df3c5c074"
+                + "c0a0f087ed4bc0a47d9d24cb30e782138779862b15b5367534fd4bcb1cd92d5e6d4f612b"
+                + "d5ea7c03585052d8468c3644ebefeff03e62a3957f8adf4f2698362806594cc6600ef658"
+                + "38d6ffea9096e3ac3a5e421f550b584d74b2592c606aede90a315ae8d580abd5e7a9c595"
+                + "cabcaefd47e224a2d35607d601a8f9bbda0743d17ec0a9e74d4cb477d3103cb88d06f6db"
+                + "d7c038b54a06c7bc6b48fa3929404bd0529d5abeeffa5c778c08702da452d9129654569e"
+                + "1fe12997143575c7a58b7de776c1d9efa06d93287b827d6f835d6ab2a4c965857967f0fa"
+                + "844bb3ae2657861f34288ace5f4f78b607efc8a41ef5ba1ee3d1446b8c4d9931ca5252c2"
+                + "b44aa4f655e80036b2395b26352658af213fce775b404712f85584cf0b7a9c017017501d"
+                + "868bf701490ebbc7c93d639067f98cf6bc9acd587350de7b6f783f75831dbea633f8dbaf"
+                + "804a019deb631c4a8b19bb36f583d5f6aa30bea7b002d48aee10ca4b9d747c61bf44cfec"
+                + "c13a75a39528a6c2677bbcd483140172078aa44992328f671b6e0b5d18edff0bc335ebd2"
+                + "ca9afdc73dab4955f250874ff7325655ba9b3b83ebb460193e323bd86c7a5799f863816b"
+                + "6cf12006a2d114c252807eac1e4dce16dda2f4dc1e9794ee2fae01bb35eb8d4d20b336d5"
+                + "6a0fb5e128a635aaafa05ad631eb03cc74bbe5a2d50d76a79ada495260118f9abd4717a5"
+                + "36e3965d917065c9920088846ea16a242cb1159ef67196b9c54b946385c3691b0cd8ff22"
+                + "5f4b78fe0aa99f4c2faaa2ceb24203c2e9e18543f68989c7dae98facd6a610a023bc3a43"
+                + "16ddf99454cda95e6f311c5d6e47420034c7c267435b7f83faccc47b0889eaa759cb901b"
+                + "5687b7cd076d30543b930fe2a3128afc2546f75ccb77f626ec392ed0918724d9c9a054ea"
+                + "e464081288292fd9e2a52e388a55badbcb352d7bea871151bd9062578d7cf26920fdc16e"
+                + "a40e8af876d6592cceaf266fa1b14b9c9e3c9dedce9d03796b8e32ea33b003ffb6dde709"
+                + "f853c88943eaf9a6a36fa821b920e28b3d48b8fa47b8b8a4013ee14ba01bc3721ac8186f"
+                + "46d00b61ec0d9c68b3919c0c6e47603cc88aec0379d23aa55664cfb15138aa739a08c820"
+                + "a2c87273bc6b7c7e2f75b95b2c12880a2002594b6862ca9f3700a1463b6a67ba9760ad8f"
+                + "eb851f8d1855b4ae4eb716ed0fe73acbf35a96dc33020143e68a1b1b884cec92ac1e3282"
+                + "9200c1acd4fad52e5d10734a16171fa8e4b711c99f993d185024f7539bf86038625df9e7"
+                + "0ae98c606e3ff095f94b1eb3263cdafc9e401d29e7b801e2875d9e8646e68e85d4b9d87f"
+                + "1c2bdc4193c5e691542894ba42197cf1bbacea12906d5c578548a7ae5d27ff96c63e8945"
+                + "26c3cb393e7fce900ae4f9dad940f0b64c61c44c65d9b0b7a498f612a78de3854c894883"
+                + "7d60765cc992a09d1dac33df336c5f3dc144f85f260195c82264cd88815362a1d2dcc432"
+                + "a9b54e670b0a7fb205352789d1a1159098d7bced399e731b80f83ccac1ad619d9bfcaedf"
+                + "dba9c6600b9a3e372741bfa9792eb719eb53f1b4b73b798c9601fe77342bd462fd700f69"
+                + "333598e493202e218b4c1bc619bcc53761d936b9b0a73a28a049be5d73bda80860dd1d7b"
+                + "ed7f9d8e3872f331081aee35ecd1e81fae67643b38d97cb3a81b97a9d0ca68eeb72b1c6f"
+                + "f985bc1d0099d12d2dbf3488c48fb8d0ec26e737e51e553990395534e8961c6797e73233"
+                + "d418f1a2086ab06b17a47fb4965524d09ffd1c997cda75d8f242215eb7948e2fa9f72c3a"
+                + "92879fd2d17cf801e03acfb9bd0d87c18f13a89a494a629d05adf7c7a61109f811075011"
+                + "196aee7d6dc85817c7a307d28ee2162f3bf61329b847641e95d83ccbd42a482764d6a9a7"
+                + "84245f5333027f53bef5f6b4300ab6a36a09c622832d8bcc49cd531af67a5cba36ccd9ea"
+                + "0a115964a00395c3e28cd168fbd6c6d4e146f5bff31c8876085bdc09663fb8387378293a"
+                + "8d188410ff4ed23bbf8e94e8ea3290c219c68180b8a59ea5f5e97cde51018ddbc835ef46"
+                + "658b0a5d79625599dfe624eb52e88934230d23a77c92c5408f62d87254ce43524857313a"
+                + "22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1a5adc3c5e82a34f307277b"
+                + "9a09c038aca00a99ff63060dad783e060e4cd9d59c5c9a5a013c7d555080fcfa56e6dfbb"
+                + "fbbc58df90cc9cc65fb10063623a3da00ba5b2d2934fff8302e88ee8c6fec5456a05676c"
+                + "14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d"
+                + "467186eeac1dffce382df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde"
+                + "4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a761530"
+                + "38370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311"
+                + "e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e"
+                + "693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa2446"
+                + "04dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b0"
+                + "52c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404d"
+                + "f9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a8989"
+                + "3a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a"
+                + "6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad"
+                + "13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c"
+                + "8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8"
+                + "523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e2"
+                + "1fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf423542467"
+                + "8d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e154"
+                + "57ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b"
+                + "1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d"
+                + "99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa9"
+                + "2466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c"
+                + "27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cb"
+                + "abf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062"
+                + "264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa2"
+                + "5cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e"
+                + "2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d"
+                + "4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8"
+                + "abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c7"
+                + "35e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d"
+                + "1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9"
+                + "b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce"
+                + "15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf"
+                + "03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ec"
+                + "dfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f37"
+                + "33f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b"
+                + "03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee"
+                + "5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e3"
+                + "50273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489"
+                + "a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3c"
+                + "f9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bcc"
+                + "dbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9c"
+                + "b583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5"
+                + "c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b"
+                + "71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b7"
+                + "7625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3"
+                + "e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae"
+                + "5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af84683"
+                + "3f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ce"
+                + "af360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c8"
+                + "49cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed"
+                + "52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463b"
+                + "fe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9"
+                + "126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3f"
+                + "fc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4"
+                + "c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254"
+                + "f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba"
+                + "7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9"
+                + "f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d"
+                + "33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc"
+                + "0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c"
+                + "0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4"
+                + "cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd"
+                + "0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875ccc2bc23"
+                + "415d4faf87c4ce6e7525be296c65bb2798f19279dc59d02830695fac8bb044790c3dd1ad"
+                + "def9b04a83f6e31ca470cb9d3cd02bfd45401100a35927c9577aa1434c71aa29cebf6beb"
+                + "bd5d209eeda8864a250215a168764a228979c671a5c5595946e83d31dc36a52777616c18"
+                + "3714588db1760ef0c8e537c7504ea3400cc4a2845ed647066081db051fc3ed7a541b8f29"
+                + "4597aa2fa5bfbfdc714fbe7493ffc401a28fa156cfbeee5f1163da5bc039297fcb5fc536"
+                + "3b48b2bce5e558a8fed0d7aacffad3f2d41d72e0e157faf66658dea45aefba44990912f1"
+                + "387af750ca53e94ec9dd72bc1e273c15024105fbaae774bfd0b84dc7314a3fae56777c8b"
+                + "960fb2450e360489900c5eeba5ad0a80325559431c9128d4d861b546b8f344417bc48694"
+                + "0b5e8d27f98c90067a849af397a8798c3d37d24be57c1d73c5f47c67cfcaeb85354b3341"
+                + "45e794b2454c5d9484eed5c292e3bc875d8ab56079e74b5a4c7acdda83521deea1931969"
+                + "bd6386934a394009f1a214e8ebcedc28c11f5c38c550dfa47d017dc4306026e12e98fa14"
+                + "89c6f0366c9e3f59cbba65fd3020d39048f84929a4bcb40d65fcea84e461c4b3fc2f8d4a"
+                + "1ba3e561be9805fdfd74741ab476adac59897ece299db3da7b4a250244c278e8c4ef0b31"
+                + "f1079980e2b9f5f2223c879568d89157ca0a22330de5aaee6501b8af26870bfc2f3fceec"
+                + "e9c76c8ddc2529b48f96df991d4cdd7cd736ae84e09ae833f84c56811a851e9bd76c4eef"
+                + "4308430d16a056d1c50a2121f9720f408fce8ae8cf1465a3386f62ad2458e2e1f9c33c86"
+                + "3ab07e4ece39d66402fc2443bed9dba5ccab665865bfe7519a19063cc487a11619c8f5d0"
+                + "72fe5935fc424477bb38be9fb658eeac200fa0af18004deabded50080c47b7315150768e"
+                + "edd9bfe586fdf37d7a0c74807838fafc5a1e5e64fe1b3d70f5dffd395f5116e492080413"
+                + "7907aaceeef41e2a77e15ce6605d9d48e28f575d2f544bd5678ee67759d0f9078a33177b"
+                + "2daf479d53383e010f7cc378d2449dea19083128f32ddd41173123dcff9e06738b439f8e"
+                + "46b6670e22a5ce5ebb2dab417ff5cb5ca4ec6d441a80e3541bf4c588a74cb2c50ec9d757"
+                + "85abb54ad6c4a5260d45a795c9326cce6561a276e4bd11418ac3db09b40a29a6d5c81c0d"
+                + "68175a813623caaf0ff586330ddbc6dba9ee39e62da280ecc13ffc22f83a8cbfe7a98d9b"
+                + "af60e824b4b4cc54db0af44b23596422323d4bffe4ec4a8d02906403765e57e5d8d41820"
+                + "167acc8edac2c4615c8cfb730052e3c1f2222cc00b61691464c38bf725e61e303323b006"
+                + "809f4307c18f798523be6aceaaeb2675c871aa3bdb23d1435ff2a769c94e39ff51ab84e6"
+                + "ea0c4273fd8cd18aeaab4ab789f61e2a44ead5cdb47cfd9f6230a1f54fe0fa0041cac8a9"
+                + "8cac7870072a28ab79574ff468a8c791c312ad81422316a86544faf93e4f54719a02960a"
+                + "ed675d4c38b11daa308f122391f53218dddb6eeec42106980fc3a46a121f507d54dad5c5"
+                + "dea73bd7eeb12e1c204771511f4d2f07e70c3ebeb08b4d61d108de42d06a069a1ea270cf"
+                + "36cf7d22fc71465bb0279670206c9cbec762300bc336ae1b89f894b2cadc9a69f97f414e"
+                + "22799c4e5312a98a72a464ce11850772c4b6bff995be2567dba1364c9828041feaed5f3f"
+                + "a9d031a709279fac65bdcf63a36cef9feb399a711a744071df6783f5dc16ca0890934c00"
+                + "dadf04012cfea4213d7a66e468ac6e7a6ebb6618f0f8bb926335a31bc1684185e4c5c736"
+                + "3d7240ddd7bd1700dace25f48e548ce21d01a4b2084bd55b84b3de187dfeb8cef29d3572"
+                + "f39c2742ce2f8c2360d8aefdf3279d1dbe472332df35d0ffc1e87c2c80f66e2c72d1fd51"
+                + "a924bb3c61f1a81d97aece880dc9bc403cf212d8ca904098f24634ce77912b3436cac540"
+                + "77a9de2ab86395c31a4078dd24776650d0da94b7c499b68a58bc695e03b6e20c77684ba3"
+                + "9b0680702147aba3ce04d13fadeeb02bb9f0b38693ed7d446213b377f609dd001f11c61a"
+                + "647ce183ea5b647ccb3fef9eb104ac9faa0a4366160a7887e65e0fdf4f5d9e818b67f83f"
+                + "7cde3a3c750c7acf38a038870cd1192fea9e23d62f0ff123ce2837a2d1c2a16227bb8511"
+                + "8e77d7630ff0d6c7f64be35751259d2ce6150beea128c57064cf05381f0d690b88c943b3"
+                + "d790c03f1e3c6894c59b2dbf489f8b2f1c5035864b6d199943885e2f8532745ef3615a14"
+                + "3ef8132ccfd21ea4dd0428d4270f939dbb2cfa4050abe2f03530da91fa11c7b898cb0878"
+                + "c5eadd53651f353c34eff767a5d42b8ad26f8d386dfdf8005da8afbb4f8ab0d94c416c5c"
+                + "dd07ec8fa329353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17ae46"
+                + "0dd2c5e5bab23b278d47eaac4998231ee0ec13f620be038de02cae415c61d06c12f74e8e"
+                + "1175d1809755ca24a7eb94b52573588ead5fb4bf5a298af69979579d50e454e1c3cbe628"
+                + "c07cccb63071d0d6c98f921dbbc1eaa03e366efff3d4a50b270d419d6acc16d54c1ffbfa"
+                + "6db8aa3e76123903ff4b169b5d0b129eef63120386e239f55cfd9f4a6f6b88de7bfa273f"
+                + "322bf0a89a6bf0d3762077634b77634b0b5c03e4fad42268f05d84ec8a6480751d779a70"
+                + "b45996ee27cae86e78c12b8e7d327fa9fe367d2d26221217910c5dda292a1b9f2879badc"
+                + "8dbf5f15af72483f4e43390d1074a64c90b2ef4e670c7acee106eecc06a1c51451ea8e75"
+                + "31d84c72cb2abec68a5c80761a3c5088c653d48c04a9ec2dd29defd1ecd59b88abe0b4d4"
+                + "fed28508f5baca75c222a0c00eca957c7093f0c76800535ce2f4395b19a48587b2e54e22"
+                + "ad4f063765739722ed89ac07c9738803ffc999afb8f2527436c69003228c944f61f837d0"
+                + "0baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d82a5b8a44a19816614b03e14"
+                + "656ad45203050629f14acc5d1e86828acfbe06140ddf140c79aea8b327d4ac843593590c"
+                + "d4749ac5e2263ffeb800e9f87c5dbad07a9be4aa2007955dfe8862983d252d8b79a33e9b"
+                + "1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c45"
+                + "11ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde"
+                + "4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a761530"
+                + "38370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311"
+                + "e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e"
+                + "693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa2446"
+                + "04dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b0"
+                + "52c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404d"
+                + "f9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a8989"
+                + "3a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a"
+                + "6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad"
+                + "13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c"
+                + "8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8"
+                + "523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e2"
+                + "1fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf423542467"
+                + "8d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e154"
+                + "57ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b"
+                + "1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d"
+                + "99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa9"
+                + "2466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c"
+                + "27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cb"
+                + "abf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062"
+                + "264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa2"
+                + "5cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e"
+                + "2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d"
+                + "4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8"
+                + "abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c7"
+                + "35e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d"
+                + "1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9"
+                + "b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce"
+                + "15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf"
+                + "03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ec"
+                + "dfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f37"
+                + "33f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b"
+                + "03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee"
+                + "5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e3"
+                + "50273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489"
+                + "a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3c"
+                + "f9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bcc"
+                + "dbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9c"
+                + "b583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5"
+                + "c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b"
+                + "71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b7"
+                + "7625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3"
+                + "e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae"
+                + "5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af84683"
+                + "3f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ce"
+                + "af360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c8"
+                + "49cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed"
+                + "52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463b"
+                + "fe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9"
+                + "126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3f"
+                + "fc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4"
+                + "c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254"
+                + "f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba"
+                + "7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9"
+                + "f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d"
+                + "33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc"
+                + "0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c"
+                + "0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4"
+                + "cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd"
+                + "0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "0386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647483b921c"
+                + "75c2c7bf7f9039f2ff3d1de086883313fef5dacdda84e19ff29306444a28d55e9a0f5218"
+                + "fe59b15437d77d2baebbc4123f6419b963c85ce97350cebd218111ad2d1dbb782a95d902"
+                + "d02157291b8511555b949cfdea77002d966f3f9c6e1ba486f60609701e7e202db4f623db"
+                + "28614f7152efabb995a79e5e2a8577effbca65919b91eff118c81038214df51fd883fb56"
+                + "e353334d0e3cdca46afefd63785d38a3f5e668bfee902b3d2b3c8ca2caf98a52fc418057"
+                + "f82ada73c81e10297955fa4e08154068f7dd9eb51a67d2e8eddd1913404b2df856a66ad4"
+                + "68e28bb2d159c550c3894ad3749108cd04968e98e932094974e35a464a7280bf0de93b13"
+                + "2074fbccaea7e25a01d304a1cf54710127d5cf3242d73b5d310780adaa30962bacdada11"
+                + "10b69b3e9aca6cc37e9c7f019e9cf19453ca13fa12ef3968412440b038f9566a34eb470e"
+                + "cbfc99bafa05987190c4775981a7a224cc0428971341ee8d76167e0743619b3011df2bb1"
+                + "89b4cec9e2ed873660b8185e82efeb3bf00155ee2152238e7a8f7749c5337909185b5d6c"
+                + "294e5b827a7773d62443697f8cba298ec69e1b8b9d59e5051bd1291ffb52287807240a39"
+                + "763b15a90148ea98297d877057cc9f4ab6401f05a666212b554cedc2dee814cd975ecf10"
+                + "7ca2de07db4bc75e6ce5015e49fe0c3a1018235b026fad4134257a1d49c37caa07d9b320"
+                + "55a7323d5a4f90310c04b44ee32537981f6d02a7baef1a8939378daf61d807a9e1003e73"
+                + "7728deb4e00609c4f087e9c178a61ea4a4c65cde7acb55dfbc0217c41eeaeebce3ef5e90"
+                + "f65629d199a9077294cfff05d27e31c6de75797287d056eef0645c1073c3aed5b4c90c53"
+                + "8bffe61bcbebfae8601db23622e41ddd6f2e6fcaadd2786cb6bf50b7e5cf92f5cc6df8ff"
+                + "ec7584f88a9deea3b5ab4a8d8ba79c42b9bcb028ece158baad045b7cb248ac42d5ebe15c"
+                + "2476a16e25616c91036c6e3a4400de3619534109c949b2f8ac1625b128253b378c5caaa8"
+                + "766ebd7c0b1c60e76bfda0b067a9ddf4746fd90227b9b6876f771451f16a5c69bc6a92ac"
+                + "8388b218bab526f645fccaa1ad27539738778292ab1c9481c8105bef50552fcc206bdf26"
+                + "ac83d77d3d8ede4f9ffcd8624bc6a46996d9a5b78c5533249637996f1ea03c949b27e543"
+                + "4b6c9c7d2fe815a09c746e6e9fe1c437971055a1b0c44330cd52711790a0e284504e9cb6"
+                + "205c87adfb00fc917fd384b5a0a8fe1db836f2ca0a16e6734206d8ff07da4ed2987de460"
+                + "3b4dd67d1aac836897e03eb38242b3f135972d0550073d19bfbd1737da36ce12ffe3e512"
+                + "c3a7b6175aba3a26a865a478840a940a29f464ce201832ce5bd731854fd5e31d0ccb4421"
+                + "e4f73f121f4b79404d06786e3318fb337d3531f0ef8f56f81789286f5dc23c091ddbe35d"
+                + "859d78b71f07921aece0e853d35b369d82948a9e0dec94cb7d8a9b72332f0acf273976cc"
+                + "282661ef79a66f919e1309c1b49fd8ee56b9374210dda5818ff593be29ea5826a99b8b07"
+                + "1e8c16e9a6a1df9ab3a1979248d89a1edda1bcfc6b23d7f08c0ec93c583d6226582a54c6"
+                + "2849c2aedaaada4bce7e004e728ff0ddad26aca520c84e5b8383be2c405ccfa3e7fb5ca7"
+                + "16fa451bcd967107b781321edfe3a72720ce026779b81313e641eb78276c008bbb378507"
+                + "d43d8a85544b1c7a496f9f8ba77531748a22e094bff7ead7e19734ac54d202bd6baa1e15"
+                + "d620ba90fbe8b357124c9625028f8eb651a03e7c3c9e9de78c2f767306321160208cb7eb"
+                + "af99dd8b79e5eadc1b0c60f7292c95fcfad7dbaa69b7a483bc2806f4c35b4c49da1799cf"
+                + "be893cd4c1f17d42136518480bfc026a534dd37740ed73eccc532262958e9d05ba25f29b"
+                + "7e91f7840e0a049e61700639ea6e1273e5115c537326d07f43f70969c23f60e21f9a4a33"
+                + "1de1b56b3aa9dd884839e2015b6665ceb4678803291ca259b03fc16c99ae02bebdb65c57"
+                + "9024d8fbfa55a785dc598c3358ca8867724e011dd6ebe2b04b3c57bc1f7bf43f8b389e33"
+                + "d1c2bf69790afd6eb03606db6aae9848e250f98dcf6654e4b2aac51b3ea38ef89f41337e"
+                + "c131ab3e3dc3959b78848f2dc51c9db0fd0fcc3d2bd670d1c29a501ab48d3368b185a644"
+                + "7652797a35c1e5b0135ae97936715dcb1be9975a37ae4306590a3a463b0970b298fd70c3"
+                + "2f852872c4bfb6f7b6853110e9b7e56760094e5fd6e6849f9ffc7e4f7ee88cb8c2113ac0"
+                + "9bb2eaa65fb5ca02376da2cf087fbe50283076a9e6ee68e92a303cdf6ccb2fd9a7e9a251"
+                + "00e15de3c3beb5562e21f1b93199e9b9b4327fb3e4d416252981d37accd370e294dea1bf"
+                + "20283adef6dab5210a224b9d7df6b603d12b0fcf9faf24a7530f2b17b7172891f6905384"
+                + "0af6f9d11ce19e128e27283e30fd68c8906f7837c1e81feadefc3df9923de87235891086"
+                + "9420eb0a78b4c5a8525b7dfcc00e919c2205d1daa5c65b7999806aa74e5c594b22aa0f9a"
+                + "baac67be10b73d1d2008ed8410ad0acbac08d3b35eea273bf9667061eaf2ecbaa8d1cfe4"
+                + "4f62ad79eefc464b3734ce621121b70e0d26df8bd7003a5c45e2aa4d554fe8047f385a9f"
+                + "833932d4c0863bda946ce30cd81de19bb5fb8a6280611d1c3236c5be268463bea4a42896"
+                + "70e195ea6d3dabe7d4c1e58477fb9af5976eeb4d6824650286b59ea912e7dc726681eda6"
+                + "5575cfe43e3b0d880e16b8b17c49e7ecd18d3a1bbfc3a5b5e6cbff2dfc01d5fdee57bc8c"
+                + "a6845af2dac6b6f30dca0ccb38eb97dba59be9c31c8daa7d941eb0a93dbe5be4f7443101"
+                + "aa017cb220bfc58f02be52081c8cfb9c220ec1b9306796c0f0c4b077dbb27ee127fa08df"
+                + "b77c7c08260472aa8a158546cae77e304190cf5661f8f0087b5135248ee49d1b372fd3c3"
+                + "8453e9bd59d04ff5409825280d38e100f29eccc045ecbb69cc28f191aee3239724a6a998"
+                + "6ae6fd0c0fa106eee4be3abe814db872b0cb8f3be0291b6b5016b073eec9c431dc83e313"
+                + "01243a51754974e2631a1cbb047d58a4600e09710e1c93982cf716daa1c350b4dbbaa17a"
+                + "95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c45"
+                + "11ed65f659cf8c579a2df7f539590f78d0173d3acb9a7bbda54a6a4076d336b5c0857fde"
+                + "4e1e6dbc6cfa548668cbdd655e2fa2b4a13aa93fc46b4bb8bf3cad806fdf2b8e6a761530"
+                + "38370eeb6024ec9cd86b07d85167e5e502c424d56512c2d24a8bb30c822cdff17ca1a311"
+                + "e94d4f049163678cf51e2f6162b06e62c171ddd7f18314bdce08936cf7815b9ec8240e8e"
+                + "693dc9e567fb7238b6d492e602aa4582434eb270d53f66898b6bb6a1d8f4ee2fbeaa2446"
+                + "04dd1340ed2fd6f0ae22f872a40d61c334473b2cd3c1a9433979a0c1a3f5a483e026d7b0"
+                + "52c5651fdf224651ccdf2b0922685778a77679d7a7d8eff40afdc275f475d4298de7404d"
+                + "f9e8b98dc81694837fe74e5845a71c23e6f03c98528e7518706b90098e391e380c7a8989"
+                + "3a04c191358d15e1642ef942cd7be0cc978f58ede44df855ea01dc8e9292a7d1fb26c47a"
+                + "6b1a394c2ed171e13a70a15d86713531be57538d0baf2c3806a9298e7411de821bba15ad"
+                + "13ffb9782faf718c3ae85e48b69324926ec5d87783ec00ebeec771dcb9fa1133503d294c"
+                + "8bbdbf3dcd08a2e117857200005270ea2c9869d871c3f027127a4f6354c87c09533934b8"
+                + "523104eeff0b15893d78fb6bc65b7cfb1cd711214b70849e4c07f137eca3a98a68d2b5e2"
+                + "1fb8f0b8bb275f5b551ca77373066a7fbc0b8fff4925d69482ea20e10f56bdf423542467"
+                + "8d203aab87c22473e3d7a6bfecc69f29134b5267bf710c0b0c08f6e92a3d98f4c076e154"
+                + "57ec5a8683aa8b42ff2b400a294433432add3db210b56bf6e358662a3f70825c43ff893b"
+                + "1baabaf5fbe8f6d5ad8d10f01405e9c88a81373dff3f59e757094cf5a243548e8db9770d"
+                + "99fa4f039234025790e29f36fdf3d2cdb1b702881e9d0e5dca476cb5006713e6e793faa9"
+                + "2466381b8c1152b254b8a002888a558da3a10cd03b40c3121825520c3af0fe188cb66a1c"
+                + "27d3d2d0a6e5b7fc91d2d71ae5212088f337ea64bbfee2d32f81dbf579ed0b8b36eb13cb"
+                + "abf043a74ae836682e48ec90b73a1d43e562296a5fd290558bb0b54cbfbdcc598677a062"
+                + "264798ec80d8fccd138444dc5f788a83fc72f0422959e942d4823453c811c93dbef3eaa2"
+                + "5cf95a6c52ce4dd99191f3993d2602e35a494aa930b89ec817200405fb9a51a34ea1b34e"
+                + "2110c396776717dc7e84ecb95352f81e7f00698ce48fc30fead202d2bd0d7d9d3ac82e8d"
+                + "4582588a6073a8255eb5760d28a1e136ad40b842a18b0acb5c475141bcd6633b8cc867d8"
+                + "abe2f8faff2ab992fe32abad052bdff6a16f2a6a0e8babfa68e59c862ec7a1c2554439c7"
+                + "35e1c42310f649109cea6a8efc58130037a3a5f25966520e85321aa826a4c5c684eb4f7d"
+                + "1dc74c97b4603419df4f257c613a00c351962f4c154897328109494629e64a3984cf42c9"
+                + "b0b58e9cf65613040a20a63b4aa24f4844c2b6cd99049298bebbaad1e96f98811cbb7fce"
+                + "15c3370c86b383386d6b3f17d46fd5a998f0d7f3315459f6b0601eb5af6d4c73d8ac29bf"
+                + "03eb11f0bb5b528202404a5f02766f0dd60772e8435bf2e996c7e488a8508d8aa30ee3ec"
+                + "dfc5053f14fd70b2b11a75d60c2ecc557411d1fc6bf6ee2dededa3b016cfc680895a2f37"
+                + "33f57f9a69368101f10bea1d862c71e45a3c9e78bcf164b0c18070794187f0f998e1d38b"
+                + "03503121f5759c298ee10aaf083ed7ffea7573fc4e7ae46563229979e8bcae0ea5ca6bee"
+                + "5f7d7c7c1515056df6db5252d043730434d4900408dca27fe2628847002db7671de779e3"
+                + "50273236a614716d8dd0971816fdba911f82e35c4dd85e3d60d74c968e623f661a250489"
+                + "a77ec2e04005dc09630d0d3c40fbbe567c19378587f1850c9161335b0c62a2dcd7b9ea3c"
+                + "f9cc8693989705e19c24324120a789b2e02a67b86c89a1a753c536027d1a7290d16a0bcc"
+                + "dbab19e1f0b855852be5744c4fd3fb3cedcee941e89af8b2664611899c9031ac23e55c9c"
+                + "b583c1dc185f0ca3f562e4c15440d66e3e173ce4f1feb0ab3b12668b9670e3ed64872ac5"
+                + "c26fa422ee686aceb141b642c25281409114596eab784c263acf5cfdc25ced796c1c660b"
+                + "71acd00d8eebc3b9343aad88cc9072abc1d0a0bf9b39a0513a85ded70261b4dc5a30c8b7"
+                + "7625944e50dc3bad22d1e864d9298aeb58f257ebee0edf742111f0d41889bad2370581d3"
+                + "e8953857822b8dc368b73f04c22e4d83f3b3c155a53cbc33da680535b6ed236cf26fa4ae"
+                + "5ab5e0b244c0c054e203aed4d661e9b6c79e43a43337ef5510401d01cde4556b0af84683"
+                + "3f0b97f1d8a06aa0d97f20f39fc16bb1056f599cb9fbc25299397c277a059429c463f1ce"
+                + "af360d75601dd0f975e88dd60381ef0db76fdca1b55889a9f770857fada11e17894ec3c8"
+                + "49cc6cb264ec9fb93853402e888482f28f4a57faaeef5ea9676137a7cc855dde32e857ed"
+                + "52874d3066c7965dce2f0301dfa47faa3c9eeff43f10ac6a374deff4a06d7cfabeab463b"
+                + "fe6bbcf14d1081f8d5ba889cef409bb5da1959c74b40c5048b8861b4fe34cddcb66310a9"
+                + "126722bb297df7e8144d0e714165becf777dc8200365c73b5b86a8b76848173344273e3f"
+                + "fc29191f2b51b39521f9419d604f67b1a628e3ac9eb2fde8f29367cce39fb31e32f14dc4"
+                + "c64e6fdbca12bffd249ea16bc2314ebb184fc7065f083ccb7d1d8a78d6d3e0a3c7472254"
+                + "f9566c334dccbb17df4adacf24e2682559989cf0209b80f1fbd141b0a8de8ca2e8aff3ba"
+                + "7b2d5947e751341747430ed57b02519bdddf42f2701389fd3249809dd0a7bbdb6d3b8ab9"
+                + "f9639d51dc9bbf201f6575a02319b4948039b423633c92c0f51c6fd32c1d2a52f7ef925d"
+                + "33443521dc228d7ccef0f1ea20433ab928157443677f15460ba97ef79979ed53c6c259fc"
+                + "0b016c7aecbd997bdae8c0366656dd979074ba42c31b8664995e5e384c941f4798d9d67c"
+                + "0ec89599466ef2048f67dac2462014ae463a3a5b8322638a329799b1223c98ce5ccfe1a4"
+                + "cb682a2f5f08d9fccf05b5e38c9882b49a6ae467b8232f1652fbfa90c66190fbe8f7debd"
+                + "0653fbff088a039d833f9e98d853648543bb60eb4ed5c4e3ec2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d730738177f2"
+                + "ee514057cf2ddb528ba9fc2a40ef7ec8513f7538dd9c73d81646082cd9a7a7c1367853e1"
+                + "5e647d3b64bb2979693f3d1b82f01e9eda03ee11d0c816a48584fb8435de581c0c0d9e7b"
+                + "7ad663df826fc12b0d059671021689776d6b5a32d8dde849f8e1700b63dbbf4beeb50e83"
+                + "f46af68830a51a6de1cea31dc3848c139c2b59229168c1790236cd07096f2e279c075980"
+                + "33818dcb4e3f63c854610654ec64a49c37975bb19ea6a0d0abc76564100c6c41bd4b66f0"
+                + "9811f8ab846f147b1f0bce939c6e3ec2751c0f6acf03ac6e515673631528cdf8580294a7"
+                + "d135e0550cde1fca789eb3028b2a9fbfb4c0a2185fc1c5705ccef5a2d7e3d08b63b05816"
+                + "2c1df1ce961b3953bb1e1b0667259bfae724fdac82c08f8ad6f840b75227af658a67b43c"
+                + "d6b5ab28016cd8fb6ead37492449d4758a259075b8bfded98d07dcbd188cafc53d999c29"
+                + "d010a10ba2e5bff32da123097d9de563699ffb5d457933bbc61cdd5bb90b270afd4453a5"
+                + "fe8bff0918f6242c989da1a2133786fb4ad726b2d6c6690f151e9a7c5ae49168d2483ae6"
+                + "8325ab4dd718c00f67cdf89e5a9d8674a502dcc2d1bda3dfc954f03efc9c327ec0e8aac1"
+                + "e2bfe96c5c2f36a156241d6b329205cf8615904b6583db7d2bd2c743a23112b9e61455df"
+                + "c938ccdb313a613bd990a329a746e09bf2860248e0908e536ce5963c8ef021ed1cad5491"
+                + "c9d14ac8bf30b51e42f627a637efa27bd1a03671d473dc3c647ebc1e532f264dff71d0d0"
+                + "73cc9fd2b03aaec020e0757bab86167b0635061e7fa2d9ff6041b2c5816b404ebbfbaa27"
+                + "6a766b5f6d6da07851b89f83d1c48e6fe5a9752a6d1aa640221c0132a7603d8bf451e0ad"
+                + "e7d2c1a0c43a648463e1582ca4479b5478f69739b57fca00d69c42ae6ad9f05d4ff8fa1f"
+                + "23f55130cb3964492a4b03367901bd77361c5fd1746e7f5ef4a208d5940924b4a51260e5"
+                + "7f572d96a36cd62a07bd5cdc8a4cf9869b8cdf2500eeb6494d968f62582a7ce964228d5f"
+                + "c151c663333e872a4977707df06d085db9ec0cff85acc8c674cae97b4990ebdd5bc66def"
+                + "d428c230e4bcb63303766317c14b8bb974718a972cc9c920e84b685c3c3c89818d0c25f9"
+                + "5c477d9b3bdcf1c761c2d612c206794fadc606e5be91d2903c37037aaf4057b1179dacbe"
+                + "de6e20e3472740ac41129be9af1ad12ac7a01901f840f3ee227b140cd6ef1470daaf0121"
+                + "cdec87542e391be845da9a5a821eaeb5d5739ae9e8deb992e9edfe9bd9dd814a6384b61c"
+                + "deb465d150683a1650bc56e95122bb38f0071c0438b9142da457dde1b806c4542752a529"
+                + "b02bf3aa15ab4194741da7b17db72e6938d3fb0a8388b2478eaaa929cae0249585a72ad9"
+                + "c0d585f317c34a92fc96721abd73c0542585c8c7e5a07b8c7621312e7bfe05f6d8e47fed"
+                + "4ef33e255ca04f90576d4bc1cf99362f7be1190bc3e9338a995447fe72eb4cb125d017bf"
+                + "512b95e64de65a81b981f416bc289764b6c5d09d99e69642b5d929192a67690567a1b696"
+                + "f679af55d6c65d199d43342aee988be06ef592b6e231f6b65b7178dd4c7010dbcbde0b42"
+                + "99ce5e50fb07f6585207fdd524d79a089da519e302784e4deed52a9e69f268ac0d7e9d6d"
+                + "dde302f90ca6d5b8b3f609a8e52f16ac9e14a7577687e5ae2f18fc3566a308fa35501727"
+                + "bf39d48d80da6e2092534662d5ed1213ac73a2c981f346de33a39367eae655c74bf9f8e4"
+                + "59e796883e931a7c7e7b422470c5244f7fbe480848eb144dac0b0a4a3647eb4451571fb8"
+                + "2401f2010e40ab0751e80ed1564e13a20e0be4fbbca14b3a7e057870c5998824a38eb3c3"
+                + "f1e1a470f334705a9cc35d6a72d3f5ec38256e1570057cb01ea879fc17c62d1fbc418d97"
+                + "d8088c680ae66417880062ab93da14f40facf09015559caacad3fe165dd3be7cf9fb31fc"
+                + "5aba66add803f8b5e6d714d4c15af8128ddc7b69ca5784b1c6fd2034600fa98d57824658"
+                + "3bda15f0083b14f68eece30619ac6469faeaa4d0126bd537af1b09cc5b9c17ea71348b78"
+                + "d5e20186b285a5765ecdc725b8ff36cc9dbcf6b5f22ed0a5d8cfc0a66eb25a7183d1e813"
+                + "42187bb4604a65afa544ff5a22cad71bae53c4cc514989fb27dd785c546abb154751b123"
+                + "a1bf6cbabf17b80eeaac0e6a50382e6485366393f8032603aa26d189a25f03948de6e9ee"
+                + "cf7fa339b9b065bf80ccaccca888933f581e79aca1d6ea50fed6578604626fed8da660f5"
+                + "1f83724e9c53da4b106456cc72fe9d1990d62c5a801f0e0b0c4a7816c3b9978cdc451fa8"
+                + "94b69cf32966ea8de9a0c9b4c4a2190d1f03ffeb704ec7bdc51550c64d13f1faff6a0c29"
+                + "e59b4790c59dae2f838d34095cea02fb9cfb8bd8f826ff6338d78fff91a64b72650e7b90"
+                + "e8b8936d8a9d2b2352d3c198e7cb9167a09e20a3f4d21c241a22c32e5e58c78a140741d8"
+                + "76286710060444655bb0a9012cb21c153c7d565782c14de425fba14872a3726f1ac6c83e"
+                + "de858ce6b760c7800bfef926dff27d58cae507568a7561b757de64b3b53fe5b521e3a619"
+                + "6b70e55b5fb91e4169eccaf19ea831c246f8426d722bc29879f2e925e87c7e55284b0d44"
+                + "9a374916fa45427495dc4ed089a44ec4b50d0610173e19501adcceefee7f1981a84a2a81"
+                + "fbe9954fe0a9af886b6e76c45cce92bf036cdb962cf5329e8fa859f955c2739c760e92b3"
+                + "743c082407d7882a0229f46fe9045cd93e307d9eb0951b10400911deb4bc2d3087e77f4a"
+                + "f13f0be51e156571691f3379a0125c9ff03f206f265a6a257cb03a3ac14ac50b9c436870"
+                + "39f13ef96459325034d3e1f616ab138691f95f09994e4a2c63992a4098938f759633c8f5"
+                + "2862256a19e7f4e34f8c9d9ff13813e29f9484cbc88623960fb0f54ee2aebd8aee397c95"
+                + "09fdf6755091b4075164901de8d9c453d64f8189714aa0481a97c183d310b8bb6ab3b50f"
+                + "89effcd8d3fe5513ed488d19ae73e775c6362975e51559d98a5ed2cf98f1285cff295e28"
+                + "e4a9467bedac800b7a9264e7ec995bbe7e7e266c042ca507cfb1155fa4acf95331ec45a0"
+                + "ace6c12f9105d877028f4df37f8585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a"
+                + "8ff881ef6fb928c0d42eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc"
+                + "59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb"
+                + "7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662b"
+                + "fb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c"
+                + "70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c"
+                + "89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e172029"
+                + "85fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5"
+                + "637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d"
+                + "3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b"
+                + "7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f63"
+                + "82bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8"
+                + "655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69"
+                + "178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b9141490829"
+                + "9b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06"
+                + "b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946"
+                + "d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b"
+                + "36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac901"
+                + "68446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c5"
+                + "8f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663"
+                + "f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e"
+                + "38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675"
+                + "311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406"
+                + "b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae4"
+                + "3ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed"
+                + "1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848"
+                + "be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e6"
+                + "6ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b"
+                + "05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c321"
+                + "09e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980"
+                + "888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a5"
+                + "1494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2"
+                + "b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad99"
+                + "8222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66"
+                + "bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421db"
+                + "de9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e2"
+                + "5908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc2"
+                + "9079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1"
+                + "bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727"
+                + "abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb"
+                + "80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a"
+                + "77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf5764"
+                + "9d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c0"
+                + "6eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae"
+                + "60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0b"
+                + "e1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3c"
+                + "ce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125e"
+                + "ad54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd"
+                + "58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a6"
+                + "29cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035"
+                + "bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e"
+                + "5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8"
+                + "b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63"
+                + "598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478d"
+                + "ebb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad2"
+                + "71c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d"
+                + "4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3"
+                + "d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06f"
+                + "b996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa1198793"
+                + "6da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e"
+                + "698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a11"
+                + "2ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "05b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b5269d0f8"
+                + "4c1e6411237d0b09c194e90e115a39561ddb20161f482842c8837ea1ad22a58320aaa0dc"
+                + "07f09b550096d5049c2f1673644528e3a27ecf7c78c552087c482d2b7215e988f404d240"
+                + "7ff8a3c537563a284e0a5de22a1c86e2b4fc7f63d256a51b46a7d586518df981426501af"
+                + "9d801152a2fac5aa26d75557c95b291ec2d3b7e5ac8583bef9a65f40d19a29068700b375"
+                + "f7f97dde44f7d58af559a13b783fdcb4429a200c9c44840a65a0a27bfcb63919cd2a0c58"
+                + "fb954c1bd1ce9a5da46d1fbe7a1999caf5aba92194e2671939aa8bca3615a609278ce28b"
+                + "133518ce84f644ee85c0e93083b2006eb08aee175a1cc19bca4832e789560377da4ce160"
+                + "0b16b5e262f6d17af05406de19d5161d5f48974740fe63a571223337a9cc1c2c842f6526"
+                + "db460826c7f1d4b0f593f942663255332e81aabd902eec4eb2ed204f78c11ce01ee62a81"
+                + "8723fe215b6f26b494e3afe334e98527c66095bed2804fc1051e08ecf3a85eeed248dac2"
+                + "cd5986425d0ea1df64bf1a25df0bf37502c9c5fa13ba1f97ee6708941a79eccf228b10d3"
+                + "9b3b3ac94453393ba455502c51a9608098fed9a4af70f938e45185d959135172268a1351"
+                + "2edf97889ca6d0b3ed84aeab8780e187c9aa789de19ef4ed2b380553dbc56be377eaabce"
+                + "b70753652083d40ea84687feedeccb1daaf3ba49a1ec4a82d668dd64d51170e10c7e26ff"
+                + "17caf0f62e40e705b90445b0293e9f68945f7a4a167d8f1b33f3a382ba9d97e699c98b8f"
+                + "79f526e096bef68138e6b57772ffeb50ea7cc80cb7ab93ad24358dff0afc4d994244dab7"
+                + "404eb0a3dfefa2290987a292ee7c5acd4133f211dac3ef11c848d3f784776c97331ddfa1"
+                + "41a36204a7add3dd2ed09876531a03cfe36c358c4beac03214ac152e061ba50c17bc58f8"
+                + "b403b427c2515ee6ef9b64af4ec86ca501edfa03ecbdcefb52eb2658cb9f25cbb3a7f079"
+                + "39969f8119356980c76e341e24d6f0c5684267f924ad88305f1edd68ce6bf9cc610dbb19"
+                + "235309faeadbbfc3853e1e2bb06ef0320d919fe83898bc03ef0a51bb82126c9d5ccbafc2"
+                + "b5825796cb716609e30b0c9082ca67beef530393b76f66862c69441b202831199373c302"
+                + "e94d2917d86a95bc27f29042661df832877b8bde2c91463c5c62d47675c089b091fc680d"
+                + "61aec35fe59547401b002648b960a1d7961782217f74bd9d82416eca47d301a3487c3557"
+                + "b08042a279addb23b3854cf7c404c84e3ca1afc8e15938a2f144955fb56d22c2ee0d4286"
+                + "ec82b9457453f7320250d197a52fa24f9ac6b4895ab497456a5bc105c9b658316cb0930f"
+                + "cb324070095c52862656ec4b9512b7f869ff4282921fc7c928ad6456c20e777bfb3e86e9"
+                + "1c1c34791272a54c9626c1aa28a4fad591fee7d79325885123cc7085fdc5a0d66ad51bb5"
+                + "7c24db9627d48ba5052b86983a6611e93b901abdcbe4e127dd9daefe38e3ecb25d2c9892"
+                + "359af26a7d13c4bc39bdd3a4c7dc14b5272de6aa92cca0b252f5727f7b43d6f1a5f12b4b"
+                + "b051435f670c0b0701ae9a98a23308f2ab1a33f7479aefbd7354c1e3f3e606d814fcc058"
+                + "47f0485546d07b6871c2781186f90d5040e970c3a45b92ce7082f97e4db4cd4efdd0e0d5"
+                + "e6f972827dd1bc47147d3df63d73f73c2bb9d058d407cfe53971e3e2b2ef9b122a959641"
+                + "b823b6b95def0ceb2735993265701ba1d1473889d287b3895f747ec0c1bec95b2408c6bf"
+                + "0db0dc8ac6ba1bd32157d2d6469decb33966f2051e0312bff4b176ce0442e1f2f920bfc7"
+                + "9c22546ffaf429a80b8824ff87de7536d6c9c67a5db1822518bf5783f9c8463301c9fd07"
+                + "a0b56d1235eadfca962787c4436671bd48c8f45fc9e99090bd82879ea4265c250e839e52"
+                + "b3117b029f4f68b3c39e0ba5ec4282cf64589df2374ba7e417764adbd2ea7cf094397c2c"
+                + "a403d63bb9c2f28a4dd767d22ae89adfdc49a6f7fbf7be8b08a1cbf6f7c7715d7634e5f7"
+                + "bdaef05855bdf04c848f15046a854d6a908f44cb0ad1cbe303210fa34b0b832cf704dd5c"
+                + "699975b5616e5e6eba0fe579fd2ea1361ebe36fd3d3245a437dc68748c658aca35f85a72"
+                + "6a955df1b2e3f047de21b150f0058ea9d3db4f32259dd2babbe8195b7b7a2a8d336326ce"
+                + "70812fea41949fd9ff9f8abf442f211311fe9dd1d134d16af258647ee32dc077fb1e1030"
+                + "c03060df079c6daf7f5aae8591f2ec06661af54276da41191fc2589e9850768e65adbdd0"
+                + "95aed21407c632535ef0b09a3858533a4048ddc086ac88b4bf60d88671da2d54b1881fc5"
+                + "8efccdb4601498781de66f0d5f1b6749163a1fe9afaa5c8390a43fbd28f930656fc97feb"
+                + "741ba9a7f12a2b612971bee5ad9186f1cbaaed73627dad5663d1ad2793eb146c009c0a83"
+                + "bf897c7cc2e3058d60bf67b2a432c7f551033b695eaf0301537f86c1e8d3e66335afbe0b"
+                + "cc8baf4fcdb70f1d502b8221abceacd30a2e17fc81f0f770ad5631f7ca50117de27c1b50"
+                + "2008e8aebaf9ee7de7a3d72967f3cccf59f1cd790829a251bdf89be3b24e65c2b707350b"
+                + "7c1e9f34ad6f315233d8c7abafcc9ec6797aa1c70a63518d9c56f020a87721f8a5c0d3d4"
+                + "8a83c09c0f369c80ed9beb6292726a06eee0d72dd753d15532793bb49a806c538d60d77c"
+                + "6490389d2746c80b0c730af73dfd40358274bf1fc1605a0c405134818794350de60acee2"
+                + "5e70b00f871c2560335e3bfdd62f51b8489dd78eda9b647805e0ac6d5db0700ad07fb5f8"
+                + "ddb3ad03c8eabd85d9168b3c25e12916f0575b25f0131e47957b406fdba12e0d370dbf9a"
+                + "bcac869a3a19a395ee00481630eae079303f7041db4a431c7e865fa7415772b91baa9932"
+                + "ed7daea77ac5f0d4d364d9678401c5aaa9b53fec7b2d56e77cbd2ed6a6f25a92759b1255"
+                + "933706c6c66bcb89e468c01f005225c62c22afd7573a069d293369be6881a6002dbad737"
+                + "d7a9139ed844e2c994d5f2b6494d77e79d008445920ca1e138868c018b118dc0ba69d72e"
+                + "02049978f50ec35cee1cc84f4f0a687b1617d512aeb134a08e0a84b79b62c2c1de14c982"
+                + "b71ffcc9965b7321dacfbb60368585e8b6fb71782fdb9fcab34528e286e26d5b77c8e52a"
+                + "8ff881ef6fb928c0d42eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc"
+                + "59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb"
+                + "7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662b"
+                + "fb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c"
+                + "70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c"
+                + "89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e172029"
+                + "85fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5"
+                + "637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d"
+                + "3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b"
+                + "7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f63"
+                + "82bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8"
+                + "655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69"
+                + "178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b9141490829"
+                + "9b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06"
+                + "b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946"
+                + "d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b"
+                + "36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac901"
+                + "68446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c5"
+                + "8f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663"
+                + "f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e"
+                + "38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675"
+                + "311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406"
+                + "b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae4"
+                + "3ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed"
+                + "1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848"
+                + "be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e6"
+                + "6ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b"
+                + "05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c321"
+                + "09e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980"
+                + "888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a5"
+                + "1494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2"
+                + "b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad99"
+                + "8222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66"
+                + "bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421db"
+                + "de9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e2"
+                + "5908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc2"
+                + "9079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1"
+                + "bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727"
+                + "abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb"
+                + "80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a"
+                + "77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf5764"
+                + "9d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c0"
+                + "6eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae"
+                + "60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0b"
+                + "e1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3c"
+                + "ce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125e"
+                + "ad54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd"
+                + "58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a6"
+                + "29cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035"
+                + "bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e"
+                + "5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8"
+                + "b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63"
+                + "598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478d"
+                + "ebb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad2"
+                + "71c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d"
+                + "4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3"
+                + "d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06f"
+                + "b996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa1198793"
+                + "6da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e"
+                + "698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a11"
+                + "2ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "06cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526eae9d43"
+                + "4387610ce18b7e4b9e796b82666eedbe0bc694618de2a1af2f2ae9efaefc30a1db73ac24"
+                + "2c475463d88708b7244abb2973d01c9e8bc326be9623431dbee054ddea13fedb967d1e73"
+                + "79d3c2c488a1f3517ea326c204fef88c79a656b7d772fbf29a774da262b8f39c739c3945"
+                + "900d2b55eae351dcfa0513cabb1e4c223e468637ce290f70af154246abd13e39f2bc2777"
+                + "44576939f18d3d2e9d04efa69baf35ce1d06c97a3fb8618363f0f38fe4eee18ec7ea1f82"
+                + "9ed4c548e980d575f83602e07bece5821253b2dff7181c84d1b659689912adea50898f6d"
+                + "807d6c45be7ad9e39e4a1404515d2c8650288c72da2f9199df58ff8d8b64757cdf0fe23e"
+                + "6607f370dcd3ddaeb9d51b38b55e8b3b50f63879fe3702be4d3197e9927cad54bc106f55"
+                + "b5f18ff705927796652ac0cc613738a21e9fe01c4515453f747295fc37c18725cd7080eb"
+                + "312cd8e76f2f2bb2f2b6fc0998a6b62c8a931b6aaaedbc5dc2a3adf020cd800b253986e7"
+                + "bc1d248af5024e8b0f2a51fe2cd88eaa8e3431ad725ed4016ccec15986fd1105f330b9d0"
+                + "b8fa163790e9ae62652ffbb42e5f57b1d468cdc50debb1ec8705f498fcac4ed894706e0c"
+                + "a73282559e244f15c98a879b06b377b3ae1c9638800a12b92da0f18237503793729731b0"
+                + "f5db191efc075ed2ded4e173e2340148054cb6eada9359680d7d3b82037a2d56e4183145"
+                + "2f222a1ffe494736a0fcbbb910547f6097ee20b5b99fe879b7bcad95df7d78bb930079d9"
+                + "061b1724a7c882efff9842c01050a194bd0f0bf6f09cb8f40c1d7f9cd989f58934406be4"
+                + "d424f4646c261dcd57ddfcf892cd0b827651e21e8b6e111cb419da52b383c632ee600db0"
+                + "f230baff819635cae5d0ba01bab3ab39853a2132bc9b464c19848fd8bdcdbe2ef7d4a218"
+                + "11d3a0a03b1ad1db93638ec44734843e1703f6c3b74dfe1e9310058ae84179bf64a48db8"
+                + "0dabbb604bb4a867ed247e7db97a8530eae6df367cebaf6791837ca7c0eee7580028a807"
+                + "f39c21415c0381d4f24fb632be74401e2329c43ddca7e1473f8e7edf08a5698dec2499fa"
+                + "ee23d11ad854f56311275b86af24db8428fa2e896dd264d1215f88bf3371048407e628f4"
+                + "ab27922a3f6b94e001cb245aece1f7f981ef6b9cba7da13a79752acdddc94ff22649c599"
+                + "fbb7e5337a6ab39767f3bdd254bf5869984b99b7bcf61cc0d8063a1879826a2ce1023579"
+                + "6310ce2f26771f8f35f1626f6ea9415d096b9955414170d5352d4bd4dcb5e5364de604aa"
+                + "c17569829254fe8fcf5de527a8da1509cac705eb3b044e7784a23892aba63c4ef4240188"
+                + "1913a8248667a52d4fc21f244d2713cabbf2952d16e15ac9e1d747b7facb15616d5f93f3"
+                + "52b13750c93b643ae82be55f67e6d94dd197038f174af9a28b87ada018cb5000e968ae6f"
+                + "cdbc42acf18e3807ad2bcbcbfffc1e19bf06aaa7d26d78700720c3171cdec7c23c9df38f"
+                + "302e0bd19ab99627e4f32810751e3c5cf1d2f8c141105d13b405eadfc2ec47cb1658ad9e"
+                + "0980e40617d63bb3faefdad93e695172e858f94a9b7c324ebff72ea0837dce9dbf0b8816"
+                + "9727c332260008a7d9bedb3d9734cc34210c4a42959e62a639e36989836bafa003a3b0d6"
+                + "711fd0ac9b77b5d4d240a7b7eb728519d48bc3ccee75af4a704ada28495c11b1a16827b9"
+                + "e142a9c9a77558f92704fad282c98a664d938b74a0a0fc95e78e2ee3310be13f94c53192"
+                + "0a77c7105b1eb925c0aa3c97bdf4ffa5573a80aab1b6c2da59d566b5d81364cfd2f5698e"
+                + "08a9d84cb5be62c2e3606d5cd3d3b2989e4131d17b2660185fdb1272bfe60b3e48bf343f"
+                + "d5a170bcf87f2a0e642ce59fefd1b8a0f1555f47445e8a2e7e52b0e21e7a2b5ddcb072d7"
+                + "ace5f6183f5e3e9afc909108867445bc996ea167df7da07b1fd17b51de178aface748071"
+                + "0a05183b8b16b2a90997be86038d060758874c6cb08d9c92f153c53b69c0f056d69a232f"
+                + "d8fd2d63df27c2fc18407945af783882f720cf687e61bba26f936a6fb2e28f123cae01c6"
+                + "fd1a88392dd9753451aece31be83d4e12f2146be5335e3e152e5b7f2d4f9026413980275"
+                + "829a5914d603d841400b0c527971a3c99ed12e91ad3f20cc9cdd92b8b0e4724eb7393880"
+                + "c5c0feae58fa89fd77158f6cf0767dc84a13155aae1add9b6d34f79a6902b3e43dcfdaf9"
+                + "4c8abc470b79abc0011d83db29ea975d5f0b26cfc18ad0c0ba42302da459559abe52ecbc"
+                + "974d9c9a1401c4fa6c371ccbca1e191f9b936f6601d22a4cd4d73865dae420b28a3493b7"
+                + "9ca13eca6f8ebd1751727cfe0309f436f49114b3093d65f30c69f2d2fb17521627b49a7f"
+                + "fab21c2462a6490ab3b8370c5f633df2ebb2dbd58e4d6e6ed6fe2defd44c429c9c89a034"
+                + "0fed6ce864af7f35d0415ebc479f219ea38f55fa6a6587ef6294330d0fc3deff1ff378d0"
+                + "62d8513c28a669d467c0442476e9049341e935d5553f8bcf5cdd70e41778b9a654854737"
+                + "5e40cfc13838541e61f5920981951ab4756188ce16a627208229879859289e3c1872f76d"
+                + "9925e5ade2838a1d3f07665dfa28df4dd01aa6aee36af358a2df7d3329c797c1eb9e36e2"
+                + "0b788c286995f5c8e91176e95f64cd1e88f6a1a7b6cd408a56fb234207ad74a94f9df875"
+                + "633e2909474fb6de3d4999929d296265f1ed03c5b1174466a4dd0f8550120cded11d4d15"
+                + "a3065f2656804c1e42d76fae12789e641f453a386f895b5340214ed4e9257121bd06572b"
+                + "4cdfffa03c0c5ba840bdcfed08935ec0dded6a8fca44b33a47d7248929dfed3ba484eb6f"
+                + "3f15e10926f1c292b244b24fb4196f2e81359381f6a5e1be82ec1124346d20373fe661cc"
+                + "e2f2d1adcd08f19bb68e39ae4b3f9c0ce772f46406b354fc94f2e7f5f29845f92d64e6ad"
+                + "cd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d8e064d5f7534f052d7a4eee"
+                + "dd94d706fb52e47e389354337dfcc66ed0122464523610d2a17306a5b43db9b8e1c95d5a"
+                + "9ea9836e698febd4c1bf00a2df217259e849896f6272430eead83bac308803c2b75eb835"
+                + "2e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0"
+                + "f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc"
+                + "59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb"
+                + "7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662b"
+                + "fb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c"
+                + "70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c"
+                + "89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e172029"
+                + "85fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5"
+                + "637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d"
+                + "3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b"
+                + "7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f63"
+                + "82bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8"
+                + "655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69"
+                + "178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b9141490829"
+                + "9b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06"
+                + "b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946"
+                + "d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b"
+                + "36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac901"
+                + "68446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c5"
+                + "8f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663"
+                + "f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e"
+                + "38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675"
+                + "311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406"
+                + "b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae4"
+                + "3ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed"
+                + "1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848"
+                + "be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e6"
+                + "6ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b"
+                + "05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c321"
+                + "09e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980"
+                + "888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a5"
+                + "1494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2"
+                + "b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad99"
+                + "8222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66"
+                + "bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421db"
+                + "de9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e2"
+                + "5908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc2"
+                + "9079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1"
+                + "bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727"
+                + "abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb"
+                + "80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a"
+                + "77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf5764"
+                + "9d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c0"
+                + "6eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae"
+                + "60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0b"
+                + "e1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3c"
+                + "ce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125e"
+                + "ad54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd"
+                + "58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a6"
+                + "29cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035"
+                + "bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e"
+                + "5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8"
+                + "b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63"
+                + "598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478d"
+                + "ebb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad2"
+                + "71c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d"
+                + "4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3"
+                + "d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06f"
+                + "b996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa1198793"
+                + "6da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e"
+                + "698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a11"
+                + "2ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e6319d91e"
+                + "b21ef5cf4e6d0b57ee381ac0cd7d97db2b8258fecb4e7459887ff51b33577a23e17f9807"
+                + "13a73b6c8b4f89fbe5e77211f58a31befaf154fd4d3851fbd1ed3cc60e353f02afcdb040"
+                + "0a99bc46bab79fd1f89a47f56cbba53061bec2d63157ac8ebbf7a0abe8e2c5280349165a"
+                + "e02786f28c7ef6da1ad16d7db0f133b67af61c7f3b3a229add5af4e895ee40f42950893b"
+                + "45fc982f0c83e644b6d9d65f905d8e227400fab2641af230283e1e77e384a8a4303e1722"
+                + "3c1ecb768af6c548c9205770b25275c0754302438d153a6a26b43931af7eaa9d18c2fd22"
+                + "ba7118fc431bf8ba9f7d9b67249f2bbd29952d200154703bb17871f07d6a25506b59c6ab"
+                + "fa8a5538bea2b56fc3eee2221396e20878fe8d7e73b3940073b0ce1af2e297d340daac8b"
+                + "fb53ffc04a68146e48045c05a1958e8cf40d0b9f0a4ba2507406f1324c1216027109e4ee"
+                + "ee7573d71388dc18efa2fe1482c5ee1c040ffeaa1a47b6ef6f57a78f3a5c2b64cee62901"
+                + "2f13ab12d4366c06d9a4b26dfd75f4ec519a4faa707a63bdd36ed7575093fcf25778b8fb"
+                + "3329414defc4edac2303dacfe87ae8a860db13149fc3d8172f9c7f6256ba9ed1e39e8c51"
+                + "9dd50b19a411dcd666d62eb47d167c168603c0fa9548948244b32e9cabea3f2e4de6eb00"
+                + "01644a1bba6a7b04be81533607d85a94abce46a790cfacf61e9afa14bce763895c874b5a"
+                + "ef5ebf0ca1aa7dca54458e2f139b87533ba10d16ed66d51a2d1a09e52a63263f0ed1a200"
+                + "9db907b4a6b5cbd9af9dfaae09b8ab51a6a64c09852d877eb2c0a5552c2c748d15c4d981"
+                + "ed5f3b1ce7439b69c809c5d121dae59a3aec834c7b2054c40cd583dc557fdb1d36bcc119"
+                + "50195973872de9a1dfde8dbb3f7e8d5d5dd5f46b08e51522d50ed0da1ccd76d2d11fb197"
+                + "9c65d209f247ad865bcfacdf64b05054aedb792fd6881c6ae51edf20a33226952adeea77"
+                + "54ebb8e920ca0f2d4861dc0ae32bc8c62506fc5aefa29bf0ac00a9da11159136caeff22a"
+                + "bf3a1e6baddd1b91ad1d87d46776a2688f2fec19f0c3518b84816534dbba3537d052db18"
+                + "960b3f1f57b38cf3fa9c8117398e6a3be53ce5b945e22d1177d4aac19315635e9e527dcc"
+                + "df38d914eada5a89afd37df41e6fc74d1cf850b44abb6f5fac9d5a4f42eccc00f0f273d6"
+                + "9c248cbd521168e75dca5120db40ad375042e54b4441f397dcf084a69e2787dd96752b69"
+                + "086ca13522b5cc089586779a2b6c9b1c5854328db9d10e59e01dcc5e28b15cb8a54de33f"
+                + "e8e2a4872666673198ab80336ed88a335394cbfaa89853364229da9dae2a01aef84a01ce"
+                + "209b80bc9d88327e473bca1925b5fedd4a7ab0a73d3ae6fd7ba4fe4528db89c74c5edd3f"
+                + "95140591ae5a746fe64e28315aec360feb49cabe55afb5f128a85f3f9741d19b1a81e0f8"
+                + "9911a6b74abd9b4c592be6b4b1af2fec7fca754b9f5dd32d73c6ebda5566db4992b2ea76"
+                + "7161e0534cd8e11d622a99a1ea43641aa7b7e63d9c85f4586246ccc6d44649bc81d48d97"
+                + "c50980f40fd6f0024c50db60b5a9dc2761e42c3225b5ce23da88c71e695e0790f1ac94bb"
+                + "56412690d6a6c3b95487401389f657a859d76c8663dfddf12afdaf409c7abaced638dcaf"
+                + "8aaee153e71690d62eda83726d0d1e428dbaa27d5c6c7c30edffdb7902b836c3b4a4bd0b"
+                + "ae759e555766c2ea9fb14701e3c94ef895dc2729f3f1f2a2c3195b88450da08c87dbe05a"
+                + "929c2f489880f3d16a499f01ac393bef51c0b098a7d3fa36f529e87d0bbfeb73e048a798"
+                + "1193344bd473ef826a4319ec3c64083e91a0a44918f919e0a74dc185dd415d2ef903c269"
+                + "1b6d993aaf663a5f78626c122c7f52cb5ba42689451d879dea903fa137a03bbf853db9d9"
+                + "55e10de01b2d8912a87684988e0da45873f78e363d7c03117012edbd4d1db36b457674f9"
+                + "bb9c773a2ddd1ec0d40c0ad87cdadda05af9d3f1527274551f875e3e8c0b04c010efd806"
+                + "8ac51a7be609eb371f0ddea574d0d32c48f0f5e1815e7d1dccda1d55ad613af776f4cb07"
+                + "829d92bee759febabd074e5e7be966b36f05fbaff1ab68e5302fabcda9c95e92cd5f19ee"
+                + "f2fcd6a3ae99b5a0156c7ce94373394081237f27507e28c28f9dee15bf15f8823ab25fad"
+                + "23a1867bda19cf1b7341baf9e8b3c61838115aeb2175f425a9fe04c77c7a4d6308aa1b92"
+                + "b1b0e459562ef12b03e11ef026b10bfcb0d174ea3cec142a1340f272212dd89209d9c1b8"
+                + "fe274b229549d5d1c2919c627daaf8658b763c7f36be86eb644e5c5b0c3802ffc7680268"
+                + "f441832fbb006ec33d293e8c6b50ab8a675b79b1981003c6b465a7a45bbb44c9a52b26fd"
+                + "a84d67104217be64b994404e2c7c31dcfacaebdb5b64e6fed85ff25fc5ffc31ec919ac37"
+                + "d2cb8d4e933bad652fdd4ba8116b1e5999f6bb67f0ce573d9a440e5ff0d1cef087f9cc09"
+                + "ba984384d6213b79ee1904edf75cc484827021d80c236d8d60f80beb836c23349fb1c662"
+                + "6ee38a808f97ff80818f39928540575d2c15f971f97f2c9666982a3bf3e4acfbe1969feb"
+                + "2fdb4d0188eeb456b4003e5f324b589f0b4cbd86fd5c70cd13e6c79e39c7ae4c083eef9f"
+                + "95d27ad452f2447bb4f8e3f5483e1a03a17df709c1081b99285284340158b25bd00fade9"
+                + "697b890273e5cf34f9ea9aa864c3318d4591024afef030b1247ae1e3f6cf25b19828abf2"
+                + "3070d893853ec198f3d1cc0dc131cc18d88e5e15784ebec5ea56b897990f5f5c8f96662d"
+                + "1cda2006a53c03527490255b54451ff43ca0c46bd920705b3d4eec935eff65c3604d4e2e"
+                + "da011135b4fa61ca144dab298cc4a1cabb173acb6db5313b260c3f61dc8beff2c502fe28"
+                + "f40c35c0d3a9d4b9f117a808bf7ea11342deb3f7e9372b80c2e2a5648d2e18cbda268fc5"
+                + "99146eba584f45a2b7acd9057c00417bda24cfbc798ff87dcca42e0ed4c56c5827eca22d"
+                + "09b9feedec97addad5ffc75e2a38c78ef9a7f7d47dcf547d3c146b24da566776d6a16bff"
+                + "1b2938a76ad84c2b1cedbd746a8f0a18396c0b9c06a40338ec77d33ffee72441c334e3b6"
+                + "62e6663476b89ef26abb232542cc4ee5760610346e0075c8f87e164a71831ddfc31f12f0"
+                + "f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822cc"
+                + "59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807eb"
+                + "7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d662b"
+                + "fb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc407c"
+                + "70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf2c"
+                + "89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e172029"
+                + "85fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396fc5"
+                + "637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe19d"
+                + "3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb9b"
+                + "7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f63"
+                + "82bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268cf8"
+                + "655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f69"
+                + "178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b9141490829"
+                + "9b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc06"
+                + "b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c946"
+                + "d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e407b"
+                + "36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac901"
+                + "68446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7c5"
+                + "8f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b67663"
+                + "f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc0e"
+                + "38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc675"
+                + "311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f406"
+                + "b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2eae4"
+                + "3ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39deed"
+                + "1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb3848"
+                + "be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369e6"
+                + "6ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c8b"
+                + "05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c321"
+                + "09e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd980"
+                + "888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3a5"
+                + "1494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482bc2"
+                + "b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad99"
+                + "8222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c66"
+                + "bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421db"
+                + "de9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5e2"
+                + "5908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478bc2"
+                + "9079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501f1"
+                + "bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b4727"
+                + "abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3aeeb"
+                + "80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b4520a"
+                + "77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf5764"
+                + "9d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251c0"
+                + "6eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2ae"
+                + "60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb0b"
+                + "e1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd3c"
+                + "ce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad125e"
+                + "ad54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975bd"
+                + "58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32a6"
+                + "29cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a0295035"
+                + "bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d67e"
+                + "5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907b8"
+                + "b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b63"
+                + "598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f58478d"
+                + "ebb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282ad2"
+                + "71c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc5d"
+                + "4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702fa3"
+                + "d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c06f"
+                + "b996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa1198793"
+                + "6da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da33454781e"
+                + "698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a11"
+                + "2ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c486"
+                + "9aa01b67a73204b8f0cbaadb040ed9dc55385c60d3",
+            "081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f9437a5"
+                + "17e998b835d7448f68cf70c16386179e8f4f8939c68414f54efeb951974c56680e8078ef"
+                + "9d974fc794a64c4731c912d3f5b72e098ba495abc4fc2b1a37cff5443cc3e5ca930cbf4f"
+                + "978441b39e4d8fa9ddf4639c543eeb4de961c29d8301b4239a3ea1a4b2c2935d4c7863b0"
+                + "4e76ef0ac071d73eb1be76e478341e616ae95600945ba59825fae6c4638985fea0803d6c"
+                + "70114657352c68d616cb6873ad8eed6e7b608c4b33584e41431d3d846742b266d196e475"
+                + "7c3bf4e7e28e213fe6fae624ed2462f837af0f5a2012b9ceba54c28ec36fc197073ea1f5"
+                + "bb3c74a3d824ff37d8ae2a116a4d408d0eeb4992c844d2140b31157786ddef5da36d0faa"
+                + "a51058b08a89248960e7d19a72ab00b1b15b468d9007be79afc90b5cc5c06e67434e1f8c"
+                + "7a5f63083d236e0856379c57512e7e4342acbe391cdc14418e7e11711b26629eb237065d"
+                + "b2c2e16d7b79f5a92b35e39d861992f9819dd03da549b695158ff68398c1a253c5760308"
+                + "c5c6e5ee9b8dfce6ebe0cbbdc2bbff03547c8e9d48cf307ecdae797b5d357340854c429d"
+                + "fb160ad8e7310473d2e9b200c4c70badb8294e3b1ffe8c950dc27e56a3cea47e61d1f144"
+                + "f63ffcfd666257115deba198d2616ff3c1ebf3e4d30d59182b259473b4e8fc5e2c055b4f"
+                + "d771bf19b68b30c6ef06476522b4283aaeb7f1798ccacdae29a1075403faba7e7e88c6dc"
+                + "5776baf1bfa9a9fc928c306a2bfee0aa814cc9431a56316b9eb95eb667ca9ca4f426efae"
+                + "5031dd45d80c7c17b6d7a4dbe895a2ce482374b41ccc954c68ad0654387b1ab457e43033"
+                + "71a63db5dace631894696e323997b388dede168f230ef0463e1b06f507efdb2972e7fd37"
+                + "81b5d08bd4cd4838e2bc1d8f1cff91171e48c3117fd43aaa0332e14e7921aa477ea13c6e"
+                + "6c30322a38081179115edae4680dfa074d8e69a3aa61aa2a4b0bbb142ce3027bc23143ac"
+                + "0d466c4f72bb9514512b98068ffc2b5b9d118f33a894fdccda725c0b74eba773e3d44101"
+                + "f88eb9c2ba0e8cf20032dadf6b35f60e6c3efd12980987a884f5ba393e7f4e718bab519d"
+                + "7202d206a545853962ce1ebbb7fabfcb52d8a2c2d8cb046fddf85c7aff7ea0e7f072adea"
+                + "76e8bee7838352a69d556a4fc2c4d0aa387b5fd563e317c08b753893bbc20cc8748e9e6e"
+                + "2ad35785d0746fc95d055146ea044d9a200af2ed9f3a99875bd2b5f4dc15294fa669d9ae"
+                + "8bf77a2014cd62a2191caa4bd4b3ba31b456d4b3ee5f5093f090bd9a06b0cd08a5904060"
+                + "f3330386b333963f5a5c3a6e20113eb6bdaf61189f9b0d9a95669310ed3cf07edf93b338"
+                + "14d7ef80bdfea2ecc874ec5f8cea3c88c8c56103798270c074281540cbcbf42e5bf85954"
+                + "6ef18b255bc150285719ecb1bfbd89ac2e7d9d4bff84924dd679cf15007cc7589e4c93cf"
+                + "049d62d3fffbb647dcd78a109ef3951a4cf682083a6f2acbb7090a4073f3f867b2ac9d74"
+                + "b1ac3b6d19b93d1f462c4532574cedceb81ce843cb37de390dd96a5bda2e75de3b6e900f"
+                + "0fe70a29252ab9dffc2e2938aee1280c5d039d92f931532061db2c28b90cada5fb349f32"
+                + "06073366d6e535e998b644c60ba6b58cdcdea80b65d3a52baa0fc4416e388570a97151b9"
+                + "8149c246e966a5d7b94c044a84067e386b4dc01e393e12d8450d1d5544ba5106f8187611"
+                + "8117f304481a99c6a16b0207f363a8c12b0bd9674e9cf0927b8cad552d7ab94ad3acbbfb"
+                + "1d1561771c614b823e59790e558063fc6bf7ae14f199df0fcc4d9043e5b470c8e7088f50"
+                + "5a28e3931b69e08248c07fe060c218c93a2fb51d01ad0fa581f40319313c44611622151d"
+                + "366c73991514fa99f1faf57e7760579a40f060de05276f04f25204f808a999cad53151b9"
+                + "1c4374e8c81614379db93424a4825c58652fc8416a0baed3839a3110a6fbe451ca86ac48"
+                + "5422ea466549e2bda3ad6e10359ba42ecc9c929aeab10d9a9d055842be9856a08583e752"
+                + "39f52972453f0ecdaab51bc73b8fac5f7365c5dfc0c3861ff627124388057cbbe2f00685"
+                + "705311193a62ec4f9fd25d8f55f35f336a4ad2368ef2947bd159a2ed3daec364e65b9450"
+                + "bb5b5e8e18364bee3dd3179cbe6579bc4e06a2ac6fa37329ae20b5605843cc8664c8f538"
+                + "91b859ddfb2a6004f197316c5d3275eb28c20e588eb1a8e8b8d2e732288613708d3ccd1b"
+                + "7d0fee41244dbdf94b0031fd9354db3588fe161ecd3b58f02936c83f421b324f4b816b61"
+                + "baec6c6c1419564e77df7766ebf29886da543e3ad1fa93aafa1c2b073c8714b3167a744e"
+                + "1558f595a385d6f9426ef1e87df55c273053a348cdc41ea71fd0d5294736403a3472e164"
+                + "0794febb9a0ff3bf633795aa03b9c71bd4aada57313cc35a596609f3586900ccaab8ad7e"
+                + "f872b8484beac99e260a78356387a4993180c12680aad89c4a90f8503483e887c518bc3d"
+                + "1a5935c9d329d2a97d12bf1fc23cf62d8c1064b717062944748d6fc4a485ef79076a641a"
+                + "f1767dc55aae1b08224d23c0727b53babd49886a3efb23ab49277ed4bec40cc8f0b237ea"
+                + "a267801364ad865b6d6465215bc39b2018aa06f9a30598be039332d29f53f1dfb508b97a"
+                + "ad5254e1ff06509f8e80fbced8a3d25fe6f9947cf2c2757c9abee05904a2b06dde79316e"
+                + "ad2cd4a14f32d7d9f9348487c3017194255efaf1f2a39aea4ebb33b8a5a5d112ea29f80f"
+                + "9ba2b54ae2bbbf800e457d99b0019dab9145049f3080cbe33b98c0ba7aa194bebe64fcd1"
+                + "508ac6c567a6382099f807c4eb75ce002ed5f022742ebe48d835c975a4439db7eca5061a"
+                + "6870300726b55d89f6a91d4d8888d6a58d5f01f382864b95e310088acb781647152c2160"
+                + "8a11394b4c2331a3531a796f51ffdf777a3c421c21dc7db47221af7bf777c3c380c075c2"
+                + "42d71a05d3ea402a783a0cbc6cbf51c6a5606177f9408f56d2bef5c29bb2294a52f661c2"
+                + "5c33906e6309e281c3202138082eb775c3b719a1444049d7a6956fcf697bc59492956557"
+                + "a4c41c961b3829a24bc17d9ccfb05890e574c151a622c2279e73ea1fabf9317cdce950fd"
+                + "50dfc18d95c18fdb071ce28907f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243"
+                + "134dfbdde74521a6e06c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff"
+                + "4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3"
+                + "bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b4"
+                + "9efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab050"
+                + "0a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed"
+                + "28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb77"
+                + "8bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797b"
+                + "fc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723c"
+                + "e9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c"
+                + "1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c614059"
+                + "35ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a4"
+                + "31f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33"
+                + "de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403"
+                + "f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f"
+                + "7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f"
+                + "850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3"
+                + "c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779"
+                + "b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a266"
+                + "35e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449d"
+                + "e0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601"
+                + "922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d2"
+                + "5d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba0918"
+                + "1896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c53"
+                + "8becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f"
+                + "52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4c"
+                + "e480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31"
+                + "6608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6"
+                + "e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1"
+                + "ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e5"
+                + "62f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604a"
+                + "bff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f72"
+                + "0c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d"
+                + "8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff5"
+                + "4efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a"
+                + "807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4"
+                + "fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60"
+                + "b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd831"
+                + "4074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8"
+                + "ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8"
+                + "c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0"
+                + "c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d61"
+                + "7120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee5"
+                + "4095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8"
+                + "c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09c"
+                + "af363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97"
+                + "fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae19004"
+                + "68c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a"
+                + "46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbf"
+                + "b619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa"
+                + "627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca"
+                + "2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7"
+                + "e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4"
+                + "a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99"
+                + "a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608c"
+                + "fa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38b"
+                + "b93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544"
+                + "f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141"
+                + "e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04"
+                + "d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbac"
+                + "c62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539b"
+                + "ec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "09cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c101571e99"
+                + "c5244a1776f85ee0d42a2d85e906ef1864eca38529bd1e6d2fed3be5c7510a269d1985d1"
+                + "78bd6add4af93aaa6a44126531a764ad4cc05a1d1240cef0c22e44f79ddfae15623e4eff"
+                + "8f7fcb03dc9092693128057fa93dde124df023ede229f7a0a3cad41b23120e79dde6cc42"
+                + "289c4c19bb9a68806791c35a12bb350ace60164c24da5fa26ee29c0c4b36275d96d685be"
+                + "2cab2d64552d33d9f04cee58a2794579db6247dd6edbe5ba76b8573b01e8c14a70d1b3a5"
+                + "28432246a1e8681806f384080b86f4a21894d613893bce6683d0bea2b739059d349972e6"
+                + "96e866125415face8043a8018606a1e35574ef658c5b71c5082f9333ef924be448f99787"
+                + "b9e4b8747c633a8e5fcb23beb8d1c8e96e79ab714c2922a942060d2183abf51d905a7f20"
+                + "87d3f61d6dc0cc5dc479f4c278c01d53dd9c2a04e65ea35202a0849b3993f3168433e636"
+                + "17bdf8e997d24088784212f0c6e516772da382f771da6f601af7fc8aeabd56b01fe29f63"
+                + "9c73fe3b67f444b0e48839035865c9326faea75109673865c6c7503229e87944357702a0"
+                + "2d1fe4b9acadee9bbd24be79f3e93b125aa0d9b956b308e336aca774b9ecc9d4b2478b72"
+                + "321b3b4b0a6f562c3cb94177e521fcb74902cf343519268598c68f61570a90131b27190f"
+                + "fdc1c1f2cfffd0b508526ca0d69f8074e3e90c1aa949b717b661863a1ec5b26f6d77a275"
+                + "542d78b4b68b05f6b47fee1f1bd8260d41cd8578b3ed2d3091cf984a4cb9cad60460e6fb"
+                + "e4a94f807793a6306d9160728c53cd999d401ae3907854d36c11387fea4a900549c7c016"
+                + "2ebb139c461d9e33858d11bb828ffd1584e1dbc50c2bacccc622813f57ccf0afbfa9866f"
+                + "8ca3257b103458156a38a807f3620403d91680a74370c9b0dd6a558f95a58a7a02638646"
+                + "0a940e5bf5655e42f7cf6b672e5aa4cef313aed544afba7cd8ee724293fa666007aeeffd"
+                + "0cab49595118ca0d005023646b0881a6e32ac338b99c3683310df63b505335a337df856b"
+                + "b67bedeffb029ccefcb8a5352de643f41f029beb961f61644f318cf9b19eab8c92cbf8d9"
+                + "f6237abca1e63749c26e9f5001c72d51097ff1512b673be0252d7bf8a355adfe8e241527"
+                + "32edd0efab6d1531732379c6d6134b6be11633d8898db0cf342281b4801eb0e1f5a0f0cb"
+                + "7193139a015bfa30494d16c6abd65c598937b427e889e4455a46c8e32b9d55e1af25ac9c"
+                + "57b203cb9151f5431fe2cebd41eeb7166b9911ec5ccf5dc7e94ce8a2051a2357894da7e2"
+                + "3ff92a2b75356c81eab6591d28efede4e5f9f79e4193c1fbbce995de5c23ab3d32a00514"
+                + "2897005bff3d92dd6c4e33876a722bc6f9ab4cee31b60bff041d017883b44d1eb8e1950d"
+                + "c6629e5adc10d419869d7511bb154f905dad9e9b674edbc88613d34a4406cb26211b79a9"
+                + "38bd68f071765d9bf74c834e720315c1b38b347c92ad300705f4de0a960aeb537285641d"
+                + "546b7e7e3a867b4cbd85ffaa38c6306e49c5de2736fd7f115e555d0d0b065242db7e288c"
+                + "148225ff008a70f1f08ad17d1814bf05ba6acc3f09e503ce5262b7ddfa0e1f3e9c00bc77"
+                + "ac331c5043f5f3cb0840702587986225892763b5232459fc9223f11d5298d93cb7392d49"
+                + "b12d9dc521bca254e9df6812787bd6970bc9276a3a6fde1187314bd590a95df35eba03c5"
+                + "c4a1fde97186b5323edf04d68069460520bde60c55ae6d0707ece195b30bfd6503dd7baf"
+                + "6a0f099867be86f48b6871455dca6df56d8e6a374214cbb1798f54ba578f9565ce2bdf34"
+                + "56aa9b9dcc6c426a2a5b3ec91e8ac15ec85a163b4f2fe1e045a5a07ed495ff666d12751e"
+                + "5a63f5757cbbb2ce2c14471b8a6ae9f4e92098357f0af51e2e6bbbeec9ba4959d4913375"
+                + "9982303482972660bbc9109a330353dd65e37644aae546f2ce93dee136729f93472f3c69"
+                + "54f643a9fc61e1c3aa8f375fe63afb962d2513fbc4a3a9d4baa54dc69c3ba4c572651469"
+                + "9289f90c502bb21119486a345a94ce23a4ef90eeae96c931a8eb109f96f81f6ca1331827"
+                + "e3b647b0e238deb774a3238f979790bea036cc6735c1b7a49f4f398319e79ec68cc4614a"
+                + "10ee2d11c6e32f39ad4959064b29421d20375b98965e6e5f0f394b1668fe75abded65b11"
+                + "0f24f08a128b2d9a2d1b22b29e9aac33545c57017442faf896abffdab377d210f20f48ee"
+                + "14542db86a7d2f93c36a4a04b984c0a70c6896bdbe74e6d8b1bfd9389e290fded125be4d"
+                + "d465ef200c3864ecf8a593ac07bf4c88b6515cc77c4546ee615d229cc71e368a71a9aef2"
+                + "565622a9fcdd597cebfc276887ce8b22377ba3628658d294db6382c562541b9f2a4a7c40"
+                + "cc3efc85532233dc314750001a806f31dc9f6b4097abc80e8cdb88493ece98f3a33414ed"
+                + "6f2accd2e9ae6e618f862afc47f87e7d7bf7211b2105da888cfd26e600aa136b7f314407"
+                + "edb6f8bdb97080ce88cb2f41ab5a1d9ae27295fe3d180af1de5bd1c17f197969e8feede5"
+                + "b19b1d9695ee90a95ceda909904e82850a27a6d354862e45e79e209d9362fa601369cc24"
+                + "45525f0b75fac33b116bee801a1e0a7a93a8a79f68c78f115935f11192536e16c7240978"
+                + "5cd513739e3d6ff022630b31be82154b8b5473a0b30613a34c601f5edebae6adf5d029eb"
+                + "08f6b4e8095151a36badb485f0ac7b0389a0dff3d1380799ea73fc347ee1823198717086"
+                + "8f720a98d77c67f816d970e94918791f01ec4f55ca12fb293ff2172d8d862d8a91bde48e"
+                + "aaa91ddcaf2fd3a672eb7d1351a386d145af68d79ba23cb4d07e7742a0e9dcf30cfb70d6"
+                + "93d855afd70ab43f756c6892038d050d77adceb8661a11b6cc160eaa22ccf1f6639a279a"
+                + "1adb010fce2d104afbaa2e8f2fc4169b06fc5f7da083288d920524e8ffffff35da20e4bc"
+                + "97a1c43eea60ff3bdf3677f890da0284b666968e769bb94dc6b3f871d34f345b2f042f51"
+                + "2852d47e4174bb4d8a240f1feeded24f91a8e5175857573800c2c0153373d1bb324686cd"
+                + "75579b5e4562a01fc28e099885420374d379d90d77875e1ee92ed681543f2edff426ca83"
+                + "d072b3d74853c6bb354f3fb304f4bb37a67b19624250e2ac1f372397dfeb8e63349e3243"
+                + "134dfbdde74521a6e06c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff"
+                + "4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3"
+                + "bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b4"
+                + "9efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab050"
+                + "0a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed"
+                + "28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb77"
+                + "8bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797b"
+                + "fc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723c"
+                + "e9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c"
+                + "1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c614059"
+                + "35ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a4"
+                + "31f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33"
+                + "de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403"
+                + "f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f"
+                + "7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f"
+                + "850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3"
+                + "c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779"
+                + "b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a266"
+                + "35e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449d"
+                + "e0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601"
+                + "922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d2"
+                + "5d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba0918"
+                + "1896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c53"
+                + "8becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f"
+                + "52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4c"
+                + "e480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31"
+                + "6608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6"
+                + "e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1"
+                + "ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e5"
+                + "62f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604a"
+                + "bff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f72"
+                + "0c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d"
+                + "8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff5"
+                + "4efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a"
+                + "807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4"
+                + "fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60"
+                + "b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd831"
+                + "4074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8"
+                + "ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8"
+                + "c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0"
+                + "c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d61"
+                + "7120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee5"
+                + "4095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8"
+                + "c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09c"
+                + "af363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97"
+                + "fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae19004"
+                + "68c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a"
+                + "46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbf"
+                + "b619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa"
+                + "627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca"
+                + "2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7"
+                + "e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4"
+                + "a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99"
+                + "a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608c"
+                + "fa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38b"
+                + "b93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544"
+                + "f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141"
+                + "e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04"
+                + "d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbac"
+                + "c62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539b"
+                + "ec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebd9ddf21"
+                + "bcb1e67d2cd01840ea4bd6c4f4cc94b23f656e9486bf4f6aa80adf8003b3005c4c78d59e"
+                + "9de695bd4c5dfdaed42c6019b5d33f73d7cb66429034fc6454e75d2d75650d910da551a8"
+                + "1c1c3e996f6a128b820e9464387cacfcb5d52510ffcc978c270dd7a369378b336e86b001"
+                + "6b203dd0476d632b95578c6cc289806a23cee24cd48329bc87a196479435768de87403b4"
+                + "14ef512a52d89c6691024613f52aa965147c0bd52824f7b4c4bbe228acb9c143a1436cce"
+                + "ffc3841ad237a3c5285e59fa7f327406cae3b53be57ab26a8cf2c5103d5e62e3293c2ae4"
+                + "2e071155443e123b13d19fa2603b6dc65dbb97d283fbb4a13966d61bec454b1693d96123"
+                + "6fc106b33dd0fff2805f84b0cb8a838289fb76d353615a31d783eb51024d0e149426b62e"
+                + "2481f8d80f6580fae5de14dbc17e4bdc782b0fc39617d55098873a83f5988fe73329a332"
+                + "c17b8cbe3b2ce147a8458c3964b7ee2386c6ae454a884f3a7a1a9b54d6cddd17052e789e"
+                + "a7bef128068599fbb19449d3b304d5bb07d36ad72c214b81ab9d1cb68caf96730f668657"
+                + "f8fe6683de7684b965d0ba703eba93d23f3d6e2a97fc9c2ee6a8dfa1fc089463c5eb01a8"
+                + "3df95f48ee11af72e1ac18e8edb13f98e1faa7b005932ea01d9766d52987de585444fa2c"
+                + "e35c037f799777766a6795b6c62071ad638ea585dedac6372114b644540a63312370d830"
+                + "1b0580b6c41287f28c302203a15739a50d1c700cb4a551f872cfe6bbe9715b742f50c7cf"
+                + "05b7585d234408b43cdd0878028d233463ee9c898ae89bf2bff4b1329168d787528c3d7a"
+                + "ba200606a5acda4d6c50611938c7014697c5d1229c1e5035df5674ad3f0c60e518cf3632"
+                + "6b5e2c2493993c8eaea63a6523192cec258f60c7c74e4762447a1839411a297ae42becad"
+                + "7b3b77fe299abed7a4070a839c0246401c5f395ce588de5799be451326734d33b08c1d7a"
+                + "7a5883adb71b8b5f15798d7930a6a8428c1efe8831e254ea87356a8d0b458284f09cad8c"
+                + "c6420ce404c74d737c8903af611b603a07237c3ca279d5a66182d204e03c1c29b2f7cc87"
+                + "afab28bf078f8c9fe5f8d865909e97758089f40d7c1f740ca0ddc133846b2bfc0eaf3547"
+                + "df8216a93dc41d8210632321faf8b67931e2ac5a923b55ef0e0150c87114749c4dd45e28"
+                + "c7513cd0e53782d8ff82bdea98b44b8b2f29c801d285bb2d7d7d62934ba8a86295608885"
+                + "b7946560319f041420646fd042b0dd2dbdcecc1c1dab180c6ac752c1244ffeefc93d51cf"
+                + "87789069056d9dfc7874e4115644b8a5615c1ceed9c9fff459ad811a6caddd1f2f1d5a48"
+                + "84c52c1f2f05dd3cfcfaec71bb792d38322c7b41d403e715f3374b1380f247282a1697ed"
+                + "35081c220d474084f1102ad1117ca4c4c5a2bcf5154561f8918a1ff7246b03c4167981db"
+                + "8600b4c4b3e770afc49c1271a0b9302c38be5db08230953af0b9e63ed1257718bb1e5b0f"
+                + "22bb29e49287acad0a0e8a27294347e09b598224d0eaa74269087c5355cb2e3e3769c845"
+                + "defd66f2ac84639e28e48a88823915812860fb4db342c520e4d1fd44dddbd0e4d5af623f"
+                + "e5bb2ff8f32b8544c8d7f490782d029d1cc5e30948da196f3d7cb4ef7774d682706bb42b"
+                + "ee589759ad9a4896e4e93c1268bd01854d6042a43b9c362a16d2df6dd3fe07d7d0512fae"
+                + "0e8b8026ec61f345aa5afbdcb11f992086f20074669199619d8ae8453f355d3772a2df2f"
+                + "4b5a5a95a9dace2f217d190966a159aee2c60a9c51903f41b63cbb5d2037faf2fc081d3d"
+                + "b0272d04947394e95f6381398ae5eefea421bc7d53927f855e1f0e741d38c62794e9fc7e"
+                + "acb29e8a4aae948c419adaf3a11ecb5f94716cb72912fea6bd7cc5065ce2e1c27673c7a9"
+                + "ee483df733e52a5b95fcd6ee9f9ec3676cb943d980c5b0abf01d9d55c6f71045fb1aa909"
+                + "24f42cace83cd45eaf7846488e17568b244d35eec1c1c1f5e82b3734051fad22fa92fc1d"
+                + "b5192f9911328851f7ed3aa02d3eafccacd51bd3aa80ba4e941e1ac9b704ba08e5191d0e"
+                + "83427f12110c08736cfd99be8ab2489a2edaafa33a8643a01a2d143222c313f3dac5bfa6"
+                + "6db2e3378d808bec5e338950ab72a94f6f28d685f13369a941407fd2d407706437d96832"
+                + "e49346e0580e15f9c1c661280f0c6dc32d84b4b6beed64c466de2bc7aa5cc602e75e3da5"
+                + "d6d9ea8ad3b85d1f6a15c7a6307260abceec94bbf7fd78dbe2c985c29ab32240212debd9"
+                + "dac89a90331bdbea61f3a4d0314c2200db0d356f2e02c9e0dde62f24c321948ab08b0206"
+                + "c8b29ce84bb719596294f554af5a22dfd4e860a9fc4113a92416d8c334afd3a8aa8b9516"
+                + "c5e6a29ea4b2393afb08e5003e27865f9c6a5ff15eb3edf61d02fcd5ada1c0ef2be107ac"
+                + "aa0a06c306454fab17b7d3511cf4c1be8b2dd2a4a8435dbb1073e469bd2bcd322d42f3f1"
+                + "311a080b65f39610762e3f28b572b8ec25a18a4bbd1ea2a8633f0cec9f9d0cd24d8c8b63"
+                + "586e2d8c2ac5331b81828f220d24f875aa77e490ec30a16e040f3f6c375decd6125e38bd"
+                + "f1a003a35bcb9eee03c488507d164fd4c82ba483b3cdb198e4eef029c542816d601a11cc"
+                + "6ac92ccba109e8a90f4b881bed7979ddc1c9fa6ffdde4ed7a7fc07699901bb7b851df419"
+                + "14612bb3c8e5b07e010502f135f502091e9d64742e01aad28bc63f9ad89c7c616cdf4175"
+                + "94f94cad9f16835800f0f641ae8b34cfccd010d98d712280713feb9a61229dfdf67badd7"
+                + "65d6f3edb18e61a7be96f5f609384c8c9569fcd9e4d8dcde8a16a0b521567a53459f3576"
+                + "9e782169548c0761d3ed37acaca8a7edc24459c3531b98aff5f323bf58946b04661cfdcc"
+                + "70c7b2ed4ba60e676d29e0460f17ac6c334e97c2c0876ad189ff439350cd8e105ba82871"
+                + "5a51f34cdd3ea3ea3904097bf6a71b4a35cfd158bd9879bfc21a9df19beb1088f0a95384"
+                + "3bdb29feed826a9b6be6a009e8aa1e13f2ea8702522753a1ad27200ca6c2e931708a7cd8"
+                + "74410854e9a26fa4eec37332d53b475a4d37d1b74a1e32b7ffd97755b8b8bb77ff8e2213"
+                + "61df97e56677f0950aec8aa57fad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280b"
+                + "f411e823ca0e4a693d6c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff"
+                + "4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3"
+                + "bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b4"
+                + "9efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab050"
+                + "0a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed"
+                + "28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb77"
+                + "8bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797b"
+                + "fc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723c"
+                + "e9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c"
+                + "1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c614059"
+                + "35ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a4"
+                + "31f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33"
+                + "de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403"
+                + "f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f"
+                + "7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f"
+                + "850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3"
+                + "c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779"
+                + "b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a266"
+                + "35e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449d"
+                + "e0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601"
+                + "922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d2"
+                + "5d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba0918"
+                + "1896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c53"
+                + "8becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f"
+                + "52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4c"
+                + "e480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31"
+                + "6608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6"
+                + "e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1"
+                + "ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e5"
+                + "62f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604a"
+                + "bff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f72"
+                + "0c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d"
+                + "8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff5"
+                + "4efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a"
+                + "807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4"
+                + "fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60"
+                + "b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd831"
+                + "4074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8"
+                + "ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8"
+                + "c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0"
+                + "c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d61"
+                + "7120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee5"
+                + "4095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8"
+                + "c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09c"
+                + "af363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97"
+                + "fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae19004"
+                + "68c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a"
+                + "46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbf"
+                + "b619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa"
+                + "627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca"
+                + "2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7"
+                + "e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4"
+                + "a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99"
+                + "a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608c"
+                + "fa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38b"
+                + "b93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544"
+                + "f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141"
+                + "e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04"
+                + "d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbac"
+                + "c62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539b"
+                + "ec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0c78848"
+                + "c9e053887ed436a6470bb99f7f34357811e280409fef6c81ecf07c477768358fe520c7e9"
+                + "b08084b682b1922567b70a1f4441573c39097f34c0098344581b22284e39c5a050080b34"
+                + "03fefa5d45878397d3fd19a8770e42aa61967e863735d16bfd7b1aeb0d4bd06ae0e58149"
+                + "7a600805c6c56e214e01b18e023ece14ccda2703c62d54f3db1d46e2cfe6e96d873dc0a6"
+                + "85f03b393d70685daa30a13e483d5c696e549b42c424b55ca351e6c8e0a1db1886122fd9"
+                + "8538f4c219609e6956d48607fd14f1a9bc62d7cc04306606913d9d93fada3cbc1ca95e0d"
+                + "f5512116708970daa627a5baecccb7c0728cce5554ba5ec8eb385a24efb1be2007078c11"
+                + "66f693523a9897e4fb92cbaf3f84a97f4386f105d78ee6101114557c5033695837f81ac9"
+                + "85e4d6498876947270a1820a4b84f5eef991e08a7f58c6f82f2bd9741b4be455558e77f8"
+                + "5d6a6c3af8d41815a2639563aee03d1af84e7a2f1df66d5bf327bb16c629fb4140051a27"
+                + "549da1cba648c4629cd87b88f285df12fce998100bef61269e0dc32f5b600008a6ee400d"
+                + "dc8ee58fd00585755b3c3d0ad5068110d37910bff98ba839adea2232fdfc85a91e4af5d8"
+                + "08a322338c0e394628b57f60d8b48923bd98df01dc6f8411ef4a49becd8f069cabfd58b5"
+                + "e9ce213b7dfa1b691180b8e208360e2f984fd5943ce05b62be3f6da1cfe154bdbf4ca513"
+                + "a3793f30c91bc001b31a7caa17431431d08e24f9cd9f1dfede9cb7a1ce76a0c1b06dc567"
+                + "b4370a0e3ff46b2ad37998ddd841c8c2778199a3c1c490e05354a651442c3594133b44e0"
+                + "12d8a5aeb450559c7cfa497844e3ddc719a26c79b11dfef604a559c011cdb24f3d09b42c"
+                + "1bfae665b5c090584880bd1123e0c28aea90f59318c8d391256c374693f490509d0dd8ec"
+                + "16bf271c604d5e1c0504003d044e16414b9c9ce34cd6027e4c60a0b58d90991c2e3aeb42"
+                + "5f19ea8e9f6092f3ca1e5b4ef812db0339bf9ebecf80fdfed6a4da8a8f7954b92386e3e6"
+                + "66df4260ffc6a40f07ddfcc528c0809f669bf145ca217844cc542125f9893c70d44813c9"
+                + "463ec1b8b6aa397784c69abd82f5b362870b1451d9752a163e630bce5edf6fe92884d772"
+                + "dcc50dc12dcb98ea7bc5f2a04499419ffb3690e005d5a713f82b2f1f237d723f2eb83182"
+                + "6fbbadbe65c1485b8f2f2b2e0bf2716d4102b6e92d27d1ed853509d53cab2f45fdbda32d"
+                + "5c5c46bb74bccaeecb4b7aa38a8bfdfd5224ebf63e82e96911392500bc69c9987475f4ba"
+                + "115394b51cf5973a68a209aab4daf5d6471b0e24fb20d7d0d6b98b63f21f3976fea89cfe"
+                + "aa47c9b88ac1256cd103b6a5c77543f90b3482c3cffdbbd6b240c24e80ae1ec58c5ee751"
+                + "1d32ba58c7ebe8a0ec475dbd01a06841d70abfce93f213526665f98562478fa58fe80757"
+                + "5bcd8cc46e7ae08641cb65ab01da44758d99c0665ef73312816cdb9f9c78947cb777cddc"
+                + "a0e246c9116e9c3329dff0c457e719f0a935920f2e96c36cd183e0b0c83e7015752d0152"
+                + "4f59ed6d4d3ec0119ce81ee33f28fadda0b0225e136b2a9beb59d5cff29560d93315a670"
+                + "b33829fcef52aad2f4bc2359a35ba2086758ccb7ab2fadd738eef37e67411ac16cefef31"
+                + "bccd42194a3750d0c03e953f83f9f3d635804fedc04fb8160e6f58f2dfd89dfdd194924a"
+                + "1341279b3110bea989a09e6c4add00a4c7144985e61212267a7a86d95ec8255fd184beda"
+                + "07f5314002af0e2ff51f90b7cb79058866213373f106084794d285a528c5107077bf2cb2"
+                + "858e0de27f5a80755e0a322bc91d9d7094dcff89fc6e490b4d10b5974fffaa9bd90a156d"
+                + "e0465c22829fac8e0fb076dc3ed2590ce5ceb7ed501ab90b8dc2d4fe527b26ab1ef7e78d"
+                + "0db615c2e5acc02ac0ac97e659e9079696cd10fcee10fe4efdbf2467fd0aa8bc2aeb3f7e"
+                + "5473a4ac367cf367c114fbbecad16245beedbf55c332775fc355ba1602297ea09f5b352e"
+                + "26be577c0a3f63908cbf7b3280eb1f7f5eab36db6d31b1bd35a82d50b659a8d9d37094ed"
+                + "21cb19374f7a32e8d00985118ee41306a89dcdb1e6ad6bb8ab1189d9687e0a3c1bbd5f7b"
+                + "bffa34a40c2d1bd4954fd0c3b92fc684233c94ae243b76a5afdc8b0db7ac86eaf8130816"
+                + "669894029251eacfbd64136731e9d006d4855b1a2680125e9c9bbac4258f9bfdd258f644"
+                + "4dbee77f36173cb6b4d08328ae508db5ae0b6a4b90c5706a43e2be102c8c32df13b2a78a"
+                + "82aa13c55eb3f58ddbe8a24fbf516e37dca7e0a680e164df2863b86881ebc395e0b4aa33"
+                + "6ed120d152bbd871be4b8e434295a5e6cb5d5bd042dc4f79a05ef542063bd8b197dae8f9"
+                + "6c39866de9adc64a8db0b0931ba86bd9ff751b41d03d7e79b2f6f80ee8e8fe9fbe0758ad"
+                + "490f300b514ef76a1f8602570f26edee892f5f205aa871bb7c7067be9367497d5888f9ca"
+                + "15306307ee240b63009784615e1573b736d2def4a91c12c5c7316cfcd5686f0f7e20e5c5"
+                + "b7059ddf249b7559d2864725256306685af995e8ff21ef78bda2bd16c19609acda9eeb4c"
+                + "6e3bb9cd73e34ff92aaa40109bbc90775da570b6577c3679d1a430f386a452abef2f9269"
+                + "4b31b77ddf232256367d0ad26d0d426696e076f4d67501438b696416c138240a0c57ce53"
+                + "5a287f879e9b6ac67edd52860cdf832fc45606d2c9444c5af6172205b2b1da08959adccf"
+                + "dcacbcbd474cf229c2f7acf7f2da5aab63356f2556fbbf3ffae8547eb2c385c996bf068b"
+                + "16344d86ebca18ff672ac1703e310f1115829325406d14da7fe2fe5056dff725804b3717"
+                + "2112ee85ab2d9a213ea3f71199de065c8022f3791d25efe5dbb90e68ba96068e67b572e6"
+                + "70ddcf4dca637c6c1d154d9d70d7a0bddcef10ed93de3ddb41e9dac9f408a6e789723c37"
+                + "c92da2ae806c37e98c7fd203e7aec27669c818a06965ef0b227a157853bce6bd6e5cfcfd"
+                + "fdd1d0122946e7f354eb0ed7dce445cd7daaa5217ef572f3e8b599529873054dda889c46"
+                + "24ebfcef0020e14d5bc2de536efd4f58270cc87792ec9feedf1f241427b60015f4cbdf89"
+                + "5d9577c3e6be45c5a72f25d342ad5276bed7a48cd04e194eb54e2cb2ec31d426aef2280b"
+                + "f411e823ca0e4a693d6c9ec3c1c46d82372a4cdedc2e2ff1e78c3458be8c9595e991ccff"
+                + "4feb85c45f41891edc54b457f45fee6a7a0330ac02b16debe81c4c99bcc72aeb8df92cc3"
+                + "bba209eb0242370aa0696fbfbff86927957cbc1d6d526fbc9840be74ce06ff3750a015b4"
+                + "9efe63736639643a58822a6f8b54ecaf66472d0301ac2dbd3d0ce21d24deef38607ab050"
+                + "0a4b004e5a7487568a2319fa2c22ee15e12acdff138d5df76aab7631f1c02b7f24e2dfed"
+                + "28d66ae700bc146105297b8a1bf7c4261e6d82e302508a36daaaea4f59c41dd75855eb77"
+                + "8bc3317e962ff61c6a70c7f80c2b061f97460738df784aad958100af7d76a9557d79797b"
+                + "fc0682b9d1d45e184f8998814e60ba4acbf440a9cecb5a05c282c97ee832024f14ba723c"
+                + "e9193ca76ee354509db4868a09669cbaa9d08ff6724b14123a1817f7609284f0f376da3c"
+                + "1cafa88145589f7992d6d4ad9eb188bf8dc0487dfcf7228fbecaeac4c9c755540c614059"
+                + "35ca5f7f08aaa17b371449eb10da179ccb6c08272ea3cf1bab572da9e1120147644172a4"
+                + "31f8e674d6cc65aeeae428c9ab911543b898291a46de834b652ca69e35556ab15d40bc33"
+                + "de89d8abd3c1be242510c3e92338822d010cb9208c2c197cfb1fa2aa6b60f6eec89ce403"
+                + "f7fe1fca7a368286cfcc25e3684027531265f48ed228424b4dc5126011ee41a5b978c89f"
+                + "7577d717cad1e7651a87e775c0439c90a53d61163c5aa887e0ebd6e8307a1f3fc91be72f"
+                + "850d5ea24b0d100b4055e097e4a80b782f8e8fb031db34534ba2e836a9d4daa3effcbab3"
+                + "c4026941bd8c8efb38997d764807d57f2caf89638a9dd8648e37120cec0677ca78cac779"
+                + "b29830311b4e4a81e3064fdefe17d789ad7b5893378e7fad9922c4353fe2cc812715a266"
+                + "35e071a5fe767f0842697177a6ec7e5b8d1ed9542e338f70916e86687f5d9fab6242449d"
+                + "e0804adb299492e34f6a7320ba4c822c9e1e82cd0d79a21d6e51513d0ca1893fe7099601"
+                + "922c4c12d491b506e8b95a94aba747c65723a4b8c2e827b2871cfc653a532448424297d2"
+                + "5d986309315020ea74e5febce670dc548f77d3ab970e907ff86dc33621a70193bcba0918"
+                + "1896f5db10e0891113a5545d5b49e325d84aa33a5d04243c5a88fa5f85283359ae599c53"
+                + "8becf5204c5d665ccebf2925ab951797c870e63d2c22d58ed9b5eb51cd0ea0758a61e15f"
+                + "52c6b846e91b022518928f11556b14cf2bce9da84d2b9913ed15a6b05775270d23f52d4c"
+                + "e480091f63d42a169fee253087e60621fc2f1af3a89834bab301ef40cc8a4d62233dab31"
+                + "6608b1781fe97efde3476af76921ab63a12472a2617d1ff461eada9fc75ba87638904fc6"
+                + "e9616fed729f8c19a11f14064a51107f9338be66d35d8171f53a9bc53b2677302a44f1a1"
+                + "ab3bfc04246e4d1640f4bafeec55e7c0521fae3615881c3913b03f32c99e94a58629d0e5"
+                + "62f4658cac16a3b2ee819baa40008811a07e4557e1cd8ed0aabf8a94c80025dbab95604a"
+                + "bff4a27164af2d1f0bf88536d8c2ef02f18ee813f3bd459f611a26f9b10551cfb5c09f72"
+                + "0c719329234e5ff041bb1863cacfad06ff2c3963a002e9d5946cdf92ae79755f6eb2e41d"
+                + "8e8ba85bd3a17ee24b7aa611c190f380026a7f7d6d0e98cfafdf023dd31d035e8f2bfff5"
+                + "4efad2fd1de6418072a5bafd06cd55225c11c2518fec70827e5bb81bc0d8ec192fb13d2a"
+                + "807ed5028e85a3ee3981326c0ea8e66305acefc1d6765b1961be992fc4f1e9388fc86cc4"
+                + "fabe4491b0958ff7c93da8fcbf5946bad3e7a4142cff3f3b5dc769037738770f08618a60"
+                + "b8d8d555384e2df4055a1a30e0d8f93cdde2457262d0289037108dffd28d76690f7cd831"
+                + "4074f8cb98b1dfdaadde0d37b1a2dede04605e4128c4de14b7861de1fb8c9d80b7dcf6a8"
+                + "ef7c2aa2db0946d089ef214e6e8da688e37087ca98a5aaf66995d994be6012a54deaead8"
+                + "c50a0c813dab15fed0d505d4844b8e8697a59ab8919eb3a54a8e6e56300919b2c95bf3f0"
+                + "c03044bde1d3177495ac1c9f6804a8628210468d5d3ea6fa639c8e6a452cb6febd7b8d61"
+                + "7120a25adf5c0b5027b63c51f38c251474bfd331705f23f2bf44b44312b434ed34f66ee5"
+                + "4095d68cb1bc34dc1e23b12fe9cfdb3a1489e0a3c49f92a2f6f47d61eb6b6105c55f75a8"
+                + "c2f646d2bde2a5ad13e28e8cb8e85b988ec9ef0039103b7ce0c876d5139b856c2f7bb09c"
+                + "af363db8023ab0042c64edc1b820eceb38d87487bd397d81a24a5c4b066975239dea6a97"
+                + "fcfaf2f661dd7de656d0ea1bf1e5ca2c7e99775fb423d11f7407ff2dd8c67d22bae19004"
+                + "68c23b8328e041e9e1bb83c529b8b72302410e820c3bdc480d0d0d528f1537fb0164093a"
+                + "46813f93432e0448fd2fe8d787dd2d096fee902617e2052fb4af92f70fb7835b52b62fbf"
+                + "b619b595fbac2aac969e47369bd442864357a58345656b4ed300fa69af64807fadb2cbaa"
+                + "627c2aff56be53ed60b455c66b52a56ebe109f9e4786d766e8b1d776a2cc1b6000712aca"
+                + "2a7819258b6a33141cd9d6b7f351a599c58b15cef7c082df74fd990e4db16b3aef88c0f7"
+                + "e0e0a951c3cec94dcc35a3bdc63cc76091f54deae10e1c2add64f492f229d2e9ec4cdcf4"
+                + "a81b2135caeae4d24bcd32d99154eb062b2321c21c219e53fddf91064e355795c30dce99"
+                + "a8c3541274538eacd3d7d5d78d7100a288b1169aca16c8b7f7af30ef8f1f86f029f7608c"
+                + "fa45fba7dd985b5a04702a9c4dae69083e80aee0a7a7137b191fa91ba00d876470fdf38b"
+                + "b93125ad7fb033dbab4e5aba8331668e4ef5ef5e66496dd6bd455a9867a323ffd9e15544"
+                + "f6de22881a0840f99cc561badd78e3c93e43f77f1446666df42c5302523a154c0f3d9141"
+                + "e0680b7cdc0d6ca1797218d37a24dd4b91fa46f28eb69b943be42e08363055ab7cb36b04"
+                + "d5968a806828aea2e4c42cbc896f372f8391bdb5c559d48d5214d40874e631fad28fdbac"
+                + "c62c66c1502086f2880bfd3082a7a9e80cffc0d662b409992755c3ff6e8e1ebebea1539b"
+                + "ec4f97e04d579558868d95029221d5ea68546d99a41b371372090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce78e5823"
+                + "44b598dcff947725bd924a69e4e36a9698be91d950da538317972cd7c9ad6ae9a6a5c006"
+                + "70ced74d83b226aff0fc73cc25f3302ed178bd05cad5c7573cbcc427351ec5c32b1f65cb"
+                + "18b70361547f3fc2a347c934eaf9e53760d2a11bf4b96f9716a8a5582f057e70faa453f4"
+                + "39cabdd77adb5733733f577168199a3eb7bc0ee3cd9cba4bd93cdf7ceb1c626ecbfcc7d2"
+                + "012d1801486c45f2ae09a60055dac74dc2ba38e62b82a4f0037ee8af97dd9d24622c5703"
+                + "9c5553735277c716fe5aca875bf9a4c7af222eb2c69bc94f8d43890ea542b4c8fcbb714d"
+                + "e7625ee3a25bd90fb445feff1ccd30239d79bdc0ed554c4e04190ebdea7707014f931506"
+                + "5507ed045647bd00e21654cc3d96efa5d3e3d1850b50b6aeba7e69dddd66743dcea51731"
+                + "f69248ffa97e266f807caab52760a648bdd25390cfa8027290e6b629e3aad1a5d429a6e1"
+                + "23dbe8e40ce116ecfa044fd064c6cfb6d75b8eda2f9fd45c547300eba311510461d5a35b"
+                + "641b6643955631fd07aee48248d7969eac0ef904cf72ac0b997afa8a5eb9652c73707e9c"
+                + "6998a899e4a559d0b8f77013b4a05d357965c42034bfbb926cd40d210bf83253d01e2b64"
+                + "54811579ec21c1d9b4204fbaa6fc0dbbe3f5a8dc361bbe4cb080610459506564cd734f01"
+                + "9c21ef14239a8a7f33736422dd9389153ab21dfe11b1fcad12c206bd7b2d1aec6cf97a06"
+                + "42ec9985fc53580ddf2fd01e743ebb16c38e821b2c179767fbbaea1eb16ed783e51a91ff"
+                + "1bbeb0c94bce53fc2793dbc8c587951468e5404495851472ea5528ada4a87931dfbb84ea"
+                + "57f9a0ffb658689dc74006ba080b7e8470b5262f2e58c5cdc378341bff5a5734ba1c3ade"
+                + "a089d3d74561ff757aa1587cfba5ce7257602ec99713949e15c8f4b4a620f3aafd2887cf"
+                + "a88d486353b3179d5ecb1dcc57986479c3a25aeca56a6ecd4f94057b2724476bb3afb447"
+                + "b9294d4171b52a4fe4decfe393df5faf401713fe2f12e5ac3f2abeb9a4ee829801eb65e2"
+                + "6c5c126cef16dd59fd670691db271a21c8b1207ce41ef26bf58c66517435ade6980ff6ea"
+                + "c73d1c5a25669afa21e30ecab43cacfd4a1fe7af058527d0722ac33b10216c96087bd284"
+                + "2446efe4055447dc3fdd1ea217ded44ba1ba81babc9783014bc95ac934344bbacb9639bc"
+                + "6cc5dc791a81ad9c1f8dee4ff1c108cd6796f64db6db160cf9fb5c5dba2de9f1f996e82c"
+                + "2d26c4c51ddc6c935a901415e2914f77ca352b877da29488abd2f9ad2ac50db50af5048a"
+                + "2135b359f285647e334e1dfea06884be93418e3e983b3e8b3f3b04247294f71ba7222577"
+                + "fc1f37c9bba01e1cc203f04a34ab0863c92b5c8070796babe791b027f14b5a1f0a9fae39"
+                + "380862a7c16856b2b5df2cab4c3de2107dd40d8db697a5b4bc123e9df20222befa9fc8c5"
+                + "91c33c8256b15cfdd54eebda108bee0eeecc7a0d6a20c620e250d5a74e007d5b40ac0519"
+                + "731bb319af0b22921c32c53bcae5c876d34d739e91c7be6b92b2affda082a9283c65173e"
+                + "1ee202853899bdde9830f660064792ded789a4b3c3781d695c5a286dc2d53ae86f8c33bf"
+                + "153f75d87b472acbe51426f5973dcf80a5562ea8e04fd795e98856aee72e165fbde09723"
+                + "6134acb53b300d7351536dd99c39000ef48ed79cd5993b36beaae946e32f719c7cc125cc"
+                + "6ddbc4198ef51a9d5d4c611aa60c5f62cfb3e819459bac21d62e1b683c0e3e2503b4b446"
+                + "5274bac9856a3534826fbf5d4b308c36a5093c0cbe8b54140765cd0337eb809611a09a46"
+                + "00a945a64eef5d2803b9944ed45c4f339741ed23e6ef4993ceef813a7e6ad7d151d95d1c"
+                + "dcd78bcb7cd5dc5f678a7c534df8cedcaf2d77857c84c9af63e7d539d0dc968582e45bc4"
+                + "c29252d1a33940dd73625b27b11997c872b07b5e8eccdb9f5db7d810f7dc34be8036f2a6"
+                + "b5c3a0f10632f4aaa2bb3e6e609f0e4ab9101237783a5d2138d9a0545c6ab847627d2f8e"
+                + "bd9d33e498e9f74c01877f93ccaa591436a61ea804ed464e6acecd2667edd3452ea0141c"
+                + "0142080ccd4d76700a7e46f20277166421549293c404e7c17707d27ba36fb964eb9863a9"
+                + "8316ccf9811c5239ecb65f3b213a3762e1c2f8d52532ea506cd3673ee9188fe844ebd8ca"
+                + "9e53da1eff8e8a6db01bb528cc44aa42850928b33ea463071c2782ac9798d1f5835249ee"
+                + "8f0b8d30c7394bd65fb59de8dff5e03da92d8ec4a41621789a8a01b9494e74eaa4313a68"
+                + "e67218e056678c73041579e3a981e2145bc27ce5ff529cdc5cd39b5740c5978b58507d8a"
+                + "b23ea40af4d4ac338cfe5f8b3bda61711d5502dce06f78acf52d062bd1cd59492e5f8a8b"
+                + "07a84dae1d81c0e0616a1009a5d42c32f99060a5728d901e505e19901422cb4b1f6e4626"
+                + "986f88d0bc2e3b1650139680adf068896b8f552a3d6c1a59070700702adaae1c62d94ffa"
+                + "5a67c6705cbbcc41eacd0cd4dc1d15042ab82fae19d89e6d67bf3421245ce81ef38b0f92"
+                + "db8116a8aa8612493e6d93e9fae4ebfadbef5332daadc80c9a46d58ed73ccdcad756d77c"
+                + "c73c32b1a0977fed2c264c09cac0efb454ef840a9a31a0606aa7db40f555c6631f6b0101"
+                + "9c274a5e035817fc6a46002c0d1be9ef692c67279e16fd07c780b37d3046e433b1edab22"
+                + "c35e80af9293912c8f5dab5c017f8494f6493b287e9bca60734cc7ce14cfe6b9594c1925"
+                + "06f6e7f055cd590c30a672e1337fdaf884bdfb081c3259c7f6571795a830bb567f66920f"
+                + "c18cea16b2e5c2fb655c6716b1dde9d1e23e49b60ae52ec7ad9ff805024f4fde50cfb35a"
+                + "652cff1285efbf4d83e0bcbe4addbeb89edae7aff1643477e5f722db76badadeaef4c68b"
+                + "e7b5527f9ad89faf0415164fa8b93d62ac00bd082ffa96a88764cd7dc077beae8a909fcd"
+                + "e9436a66acb8c3d0eaffb1d94396373fcc47c162fc7ddaf3e42c26258a0a771839a6c18b"
+                + "afef9f2759f7db0475568759a9de77af9cdb05bc86ed4952bd96b0ca2219d3588490aefa"
+                + "11cf1d13ec91ef65c2756c7cab038755ef7571a6c396596b3676e62fc35d9d336dfb10de"
+                + "120b730f5e668ea7fa9ab309702f0030d0436282667f9f2b4fb849088b139e035728c3bb"
+                + "ba3e613731974cb831aa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8"
+                + "438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c3"
+                + "9466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d"
+                + "11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b63"
+                + "48ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35"
+                + "f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca86572"
+                + "5db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c8"
+                + "0313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43f"
+                + "a773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7"
+                + "985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d2"
+                + "3b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513"
+                + "d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22"
+                + "dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c"
+                + "566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b897415696"
+                + "4b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee"
+                + "24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d"
+                + "3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35b"
+                + "ac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b9"
+                + "5375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf94"
+                + "1e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd"
+                + "7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5a"
+                + "d677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e"
+                + "41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6"
+                + "e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799"
+                + "d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2ad"
+                + "c440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc8"
+                + "95e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35"
+                + "880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017b"
+                + "bf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5"
+                + "a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556"
+                + "d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb583406"
+                + "9f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2"
+                + "efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711"
+                + "c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6a"
+                + "f95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a8"
+                + "6c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa923"
+                + "3ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a4"
+                + "1da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89"
+                + "e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9"
+                + "017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3"
+                + "f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b21"
+                + "65ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e"
+                + "88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04"
+                + "b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a87"
+                + "23a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06"
+                + "ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6a"
+                + "aa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0"
+                + "c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb5"
+                + "69a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240"
+                + "219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7"
+                + "044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677c"
+                + "b58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b0"
+                + "81b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725d"
+                + "c2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271"
+                + "cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2"
+                + "e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86"
+                + "fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7"
+                + "e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e"
+                + "1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f4"
+                + "9a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b435"
+                + "5ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f47902d63"
+                + "013c6536ae47023e86bcde904f9aaa901c57dba3621a06357d5e6a3c65f88a1d681aa8bc"
+                + "75416a9be5d1e478f6c8268172110c1dae6c6821f4bc3b36e907aab3fbc16e01ef6671df"
+                + "f82e84e76985a0ea7e25ec890edbb1813ca87bfa61a002548741d792691957e43e774b81"
+                + "41ef9602caf775cc5be87c9abe6aa6fd8a7b83d7a13b3635076b13099b822c4b4b7caef5"
+                + "3a800ad740ea93f1f8d06842d8c0b7158714d4369ebdba995ce1c7d45fca8bdf8b6e7a6c"
+                + "b0efd228a5689c99c01f90a9edecad9409975594b65c0404da45c18d18be04a61a87cfb1"
+                + "ae1c0b6d4be10024a0f6d5c75ca06f0b9b15a582abb52ab8b347dc8d7b2222f93bef0f07"
+                + "608905cf8c25080f613a8e20c5fe9daace2d06b7db380d5cf6b94663960743d5c88cb039"
+                + "b71278fd08795da93cb474e8a9a20406ff1792ae569395e409647cbc5629fc88f2b1ba15"
+                + "cf4fe0e1864b2bc095868b92960b3496d903c5add506278d85138d6b11a27c19c7506c74"
+                + "b1c8fa6796867413c37c90386fa91e50fbc73e7f6bf4b22248d679d6187607fd4ab227c9"
+                + "5c783091f4165e35a425e8e076cdcc8986f5c6756f9d96bd9a17b3f89256069d6dcd9943"
+                + "ca28ffa6bc60f96190437b7e2b39977ab8ce858cb138bb4ea763edf76281d0614ceba6fa"
+                + "ccd4f5a5301d2edfb9074b28d73d929aef9bd6cbed546cb06e9ed31e4f998329378a80a7"
+                + "71f6c0aad2fe68ca974c2f09e89d837f1062454b957f02ca15ffaec5eb0b45194ec03dcb"
+                + "92a9b75f26a9ff0dc99d98c3d5107c2b07fb858c4c9ef3207c63bfc73da979182fef927e"
+                + "d234d04de03716ff60743d00b16cc1255b35e46224ec0ae6e2a78ac72165247d26c1afe2"
+                + "959a741d9023e8dfe738be95c0f9d1bf0cf80f9a2d1e4b2756ca44d5d751b522450b22bc"
+                + "a9b5a6b1d14a58e69b16d5a2e074bb0e5787bd7a9d21532b3c8c8256c9cba44d4ab159d7"
+                + "756d1e49b184f2ac032df5350013cc37e931dec91ecafad2a202a8aa7850b306b1177e94"
+                + "f30b65c8f0cac8fbf05d9389b07ffa222328922ad9e9318fd1dfe03ec37d57da7ade4bf4"
+                + "3007534e9d3616eef5e1393a6c7a42108eb019c0e706a2839b86f013b72ccc833ed93777"
+                + "d0f1bf70fa2a02d2b52e11391226836e15c62302b7582b3702111375ccb7b775407f1e53"
+                + "df3c877ea97668c07f415d024092cf3ad7d59dbcab565165d3e2027cdf180b8ee006449f"
+                + "4bae1397b2e7565e6de14bd79a75ee69d1d4b814d92e8eb3e17bca30ce2e7a04f1b1d15d"
+                + "7b3d8beb79fdd2bc97637910c3f8052a68339bc01a1507cf6a29585d86605ba57ddf1d56"
+                + "fb0764a68c6f8b6a88198aa6a9064a826bcfd0fa61e3c9721b19c68ae1ef17bc2f0d7c4e"
+                + "39d176de97bcdaeead1ef5f1fd74e70df110c7d845ce225ec148e60a1f2e54a7e5b2aaa2"
+                + "e0d9048f993ebbc8b1d8720e9e24826b58fd073b11d28f8bde2f883fed8ce609738fa68d"
+                + "68cee4c8ba94c2460dce24aeb32b373ff875f6a34745d65d8fbe9e843af826c0b7015bed"
+                + "00ed6bc45f97f2f511f96d664f7d852f26bd7a4cf9436bd723a5db23191c8c70ece261cb"
+                + "26b73d4087b8b51dd3b6e97370a009f89924b0c369ce69a57456c7aadac0d4a4053f97b1"
+                + "829a902be22a9ae7dbfd8347220b34b1d1b4625a3eb5af1d6cbd69068293c236fb28c659"
+                + "39afbd755b937ce0b732e49c02872710c037508b097cdf6ea90ce9c1d8626232ad444374"
+                + "519534db2b333fc8aa107f0b269bee8f7a7f78a60ee95c97398e7e3abd13eed9a9c3b6c2"
+                + "eeb2a5a29c53f911fb1c67b7c57b33da087ca760aea1325bef7fa2f20f7bcfea67b8ffce"
+                + "f60f7de082497a0a52b3e56814d666162eb2df2771e47d8adb1b6d376d7b6dfd47f84fc3"
+                + "803808a9eb048aba65f461275ce23ca5aa3550e366d4d12826ceeaf72edc549c2959f611"
+                + "029b95eda6b5af1c3d8f6158170f238082423f72513ed672d78831470aa681626615d0ae"
+                + "6da78251ea96e03206889f5d8f74bb71491d357117704e71b2111fb51992c3832176736d"
+                + "8c5b31dce9fc452d7c6d78b407b7c6b2b145b273c805365099447cceca2be4cda3b67e9a"
+                + "e3926f636a9b356e945b653e02c86d64c40246e635e033926724d0d709558a2e1cff8887"
+                + "b5a3382493658aadf7ccb2a09143bc320670836e08125b44780d5c90220201f5bf7042d6"
+                + "8c4affa00ea8a6db3314b0db60d25ca5cf69e23fa890c0bc770a9320fc2288c775cc6ff2"
+                + "59b8ce45fdea8aa1e4e1d57cc1396035fad1f3159209133dfbebf47207ab25a8f33655b0"
+                + "9178596e30235947dcccbff2f6a56a1fe7179d7ae76aebebfc9c7880e602ed92e166ea5a"
+                + "1fe623cfb5351cc8a911cefe676a638457ee40380941e82a44c0fc93b124e366425ab315"
+                + "c7fdcd056c335cedc1003571d8dab54ccd2d5a83c4c3afd9c4a0e04ffd021f236fbc42fb"
+                + "b1b229b2ceed70db9c47f1e9c01973bb37e068a46a58538eddfad948dc189cc203e1afd6"
+                + "71b1c024841f24c9c78a78c3a2635b8289cfb7f46500bec7927e59db76751ab53a845a5e"
+                + "9e0309ac376158cec3a9cd5c9856d30006b17bd58924eb287eea2e74d5e4a6465412f906"
+                + "bf70e0b0e37aa6351ce9d9894070417b554d188ad7072efde03ff7102d31f6cd3ba7215f"
+                + "006f87c346144e3ef2283a1377eb60d648c1fdeaf5726a6f0d93ab7129b1f56c7c4fae65"
+                + "a281aa4452907b08fdf2fae131a34d215dbdaf60e1ee5be13af4c194a1eff4f17ce1aab6"
+                + "f6f9494a050302c30734efa6744c3c92e53da53e414befccd66997eb20e34c6e561beeac"
+                + "9788adf6d3565d69df1ec7294067ce85962ea4f5a21d7c298354fc723b90fe4737635cee"
+                + "f13a2cf985c7ce2bbc2a737ca426bdd24533b9fca54274928c312ac79982267e02f3a11e"
+                + "53487934fcca2d6d1222037087d11082e5e2f86da3929235bee95fc6a19364784164bc14"
+                + "87bea1dec2b34a2a9fffa842983690c53eb11ec2ee330111af072625f92532f50a8c3bca"
+                + "b9f05173e059b01658030d76f285299b9acb7bf48452d347dfbb6c60a28d0f34a475c247"
+                + "80ed4ee656ba8ac4c3947667c12f0030d0436282667f9f2b4fb849088b139e035728c3bb"
+                + "ba3e613731974cb831aa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8"
+                + "438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c3"
+                + "9466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d"
+                + "11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b63"
+                + "48ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35"
+                + "f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca86572"
+                + "5db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c8"
+                + "0313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43f"
+                + "a773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7"
+                + "985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d2"
+                + "3b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513"
+                + "d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22"
+                + "dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c"
+                + "566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b897415696"
+                + "4b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee"
+                + "24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d"
+                + "3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35b"
+                + "ac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b9"
+                + "5375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf94"
+                + "1e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd"
+                + "7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5a"
+                + "d677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e"
+                + "41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6"
+                + "e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799"
+                + "d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2ad"
+                + "c440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc8"
+                + "95e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35"
+                + "880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017b"
+                + "bf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5"
+                + "a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556"
+                + "d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb583406"
+                + "9f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2"
+                + "efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711"
+                + "c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6a"
+                + "f95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a8"
+                + "6c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa923"
+                + "3ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a4"
+                + "1da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89"
+                + "e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9"
+                + "017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3"
+                + "f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b21"
+                + "65ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e"
+                + "88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04"
+                + "b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a87"
+                + "23a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06"
+                + "ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6a"
+                + "aa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0"
+                + "c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb5"
+                + "69a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240"
+                + "219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7"
+                + "044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677c"
+                + "b58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b0"
+                + "81b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725d"
+                + "c2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271"
+                + "cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2"
+                + "e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86"
+                + "fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7"
+                + "e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e"
+                + "1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f4"
+                + "9a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b435"
+                + "5ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b8279db"
+                + "ef079b1a2414a978211d00ddb8cdd0335dc54090f23da05a4f0ae84bd0631f4394a22ff8"
+                + "5d1381493676890f4afd8ab2c36ac63d55319b6dfd9ba54e58180be85e2ec5165524d807"
+                + "9fca088968db3a34e608dff56467bd9f720bc44cf2871247c0ed88b45a8ffe895cf00c1a"
+                + "5c397d6b1c7acccd1419cbbec3545b2215507e29228c3621657abfa32984f3bd1edb451e"
+                + "2fb41147cc7737eca098a8bbe748a625cd334dc99d56c2d69a2f0d9d1573f6753408d16b"
+                + "88dc8a84a1e68107aabfd8fc994bca9585811a1d5e8b9c83727fd7efecf4c643436d2932"
+                + "4db9e29802fb67e6597ae76459bf7ad7bc30179c9694a57ef693e1a01864929e69070e11"
+                + "f5d32a81f12a58fd24097bab1f32b308b63779e44c180654da74fecdeee57a66777d5c36"
+                + "19bcb1f01c8e87f6d46ccadaa159bc8074e33dd1c4467febc6ca84de82e94eb4a2a9f5c6"
+                + "ec739084be66ba08ac8606eccbb238489c5ec4403f3c18fb53ba63d55a976decfb5a68c6"
+                + "bba89488b100ded7fefbc1a022498e1d5a00808dd13ea4562bc17c3337103e1528db0efe"
+                + "480d56d10423d5f1f484b9ce1579a77a6194d9c4174d7d04bf54853f1d0ba101fc5ddf6d"
+                + "e4f5143c02348e61be28f27e492f9ed00c58fa66d437b2ac651cbcb23d6692cd0c0afa0c"
+                + "fb2e7837ee5fdb2ef1956a3b79ed80c603e565e38a77686dd9430ff9024d1c63b0723983"
+                + "5a87a6038e51eb9a394ea48d9529e66008034eab069e62fe6e54cd06bc0c0132c1d99e9c"
+                + "b7f1399181f777a60f749310ce4795e54039ae0f393cbe7bfe37e41cb4de14d9def7847f"
+                + "fc1e24b565384c508aa9311f61b6b4aff4d5d64bb3856026998dffc286a799ff146381db"
+                + "72d5f240c9e7c26da94b015b331b8a0a039082b46cb9adc30ce29ca68ef3687d00057740"
+                + "af97169febdb45f45f77e2566c915f9543db3f6456e632371231c9d6948404837e58d3b9"
+                + "f6919d12153286fd9dd26587c0963c7a712547a1f326c8cdba3108e3887877a6c39db2bb"
+                + "3a20e27052a9481efb0b3bb4f5296be74e942a7ce6ecd83fcd0fddecbd641344af647e20"
+                + "9fb4745d600607a21fb42ed9941fc49b4d321f478e96a0b4fa2246a0815ab6ffabe6b740"
+                + "8358a874ec66cc7def2125529fbb53fb23cc78b6c021d9c3ae3e32816b6377b4c5a72285"
+                + "910ea4dfa041886bdc2cd3151a53bfa23701f1dbccf3a4690a2e4f921387793fed895f42"
+                + "bfcf958748abdb87f27cea9b211589ae04ef19e173493be44a49178a19bf1d86737eecb8"
+                + "450ceb10130c25e84dffe681b66c6f3f5bde01873f5236d4d4abe16368d0a4753847a25e"
+                + "5d48e0d3ba4ce3d6ea05f70e8afda8e4ea4cd107fedfdb4332c116b58f7d69ec8c08a0a1"
+                + "0d01bf49b1b60f0190382261daeb53a035282658e61988e94fd5cbbe15503294ba04f947"
+                + "665edba644f6485a89d0f3863b1b2db482c95142a4dc58a14dd1d77c2a24c9f60e3a7213"
+                + "71580be29ce7e4816d496a1a4da2cf18ba66d372c714a7230038bf05cbc77a9815ed63b3"
+                + "dc4263e01246ab6898cd451ecf143d32d3a546ff905ea5d80e54ced07ef2b267ba5cb536"
+                + "216c5fcf2d4aca39bdb0c94733392dfe080eb87796d0ba8b4e690c452e794dab735829f1"
+                + "4e9ad1520357bca717ec4b15d40b317fdde40fe28fc543761419e10c4b665987f232d1b9"
+                + "c78cd84cbcb95bcb7731639ad9e847df26abfd921bb320121a5f31c683a9e6466a9cffdf"
+                + "7661f3b0c788dac5c4823d20ea9c80494f2cf2de198dea0caa1b3bd2b3cf85052a6892f8"
+                + "dd55329cf075df23f139112520ce6555a489b2a273cd7ad40ee31d344007df6888bcf87f"
+                + "966f04b50bb6a32c6b521b2163bedc55d42b12c9b9bd9a3b7882bb78fc9681f788ff5bb6"
+                + "c7abac783f154a42a3bc87afe43947407e4f9303529cebea2cb87ec78898966dbe665951"
+                + "edbbbb2edb2db9237eba9b028e9fbc6146d1d38bc9ba5731647ce67317e5c205c8de3375"
+                + "fced28932066ed6203c6d37e5db2d352be54950f71b5a569c3358dbd864e50d89f676fe6"
+                + "650897df51633b65d93840aa71e568cceaaa815f94475bd80b74c9c3426a033b6582b426"
+                + "8bcb247bf22a6b76faa60e2d0593fbbeff4d6917982c847bb209922aab9a04b20d329c60"
+                + "a26cf2d09d6a83ffab185a58152eff499bbe60a1c617193f2cae87fcd30cf8804e84b14f"
+                + "9699ad7f160b96c3a9e59898317feab31524009d6ba03e2f4834b1aba841fed06455e4bd"
+                + "d397de41b4164207a50dc00096116ccd43318a40362b602007a840c69c33f7ca989fb823"
+                + "11b8b40e363a59b01857d3dcc30f5df29633c9bc5dbc0b379a20857c3f53ce51205bd3a4"
+                + "e662b8ebcf280ebb40747e7925b6f75a477dd9c8005f65324dfdcafc94b0e898f40ebe34"
+                + "7120ff447a0f1b60becc621120b3db00c172bf39b55cbd477b010580a155d623f2e31a55"
+                + "d61aab9e355b65917198a7167c4240f78241c5f5dee52d27f04ae236052ad1eb26a25452"
+                + "7226aa671b0144c804166f70f9a0cffde31503555ac5164ed9157b16552fd175e9dbf075"
+                + "2801a4fc4566e6f798dd3a0644c64b54bc299fbec19f2e7309a4d95a7abf3bee247e34ce"
+                + "64154e67a032eea406108790a72468480711ab4ca74d264ae7057bb3136eac69ad8c6313"
+                + "62fdd2bb9665d0d05959a0bee5716ffa8254182e95fa6402397d54dfbdbbe6ad0e477419"
+                + "ac96d3a02e92e8ae368410948fad6886915e68c144ab004c314abde7925a972a518e53e6"
+                + "a331aa8e8e0b686b6606a3bf68ba818095a8c528178f9d550916ba283c577ec36654f03a"
+                + "989015b4df73865d1a8a89c6403d8b4e88e39bdde7a36d0ff80be19bf0ed431a93796d28"
+                + "1180fd8851524e39ac512f00846955922a17665f5f6c713d3862a5a65d843fb412c08005"
+                + "08e23a92d49abfebe2c8a4f593bb4373047b46105430dbcd329b294e258eacb4bb453ae1"
+                + "ea937f17da097000cfa38461bcf363c60cac09f7ed374d427909e5d3e699fc1a20705bfb"
+                + "65c82414b0c624e1fa3e24a288e53b7fa31e842ac7ed1525144747ca737fd05256c39c2a"
+                + "349d6f6e45c0feca1cea47d9c51aee2513e2dd53597593a7cb7483192177d7854778d249"
+                + "b181ecd72cfccc0dddaa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8"
+                + "438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c3"
+                + "9466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d"
+                + "11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b63"
+                + "48ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35"
+                + "f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca86572"
+                + "5db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c8"
+                + "0313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43f"
+                + "a773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7"
+                + "985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d2"
+                + "3b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513"
+                + "d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22"
+                + "dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c"
+                + "566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b897415696"
+                + "4b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee"
+                + "24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d"
+                + "3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35b"
+                + "ac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b9"
+                + "5375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf94"
+                + "1e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd"
+                + "7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5a"
+                + "d677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e"
+                + "41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6"
+                + "e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799"
+                + "d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2ad"
+                + "c440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc8"
+                + "95e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35"
+                + "880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017b"
+                + "bf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5"
+                + "a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556"
+                + "d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb583406"
+                + "9f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2"
+                + "efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711"
+                + "c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6a"
+                + "f95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a8"
+                + "6c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa923"
+                + "3ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a4"
+                + "1da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89"
+                + "e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9"
+                + "017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3"
+                + "f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b21"
+                + "65ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e"
+                + "88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04"
+                + "b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a87"
+                + "23a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06"
+                + "ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6a"
+                + "aa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0"
+                + "c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb5"
+                + "69a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240"
+                + "219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7"
+                + "044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677c"
+                + "b58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b0"
+                + "81b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725d"
+                + "c2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271"
+                + "cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2"
+                + "e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86"
+                + "fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7"
+                + "e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e"
+                + "1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f4"
+                + "9a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b435"
+                + "5ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2",
+            "0fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a0983810316e28d"
+                + "d113d90b6c648c1d77a0d01f950f3e318b7db7e9021de9ff06ef4cacbc6e3c411807afec"
+                + "a3e7b0984c1bc41573b41c374afa07825a322aadbd66ce4a87e408bc3d51408d1e1c6cca"
+                + "9bd368b2d4d918a07229b955868fc688ac447bd829a42673d263a05e53e8d171c67f5390"
+                + "037ccef9e7ecbf56b8c9ccf7a59efd65c8c5daabc94701720b52dfeb34126485d61caa52"
+                + "2c37a68e149e58c60add76e381f4e8e79805e56a5361fd7b584b1e0eb3cda8b3078854c3"
+                + "2b7c2a28f433dd0d2fb6cacd17dbe8dae7cca9128680f0d29215b3248af347deb24abac8"
+                + "6ed9f5246ff7a1f92a37162f7eeadb9c8f2c842a93557d02ce51401545b1581554dbf494"
+                + "e978c6f44ea2a8666cc0f6bccc9532de42ae349d97b4aecbfdfdb02f36825a4d194ca03c"
+                + "9518f912c833a858486dc4c0dc9ebdafa681cdd8255a0f66b7c2fc01e04ea5fc737ddc9c"
+                + "f638cf68f32a532177fa1e0d1738a3c9c1e275c82465725980c4a8428b2325b82f509072"
+                + "ef6b5a2e13cad3ea25638c04c542d970e0f89143923ceb51a1e3a20abd9f71a277a2786b"
+                + "6c4dea616fb5fbb739c8aabf8421c43fd77bd7c58765c72e564519919161293061c2aa58"
+                + "90b1a2e43dc24ea5847f8a12b278688176d01a22e9526c035b9e4c4ec58be5e3ea375d03"
+                + "3316f31cce55f476d19346b0cc4b8cfb6c661dab3f23db42d82e3130a62727ca9eb20ea8"
+                + "94e22ae51b9c08a1aaca0f842fadc71219b202101eda2fd1ad3ee39604b4dfa8b6b6b00c"
+                + "5999ee26c67e2611d718a8f8826585d762e76c106d257aeeb5168c9c666eb5b2063ce253"
+                + "8b60e660ccfd430462a802fcb1597dead1b176951fd09f56dc56cbdc0c2a66f1d8a10a7c"
+                + "2062d89062fef49c095f7466bcb1be2490d8349f50145693bd6a87c1007e3b8c8eaca21f"
+                + "5f7ef839cd663bd7fbc7d9a11920e2fd667e9409d965001ad7bab843bc4c935e86f063a5"
+                + "6b59c3f159b543733799fecf6d369b9a0ec0cb6607f0d708f430e9190b18495bc809cde6"
+                + "f1cc041d6abdcccb4ebc379d78c618660336d83bce7b1140700020b18a24adea40727668"
+                + "bd288247d5b1d983560b47000060a0584dea28e6ae8d28b8a8466f88a5dc47760534d3df"
+                + "b4ac27053f60dbaa6e81d5a93dbaff02078dcc27c2c43c198d5ccc7f32f734b48e7f8b45"
+                + "b5310810596f79f0931b08cb4af7a228ea8ce40a39d0ee4e76e5c083dfd10118ac130b76"
+                + "ecec80edce962e12a7d86beb61a75b32ca602a029618e2cdf09604a34330646c3f4e2691"
+                + "66b1f180d5b97d9202befcd00a1da60d4232b45e4c336e29fb9ce4e4c5389dc5e5ac2bea"
+                + "dc28bdb6d61fe3575517c80f589b599ed34edeb29e43d7960566f8896deb5f35ba109273"
+                + "4cfd6520d22704aef659f4e62c4e17977b1048940b57ca65985264013c611f357e8b9aaa"
+                + "57868e89403b62a86c7a9cb5319b496e8a92795684056e786d4af5a60456c1dcac8e9d77"
+                + "4a2657b9812934d813bf202660bfd1c4b7e753b1c8190edb2c040bc0991183c0cacb059a"
+                + "b1e641c50da895e4c0ea0fc5ced262d509db22e7844044b006bc0fa961127f58f157a8e4"
+                + "2e7d4d8308829ae7d95d1851e3c78f2e7058a726230510318789d62cf1a0c33d3c1f9031"
+                + "e0c8f17bc1a478c9d336a312c0e99f45e52451d9b93546907f354fc25b0ed5f72e9144f0"
+                + "6069577ca677377017b614326da2f4c7f1a7df04f61d67002facac2a60fec492996bab08"
+                + "ce9bedcb7d40812714d05f4514e6bf6fed3e2a050f3144eda1daf5e2b98e99b21264ada1"
+                + "2a7ac812ba904620345e32f64954bfd6eae0686c2080a2ec62b51c348505712c888226f1"
+                + "5a8d72044e8e8180bb145ba617c681ab08f0d6f67f1a46148735c165c34c8f64155ffb69"
+                + "5d030b4f4f25a403d8d87c8bb2986427e5d8037a2ccf52d3fc58cf4e87262a280c327c58"
+                + "470b597b655baba835da4a51be561704aaec675e55fbff770d694802c5f217cde7de5ec1"
+                + "ba3b3b03e6f2914ab2f2a98ab6d5801a05134ef6cacfeff7dc90f1d768012dd0ca67a4b1"
+                + "be06df19e0032e2fa8d99630ff935834519b823b0c91c6541b9396fe1ffb3152243249ca"
+                + "e0483e1ca5718ebc61f19aafeb8419fe8f75e267a2e3541ac00abaf05140c10cfdefb9ff"
+                + "bf8a045f0ec3494ad47e9dec2eb0031d299f07cf3d164fef3a2a9bd6d468ce9bd343bedf"
+                + "7d09a9bd7ed7aee8ffec01f4ce35d743f61833922c0fc71b1ade92cd4253bfddd04442f8"
+                + "6032aff76bde1936ff387fd5809141faa0c63ba71a7fc1aa7150ef55e645bce21a939a58"
+                + "1b62618dcda367ff9a0778277371ff592e19c4da4d4728ef726342b6209c81d7bbecbccc"
+                + "ef477f1b3169b2b5323d544b9649fb60cd6cb8380a7ad20562d2d7f0ee59919dc05f848f"
+                + "ba4e81b8b48b468896ebce44531612d993da9f42696ecda2a7171b8b6dbf39125517c8fc"
+                + "1f8dd9d04b313024fd9fe8de34d930d8119afbb47613c1d74ddc56ba57a75e85ab0a4618"
+                + "9f974ecb3bf09cf5f40be99824b37c37f449ffab05ea2b27e0b1519b0e165c522d50806d"
+                + "fcff884ba3042542874ef0428095b571081fb180719d9e2ff590380a15b9d8576650d2b5"
+                + "46cdd67b00324fa5dfa58d49168e2cc1939a491513d2735dac6252b18e6cf69db9f730dc"
+                + "02d7864d5a7e7a0b8934dae6cbf0f1e170f7bb7a9ea5f140c0310caf41d1925058aa86aa"
+                + "01d100a3326545bae9f6335d583fdc12ccbe378063893735ee585b50f238caa4dbb9eda3"
+                + "7d9eae5fe2a6dd62069dd13e6b2f68e0817648f8d728c53296c14d4d276b0ad7881e4eda"
+                + "43bd8c9b1aac952224c5164fd95225ff5e330cf722d4b457acd968c80e9dfec8d35f1671"
+                + "5d851d91674f3ea4c4ce02028975fd6a349b8345544409b3cfc676f44dd35d4179ec3e25"
+                + "222b91716c09aefacf65787c0c23dcc4da4ae5e1c4a4da82d0e2fa467f8bd75328256d6a"
+                + "256c0bc76d98fbd30fb27d3f423b2f006cdb35f64fb9f4edd76127c860c97ec250d2c4a8"
+                + "72e21b1f7b3286a8dc9282fbcbca45c877d13364742e3c49d378fde024816d4f2cf36f65"
+                + "50821d8f68fa8cf602a2ccaeef1aee2513e2dd53597593a7cb7483192177d7854778d249"
+                + "b181ecd72cfccc0dddaa3dba10773ba2743b2ac300cb53794bcee7f7576b976a580c70d8"
+                + "438a1eaa413986d58a66c5d218cfe63baacf9cbcb7c8ee96479719a57833e0451fef26c3"
+                + "9466333d6e0903e92c2c0ff2edf3ff9327532efd768f13962a42ed1394af185518d4da0d"
+                + "11c5ec362579da97c2c977608e327b72c312c3f5c44b28432ac26b97a3b897e2de036b63"
+                + "48ae702a8fbdc73aa4a5c85d7ded66aa866c0bf4fa524b92d2d55242b9c0fc7fa768df35"
+                + "f5bbf845c8b27ce68235569fabe4a48e5c6d52b05cd4fda34346fa3780ddfba41ca86572"
+                + "5db2f38ecbcc9e43a219018b7a0cafed83b547b29f158f35a8bedbbb5a8ed10d7d4216c8"
+                + "0313fd21a15ec411c65cc9d3d0220448f0b933e427156ba1a28913b49ac83bfd9d67d43f"
+                + "a773ac64da6edf60d8906eb64a05f67d0a40b3b3f48b699f14cad3835df5db59688ab1f7"
+                + "985d125947a36786dbd41c760331b9d2c2cf77a905a926cff0b541d98da7e1062821d7d2"
+                + "3b5df14c1f9b548dd3bfe70c0dd975cb6c67d5adefc9ff1bdf41d469116d9efd7a43e513"
+                + "d46a49fb6d0353274cc8529205a11e8add837359b0e5bacc9ad3aa1bb56d352fb114bb22"
+                + "dd121ef7b20dcc6ff7124771c116d6fbe109415bec3b935df48a516ff8b4c96dd69b6b1c"
+                + "566731939f47a67db17735d746ae7f5ff6b82f9355452c79e3de14fd17cfd8b897415696"
+                + "4b0667b236a2f6f6f6a518f8ffe8752276f85e8de2aaa644cd5f0b8dffb459bc2d4294ee"
+                + "24229467740290affd67f199c4300e70482967d40886b83c2dc29496c789b5e1da8d9e2d"
+                + "3fdd24af5d4b98f2e6525129816229d55126f1d3a495f67ed8acb447ebbc9aa07765e35b"
+                + "ac3b87fa8ba9cddab6fe6e25d01798c62d3f92047a82fde74fed4ddc8f871bdcfc28d8b9"
+                + "5375c09d1e57713a543209b3fabe509f31edd5a9677807c38aedfa5912610cee503baf94"
+                + "1e6866182a64700f165211648e358c5db2fe7a3803e08990c00aa048ec1cd36eb1b23cfd"
+                + "7b118e2b07cee67404f24ca5bcbc84aa11fe404a79525aad49751cca531f1254ce18dc5a"
+                + "d677e046e976d695ac10fe3097489f47a375ab6054e3bb0c5f3f47d84664dd0840f6ea8e"
+                + "41dbff2efb1c8b4cb61ce5d5e7e43906714dc3cb1fb420359f2c52cc7d91d13d612f44f6"
+                + "e7f16362722f8314bbd60ea984c0088e5dbb74144097a7e4ed05b0b58efd733f26e55799"
+                + "d401191016e57a486d19ba2076f4a53c6d21c953e1d1b130e2112066fe7fd38972f3f2ad"
+                + "c440bbdcd2252b9da46cacae4ce750775d6cdfc070a398cf3664dc11f28d149ee76d1dc8"
+                + "95e509cc292bcc4820667cc5bf650b5b34476cf765f3eac2a2809199dc2e0aa9b25f8c35"
+                + "880a169ca9ed155eb5cf162fafe24cb5f7f8bdb4c882f061ce7e75e491612d50c178017b"
+                + "bf5d86b23d07d5f21457530fe1fdb8498d96ab1b45528139fa3af5b7711bf69bc1caecf5"
+                + "a9f99a73d6eabcbb3088c0918720b809dc706fa26985a1496afd35b46216bddb9febb556"
+                + "d9c342e54a742d3f2db7a46813561e3c1b0672ba54f616617cade548442dffe1cb583406"
+                + "9f652e047303ddd7285a30238b521df26129ba696638781c1100e2d1ff73f32b3de3c7d2"
+                + "efa4968e7d1cddc6dec9b1a79f80c77832ffe4e015bcb905b8a196ba8bc7e97b2f6f6711"
+                + "c25ec582cb43f7ea8363e7788e173bc8ea9ad7daa5e9d6bb823862534f081195ef9b2d6a"
+                + "f95f8d6823fc7c00536b789573b2859d613a1bcf4c1ed4fb40d9e645ad561d92d37432a8"
+                + "6c0978036dc74052ad4e70b71d3ee520d30fcadb021641336795e61389ba2505e43aa923"
+                + "3ee78877c9de5a620f0edd5f5d6841763b062eb56ad6d0ffef4e704f625c91d0320c58a4"
+                + "1da815525c9b9a8b47046cb909677f87bdb67fd2dda2aa0539a1a333919a58ebb91daa89"
+                + "e2c6605d16a04bec8cec4868e4c6ed466388fd13cd8dc09728ff957e6a84bdcda7e1acb9"
+                + "017a1c0e6fc02fd497d4aa64dae91a4cbba3f471e534a6e33a58401055c46bf5f17898c3"
+                + "f34b65d6a208e5e872e8cfe4c1dd2e399efca08b846b41fa802c35f1c0ef975859129b21"
+                + "65ee7bb38dd9f823fbd4aa40e807d144a9e0a9d3adeddb18d0f8a0da7a14c113715f165e"
+                + "88f69cf7902de907146c4c9e5049fef6bed6c00234e0c2066807b64a76c893463e90aa04"
+                + "b32a41a1191af74825e24adeecae3f4ec2ed1193b502390b66ecfbdce9f3c00364674a87"
+                + "23a8ed673e702da16cbecb80c017c43a7f71378f9171c5f251ca4247d0aae131a2f51d06"
+                + "ae51b743b72752b1505215614c6534a7e241e0963e9b88a3cbab6ad05e2295668355fc6a"
+                + "aa8caec4d880841651e440fd2b8c18e1acafc82204f194790ebe5d5da9b4f63e330912c0"
+                + "c1794d04b37338373787f3a144288181be492279122f1efcd157f91e74615bbfeaa74bb5"
+                + "69a9dae0dc8dbd11ac012678b90cdc9f345e8a9d048d4758a4a4b8bc5389d6cee437d240"
+                + "219d0bc7a0d95c229704584d32fa8f3829f9fa4bfc06fa4e1fdffc8848ef9ca999468ff7"
+                + "044ef20859c38edc70f343b97264789f3ef0ca36bb614a6496d6b943a1a818288994677c"
+                + "b58712618204d72ec5eeb810bf4e4574b17c49823d91d7cc4586493adee7e424082bf3b0"
+                + "81b6583a58adc9d04359d54904e6b443e82ec13c8705c2e557af7ae6daf9201c0201725d"
+                + "c2ca00548fcf4dd3985eabb7175f982b2eddc997550c6f2ec7d98f8776778bb094cc5271"
+                + "cd3960cd39163bf31c5aec1949491aa0c2cdfb98145802640db5ba5b7a825d8905203bd2"
+                + "e8cf8f4bf9cdb90d6b1ef4dba071aa39907431e00ea382bf5c89e5086dcfdc78f70b7c86"
+                + "fe7fca175f589e9be2f92bdc48caac63ebbda83f09120f09e0c23391e7e7d5776a93d9a7"
+                + "e3395c97840c00999bae61133683832494792296b2aa00be848317c69a7fc4bd7b2bd06e"
+                + "1b95f174ed62fba6e89482a62776b82d5174351b2a70f862fc7b44ff18bba52e76bf50f4"
+                + "9a33433333828154209c05671a8589006adb258a4578a8205662c225fb75124912f7b435"
+                + "5ec14bff14174f644d20d9715f82bdb36928e837191ef586f3090b2877a8b62d171d25bf"
+                + "8e38b0a9a089fac89496294a8542198d67836112a2"
+        };
+        int height = 4;
+        int layers = 2;
+        XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        for (int i = 0; i < (1 << height); i++)
+        {
+            byte[] signature = xmssMT.sign(new byte[1024]);
+            assertEquals(signatures[i], Hex.toHexString(signature));
+        }
+        try
+        {
+            xmssMT.sign(new byte[1024]);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+    public void testSignSHA256Complete3()
+    {
+        final String[] signatures = {
+            "0006cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e127f"
+                + "6d28c19102daafd5132d989b6c61bef63c5d3c1c163861be3aaed7cd10e9fc30a1db73ac"
+                + "242c475463d88708b7244abb2973d01c9e8bc326be9623431dbe546fbf9ff58adf2005ef"
+                + "c9ec3e8fdc978d12d6d82226b373fa4907e409b9152526f96e4cd1e74727dc48798061a3"
+                + "c79d399da7110f4026e78d5333969073fe47ae3f6b12ece2b4202ff407f210b2d052ff09"
+                + "ea3e71157f052af45242ad6a1ca0e51b62e1e78e5e764b75b4637e98476bf7f4a3555b8a"
+                + "8e63973e6b23f979816123c361f4e2b0b1f30870a54f387d9c60b17a3c5f448e7c149a4f"
+                + "1a2d7b0b16a7700b7b9ab9893ba914ce3226a82b9cd190e977d2dcb6b9b9aa7d4fc647c6"
+                + "0a75514bb7784752a21d3e12a9e77acbbe4c23bf10774c9a9f6cc77fcd214fd48bcc2fa4"
+                + "d6e2ee562a3aa1d07577cdbc4ca1465f4eeabae7f0272fd2df9ae0d14144dc8ed8e66a9e"
+                + "bc3176e607608dcf3cd533f007d17408608a33f2cfb183b4bf038ba68674f13fecf8b190"
+                + "3f5cc2b81e891d47876bb9b2b9289129463fb0522a500f7e6688a342a2aa4ebfe18ed13a"
+                + "81a3441bae2a200414ffdeea5b1ea39d8581f7e53b562ddbc8f99d4926840b01b00b6663"
+                + "ec58a55bc5cee700889a5b45535cc65181c6c126573d886cc618646adf9f3c192b05959e"
+                + "4ad1a5b8fc62d8a9dac0464e86a483f888e4fc2becfe85c1c5d8bb9bbc946d2f2919a604"
+                + "b7ab6aab1945cc32f3d7bc75d697c5f56ac2847261d2fe2419a4c3b722a67b66de61529e"
+                + "820d89f46dbf7bc8d09d9f0c580744cfd1c64fe06ebb73264c9fd5c943a730d905a8c016"
+                + "5b0dea4cc921f000e5108c52595c46cbc4fbdb8a5ccd8a9e36d62615c43c3acac4883f45"
+                + "12a4ac4348057d90813786f5056fc6fb907b24664f0c1a944d780908646643dd6be88ae6"
+                + "7f36789191d05763ed0906524576bc3b2f02520ad09b6dfc4fcc3350eb42f55015f227d1"
+                + "bd3ff0fbf1bc08887c552a7f933036d2ec94c00ed83b8e53e348c4bd6da3bdf50ef533c3"
+                + "3eeae8d95b1e61acf76ac196e1e9a7d6a7c629aaa3b60be502ebfc1f2c76a030125b3d08"
+                + "1a4c8535b40662083b83275b86af24db8428fa2e896dd264d1215f88bf3371048407e628"
+                + "f4ab27922a3f47e71182a4ab4e346d972d89bc4f4dd2cb9b5a7dbb543e04bb411d48d18e"
+                + "7628be0d8f0f8b0dce180b5d23cd023c044ae3b1614424a13a29f432886fc3d7f18726f5"
+                + "69411f7ba31db4f998249b0f90eb6333fac94d86284bdddfc90003fa29d8e1d4b210e981"
+                + "aee9045451bc71dbe18e6d71d3f66083434540de6fc8095982010937821879de16fc13cb"
+                + "7669c4777919ca8ce149c3f1f17dc4536a1a44768614286ba56ab61a94ddbc9834ad73de"
+                + "154a4561c6fa22f08c65eda9f2078afce06cae94ecd6e604df3350345749780aa3103580"
+                + "25ebaf11f0b78f08334d0ea7d9c3d8b06f1b37fb562cd23167a87b10fb4b2f3a76c13e04"
+                + "5c9e8585bce79f9c152c9ffa0d54a993a7133fad225d3fe5dc0f15a5c40112237b330c2c"
+                + "ac94116cadf29c7d7b5fa75901f151994d1956eacadee3845850a482900b7421ab712c64"
+                + "f7dfaebb68046008e459ef0b75bf7068703c58a9f8f8a0438d83683a2bc7e54861769b60"
+                + "b81f1b83f6ff2f2c92ee68a9cead253e3b38a99e064d05e6d89f5db101626e79872d9f72"
+                + "086502549fc9e290412ef3bfaab45db982f405c74c50269942935b02d4c2903876233294"
+                + "d2c49107db8516e6075ed21f6745a9724e0f87bcc68bb6c2da59d566b5d81364cfd2f569"
+                + "8e08a9d84cb5be62c2e3606d5cd3d3b2989e3102608f7247a73691e57031719a19e13d6b"
+                + "aa8a1614099f9609d37ff0e408053782cd0022cc200cd8f885e0fcf6e15dff56126f00c3"
+                + "42a0b6e00a82f3b3025d4a93523ef509bcb759ce64c5c95a90386c44f5ea34cfb1f98a48"
+                + "ea081f4c490916b2a90997be86038d060758874c6cb08d9c92f153c53b69c0f056d69a23"
+                + "2fd8e99d57b537483acd1268429fa18a17368937109508f783e6ceb302b973a2ce41caf0"
+                + "714b3feb11a0b900963a0707f950e7734dfd72c458a2bcaaf39ff6cb61b2ce44f76cbe8d"
+                + "2dc5f4ba6bd48eaa05479215cfd7315daf830b249e89821c73a93d3a1942466f1118fa55"
+                + "c91f682c0ab498bbeb0161be5cf5912bdade63a3ca1bc1774ff595fe76668bd5045fd7bb"
+                + "c8c2e8e10cea030226fae68c5c4e665a1fc72983942060f14dfd0cef924b08102518e677"
+                + "d93dd1184db84459e86dbab1a1e5da423c7a07afb2c65abdae44cd81aa0ac34c836cbdee"
+                + "fdae91e9ebd0577005fd31ddac5d79e50743df035754634930d233bdcab7e66c7ccbd9b8"
+                + "32388d02970b2c604618e04055df780e3ce0470e0783822e808cacf6be550e37a56a175d"
+                + "9872c7af8d2d79f614740fdcffe77bc8ccca1d1a521769d342110afd8680b2850e3e1984"
+                + "3c760965dd702af43fa6916f0b281d833497c4ce1569b85cc4c89a972721afaf111c6c45"
+                + "babd3876c61be5852c3874d6a1f36fca5a1ab95e180469b30a67e2a0ff832abc12a3cc6e"
+                + "91f6a459fbf54f0f06916addaa1ae07ea48205e98bac1fc0a58858c7f63ad04ff04e3c45"
+                + "c4c21d9f32b0e3eec7b0ca715ffeb9d6bc2e99c15bb8240dd6c3e2fefbe3ac285e8ebb44"
+                + "5363d0962768b4a7578bcf08cf2584f46d45202cfbc38c131ca7fb6c9528a21fc1446334"
+                + "9a7f71bdfe534aa2bf7d85fd2aa9531229791fd4e82ae43cbcab09997388e22906c47e0d"
+                + "c5c0b0f0f9478ce5d2f2e3938b76f4a3723c14a137257a89da6f00c7c0dd22155363e293"
+                + "a985a62bfc7e128340afe31e80dba1653ef1607b7a9e50b35070d354bf1cc507592efbe0"
+                + "0a42cb7f3f0ec5579e06e5f6aadf7f20ab2a6e65d60786cd0a20afb3d6909845f92d64e6"
+                + "adcd0be9c7ea610e6c85e04630d0449b14063fe8efc247a392d876344bddd416aacfc32a"
+                + "31c9bd688d8e2712f2e8e78cb38b163db158ea4eb9c9f45a29e7e7483fc6a23613dd2ee3"
+                + "2559863d45eb5954905ae83f57094eaff4a349896f6272430eead83bac308803c2b75eb8"
+                + "352e21f23e7ebd4212e102fed4f7cc4ee5760610346e0075c8f87e164a71831ddfc31f12"
+                + "f0f554bf393bc65654862eb2ad43b87a08a4146cab897d8dd6bf9d549d2a326e19043822"
+                + "cc59f29ef116c58500f098981f7d239b2f0ef9de9b30a77a01d56149f23dbac51f294807"
+                + "eb7f7bbe0cf7812f9918037fc8e460cf3489a39f5b5d62d2b3003ad7d3320b10c15b3d66"
+                + "2bfb4ebdbe464ba1b2a5d852ddb43a793eeb1ca0757648ec0c81e0d155e980d1e2f9bc40"
+                + "7c70b3580ffc9aca344b3075fbefa9c13e1148d4f9ef1c8d5bc08760d6bcb79035212adf"
+                + "2c89ad715b22bf80799f52a415125fb2e9b5fab1474ec7483b631612b4cb299ab96e1720"
+                + "2985fc908d3b09ac34a729092f6659b2fd0404a49cbf25a01fa872dcf5b17ec6c2d5396f"
+                + "c5637eb8531e40501c38f40b755fd7392ef18bed62ee240e6283d5285df28e4cc270cfe1"
+                + "9d3e257bdf04f730a6fc8615102400b8bc712322856a7891db403d759dd54c5dd8bed4bb"
+                + "9b7c34cb10c4ea08d0e6c1d946af90e755e33c9df0b81eb282f6896e2ae4c21d4d4e359f"
+                + "6382bc294975ca1de0666ae28ea24531f1414229c31e98aae2b46a43f52aee23551c268c"
+                + "f8655043e6ff8451b9c9a18a4d393bfe89b765e4d30e683ac1d3f76eb474fbd878a73b4f"
+                + "69178e20e88ada7c18c5e6e2e7e5439322ae704cfc9a5466d071e6faceff991b91414908"
+                + "299b6b7cb89706bb1aeb2e51fe53662489b8f2237c78ec6e894252309d1868bcec1e53cc"
+                + "06b9eb808fb04ca9b36b441c8479b92e9f3d6d1239fc194550f4ea12f88f13682c92a6c9"
+                + "46d5de07621c96d34bd928e4ab654ecc5f9a05ee20b94b7eb52633a91117715da1f73e40"
+                + "7b36492c0cd18608c4ca4cf222b934f324a6ad60db3a5dfea14d60cbbe5185e27f871ac9"
+                + "0168446aeb971caabe6e2392e721a919a0d6bb5e210f2ef40c36d5d67c23281587a80fc7"
+                + "c58f1c45b892d7c199de4d9b867be97933d273afed6a9bc7819595f4da24db116a09b676"
+                + "63f677778cfba1396332f863d4c20c3ae0e11df868dcaf088f4ba500317761ddae6509dc"
+                + "0e38111e2a2cc89f33a642bbb7f21987587b2644d9970c31b6abae949bcfeee26e30ddc6"
+                + "75311ebdbb2cacc3b1d7f36522b499c7aa345597be247dbdb2c9cb5d35b00f0b8068b9f4"
+                + "06b7df27720e1114f964a2d4068a4e2c3ac89e722735d909b2d21c8504525dd313e3e2ea"
+                + "e43ada4096f2074dd643f32ca1fe2632e079befdaa3bda951837532af047453fbd6a39de"
+                + "ed1899c226478c47cb28a4ebeab0f20281f9771300c0a00b08476343d714026e96d7cb38"
+                + "48be5d436072218bde7a58bd929d6be1db502662314063ae9c8568a46e147222aee8a369"
+                + "e66ecfa46a8e98811007943331f2ed3475853451dba43ddfe8cb76860e00508cc9cd765c"
+                + "8b05f95003f3636c3059ac8891ba40c298ed0f86c801a33a989981edc2770b3f6662f2c3"
+                + "2109e9b79a81aa8c1113d82c54f2e4a0fc5d93f1c65707aaaf8450974aa357ba48e75dd9"
+                + "80888f5a4be5f6f573bcb027667168a9ae0282d62886faebbea770cba91e523a8f67aca3"
+                + "a51494bcb10ab40e68c960c4d9136f9e486b0da9877cc522cb3d8d353464c3222dc0482b"
+                + "c2b4a5589abd4c7790e89b42bf875fc8068974e5022f73512a033a81d2653da9487c70ad"
+                + "998222314d8d7c5b42aee0a58390a0ca277403ed68c89d240bd40fd6672e0341bf12ca0c"
+                + "66bf149007b8957a5ecb5db3cdac641a951ceb4ad29e08ae6a9ecfe2b6476e0eda521421"
+                + "dbde9d89562ad9ac49e46e0389392821b4fbff0532a4503eebe0dfa1fae4c2bf3d02d9d5"
+                + "e25908484395a1d63cdbf534bd93d7cf2e0ed93105640bfdd1a8f2e7d7e19475c65c478b"
+                + "c29079e00d1df36b75e99dd4f45bf8d4bb5e5d2f07894d005c001d86ded5af7e6eb1d501"
+                + "f1bb03b765eae56717f1fc3a3b0a4680eef595cba1e7583c5132be5a09cc604903ad8b47"
+                + "27abe6881b927d3390db8be607e8865999e65347542f7716ee1a7d20043f8227683db3ae"
+                + "eb80cecb170fdd56edda4892e74169c96572c24b6107c1492b7361eb65edf7489521b452"
+                + "0a77215133902418caf4bf1226826d9413834a0d612e325d1ac0f4a460b34520d34daf57"
+                + "649d2864cc5ef375cf6f5793305902dfd9ae97252b8e127925e33861ebf70802d30e7251"
+                + "c06eab16972f59336444a758552fd60eb02b47e60bb2bb04d540f3e957444187e95b45e2"
+                + "ae60daf48f47ea8c33e263282356d8f79198dd14d69871d5606a8b4f86a6f2444fef29fb"
+                + "0be1d46a7807185a668c4981282e57ca0bfd40ebf678bd373e5a0a6f0295affd4b1274dd"
+                + "3cce34651e4de67c3615f69a9c215bf253e9bd53a6b43fad72b648c18b1c3c35b511ad12"
+                + "5ead54e82b44b9a1df815593fd9778c1177c3ecff27cd24d9cff9c94ba07a27ebea9d975"
+                + "bd58e51ff88c7c2bf89b4a1421a5c87bbfb85b8feff57e1c6e2d074fc6123cb3835cef32"
+                + "a629cec9daa1c12482e8e16da2e3a9fd63466be85e85d4507fe6840580ce1a6128a02950"
+                + "35bfe3e9c73f8908413480c8d5d040e11c94555e47bbcf369a2589297b993fc66b0394d6"
+                + "7e5362c348989a7c38d55c01d2a5375cc0dc3351371b931158a4695ca05aeb255d6d6907"
+                + "b8b60cc646ad8bfdbe5c538d3c088ece388f4ced4ce4604df03fe3b5299158a1d338a25b"
+                + "63598b17375969279685a7b45e576b1fa35d752eb801b97d8d5968d9079ca3a05c0f5847"
+                + "8debb8d39339e6b68710efdd7c1882424b2823a496478fd1c4fb1bb89bb186a70198282a"
+                + "d271c774f4385943e55ef87dd94b448a1efb92a2c5f6b8008fd6b270b2ed5888f78924fc"
+                + "5d4da86788d2123870210168a8a929151b255221edfb86d2c9abf28c269d897b025d702f"
+                + "a3d795d022cc519cf3b0cbca56a22b165f7ccbaf35a16e2ad9a15396c3eafbf970ac87c0"
+                + "6fb996a29b550ff58bcb0573ae39f79050c6a90ae21ff1561a557604aa328166eaa11987"
+                + "936da2276c7845800944605359793949af3479d5f3fa57c48a98e3925e324d8da3345478"
+                + "1e698c78070c1cb2306d04538591b308d3802f064fe9c5ab39d3df891c5b3397ef63c08a"
+                + "112ae23cd66779b7d12eafeb1236818d3cf92c01c8e4e2ea1e8e2c0480a7b2fe85c324c4"
+                + "869aa01b67a73204b8f0cbaadb040ed9dc55385c60d3dcd27ffe50373117a2e90185e2cd"
+                + "d4c636e705493ba1a31ccd162862510c0eced86a4c855db8438d59727705feb2533f6b4d"
+                + "520028d4d76fff9ffc3beca001547c5a60c2275f2cacf4c0cfb039579dfaf49c7b2641c5"
+                + "799576ce34d342535ee5fb0217eb2fa11e97497f0db7a370dfcf5f62af311eeb33711cfe"
+                + "bc494919332b30a705273d0e81affe2570e2d7fa60b7f8bee710f05fda3cf2f2b0ffe8cb"
+                + "0d58a8d0d7e3d0261052970b75d6cc1d359f631f4057506d80da72a7aacbbd2c4b459519"
+                + "7a04b000ee19968ba5330f09928d323e6ee9e79d29a5a782284ff77c0548e734836a3e26"
+                + "7d7f400ba036d2307f8046ee354c7e38ece1c56d287f97ff8e15b863098124a8db672fb3"
+                + "4d03d643985e792db059c186ba0d942dd9c8f07edee0fbc32a306a665d12fcf1604c64f8"
+                + "907cd11fbcb6b2b10aba8360487da02a36afb3394cda20a86831da07ad163903accd4f18"
+                + "7c04e8f7338d530e26b8900dc7498e2ca5e0a5a1c0ec5c3fb6e88add97b0494c050f8936"
+                + "c1e47556abefb089e47e4c52d5295494507a6c2986587362e0a38cef01abb5e1869b724d"
+                + "a3e4c663311bc7f8690fde3620846175d0bd8ca8b8b988ac5164534fecca9f27e23fc1d6"
+                + "9d01b7fc57a3607584318adeee92cdf84316662e8c44336a73fb034b2179e22bfed2be80"
+                + "38184520a30e3f957fe14a9094f02e2ffdeb2f957ad30cc76fd1d87e979bed9eae662bf9"
+                + "0f9402ea80103a4f0d443c1bf8b9c849bd2d8e926278ca480cf35f9c25d5ccf9b2de061b"
+                + "76f31e47e9e5dd94bc0d46e89b5a7d39eeff7c450f527fad774238b0555b1aaf3241f127"
+                + "adbbce858153e7a0c53054f0de415c9e9822f50d707cd54c3adafc517b6f83009b02c7fa"
+                + "f1b891467dbe41671a164d265122e9e77330e480292b1454b6b52ab209e4a69245d3f7b9"
+                + "1c2b2387368acf126f8e59dfa1d60a601b11c1f06f2b77b4a955cfc993938920584c8606"
+                + "7bce8a9e8c8820d45f2e74223b3f84586cac70e59848171b546b450227d68e802878f3c8"
+                + "b2abffb375b8ea6c3b5ef1cd6c93ff514664504d7c16e6c53b7b6377528d865581a63176"
+                + "d5e5748251f5e5876008d95aad25dd6d3420505a973b99ccb45b8318cc3b7fdfdc2b61c4"
+                + "6634b3eb9cbaca52cba4deea66480e72ab109ab9125c9084ae912770cda9a71d4e33e8fb"
+                + "af8ad2420dd751a71497bdef1bae3bf76ee27ac2d2654ff72a2d0a924de7f4aef3a5734d"
+                + "1c4dada0f9e4783a29a831299af80dfe1ef0387e9c268ecd25acc6c6dd3b1fa3f9d9b5de"
+                + "d2b9c4cd1835c2eebf659b87d91ea29ecfd35405463168b8227636365110eb3509394735"
+                + "f4ef9b97e8e724b463ef5478401ea9ea67cb66b14b2ecbdd77eb62bde4ed9f04a22d0e05"
+                + "d0b97151810724b0ede85ed777e149c6d4fee3d68cba3455fc8b4f0b52011b12c1f4d662"
+                + "417bbdd549c7beec11303559f656b9cbec18ff0960febba208a2b7d532197506e0c22882"
+                + "d7b63c0a3ea6d2501bfdbbc904b8a2e080685b8591348e5443942a1a7459c60e2a661d2e"
+                + "6b60e95e79d0b34e54e7346580775352a8342e7f8017d8082a0a124d8cc39dff4ba8ea67"
+                + "b5b80af215a6d9db612ee4f3864e309874d5f7623af92ac013144fff8f7f4dcf1ad1c4a3"
+                + "4c3a5507cf897f6df7a942bc1bd04bbd25793c68d25be9bc4bc170b15d0dba42f02ff2cf"
+                + "a4ad68a359cce4818e5d4a3199cc4b9bfb61de9c636e85f1553b895fd2fa25efa9aa2d48"
+                + "7004eb9a91a869085b3854ae7b08c1909d32d4609895482d64616c59dc2ad593646372cd"
+                + "83a0f836eb6e9cf9b0a6ceb8d585eb615f7e9910d5b551501c2041625f8ffc3ed84d89c0"
+                + "dd7a44e9fd95960cfb24041df762e494dfb3ea59f3da398051032cf7a4ed69c86340db40"
+                + "54b44248224bd4414d6321e5f62767a0b8e171f3aa93fb282712a226bdff9601529248f5"
+                + "f01d6cd849bce142ef25cdf9bbda6d7c41f9ea28c86f918e1884fc59cb249a1495c90b8b"
+                + "c80bf7e040544145c39f30d9929ce5af1eff90eaab34a6b403311e8dba9526ed62a2eff6"
+                + "2abfef405ebba921a3cfa227d7df759f291fc681696be8ccd751acea7d73c5a46c612dc2"
+                + "83598ad1f900a84426b22ded887f4d86894221eb08fbda9ac7e16117af2099427aa2a9c8"
+                + "0c5e257cceade53dd5263a82bb50b2c5ac2c7152d30a94a15013965083e5e6acea191bd9"
+                + "6305845d52748490e0d7b6f2021fd87d58c3cb0f98674633f2d1948cbcf26283f93d96e3"
+                + "d190dec4597cea0d901094152211e8bac1caea98399777a78d50b004dedcd9898a344b0f"
+                + "183bb92cd443ee23217d72ff2452322358fce49b933cebd7ae38738995ee717b6caf235d"
+                + "aa7e0fb142baf37ec671223bfc3cdf1c72033dfd99cf99bfd2f0d6bb036f238208933fc5"
+                + "cd15aeb2c368902e718d5d56dc838668af67e6a31558570ba94b7b0ad4996fc2ce020744"
+                + "615b6f8f54e4a9a8698b6c668a763429ad9ce67ae3564707cc67cdcf1a204eb1524e406a"
+                + "6b0322f31dff65b3c24be95f2a2a41a5374a0296df8bbf26f6c91f35bed4f3cca9360216"
+                + "1b85c6df668c6b3fb0b64856e7ed6b92dce7bbc22d113c47fb83d73a292574dcb83e485c"
+                + "9658cadbe9a5ffe3cf7bdad2cb8c2353f7cbd532afdc145418d8da7a120c4eb76b96dae4"
+                + "171ef38de5fc358c018e7ae5cb19114d561f0f8d8c694681835a00f24e6b96ee17018ef4"
+                + "c55a89a6c2e809f84e9ef44eda5b3fbaf555ac559f4bc2f4fdd15db78a71a2703e839149"
+                + "33c02fba48f662d7132f53c36bcf5e368e3c229f65185ade9fe3c7c22b35b9c2baf66a6d"
+                + "634ff38ff6323500b06b156dd979fa95069e04920ae4cfe3ebdf4a1e9989f2a05fa671f1"
+                + "aee8530aad437486955e8dd550dfa6d14581ec96a461e3c8dfd7e665a48055d75c9d18dd"
+                + "90e25f07b7da7655a00c7772a10cdc20971df1a40e717df3218915b482b4391be25346ec"
+                + "316fd383b073f3cbfc4cb8010d0bcbe46d40547114a965cde92378948d70ad0ad303d909"
+                + "996d3647076b0ab34f416eb0de2ff650e88fe262a89798e3b4a67800af38e9f4e9708aba"
+                + "2d8d1241814161a5ea8e8f5419f62d3e1cba998a1fd7e558900baf4884a621c26af5ee59"
+                + "6cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e"
+                + "4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02e"
+                + "afc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c184346"
+                + "53fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d"
+                + "850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8"
+                + "c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4"
+                + "082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c4389"
+                + "76e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad"
+                + "1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec"
+                + "8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c"
+                + "112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abe"
+                + "f7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37def"
+                + "d5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25"
+                + "978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe"
+                + "2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924"
+                + "ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e0"
+                + "1ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477"
+                + "a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419"
+                + "daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd"
+                + "567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1"
+                + "b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee393"
+                + "2ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f"
+                + "2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111"
+                + "cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235"
+                + "b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d"
+                + "9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230"
+                + "fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521"
+                + "e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e925252"
+                + "8c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041"
+                + "afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e"
+                + "359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb"
+                + "84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b7"
+                + "80fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bd"
+                + "f077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7"
+                + "b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d4"
+                + "4bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be5831912"
+                + "5ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a"
+                + "02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cd"
+                + "ce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee"
+                + "2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5"
+                + "cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6af"
+                + "ee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bc"
+                + "ce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde"
+                + "3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524"
+                + "c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d7329"
+                + "3cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe7647"
+                + "0425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc1"
+                + "2cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd"
+                + "2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec9"
+                + "2b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc7"
+                + "1101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371"
+                + "bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc3"
+                + "2067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6b"
+                + "c96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a"
+                + "1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0d"
+                + "f348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d"
+                + "3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5"
+                + "eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa2"
+                + "7447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da"
+                + "0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86"
+                + "c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624d"
+                + "e030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55"
+                + "f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd152"
+                + "8004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce3"
+                + "6756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412"
+                + "b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f"
+                + "68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45"
+                + "c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8e"
+                + "f68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206a"
+                + "e39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff"
+                + "3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575"
+                + "ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba"
+                + "21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a2"
+                + "3405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca3"
+                + "4d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5"
+                + "403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7"
+                + "a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960de"
+                + "e118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c5073874"
+                + "0aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8"
+                + "c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b49899"
+                + "94904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c2"
+                + "7aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c"
+                + "4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430"
+                + "f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a"
+                + "23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2"
+                + "de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaeca"
+                + "d859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c"
+                + "30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd"
+                + "0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d13"
+                + "59bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b3"
+                + "13b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3"
+                + "310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f0"
+                + "95ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6"
+                + "b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca"
+                + "11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978b"
+                + "bb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e54"
+                + "64c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8"
+                + "ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa8"
+                + "99c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc6724554"
+                + "05e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d7"
+                + "8a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732a"
+                + "e1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06"
+                + "067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb091"
+                + "4ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a2"
+                + "9e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55a"
+                + "f7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf"
+                + "976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb"
+                + "5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d"
+                + "90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a2"
+                + "1d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91eb"
+                + "cef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bc"
+                + "c4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7"
+                + "b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee8"
+                + "7b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a"
+                + "47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b841"
+                + "2fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1d"
+                + "f3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5"
+                + "f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb17315"
+                + "23ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3d"
+                + "cf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0"
+                + "d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8f"
+                + "aaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f5"
+                + "4be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
+            "0023a2267c7d68930fcd162fb7b743e94c520197ce16e57ffcb0af6790245d85cb056197"
+                + "64567c8c9935c52d3e16f832ae63ca4e28dff7a13a41c76b791ad61b38479bd43d4b0325"
+                + "678ba1241a244a0db58b15ea8ac715ef3e0f6dbfc0b595c53b7178f2a899af8f68d04f9f"
+                + "8a07b4c5107c4470a3cda8cd3e265268e92217fe52d41956a1c4aad1c0982b8c38020ae8"
+                + "4eccd1b9d121462b33bbcd5230d64836e3666f369f253dfac6c906013e00575422287092"
+                + "1ea38c60dcb096de3c5c4aff406689cd945488073df096c6f01edcff13ca3a06ed959393"
+                + "c71fb7e6358364ab75c6204ad6fc46a736c30cc52a1ad78a74b767f8595c473c423dbe2f"
+                + "027e2cd9436b59c7a48807fe1a4d9059353baebb6c93ef5f773f53b8d54b5e96955ac98f"
+                + "19e9039b3b66c2d0bb2715f649e30abf9334b2eae21ab6f30c85323e1cea583e81178de6"
+                + "49e14b6a8f6f7b08228f9de1182d4f228afc85eab0f522a0e48d97c7d2ddb7dba82d9163"
+                + "517653652bdbf7db2c53d3f063dabdaf9e6fc6d25f38b221738570c65785c2c721862a73"
+                + "2b098ce725483e00319764ebb5d9dadea279c742a6b9d4a5d2366f1af726bfd38a9dccc8"
+                + "37e161a8874dd6bd8faa9faea68c048d86ec7f52ab14953218ac2d3f390e1c414c1fc31a"
+                + "72b80965469273d39a43c5d4512fbbc6d5e56b3c50646198f3a91c4d7300915c8342f2d0"
+                + "108bf21ea6a7c5c7fba5d6da11bb4508257a7441b78bcad9256b0be8bd0f68cecb7dfa1d"
+                + "57bb22f765db60c43486d4b32f753abfe9ae1cdbfbddb8a5b53477364324148e8e53664c"
+                + "b499cb9d9006505c3dab44539398b4e517bbd03181588d89ef68167ef376ee8a88b10938"
+                + "946135543b65f81d1f2e9603d47f7aae994a7dd10abbd5de1131dfda96f2997422755395"
+                + "e5d0cc605de27c37a9f243a7118521f3e18cb37301ff545f7550765b3af69ebd00cefef7"
+                + "1b54ce7942ad89a7f11ea835b7258c1da2d5eb1bdded96dd46a9271410afcc82abe37521"
+                + "6bfaed58db4974cf1103c02036577e72ba33d9c2f9f9e8e99e85b93dfbf832acaf75f31a"
+                + "8a99ab98e899103e9121ce94993bcc5f83ebee7051a168a0450a02283cc717106d8d8611"
+                + "80bc8ab078950e913aa8f0697e33e4a0508f4f52b5c1f3937ab9af87ac86937f3720b4c1"
+                + "cf8f2f17830ad03c0c509ef410a1ed0ccb89a39a57622015e19dc0a8e8cc6f0626d5163c"
+                + "63d5594abb21ecdc3925b1734c006f35d59f2a636d6da6d96552d18e8167a8dad361cd8a"
+                + "2106757248b4b2ddaad4b6326d89d8274b07840060269bdceaab449090a263adfa834c44"
+                + "78e129d3a44fca4b62191764cea33cb7c451284c60117630ba43a79bdd30021c809d2ff6"
+                + "17c88410b25fb4c77b244ca4b92fde91116945d0f89b2bd5c74c4227e10aaca2fe5bc303"
+                + "0495e66e628f6679f826bcf1f7ab67ff167c687427a65809dfc53699a11d821275613369"
+                + "4fefcb0e18451e35c17287c20bda162321e2db4a1d6f97346da22fa44bd2f251a978c963"
+                + "264306fae780abe6e391cfe512ed7fe4cc410505ec4d5b1770d6519e4c8038c7f7d32d2c"
+                + "6e8cc654bc9a92a6ea459c0bd0f1195272d43093bca508dfaa891f4e65c62c6b2957a0d9"
+                + "ac592c8f70c51d2bc657d837cc7c786e8c3f171bafb2e1460d881637c87286706a545afd"
+                + "b2f14bed61e2d27365da891ebdddf9f0af9b221ed8e146cf672673e3b3cdeeb7f7a1655a"
+                + "1673af55d06a22379e16319bb15a0dcfa4d970c0cc9a56db378b44e7302667fbf87f8a1c"
+                + "9b9f17189aad966f44a9ba609c58eb41659b3900bbceb406c1845fe73b29373b03049e4a"
+                + "dc79aa2afced837171edb16e3ed234cf00ab960a3fdd70e3e5ab89a986ab25e6de93251a"
+                + "6d61f0a4f9cd1524c3b2cffba2d773ecf367af2ee3d1f27eaab963a65d644fc76ce1835e"
+                + "36f5a6a501bba82831c4895a44546dbf7b7a0d4c5c40c094ec416185a4df54a86f37479b"
+                + "025b923f0a64f5ca3dea1627452f2a08a3d426ae9a8704c66ff53005536a8150b3b5302c"
+                + "65f49ebcc5e968ec7c54fe3ab5d1ef8c7b4e4e47302edd619530bf6cc42543eef17db409"
+                + "d92f50f5e6f5bce845acd9f4ec5b20d042ca48309f1079d2e2b01cd52fe9d340852daba4"
+                + "00d3fda682162b108cca7ab4b5356f444fb4c87f48541c9b5d1e58fab94df1e0bd21299d"
+                + "c7bb7144cf9da351c3baba8e920a4c14b59fa84da1d1e2428818df608398662ea0500932"
+                + "a92f16b29b06e681830f6afc25ae6efd2dcf7134cfd3bbdb74d35bf2ea0b59f3faff3298"
+                + "5a2c63ee1c206de9bf6275a4539e6afaf986b9b86535fc81653d87c029517cf1b599ccdc"
+                + "591bb63cc13254ee867e3432397ccbb877bc6fb7a89a6890e1b568ad5a9b810cd0d77607"
+                + "23f13f5468656fdd1c207f6a2b50632bd6a7b2b7e8478c2d2a82f429b6bae63907846632"
+                + "b43b00d3716b83524f3f92b764c35cd2eccf8f167aee4d08514914abbcf39c2fa2d6ba77"
+                + "500bba727a77cba850988b46e9c0240b11ef4299073aff9f68e0f51ff9490078ef26cd19"
+                + "106a1bde8d626659862d3414720ba6f3269c70dee4e86d307d29937a792653138aed7ff2"
+                + "41c71849c97d57cb2fabd161083f992350131d3846f3dc2aefb4cbac1148f532680425e3"
+                + "258243ce83c1569934f7e678382dd78eaeedf67864c0527fbff2d86d735cf18b99b55e5e"
+                + "853868e6d9a05db58e3b2a17dbfaeefe807e25070a3c177fbb7b2feeaf0de13569e4a24d"
+                + "a7d801fc8e95a153d48c457b6d59b8d183393e7aba668063e1db92980a8f428d1965a0d5"
+                + "77d8ab169fad00497f12405caed3b2a76c0b46bfd7392f581db48393280e16ef4fcdd2ca"
+                + "49be4f4d964ec9d74cbf0881eae7657396996055196991a28facd2e025681d05d066215f"
+                + "986c44a6da0ca71120f7c2032f7802c9125355e5cb9450560f53ad002b01642f9356f487"
+                + "3f0c9735f939ed450af4a2b78a60d38ed468eea4f616394fd5b1413c8cf72217fcb03b3f"
+                + "3d62598d2cc0daea758697eb4c9c365379b7a17fe2fd3145dd0d456c0e9c54a9ded677e5"
+                + "817d8dc19fa0e28215908b274ce11e6b094e18294c7ee8066a533cde38cc172508fee96e"
+                + "00673a45140b99963fee4078c6781c2240822950ac763b2dccbbd2f9c555f02385efdf73"
+                + "66b7b87c7c2e9a0c0e385fdef6f2877590ca88e637f0dbb875b74d8be4bd2ea75e068fed"
+                + "6e21c9ba7051d0dad832d69d94e609b50f12c804da07a08173e5635adfde10b57405c544"
+                + "3749856722e9bbb5e0b9717088d4717556d6c35e314731cef73eb6d27050adece44314b4"
+                + "acfe16947db8fd71161dcfa7b896b660090f29cebedc8557befc0dde717ccb565cb56ea5"
+                + "c200bb09f598ad88814e28cce3467d2179b83814b5efb87a4468ce4d2dfc3d998895fe29"
+                + "52e5109dbd32a9793e5ce6cd9a7df9f164f2063dad85dabd972ad48b9c803f41a31a210b"
+                + "3c0e2d6ce32d408c96a158dc86432a20fe1b7817a52e046108a27afd13c6a2e6fd2b3d0e"
+                + "d36cae56ae26cba95173ad7d2c1d8ba46d721f01202e62f10e067b61360c0704689ed811"
+                + "4671f467c84bfb7383f6847f2abdb076f5b8bfca9b8972946fedde492872e5b1113588ac"
+                + "dcc11b0786ae5b7d02ea27fcbb9806947f481fe684ad95e82b5da216bd9f89351714110d"
+                + "c2acfab187a7c788c40267434050e775f93bf7242ccc32da47f065149480aee64cf03446"
+                + "599a60b9030fb5428ce27005588fc3159b05c0718201103bbd569906b747671ee21c3aad"
+                + "7632fe88273ac591c381671599b229c734fff0903a166522179962c6b460cedf1ea42de6"
+                + "617a4f7b58cfca76137fb5ebea25b3de1336c2e0673f4df60f3f95eff5e1af75fb432358"
+                + "e3be311818dd3d8f6706fe0d671030d5b0b564cf51ec1cac9e91c9bb3643eaa6c4c3f2a5"
+                + "9d9fdb7fe44eec5f8158c5f8acec22f2e43574fb9f408fa993e8f9c471906ea4398a5092"
+                + "d182a488b2539747ee0d36efa43ea7dc2d316bab38e3a750b86b075fad35c330f2d5d9e4"
+                + "b9488dab41e9355a681d0a2d230cb54251e333d14ff21ba55e31c582e6d4e387a4f3db77"
+                + "473bde3acc689404ab6ae939299a2cdc46be14b7a6d615f9ad22d7c67ca73f81c3d0342d"
+                + "bc83b94fe9caf536b8e9db70b0c48bf7a7169fb0d65b792e5970cc333db03bae8c202048"
+                + "23eb5ffd3db08b4a3725040cce81b09aa986f11c39ee526d9cc1a628aef0da68c9075512"
+                + "1e53b70dbd2ac0019152003cb5fdf6f764752424e6d036ca162d1915ba6a78db1f1e18a5"
+                + "1b555287ac3fc806a4b6297b8faed1c23682b91d79a2d7db4a37822b402c4247108393a8"
+                + "fa26ffafcccefb73fe7bb50ba8d4240cadb8627c72c8f6399c2f44386d9b32e477bf6249"
+                + "931d3f2ff9db055fcddd7e381122bcf466f0117645365d72ed9bab6460e2e0a02c3a96bf"
+                + "d91e491326cd483f72569d403e1ae6772f9925eeac01125537b5df37d162c24ef331b5ee"
+                + "d9a5871e12873ea44443b12296c696688d6182b7368c6479bf9e1dc68bae084e8cd01d60"
+                + "aeec317c4d4db922c21a9d57d1185ecba62cb91b1ef420889a174df735c1be89d7cc859c"
+                + "0ceeb61b6c4965060ecb1569b540c9db44f6e38399e0da56ab959e5c184cb1c5e013694e"
+                + "6beb83df866f2fcaab85bb8e9c30d8177a37e57a6dfcc36635402d680e9da7d690a8438d"
+                + "ebb2d02d50e796b00999ee857c2de3a650561226f1a6107f09e2ff57dbbe879c6da998ef"
+                + "4cd2e70132c6ca38ff651c0a30bc3a0862197b3005322eb854c71f12cc1de17907b0c2dc"
+                + "6dc26cabcea975160e993859f75d0b8072d83877ac6e67cb9ea5a629616361846bd541a5"
+                + "65ac9accfc499492fcf68bb50602b2b9caeca133aa1ddbad9f200cf46c60625c73fd8cea"
+                + "2c28da3afdce5cec497b42bf3e1eee0754e1f0489ee3e3b8fe22bdfaa4cbc7feab401e91"
+                + "ae0ebfbe89e8b3330a7e417ff99121f68ded9345cc02fc38a89d8c8f8419187b0cd565b3"
+                + "19d14dca076d17d99bac9a73854c707ddc9f4b0247b791951234151c9c2f6e7869efd83e"
+                + "3ab6c6803e4a0a3339e572893bc53631dcf0ff247b355a8552807073ecdc067227c66fca"
+                + "b0fa3be117162f8104ba884a086e70e0c757dc53c45941e262bc82d6ba33a94d68b7a66b"
+                + "ddff6ca159ed7bdcf3ba3d4082469b1bfb860f4d1e1e9efb672c69d99cf89412fe481bbe"
+                + "8ca1f6592c686e2acb996bafa887073dd17f08eafec9f196423becd348fe81045034689f"
+                + "915976133c678b8f0412366f31cb88d0fdccf984ff6578951f0688af37c7f560ae540858"
+                + "e438bbf252d61cd603cae8e5b734b7a4cae4cdbbc7b6a97e5de5eaae07981f2c4bc262bf"
+                + "6220a49be02742bb26b32def0c0f43f25efd5e0e6cdbf5bdd11b26833af51baca8bcf6e9"
+                + "0575d764b9cdf4379cb1740bcc53d3fbec9c8022fab9b43f7d975ec8c3a758e03fc89fc8"
+                + "645bb067cf6d88204b4e73cd630844b519f2efd32ddfcd8e8f73cb294ac10fdaef716c0b"
+                + "313f89d799871093a11fe0a29862ef12623f7df601ceda2aa3544adadcec0d99852db137"
+                + "fb3cf000761a5f55f8f09c390627b14256daf0702d4ad0ad9e207e44c7a40c09a35e4078"
+                + "fa88d08ededa735ec54083fde98dddc296aa2be4b2b41b814f4614ec9002c42cf1589098"
+                + "8b03380a9256e30434f243e826083acb9e71528eb6c41f45f55585c6eee12b8b7a4da15c"
+                + "6e2833660a284f74b4aa6c20280e21bff1b1ea5f6677e9760681a6c3596cebb4f597f246"
+                + "f10a9ed875c9506ee2992aabe34feafbac4c0d8f44ccd2e65e3eede4808ff4fbe98c9937"
+                + "efa76077dfdcc59380d5bcd11b00373462b8be8bde74ea4f0ff873874ba86a16462da7d5"
+                + "9daa6e77c78ade093342795e38629a81200949aad16c5dc6111f8c3c8154d18fdad070ba"
+                + "67b8c18005f91d60dcaaf252eb5e981be537580415464f31076e5ae6101249151a9f14b0"
+                + "9f12dea801d2c5dada02c29cc5be3002f578801ff5450e6a2127301befd5013db31c6dbe"
+                + "bd92466605578d820541693677c48ad27e8070940f33a14255f43f57290b286ba0da1cf2"
+                + "250240cb85b8794182f95f065d60288bf77df39583ee863d064a7be1e27a75db75e0ae29"
+                + "f880a4466142b03ccae446821cdfc901e214807cf13a6dbfef33e20192e74e10641f4b1b"
+                + "600dd21ed6d816417039e81494b879f9f2beb31da8d9aa493aa121e6f22733f1ee06c0f6"
+                + "b96dd5d5689b866dba9c7c735503c38408ce3424cdba773abef65212e9ff3770364ddb02"
+                + "60fe3094ffee9b89ce69a34f9159485d4bd4c4372774e20617eadc0c6950f612dd2ee05e"
+                + "1ca2f1ca4e21730e472fc8bdd6e2d7140bc362f6334a354ba250341994408ae7469b996e"
+                + "ea196520ae1569ee2a681bd2a175187f3a57d3716842c65c82d8e4aa9d6e910f8c3d4a60"
+                + "a7fd9c2d6820535e0fb7abcab820750dac95b9d166b8a8b506d75fd55d1aeb1ce40dbc2c"
+                + "9f2de3de227392faa0b79f2ea0864aba2e4936f20c379fd6426f85184b615a4807e86893"
+                + "e920794c886dceb0d766daf9f7e484d55b177fe89451db5b053afa946f238412c96c8d88"
+                + "da889190de7826ee1a2c712a046a5676a8e92d9ecf2ec923608e52319751f0442f583412"
+                + "c83789bdc5fdb17cd3940f6e091cbc69b3fec8a0a125262d63c6e2bedb5a415cba00f4bf"
+                + "16e07a06250201def099da3bc02d3343091eb1ad334533ecb8fbf244f2c1800ae1ced35f"
+                + "fcb5543b09ae6e1d45a11d7402db5ee432c5cf90a41ac9aeddbeebaaee26d905fd265c77"
+                + "efa0ace6688b5cec7657485facfa881c29b3e2e7409f89396bf534fd9dd348ff7c623f58"
+                + "85eab6c385154ab42ad11d3a015970034b7a84616c4e62e6bb174405264c4aa98836e4a7"
+                + "4597c842aef4d267c8f1118d1056ec2afd3ac790f07d8bb387060abc16fb12a2fcc50d64"
+                + "7a8a8f36b693545d983466b27c8f982f5721b16f3160d77d7c391cda673dd320784ba4ff"
+                + "cbe527b44c2ced6fb7079eedba182ae0d43bd92735192f5715e7eb58cce42d29d69bffbe"
+                + "0696b573ba7e634ae42bc662b3704099cb63a23d7a195fcd90536bbe6ad4ec23fb88f25a"
+                + "9dc398a54c7239b08bd470aa37d91f5300f0f49983262efe29d15ae548078122d599bb6c"
+                + "67e064ee780097ab491b2f0ff25cefa3cee82b947fec16fabcabb6f2410894e53d7ca82c"
+                + "2eacbf6a0bcabb999a6be56d3562e6ac18536caacd8520c9371899e57aca2259a8467a78"
+                + "3052be0debedca1a3d947ae175bec985e1346a36234f8448651a200d99402bc47b7623f6"
+                + "fc0e15a866a361dd35f5d220f5302e48ebae027c8e71e53c7273f5c0a8c70de15b7c141d"
+                + "a8ee6e11b7596c340366c5aa0621ff1e39b05c6f56317b146464415a6e1511c24a2d02b9"
+                + "1a8dd2397767c4a1f0fef5fb3f211f729755a27f2fdeaea572ea5b46f63595393c112aa5"
+                + "6356085b7d19ce4bb6adefe32de2f08e3bbb431047629647239d40e0917664de38b739a7"
+                + "8bcee2d0af9270662f600b0fb38d26e9b43390b73b80c0cf0325e77a8e0c14a6dd9eb50d"
+                + "dcd81bdbc060c9f0c5634711adbeaed2dc40e086a455f07c19b016a95c03c6a4ceccfefd"
+                + "dff7aefee523332bf5894a4b9dabbaa26ea310d8e5dd12204d4277fb8b1451ce98f26376"
+                + "094601a944a41ca297e2e9a66f8b72731bcdabffe454e389f250ae73b8da40e268b70dd2"
+                + "4ab28b329f87242319d6ae5b7777f8b5292522d93dc6c9ba99fcf7dbce171155dfa143cb"
+                + "71035af6c5406af6ceee2cf67b1a72ce2fca03aaf471fb5d8e4b5c529f67d90e9e89fc1d"
+                + "db3178b4f7939c6cccf5ce1639344f15c8cdd1a6a2ff6809429de678b796cc30e8a76fa4"
+                + "afb6251f6525f2a536d67f608be21a1477ece3e04ea67add8549adc0b59254c4f2698d4c"
+                + "271125c0dd70674346cfe9163e46eb593e144577139611fab7ab513bd8745fb610e58580"
+                + "9058e5d21c3443c0cf2fcad51efda63c3f1e24b60d7aaa53eee8267ea9e06cfc2b181e1f"
+                + "2a372c1ad7172372aa3cb5525a4fc1b6d15d4d61ecdefa9f59668ac7f961785ae02b4521"
+                + "49327df8500769dc5ff032b805da4d772c22177c4a968707ac849c590209ce0b6e2ac691"
+                + "67f03b7488c8dd497754031228ce5b9c0e65472ebccb070afeff49087ccfeda42c692395"
+                + "e81121483125bc718c817f1b39498966a40dc3409a1a620b4692c2c76b277067989d7514"
+                + "aad2718e850f4f2f4be71eaab33e3f7634735ae1cb65b892dfcaac2ab3dc8c9a33efee6e"
+                + "ef2b7a95bb1450ffdc5093eb25122c0d76279e95d6038da4e0a8c960f9f934b28b112923"
+                + "a74900c633b490717e104110893474379581e8cec192f941b6f78b0fc9e45aa509e8a719"
+                + "86e8e89087d815ac461c6c97f42a50799b231b1a46f172cbe41d216caf32e658a4dd5f7f"
+                + "81aafe7eb4a62b27470dd28f6c736cd47688a9fbac279cfdefc0eb3c168ba920463bc283"
+                + "82031e59f936b019857fc3807b35c492c9318642fbb976fd5eb0f2a3f044e3f05d569f51"
+                + "cef65d23be91f4c2112c039f7776b156bac1847f1571853c4af9b0890f3fe1b35bccf692"
+                + "534d444d58a42db0750a194e1a567c0a00ff5b443f4c6f6d6d2cb65d1713f326139c48bf"
+                + "6fcb762bada7707ec291fe74aa879be3c4ee4d3700f13cf13d1d8b7dea8620b805c780a3"
+                + "0e4254404b02da6b957fa278cbf748fa3277d39f187aec7255a49aa7e3a9ffe62adfd6e6"
+                + "2469fd106a022222e407bdccf0adb0cb7b16138a63b3c273a5b7676b5b72f128a96bd19c"
+                + "8ebfedab3609822d02139873542921b87de821fa65e38d5c2e6ab662e53f20d294814ad5"
+                + "273ee8ca7d66f59d2f76a2e97d0690bf7eaf4ff29ddba74040ada31dee6e5d80ade45111"
+                + "c8e6b146b1310ce2f109a45b97836fc23e453b7103819506b88b3829ee09f22157e783f2"
+                + "79e06f1d5fcb22e9962cb58827dca7a037350b0c7c4ed32f1caceab7b945387d5738fab3"
+                + "45640f20e64cae09dbb99c0435542084b5cde398313df48538ca91c794de544e4b6faa01"
+                + "9708213c6dc773292f29f7a0e8e3f8fcc507b9d4fd96db13111bc8ecd38d712377aa2fe8"
+                + "4567e9fab06b35fe6051fc13b07cd6d1bbb933439f5efa40d73beb52b29a0386fb1fcb48"
+                + "a5ab60f9352f8efe8795ebcda35b26d6f87c19dbf2fed86f90814f68d2c986041e779c63"
+                + "4ffc9532dffd0d2e217c3b4fc7f7659fa6dceae771975a7ccf5f07ec5c1ef43b23309f37"
+                + "2baa57957508f43afe8e921ee789efaa3eb7c47e077b62a537ad35b51f04e837ca446e84"
+                + "739c7508d900b0cf66a4af38da2398b9573cafe2e72cd0316cd53556b4425533140cfb45"
+                + "a87b7b9ea65f3a0267c809cd1a8e299d7794702de7eba060d9740b839e8c4633db1f7127"
+                + "b787f8229317371f6c9a2440f7b67faf6c653569757f84862b7c473656223161a223ee59"
+                + "6cb9912168a8cb7f794599c132a4f30ec650cf861df285e4ff09b6dbaef83283bac83a1e"
+                + "4d0e748f809c22b95f3ea77ebd158a43c5dfbb4d298975d4f80d7b2af65efbc7631de02e"
+                + "afc1bdd75c9c604322ed146f8da3d9a605b1e69ec0d22318ebfde140b1af07990c184346"
+                + "53fde6a6b3705db69abb161f9745c56281e7bb28f12f2d6e8936a64ebb9e6c7f8840475d"
+                + "850d216372ba1a3e024abd90a5fe81aec6e254c516e830b437f94f17b32552eb3b2e16d8"
+                + "c3973d349d7ee99d4b95118e1df2c6b583bebf64a2dcd7b4441b23b9023262f27479d8d4"
+                + "082b2f2f6f7d46e1a8a521a4a504f5f342b92406db51ff275f25b256fce44ee22d1c4389"
+                + "76e9fd64b9dc31c96b72483c22583ef2fc7a975133f0625f8dddf203d526d9380c46e4ad"
+                + "1d78808b5b767a628a78595db123676f094267e89d493294415ab339b8f510417bcca9ec"
+                + "8ac819a70c396a86e7589736179b7bf8f4a454162af1e8415a179be0fe91c30d9c32677c"
+                + "112b6ef56b69c87dcdef27c68f711d1c5fdc27f5e0a5b2f426753a946413bfa22df63abe"
+                + "f7e141e2d85e5c6ccee03931466455d498542179b52a19352cb5578b8a66210e1db37def"
+                + "d5b1c973d8dd91e2d996ad67e3e4df65495d6b250df29a4e17fd2ba03cb8d6e5c0b88a25"
+                + "978d921e88fe1f68cbba6fab401bc1e0d092b0cc05180afb6cef33a9202a4841bb089efe"
+                + "2384d926542fa3dc6eb8ef06aeee4373cf1d3eb62dbcc0a97dc4bab0a66396b8af938924"
+                + "ff416c6627c1dfc7b9917d5c7c0d23625d6e5c82b938b72b21329b2e89ea867fe10054e0"
+                + "1ee7c3692e796788d236af325020b3a24c4cdcc02762ad5e6ea70d5d6a1afb34137ba477"
+                + "a464cd13c033a8e493a613307b7ee5b2dd06912ec0a9a64d2d81ea4454773ce21d8eb419"
+                + "daf7686b12f13bf296f959c040cdc4c43a69a580679e61a503ae92ad8d3beb250c9731cd"
+                + "567c7b65ec13154d0b78e38e8c782262895c78f3293a0a1f88910c55fb45ecdd2e333bf1"
+                + "b08cc4e4e5ec856786b549eaebf7c8a56b8a0801cc12c785888b59459551276a5b5ee393"
+                + "2ef0801fd41a977cae1967d3c1e6f9d3b031b3cd01948eee0e11bb504b19b7b04968da9f"
+                + "2157ecced3f493fc0c0f5f22bce33e4b343ac849fcd9d90c133540079d743054f7e02111"
+                + "cc2ee9c239db904ec2d2e8371308163bd104b36fa4c8fab5d9e7845f87e73c8350387235"
+                + "b1b184a29fe6addbf3d33bacb79597a96ec68b2ad564ab631c58d2e613af2a3afc00692d"
+                + "9c2f6957e9e3713dc942c15162c85658443002dbc22fde900b1b610e4cc1c3c9be6e6230"
+                + "fa3e401f9fe2efc8c58e805ffbad01c28159211026e25e168b7eff128a6d0d4f22378521"
+                + "e3d2b71c936bba99436401ee53066a49a5897c1790f0648df0bbd724b00e28b70e925252"
+                + "8c2319a82a28e97c829c000afbeb414aa0121eac2928c1df2569eb887b97d0f8238c5041"
+                + "afcc539eac5cdf7c2bbd44995a11486d201780359010bdecd3de2eb7ef056e5a376d972e"
+                + "359fb835b10b3fbf44c965764f8ce1a1a0be53105c316e12ad635287122be7a9b96571bb"
+                + "84749178f0e30cbcbffac9998786424b231c1b83b6afe5e8d256678d019b700cf268b4b7"
+                + "80fa0c54de7d5c6d73aa631970e615a3640de59c7e05deb3b575ce031b07520a3cbc67bd"
+                + "f077ec8cafd5d1ee3fc327bf5650371de243dace406685c44f1c49726258927491b93fc7"
+                + "b6c5124414fd5f412448ea50cc9f5114d9eb029dc042bb414496c44ca41845b2d95013d4"
+                + "4bca0fe0e6206d0e996cfa2d55a2ec8c3812624581087518f524c243652a957be5831912"
+                + "5ac0f1df744bf3feeaf0e51242bf5888232d98fc8eb22fe4d4bf0afb7bb6088e7622a13a"
+                + "02c68dc99d85158a43ba8de8e14c4d2f3b7c7f7cfc5f2a2a2bb64117c917f3f47c8ea4cd"
+                + "ce442dc0f1e6434fce047103a5a2abcaed39f631ba9b939f064666b9a42037d9ccdbfaee"
+                + "2a84d01affcf8d1c1f6c6729cdd68da6c7fbdf21337d1a04b2b23353b3f0c471db3470f5"
+                + "cba3cb85804a414e0f47bf1959935ab7da803f70eefa76b8a52c9ce07da009da4eb3b6af"
+                + "ee77bc4661c4a84c0c433ad1dd3342fd09e5fe76d1e19f53ac72daa711f40259306ae6bc"
+                + "ce4d909f0673f8350c3b809c47cb34e40362185f78b0b1614d870872658c944e53e84fde"
+                + "3ea5fdcf649d7299cd74a108b89c3685135752932924a7e435af3bfe5b0c06f8c9173524"
+                + "c77ac95b83bade1a46d8b05f3b0ce3aefc97d6d80d9cf20f4c512cb9a535ca70266d7329"
+                + "3cc410e485f745680cecd5fc2f6ed427101a83bee570429775af27d9f10cdb789efe7647"
+                + "0425d5db1049952f7f09cd1bf0c4117446a49ffdc7baefa63500d44924a0d0d710834cc1"
+                + "2cf9839584d11884ea1e3695a82a3e4aab26e52433a6807ed9ff3183a629bfb66b0680cd"
+                + "2fc1a42cdbdb961c143b0a73838eb4f868d75eef5e1caf4d6537e713ede3bea66c400ec9"
+                + "2b13ac0fe873d1b6ce1e341f26ba63676fc8ad1dd685918d32da2fcb1a1c8d506bc33bc7"
+                + "1101dc63c5d1933c5010b4cdbcee468f78ad6df53fe0228b4a61e58d0e41d922f6b44371"
+                + "bfca2b0c733fbd41141636752c7e67f478fc59b8286f0edecd2a6418e876ad0e5ed79cc3"
+                + "2067798b19cbd6f886e27d3b454a4fb716d21b674ff67baf68653a86bb565d69c36dba6b"
+                + "c96c4b291f56931cf933a2e6e02438359669ddf5e9ec2f45f8d63bc12ebc4653e410614a"
+                + "1c75cb94fcce34a9436142c3d835948bb23244e7a78f8d88283a142abea4938d673e9e0d"
+                + "f348e5c65575095257e87d6491a9ef96458d698068c63620e4d6bc7042c8d43571d2b39d"
+                + "3e833b4db28c8aee0ac286ec3a372b9cba32f4f15d66ae625974cb7347a1dfddba2479f5"
+                + "eebcb95c8cb33aae8cad5f2a804288266cd766e1b1184fc31bd339a8d81f61c013674fa2"
+                + "7447c2bfcfd2fb6c8939e834f6e49063a9ad044eab87d3b9ca0ab5684de341b3edd450da"
+                + "0d6e9c2c635705535c8dcd022979f9517de188e7473155f2ba3c7e217f115661d56d7c86"
+                + "c3e490271c2f965803eeb76db142250b7a73691d238dd254954a32a2804e5c527998624d"
+                + "e030b746af16e8d2682bcccdc68e2d59aebd32901bd22353199ba3ad1b7c2504778aed55"
+                + "f9b5bcdc8cf218d3a6e19f9225e42b8e0935065aa49c831f4216742e201f16c62d2bd152"
+                + "8004d517956fda9dccaae3887179aaf65749151d36eecac985fa0310a61d815ab1b5cce3"
+                + "6756baaacff6151c8b428ea46a036511ba3db424922900f27b7a85715a17bf77d0807412"
+                + "b79dc7e22698aa1b615547ffc18bbcfbf66f54c82e222c066fe627f8997e204ffff0355f"
+                + "68d91a25d07cca0f38705aa8df9103b48ce62b85d0fad764b72b8f020f522c854e191d45"
+                + "c7e10576420279c912f8d3d16e4e95630ba8db0f59c9169019522da8015976b9a2e7da8e"
+                + "f68316acf9b09efb9fcdd712622fa7c2a4255cc89d1bfabd9c48ef7b15af536692c8206a"
+                + "e39ba495a4d07be2a9a574b55639a7d064bc3e555c0da2cb5134560d6dede9d9944a83ff"
+                + "3ac7a839df311a190f5d9b2ee3ea032921e2b7d1df36c0f5239a81927dbcea14d402b575"
+                + "ffb9d7402de2f4c6b03a6e7a709115ae160087ebe31bc6d96754a3583272072d2dab1bba"
+                + "21a04872641f86c279e44c8b898fd2fba0472728582f0916a1f2df6e646997b0223638a2"
+                + "3405b408aecddd5b1ad27a0e425353ef5ef8bdd282aaafcd96ba2c4f03517829b08e2ca3"
+                + "4d922358ca460845276b61f75feacc12942a6cb685193aa246ee91de431d31e4f5573ad5"
+                + "403bc67dbc695561c6888f16cabf67bc240479b628581123c2508ec640ad8b68e0ff9ba7"
+                + "a88c0383dabaa460bb248465a72742d158629fe77c7d54f86487135543f5dbcec02960de"
+                + "e118edd5971f31b2860e271451018288c3bd3e8f60a0b521c48c55b0e3ec1135c5073874"
+                + "0aa465d0a00f5d8c072d3823a669262cdd7a76b1696d04d94566caf49091d587c41945c8"
+                + "c3da080c633cf24a7541bb7a888074dc3c145155c2e55870f59d980cb275a926b4b49899"
+                + "94904d35249697e2d8f3a03ad2828ae298c91da45073fe68fbe8b148183c38d5514ac5c2"
+                + "7aa4bc300280450c42eb53000bd789cf466613e1f799c6cd8c89a88a155308f732237e3c"
+                + "4aa75adefa0e376d4b6549680aef721f2d1f6499f1869c5d19a1e4638489a5dd76bbf430"
+                + "f62d98af552e1e323b906a4f297ea41ed799c448c632cd0831352cf61dc5d292b1d3543a"
+                + "23a4df7cf769a4546b627901032ece8a0f7bcbfcda27b1b22bba825049a702492236e4d2"
+                + "de20996c6f80936a8ae1c8d09a8de958916275d3fed29de01a2ac5d467382595300eaeca"
+                + "d859f58910775f6621f0189c771189abd494885186d0075dc623bfb716f976bb3097be6c"
+                + "30675096a2da480650a6af6de5677105c808aaf67db6bee7b2d7e8d1b8e754893d4ff9bd"
+                + "0f06cf92d38083eb3a9a1a107209ed75b97b0ac8b033129b489e78a54723d082dab46d13"
+                + "59bdd868d489f471a6aa389757fd990d713c76ecba3f86f6de4e7deb61f59c997b4ab2b3"
+                + "13b662bb4a41e8e73ed19f8923629e28af37d986ef4a1d56cbad336f952896256b0004b3"
+                + "310fd55eebb3e2e8b2783efbcbf564b335073d6b54a09fb108e8f385e271514032eed6f0"
+                + "95ade61c9287ec968f253d520371cfe732569f52ab9d1f77887f7e737e6b2fe721f3d6c6"
+                + "b09b82b91c8b4212e50aee1a89e6d7f60d9b73f2f59796cc3f1d8e34afc30cc2520092ca"
+                + "11e03a141d45b01cedfd219a7c2e03475475c50000516cf51786c5c87aca790ea532978b"
+                + "bb106734fe46e51e69faa68daf9d4b0830db5dcc57908abe92535a90e573c60bb65b1e54"
+                + "64c8a60dc4d97068c4fb9647e57ba8208aeea49e2b9a37b79eb01233df8ec8d110a71ef8"
+                + "ec9276b96683a1595ace86f2e6dfbb0514deb91935824fb9b47032740796cd8d90fbcfa8"
+                + "99c1011fdff1be10b65d201b92bf7f89cf1ab6b09e925dfaeb43c4febd6941cbc6724554"
+                + "05e8bceea0962549ca51f8081f508cdf9d0ebab48a63942d38f2c2d759489b97e234a3d7"
+                + "8a35f8ff140c64e5409d8198264291793e7c5d2b25ae63d62b12de69eabd00d84992732a"
+                + "e1080ffdd91ca97e5c396f98ffc9b3702c5ae2d9ecf9fc328f0b412dc8b87801acbbcb06"
+                + "067985e3fe7143578fcafd391b62e8e4929969f989d9a6b36b3de7bd1b5d927acf9cb091"
+                + "4ccc051efc9f6a6b1dd9105c9cd8a04e209e59bbe2105c5ec0c39188dcf830b59e05f9a2"
+                + "9e39024872f21c634230989a09064b4795affeb43c6827102e1a3d6d9f6d39ae3302d55a"
+                + "f7c941802d1f57bdc1927e46307439e7bfd2366a0bb8efe51f488d88ac523010ec17eebf"
+                + "976d3d0b9295b04a15a1d74d603fc040d7c39c7496d9118e8315a0cc59bab9670bd2e4bb"
+                + "5a13ddf1c9059acc06483409e8fc6df94b186f1bd91b34c650534620fd0dbc01bb33877d"
+                + "90be97e16d1c1539933a3f70ef2f47d474a45e270fb230a0381b04cd174cb37a6193c3a2"
+                + "1d15ef1d648d147b8054ffda79e6768853cd1cedf6c0abde8b188ed61ae757f62c1e91eb"
+                + "cef592225e2a906b927cbea0561e745477095686e79c8827464297bf57f3047f853399bc"
+                + "c4e623a0a2aad1e027dd3ebbbdbaa56d39f5265efee6362b0609a60b5d2de0a0b7014ad7"
+                + "b4c1b2c1b6b0c66ffb52391859d69929b8e14580398c9b582b4ee30a8e32859ea51a8ee8"
+                + "7b9a19a38f43d61e9ba849a02e5383330f213c3ccc95c1fceba1514e21e978cc7fc8217a"
+                + "47fe3bcf8da76f7b73d903d1b4b2bc9e19ce2abc293300d877e339e233a89cf9b848b841"
+                + "2fb2b28478ee71f793a8acc0be59df1ebfc0e9cfaaab420f34e1ed986eb59bdcab725a1d"
+                + "f3311c5cc15d1a9e95d4abd02cd554573a8fea97109bf1d71d19009314c0eeb0a47a7da5"
+                + "f4d30f124f3b3a878375a3f40a35a6229ada4f8ba424b1ca3359e71747c3c4328eb17315"
+                + "23ae0b5e8e9ce200901502db37c216bd8ee04c5ac13b934868dc4cce31b799198ba2ec3d"
+                + "cf38e8ff87a822c6338d529aec616af9c85cabba08c51ae112ca72a2edd9c6bab17540f0"
+                + "d12906a332ac3676df445ac81ac7515d19074b590ba0e09f7f5810e90ec65feda16d5f8f"
+                + "aaa335411a6d75d5ea5afeaab398e48f8cd3a29397c8dd33ca3a37c767b702970f4214f5"
+                + "4be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
+            "014c8c263f11d661b874b01e6b3e13d75497978b7ee0ea10bf494646c61cc5353e57d89e"
+                + "ef5804d798637519d83795b5d2e9c38c61dda8ff2de50d4f5838223d20d9bcfd0fd0e881"
+                + "082ffe225b044bcb020a116b7e8545b63d0c956306098e52967b26cb7e0384053519331f"
+                + "a0abf134235b5fb3f8f540b6520f75dab9525c0d4bc046a46546820d91571522123fbcf8"
+                + "f4ef762e2fe8cddca417bf7e191aee05e31752981a811de03e8faff60dbcc0f98768baa5"
+                + "282586c412a7c1de05291a198c22e7a49649c3be95352a68167a7ef3a2aa6624ad3c4b70"
+                + "c136324ff38275d422d960d4a47a54e02912caeca1a30b9ca9c480d38bba65acb3fdd03e"
+                + "09f40c65dc6d13783eb2148fc50cf3ae694c42f097e06d9df1ba629118af03a321b972a6"
+                + "af9ace9c0bf902d8ada593c9e2642af4c5568c03643e6847a55f7272bc0ed198a90194e8"
+                + "436a0b58da32dc64ee3408034d9cbe66525f2c49be26a356d981a3272cd278513803a38b"
+                + "dd6e880300f6ad1de500ba63ad0ead86597ad6eb79c941f3644f7c736a941fe9c3798c97"
+                + "145973cf9aff08dda23d291de425e458105077c090a6c436d4c7826ae19778661814cc03"
+                + "e51836deecc44636035f8a8d1f353f94bff5a3bc01a9b917210ce6d8ee6ac27243668c30"
+                + "aa63ea2b18e3ade79f3ad6e90d5e5f216bdc9567f0bb8fd4eb4510c6078c67a4a720faab"
+                + "10e278da76542577edeb34575aef42025dac2aadee0a918f17f8f2f2c241a3cab3882dd7"
+                + "2c74bc615d2e94beb7d55854046aa8377e4be48a0c8f24b113537d31a7e83b22dcfd35ae"
+                + "8d948c1bb162fb5ba47cc9744ed5c9472dfe396285d72cadaff73c1e582e5cfeb7413244"
+                + "f221f123d6f5b5c5dcbee603d9f7312444171d48499f282de4b3b929d6d0b697f71ad4b4"
+                + "f0f7f098b926221daea713a070f74e79056b0dacf04e1761b57151b6e07ff8b070df5867"
+                + "e3b590bf905e89f3c7d362492c9663487a7133cbf659b4fda61c2bac98e8064ae2196d67"
+                + "dc08c3cda352126d8b88b8ca89ff449d32f47d0da2aaa1c639f50189b6cd9b598cee4644"
+                + "9d654ee79277e24ff091ce0b0a312d35775c9b64da043ffce21e6721d86fdd073349c8a1"
+                + "2ceb9353984b2547af8645798f6304300c050fb7ffa3bcd82e3932d4bb4fca19455ec44f"
+                + "c6d8cdc2a14f8bc10ea62cb6a4e616fe2ca57bc2a8be2ec3045cebf7f800c8a29030c88b"
+                + "862c6f4ea85a986e3474fe114543d4d0db8e398c581867d5eea316f0928cc740c5b02f53"
+                + "a29fac34d1269507836a9f85c3609be0867dd738559a739afdc2d07cc2d7c7d234e8a37d"
+                + "d40742fc3b91a27281d76f0c887b692613857859e94038a11fca4d704da467c81bb1ea0f"
+                + "872ec659be203dae5002f84399d2bfadc797031e2e09ce04807d234d8c0d68d65562ac3d"
+                + "90e3f679cd9ddb1625da1f60023f31ca1f62f570f98c5886b0600a2a6ae829cab6ba61a4"
+                + "47e7954c5640cee83c5a8404933eb1a95f6621fd20e0d67b690c765da1722fa77a07549e"
+                + "16750f30042d5a35464c327a979f2fd099c6d718c5924ea778dea1e844db0803f2a714f1"
+                + "535bc24b3984b58c914980805148cb62e9796de50d3b709cb4201ed211a45f094d35b115"
+                + "6c6e3cf8be705d6f4290e5b8ce17f105576ce8801f36ac6e92986fcd49582fd0f882788e"
+                + "4f6919fff1145d42dda525f57c93a0298263408898faf872e654cf4ae6babd97d69389df"
+                + "817cddec10008050f4d87fbfef4dbc842a0e1e200aa9d2fa2e0459ba0a3aca44af1efa7d"
+                + "7def9fe3eb7b92db20acb165361dc4b22750d34a2ae1ac5a74ebe23cd7029ddc1c452724"
+                + "309726b5bdfaa87b6338701bf29d99dabbecd3bde338b1e3790f3844537da268c357a900"
+                + "69549ff94557d9d633be69581b2ae9f3078351a3e32762d494e32d16dc173a68fb7aa3a3"
+                + "d7e0d0e80c34ee7b5ab8b173c82ce8ab9390738f11c6be502865f438487dfb7c9b1832f6"
+                + "e6df3687c42c59e7d8e1b33a89546ff7b633a5c541026cbf98ba2e56738e34f2882f45f0"
+                + "f5bf2ec8d2c404bf6284424ba33d788fd2d9f4a7d237aad788417d8f417a415466ab7e52"
+                + "6264b7214834fb84226ccb1cf7d2aca573d6a2cec56ddfe9924a85cfd1b35e217abf6695"
+                + "cf467a65d3a3eed94c29488ee5a54da11783f3092ed4f4a86d640180c9b898b6dbad8f95"
+                + "d1156bdca574a33a922b98e06d2ceb8a97a78caa88cfd18242bccb97d3da15b0e3dfe0ca"
+                + "29efb2ce4443ff6262ad08d6dc9c9a10b912b848ce61f0c71959fc451c0580314e0613ea"
+                + "8d383f1cf4a266568f67f28fb51704bb82b3230173337c97cc9a63679630dbc08946f208"
+                + "150d683f36baba8e2ad0fa40a7db18029841f2a02a88c994134cdec08ddd902c2ec11135"
+                + "64f578103af43fa5423069b72bd7f59b013ae75e4310672700c25f7a653ea9a814484b90"
+                + "10ddee16d7412141178bd32ead662963803412da9b000eb98ba89760d5b0c372b00fd8de"
+                + "1b5706f189d44fd59cbf7f26c60898c156a9aaf3d47e20dd9e374222a4b052a90ba0ef25"
+                + "0ca8cfb56fc04cacd9f7341bcd400323ce2e37dbc1091f6c8cd42eea6a9bcda68b4e9643"
+                + "3f26c6ebdaea50d73c6cd5e1cc9d89f66ade892f45bb078d2bb4d19c28b6d9dda2af3e89"
+                + "d0ab472c8234cfd05a8625991c1fa10fddb7f12dfc6a882a6285ba93fb0a6f8479db50f2"
+                + "e9aa25081e167cac8f8ad8e025cc74c2c219938a6a6b381b0b5282188061c2c1c46912a3"
+                + "68110439ac0d0a41981bc6762c8a4e526c38fa23eef95ea7883b6590e392a86c8d78d715"
+                + "1701d12d3b71789e35a2e61c0f27019048f9f53de608e94a1ba95d61907f7f17e9c87b2b"
+                + "88aaf9f738f95e68325371fd536e171de890654dd33cebf98e246b9e0e69e03cc0228689"
+                + "1a38aa7d3af6056c07b48822525d36360502b99cc0760cb92b0ca84d259b3f876680e624"
+                + "4fe2aeacec78fe6ea104197ff6f9261b8bcbda34b33c85f9fb71bbbd9cc5438bf9c91430"
+                + "8d623868aa4dd2507c22e4e01cee8a5cc402c385303b19dadef511f0fd843d1eae34023b"
+                + "43cc09d393a5b8d11215a335f41a4d95457bc2ae37981e5792ddd20476fc6e1ba59c008b"
+                + "6f0da15e67b8b1191744094ae57b9c568c68a61439dc5c62ef08dca117902e513b530595"
+                + "5beedc5e5969f13ddb0f733ba1342f53ea259fefcd8ec2044be1d6cc1b83b6e63df35eb2"
+                + "dd4430588d6c2d02668189d7cd7b9bed2540c92b22298ec1e41860caee32006a2624dcd0"
+                + "b0a03348360041efa35f013f257961ae911765ef7359602ace8e1908dcdea92cfb715599"
+                + "c773935411dc6576bc58621de95e308ff151e975bfa09894e03b5649a69f46d24a035504"
+                + "55c1c1666214177c0d2c3d8c20e99c980eafbc50e01acc76b62a28bd5e8df32040b5f106"
+                + "73400d26dc5c35069af6481fdd991eb8a041b90f6185f0ea375c0ca814c29652b68d4f9a"
+                + "f97c8a838bec2871357c199d7337b7bf237fc8bd036c7d0c1060c646d10cc3113a31e07e"
+                + "ec184ca29a0e0c6ada346d73c6f528ba529507823c989be3c7d08e9d4aa3a6976e6ecf0b"
+                + "ef2f65fc185a7a1d1050c9a2688c34c932d9eebfac896dd3278363819496704792557943"
+                + "afff482e2be44e404ec2ed633fcafa7b776597a64d97bc56b73b8978088d851218964ef3"
+                + "6b93af1cb23454e8515132d58f3a2f7daa8994f771822d239066dcda30763f98ef18b799"
+                + "37a30afa2844dbff9247302da9edd54fe619cd7fe5cbb29d5bfdac786f8e0bce195dce8b"
+                + "3942cd312ab4bfcfed1af8d4fc054e30c811fe03ca2b2784e56892aeace51c8ba886b4ac"
+                + "302f55fe126fa2e6e4b8e52716245b5d529cb1d3056ce7f4fc93abe6b58fe3c84ba38715"
+                + "300013959c93f717fc93d9a7a38c58f9dafa3f25108469d15ddfbc077e73a257d0f309c0"
+                + "fd3ab1b650e389e7d00f20e37299338673d52c2b0f8dee72ce67884e77acec0edd8dbe7f"
+                + "981bcdae0fe643b615341ac3aa09767553d77f0bc7741ed4087e5a03041e5b724aa93b4b"
+                + "6cfd28cc7e82d0ffacfad667f589073df06bd135cd00d55d68fd8950fe997b09bdc7dc4d"
+                + "90bea36b945f709d167fe4f40e8fbd6be9eeeee262d8b8fbb50052503403840737530b4e"
+                + "b4f9a96abf6b1372ffff794822043eff81d4ce61fdd7be47ad9edb1899a5c6526684fc68"
+                + "561a13b637db7af1443a2bd2872ac27afb2de89f8c3319bfb3f342411f1631f95b867a24"
+                + "5628f6c38aeebc3d2f6bc49e96595c844a40afe65bbe1b0531fd33a6e2468b6348f988f0"
+                + "b70846e07d78f32a668d693ea3dff016da2d9540f3251a8b89368affc57d342d1032ef78"
+                + "41915b16c4768d95b77670d91a035023c05b93f080681835604ab93dbdb5c5a8c86cc856"
+                + "749d102e02fbafc494c28b79b899c17be7e6bd5cdcab2f23d2893c9469ee8c23378c9f4f"
+                + "695acbb43b105936591a7b7dcd0a436cd72f461c23970571fdbcb6384be7ac14bff951d7"
+                + "0e0d627e0d5682a0077954a5c081492f814982dcec00a9c9f2ac87c88345bda6079218c1"
+                + "94d95233853b322aa24116573d9566996af0db91ed7797baebce70126b26b8682e2da764"
+                + "6d0a66a375d8168b8b550d0c5618fad7f2cdfc50dd706b0e9ffeb94faf76523a3734f3d5"
+                + "66792f5f501a5d29d93c42a7299a851139b8a50e13d5852c191b250295da2ed07bd4c2d5"
+                + "3a2fd53cc84f8ea352e668d0de8f830f61b41b9ca8e905766e816eb2581b76960a436095"
+                + "5f59a04bf96ee5b47bba41eb7cd73414cb154dabf1f007790af951dc79add700d897005b"
+                + "d13e151c46a3a4715a523fa8bb10dc16fd13a474dadad37e239345f11e1609a485a8eda3"
+                + "385655ea6c29802184c30b391e183de48016f27753758ac69ced125b5dc390ece5695bcc"
+                + "8140437acc115f10e6df6aa8d7fb0ee34edf8a4b6e26d14e9dd91c836fbea08bf50f978c"
+                + "47c190ee1b3d4e164df00032898238363610a38c2619303a1c2a00ee84dace21c9e4eccd"
+                + "082694f96c8b8f55bc417d4a3a040ac1d349d5e790967e36e18a8ae51bfd7b47f68382cd"
+                + "0512a9e3754534414173396112e4de51a62cdd3a19a7504555affdb35252ecda828a282f"
+                + "9743ed2e2f086c9ba5bb3876a4318717a6cfa34b210b817b3492769b1b85a9351076ed4b"
+                + "d94d688d945cb195b5ed37b77a5747c5c078dc65d15198188a6c1df4dea677063fa8b0a1"
+                + "0218d0f86ad17c436bff768988a5527b4f0505dfa295a0016a0e843d0eb135a31a9241cd"
+                + "9f686fac777dd00f6a29225ceb4afb9231c887179930129e0d78a6b491e8fb85df0c65f1"
+                + "b20b45dac097a93340e0cd7e12735d0dc68889cffe0c70e823d021824de50339689a8ebf"
+                + "0cf193684a51cc4a1b89d091edc39ccbed1972602e465cb27e65cea207bcd5445f5afef4"
+                + "a157135b24835b3fc50d2da7307bb82080f81b2f2ee568e02781c902065ee52cdc33c6bb"
+                + "7e97befc02c65434a2d76e09e486af7f777fc8466ce646123d96c4c70a702dcaf1325388"
+                + "724aec2effa05e020fe152548585b08b90780419a6c037fb9efb5c0b3d26e64cee05b53a"
+                + "a3cd9de907a24b7b69c10001832595fa464b3a3c5546d576e7853f869fbf343b27884e35"
+                + "a63ba63c67025fd0b26defbd3f99afe8bc17555f70d306de4d5a293b5bdd09d63d94358d"
+                + "0daf237886d3479b8bb54c3c64bc3f70ee22b071971f1e293561b60e7928c3a081169d15"
+                + "a83cebcfe6cc90f139265926cc4608522cc73ca8f1e0c8c5dcf15b8ce284614d9292472d"
+                + "f1c6e6c55644124fadc1eb3ffd640abdf8d1ea21997f8a5b8913f323dd6597d61977d48f"
+                + "583b355166d9b32215ecc5e532cc616ab6c7d4cccdef69877d45c479924f9fe01e0a405e"
+                + "bd84b943f21720088b93bbda4bc10df69a31419862fc635006d70b8834be694797dd60a2"
+                + "d0a7d569f3e0a34b8c4a76e89a0d968b58355c64eb2c8da5884cc53bdb42008c649bb0a9"
+                + "03bb9805a74a87d50ded5118996907e9c4eac1f08798c48758cb10376fae499f4203f82c"
+                + "a98ba8c3d8556908db478940ab30c44b4ecf0c0444667bb88d6411d01b9edb699896cafe"
+                + "fa342c6dc4d65ebf08e20251d37b001c27d222cd5007144742ce12092f618dd367732476"
+                + "5a049484b403513e27b6e9cda013d174310fca12de45ab9542aa150152700ae5b8605666"
+                + "58f2445a876f3a733ed32bec5becc32b1a4ae5c31b6260a72e642264f13d7e28273358b3"
+                + "d8e822c88044ad674a8169a62237175155109858388cfa897dc4c8770407c4cccca1bf7b"
+                + "c7506cc0e374dbd85f1c47031a47c6ac78131240f249973ff5571256ea813a3d20e18e81"
+                + "cd1a309b91a07110b3847076886ffdeb181df8dfd07851a173e381aefd3bbb297fb636e3"
+                + "4b64620d0a85f3f5bdea2f44c7d759975a82d66352f224b357beb9f57172aed019432cb2"
+                + "d4aae03b0c8085a64a0ed4f6fbd1345a9698d0b65ffdecfcf39e8fa4707f55b0af4d6af9"
+                + "707dda0bc86144522fe8b2024dde6f86e580f67141ee95614f6ae96577bb8afa06ad8ed4"
+                + "70327d01d5e106500ac314494dd6c32380a4179983fb5f93cd0e9565ddd2afb8b5410113"
+                + "55fffca391939bfd4e154ebc75834f85d9035fc23c33240c628b97fda26fae787aa58589"
+                + "4e9fc5bb00dcc41f802944b08a407b57e28cbd792c7b23e3ef791f713fa9f827bedb1552"
+                + "dae90b5ffa3285d0be4a814532369f2a96b789915ade54b9d0585550b60cca7539d1681f"
+                + "7a1b0a23d0525e65e1dfb59bd4058e6727b4c97350d2a107b615536d9849e7ab3129bddb"
+                + "8ca50cab26d1e1ecff1502643a939292a3725f2b556f5063251f258a01eec7524146f657"
+                + "7ed592b301f7dc545f17dc1e72140d77a7d0b2b3555b25c5adbbc874419fe1d2bd40e31b"
+                + "bfcf78d5ea9961c6d69c6c9de28c2af118cf73649f031da80d674138f63f598da4bd183b"
+                + "32c0a40f4c5b461985ef074c5882d2beaf823976e489f6bef8d0500f45853ee395e5ec1e"
+                + "44623c698d2e5880c8711a7ee85161cbca4952704dea3805c90ab7c467836630693df416"
+                + "6d000d5c4fd0d7d5da47b3419d8356777d74e55abe6da56196a0ed5c8e54a9e83394cc97"
+                + "24bc52d499b8c898483f6a09f26dd08c5639eca69f541fd5122f2b9d1880b52288540667"
+                + "2761be011bc5ac9a8d7c5b083ba910916e20b2ed3bc8ff630b55d3642764fe613297a2ed"
+                + "3f76ca70d6e72392e4a018a2a54f87e9624d9eccd5e2acac1b782c1de2ed14fc2953c2c8"
+                + "78372bb068205359975ee1cb7f454491fc5d25be953153a2b14b6d0202af7cb6b7acd177"
+                + "39619f526c0525ff22d4d98566f4b1717f7888ac4c1960cd3e0e792c1e4f768117bef387"
+                + "bcff5ac0d407c296a9f177a290d2b397a4c252f6d442820172de7646eea8d3976bad6923"
+                + "f9040c11bc24aac3aea650da423f30f5a2f1bcdfbe49f588614c452ebc18fb20fb81eacf"
+                + "0dbeb719da57d02708b7294412a9d813420f0d082b88b10b6f0f6a61a339066d367883bf"
+                + "805dbd6276bdb7a6b97230cb51f30aa250f3d8dcf761e1b3fb8ca57eb9d7c0f1d32d1068"
+                + "f2b0d2f7401b87c47a46ae9e2f711ad2d360d00873b92de96e8e18672aeb9ff6c938c7bc"
+                + "711c55701211efe931be163fdf9b4a9c3ecc9c091137b3f785902ee8c1cd657c4b66e04c"
+                + "9ebcd1e433a52c765afad6f2c87967f796fde54347e145ed4061e747543c9b305a345dd0"
+                + "1cd42e5086ee97f8f73faffdba2a36ebb28b726dfef906de490021e9629ba847d31556b7"
+                + "ae3ccecba3cdce5214787c969c09d797baefca31806570320f14b4c1c561b2a9ada51aa3"
+                + "2f802e793bacec077d8de7e06d1e3da6603383f239ab4fe9301eeaeee67025bc6de61594"
+                + "92d2a7b91126ce1be61b0637db9e2934fe3debd2ed04c789e04f46a4584c8d722f0d9d61"
+                + "e813a7a5ff20e3585d9902fa51d20945ac45a022b2a19c4680035b950abce7728fbcf5ca"
+                + "bbd2ed50f783bd54ccf0f65aed06da48044bace53050b857c407b1945eb366231d85a7c5"
+                + "0eac4910cc865ba96361921fb44a626b49644cab0b06a7974e622f85af83d59918578b01"
+                + "9655863221cfb96e65691a374b84f4df69ce62b2c90e8df9e0aa560af32f75436a455681"
+                + "7967af2d244a521c4f92ab1396e28bac827e6e55178f8ce1c0b19a8ba28bb8c5bd3a4f42"
+                + "570389af775f6ecda0bdad6f74eea2963849ced86d7d06a9aa4e0f8734d30c1b9b5d4b24"
+                + "e42f2a95dc860aff5ef37fc276abbe7a43414a21e91461531fa078df9c41215356ae4ee8"
+                + "472affc16b696edec6841935458328d666b79e7d7175b4f5c17d658f506348e0b6f8af44"
+                + "024992278f47cf08f446ed4ba545094fc45a17dae3d96c5bf54d4d3c056ec4a730c24495"
+                + "9f9660877c7e8a78df2b9db876e5d04db93ea7a3c814f9b2d7ab7bba472d35451322b4b8"
+                + "8a90ac6bf2c4b8d9306866f928e64466cb1130ec6bd26872facbe7c86fbc224f8dd5c177"
+                + "930962e6e4bcbce221c822f20601b886ee7ac39b89df238c7ec2fea1a2fe0d22397537c0"
+                + "4d96bd92e00011b4c317ae786f16d4697cdb11b92fc0fb77c0424fc25ce76b0a916e36ac"
+                + "587bd35dbad8904cae4265a551b6308ab83f10eba6dec5bafce68704ef617e10f48319be"
+                + "ccc17e6846fdbf77f413e41ad918d33aea32c2017f0193aee6b9bc1cc9a417d8a1ef3335"
+                + "fff07c23cbde8ee7e4952964f70c032f9d0391a35259de6d6aad65f9525db971cb2cbcef"
+                + "334dbec2d2c95732f75558fb2ef04bb99ae9d3c4a674b6f4b29f7c80d1c88d1f02cadd3f"
+                + "c2a03acbd3dd9784a3d25a8d5d1bd0087044b624e5f585a9a4b2ba153352316f261aae42"
+                + "09d91ebccbc86b2ad0942a30dd44768177d9d1541a420fc47c66e84a4a76660d72e2a52f"
+                + "c143d0052bdc9d5b6488ec556aaedaac2613c7f31b7e0d1d5936f560533a0f9cfb7bdff1"
+                + "8ec3ddd0b6a6f4e3fa2179859b834e5af7e0450bb776614a1bb03c502051c2598a32cf68"
+                + "c604e354f386f322549aa73f4b8a03d58bbff4ce2dfe9454d9d4389eb8fa8e0ba12540d3"
+                + "b144aa82f0dc1d906507180b49899d1d007a4f8e6501cd1a65fbdeda9b007c551a59063b"
+                + "88788b1384e114994c1f68cf3948899df99c0401a4c0ddb4a794ccb5ebaea877fcc53dfd"
+                + "d78b8bc41faf05a2adea2aeec5b11e060938e9ed772fd1919d64f6bf089df59f4ad92606"
+                + "8df8dfb8e13863ca82631f75a825b2dff182d127dab53d1f0cb69a968b2cba950c02c7be"
+                + "53890bfffcd7443e01f2bb74e7d119fb646093090ef903f71c47eeba464185edf40e5320"
+                + "6de25bc08e084f90ade8c2485bd562b1969372d2039c814f2be4bb222f215e98cd0a7c56"
+                + "7c5735bcec9cb72077fa94516bd493c6ae887cfe74fc0c6eb1fa75f3e150172d181bc6be"
+                + "a02ed54e7fb100f4fccd2fe4ae1894d2833ad3f9e6fb3a5bfe703667e09053374bd45b71"
+                + "fa46b611d6cea440728766a4e3e176c17917958a4bc1144a03f0acd0e010e3272a135c53"
+                + "9a8d94239a7917a96721acdd6a7041d7f709a7abf6ce3d9d2d5a20da4df857712381ee88"
+                + "c3d5dfbfc2cc5ab6e5924dd81750fb2005810fd200a9d6048f954abd44c9dd37b05b4235"
+                + "00bf3794c8677714058fabb75066838198a557fd8ac9c333c8e6a41febd7d9bdce3e7b5b"
+                + "5f2337da32da64a805f5b96edc0183dbbdc46072559614891c29dfe6502993160c0cb907"
+                + "73158377f072e771300885221f3c94d7c5fabb6e0a4a59ac567d14bac9d5aa2f1f50b29e"
+                + "053f36bdf35652366483729f4a133f0980b21fb4b7a69956ff7376b0f1e71991f587402d"
+                + "0be549a237b699ed3e4b03d33099f893ee96b49ae983ed021cc3a977fc2a2236c8ea3cec"
+                + "6c3c9557c80577ee784d9fc082e6b21b306bfefb91b5e0edb3ef7b1536c4d5ac80b1d437"
+                + "869af4903c0788ba778ba1fcf7062cf8d9acb2af934b38a88e532f6474b72242e01a118e"
+                + "118e4ce3163614531416dcb5ca7373164012f594d25db938e4c12435d6313887efe3a417"
+                + "78526d642ce595b3b97a834ff0fe8988d1176d0d272fc82d962c61c8fef74499ee795786"
+                + "d4983f6731a1624dadef88c7697f629033798013b9a02af4a3775e12d5591ca3d49e10c3"
+                + "2e8c8739cecf0391767da35de9ff6d54d0e203c0090bf39b78e97353b20268439dbd6990"
+                + "b136a57f43e0beaf5c17f8c0b66e3471c908dfcca3f180f2cf26341706f0ddc24027703c"
+                + "a799c3d0274d6ed279022c01d029ff3197093a82d81a6597960520fad548278acfa45de7"
+                + "4e5629b05fbd2002f5daa0dc43524140ecff4aeda89537cb507161d83eadc2c52a6baa22"
+                + "c01874b057d26bdb83797793f41304a0769b7fdd30201a1309821d334317e0bddfa987c3"
+                + "f6a0aef90a539220572005dbf2dd73122977102776d4f93909fd73e734b41762900d14a0"
+                + "38cb448bfe91c53b0b5df2e94da9b49e41dc7d9af8d69d0cea4581379106c7a399a81467"
+                + "b5775cbe7a92db95ad1bad091cd95640b014846a7b2c0bc7b97a672a21df791c111147be"
+                + "dd8397b225fce285ae27474e5890dcdd568afb7f781f239189b04200747e7952e01170ee"
+                + "926035317daaf9ce0cf2620f078f53a8b7890ab420112e16ceb7108d3e6cc5a43757524d"
+                + "2e1e125eeb903348398806c85946c0f20c47e2773992d88ce7eba0a7dc17e6b8520f16b0"
+                + "23ec6a94150c0eb9dc78d9253f61faa3a55b6c596d5d6950f84df554809956d142decc74"
+                + "5c7a072196745acc0dc617c0f55cbeef5bf98d0155580e8f7723f2dd17a0403ee866fd8a"
+                + "11ef727ef92e9b88125dc5f265dbc720c82891697a7469b10c00e0b2eaa57fd259fb1905"
+                + "e21e86fd1e32440dceca047f21771cf70ca2e39aeec323840dd4a6688085981472397bee"
+                + "cace7a2ef1762a8cc48fd913af638d361269e22081be5f948b040d8c1b136f9ffa8662b0"
+                + "2a7d10b951d2030a733b0210d52d343542e9cfb41b1af66fb43d8b02db01f7439776af4c"
+                + "bfebb1dff6e5ce963fd6f70dec299fc0f67ca32501127d16b79afe38de60ad90040d2a0e"
+                + "e4b4c552ba382ad29f3b3191c260d2063aa1e44db6338aa2fd6a7f24aeea376944b4b9a9"
+                + "e0b800d2b8fba5bcbd1264fff9f6b07065a3becbeed633f3d28d3aaede5163adad84b2c6"
+                + "8294ed843bbf0d6e0f2d80637092334c09fb907ade1ed65e6f15920047f32774224b48e8"
+                + "fe4d8b626ef2d559add44a8e8ccbad667c37575510bd7b63bc2d23e1aa433c7d2542c710"
+                + "d1e5cabcd573e20e838b2573c99cb2f535dd51e6ef46cb2436732610df002ebe089be2a6"
+                + "dc9834308dd67542fca2d01d15d6e5319ba547f317e37515de90d8bc91380d6c1387839c"
+                + "4339bfe83fcd325f81d3c11c9726e2007d45ab00aaabdb0bf056ec837a26266c8c016a68"
+                + "75d786795ea5833c0b1e209eafdc079f62b44940002a24c3706361bc0f1c84c97d66a030"
+                + "cd6abef01dd04b1b09ba246242a36339d562ad062c57363f7f53cf09eee2819f85f3a4bd"
+                + "498c93e0d10bbaf781aea9cef820aac022185ab5c5fc39c0dedf381ecc58a6d758f3d338"
+                + "9c11f20a7e850ca17db92b86594615d7ecf7b9facbd21dfafe15dccafca77109bc28f40d"
+                + "81e8911852890960d1023ede7b4d5dae80f275100bf87e261170d302610c5c0bb538e3f0"
+                + "808b9e577b5b1e75fb48ba3605447c98ac9c7342dcd8619a9cb1410e18a57c2ab2bd2a90"
+                + "8538a9caededf64d1c9929b2dfc02d4c8c4a336e469a5d0b3626e956efd73812d932268a"
+                + "e5097799414800d7b8b02134eac4ca66b9a2b8e39ec304897c7f566c8e23b12f2ed55635"
+                + "de35e4e1ed1ffedf8088c55cac9df2dc233c5c11c6e5fa39f07b6632fac9e87dfda54bf4"
+                + "33c82899ca6c39b8508da380faefc73c39b3996660b59deb154acf9fe90b78f6b244d099"
+                + "27f383f4892daf59c7bf43cac3b4c21b64efaa0037ca42c7e65780816efc1b8124b30f8e"
+                + "481532a8a6300969dc1032bb76f7f02e6e33dd329aec8bf1a122b8d99763b971f935a74c"
+                + "966ee50d3bb2bc257c9729fbba1aa2841531802eea879e454d81bb1c968ce3e9de355bb2"
+                + "0ef0653856ac01890059da9f367539143e5ac5742e08bec6e6f7c1a8871495a07e56571e"
+                + "fd24b0b1cdb1a5e35e00d916c2421d566fb4b4ecbdc19c7697111737131f1c8788f731da"
+                + "e0069e2d3b608dc2ecc7422816630156b89cd83141524011b50dc513cc7864c728d3ade5"
+                + "dee1471219209cd6b2e17ca824fbde97973e3a5f26d4140e729bccccb761fb9ac2b207f7"
+                + "177f145c5d0765f2ed43476f969aa1b2be9362cffa465b228fae06e0ed23c8c0168c9395"
+                + "0d6b800a68022675f916d5ded69fe457c9e7d56b7ebf95cad7d97966415a6fe6cc3ce2b9"
+                + "67beb62d2aa34ca7d9dda52828fa9256c668263eac6704d9e5e6b62c0cc5394126717f98"
+                + "b12e64676173576be5a83fcc748b589ecf3e1c492e84a9a1bfe8e3fdfa41ef2820f4cc9e"
+                + "743d109e147e29e415975ea0bd6d087343b39067dc4ada9e7d61a2ea4c1df3b83686397a"
+                + "bb9e8fe1093e5f08d283a00e46d01814346d41ae61e6001cabedbb3d4c5cdc5e4c38e8fd"
+                + "d6336710cc8b36ec10653b7665f3ce6d1b5e34b835dda0cb2b8e6799b07c1e5a3764bddb"
+                + "1d075309d4907e61e52e049fa57d365bc52a14b252e54d90ed79eaf83e6736f66787767b"
+                + "8fa6f7e583931581bf5dd115e7c47fbf9a625d9f0ce7bb817dafa9cce669056f2c11e003"
+                + "2624908c93811570f2c245ba61f6abcc15793420869c62a448ccc372ff0c0e98e528e71f"
+                + "d791e37464395e071ce23f6699fcf4a79f4c7ffbc341b8e950da3675094a7386693384ec"
+                + "6aeb71faaeea14e973888acde8f46be1b77184ce546570c9b6e07612c7c6e846c4803880"
+                + "0b433c6bfd1ea8d250958d2c78daf0ad8b4c4efd85cab7600fca914a22fc2735ba3b7768"
+                + "825e54f20f06a500c31d8ca2a557ca209c2324deca382b27cdf9b4b7d0ea21ed164cd900"
+                + "3971a7de731726d7db35b3ed7daeedf8ab151e484504158cb2eb090644de8309f837bd8e"
+                + "f5a633253a75fed6399b880e911513ee8b3bf56251ea548150eb0ca77fcbefba3b41bbf8"
+                + "68a97f0a5097466bb4963b24580640ec08fa10f666ee3591d9232606ec2e2e06a48fa6d1"
+                + "484eaaef5e7d64b8df9aa4aee033d5353de9e9053e6ceb9e1e59a4786169eaba14ad6be4"
+                + "97f62053e6b15bbf749c3fd5289efecf4cad5e451ac16e1e082cc9114cfe31835c25b764"
+                + "a5f0b6c66a6e3d59603e3a503bdaff870ce3b8fb4a014d6e8dc7eb066a7f8ff0c077680f"
+                + "cccc09e03069c3ae99b05aeebba06b54590810e11c7109ee420a7b4da2e3cfc18aad0d3c"
+                + "ca9faff93a1006d022a4da3f0799a85dcbc79d4cdbaf8d07996e2189a4a37768791a2b15"
+                + "a0d77afa56d0d12b1addfd49952cb7035832925b46859b3a726cd5b6da895ddf6b797799"
+                + "2eb9abd78746c6c4d034de1857408db162b0eaa521a4d8bc56323d67ce85735571fbfb83"
+                + "0a76ac93154764596cfdeaf6a9eb37df0394e324c7896b328a349251f415dae7dab73df1"
+                + "c4b4c443db8afe5b8e56706982fa5843bb77460d8a229808aa085b07b0aeb4301bb21301"
+                + "3db1a8ae76ee41edf189a9410c4de5fccca29d85e6fd6037d9a430837fff1c80c03f6b3b"
+                + "455a83c983b6289dd10ba05825f0b99b688c8abacd5eaa40bb104daecb06ed65ba25996f"
+                + "cbf0d545cd1c1f24361920fd8dedfa893a45fc7c7ab3a0ff3d29857c77d4af963033bf6e"
+                + "d5b3a14938654acf74743b597d01f25c31aa32c271945a46149fbdd819be1142ed959bb2"
+                + "f1f26c84219fa07e128b8aebd029efbab1a8962415928fce48f53cee74e8b0fc871ee49d"
+                + "52fffceec0a3182809332ca125a59893f540b693226816d9df294bbf8b470cc4e25f9ece"
+                + "111da040524329318bed4bb09ba07f77334f23e4334e23e2de9f3f34813ee2ea68b983ea"
+                + "aaccafa0e3d92e8ae4d507793d13a548cf6a25ed7524dca9c702c6fb68e97b3880861dac"
+                + "03a9ca1b63d5a7decb15e91958c7eaa8406291f39b4eae652d3ee22bb8ed9d2a9be9c97f"
+                + "393c5cf335d5b7775733df6f9adedff0e882a52f04079418fe0869157576e4f18cc7abcf"
+                + "a7fbc84629801b4b54af4dc3c2a9e475b5b4b2ab305ee0da237073df4e59ad3f11656912"
+                + "f3ae92574c07d41853244e91da13c98fd574c83fdde2b62a3e5a84d8904df8a21347ffcf"
+                + "9e1d3bf96c5d25c12087ef94977b91fbe6486d6efe219b5939a587643ebba464258d2ff9"
+                + "1e34225182a3f8281d43244860d740693ef88013982f41b4f8d4c971fda2b2386a84afaa"
+                + "37c5357f6555aaaf03b5e85878d917961eda027dc339e621e7852b143f0616f91fc7cf60"
+                + "cc178a1abd95b5303f4dc6bccfc32302f5c6c8b1ae6e4a532f2647394b720a233d5eb863"
+                + "bbd4eb9e74be2f802f7daaa9f76cfc1d9d0ddd675d3bb790673105b4acc2c9a6f2e95f2a"
+                + "d01a42eb63480f6a37b716cc76a24627e56b3d48ae79d858b03e1d31cecdaa95aaf96822"
+                + "bf355dad141d51b5e65d8d93e4623e1b0e459d005e4516539ddbb80bfad45306f7079f1d"
+                + "52aec4535af26ffdb3c1f49e4b436b3e98dbee3d5eeb89b97d9eea318f903d291d286cf3"
+                + "3eb88af7b7deecea0a0ae844142aad00a5cb6dd020cd5fa1adf890d21936a6c7add54005"
+                + "778e720e8002fce5ff01e891b97f00459d6e5c7995ba785b3300ec6dc65e90c5e0bb3f76"
+                + "abaad636e0e89fd8818a4dc647dd146b3f6b0a1c953c4cd9a8c98dfc502a733b0d8baacd"
+                + "9faab6dfcbfee2c82aba9bd0fa3922a8d8bf56979a460e63733c4834929d2c9d452e8b4e"
+                + "2e362a67ffacd43136a1086f3b1b1a91ef67b6f317b6b78a3d1f6d3c635444064faeb218"
+                + "3d591d0d042ab38ae354ef382bb44f8f97cdc3e02292d0e02ffd5111b39467570dcd5d75"
+                + "29ee20dbc33ad51cc9bee34767d9ccd642e846a896883cb79a9073b57f7bd72a3d23c2b9"
+                + "b98de0834d9a6b650ae902c1511276880a6265c5dca59423bb3079b593df17ee4b04cec8"
+                + "aaa44251ddbe150eb9e54a193d3164054c74674f037a7448d51bbd693cff4c42d160fcdf"
+                + "a9400ef8d6bd6647eb586306aa19e3d94979ed8a539f6a75cb5bf6942c9a22db8f752b6a"
+                + "743602ea946373eb0f6cf22c12851a56a6977b35a1334a1350cb4822c6b12de1d785a231"
+                + "9146e479d71fdde15958faae8a73f8cb2650818becc765d79250ff1f07a3c87005d035bd"
+                + "cc959a62aedd59d26a29f23a580a98af17e8c80ded13d12acb47ef78ca7b4d36e426afff"
+                + "a7c3a75724330e1d41be5a9e829ec1c284c50197210b3053a3241dbfbe737a906c4f4263"
+                + "66c6417693ea6c118bf699297bfeec84738acc3bbc26da53f6e102c1c1d3d688c732882b"
+                + "4f9a363e5b01d21913d2d7cd6dfe8ae667a4b4b6c9e5c78bc2ccceed96697c8d48434e8f"
+                + "b6b44006eac75fd65ac755386c6263c77a27f12002bb829e611c14ace578c2c563bb5df4"
+                + "c3dea110a8684c617a0533ce3539b8da44f5faf6a43d14e7ce71a31537817590aea1aedb"
+                + "f74e1cb6b2b3caf6b9297437d9c0974ab78d286e1bf6e4d63553c0df68bc11c81c8edccf"
+                + "989e4440f3de37cfabb2e84605954835f89512055281aa2cc60bafcea2de151aad2045b9"
+                + "6c2fa77a2a2f1b95e13d088da1566567b7e5f24539988a27337b37c767b702970f4214f5"
+                + "4be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
+            "01dd2f9b3a9e6b46a98da3f7dbeb4e6bc7fd9629403c30577e876f915fecb6b33450635c"
+                + "5e6fb308eaf1c7890e4761cabb79a373a4f7b67284a04330696a51015cd7cee070559cfc"
+                + "966dd65c9244382b4ef40da2f7a4d82d82583d95deeddb950014b74ed8618350f8303c32"
+                + "a0264b526e679570707c15d9f989773f62957e5aa1be13806c592b08807eb7781bd8ccab"
+                + "5766e56a2fb3458b67595d36f22e25adc0f0724fd0577af295ed997e1afb9013b0f94074"
+                + "0c1a2567e9cd78fcfa8743e8a5ccf170a3e4b4e56cc551a3a69762bd11cc981b65c65d79"
+                + "08086f8e07d0ec8ab44a58106f0d064fcdd0f98fae905f79629b2a76e502cee978c0eed9"
+                + "4facaf2cdc3eb06d350363287ba2771ce0dccce6bf7607238152301bf37567c8665b50da"
+                + "b15c62ce50a8a32743ef67d68ac95a9cc48e9d8b5e1651ce02d94ddfbe735a9820fa3570"
+                + "181fe11dd350151beecbf49fed2be0c3d3a83455a38440e71570e2385bf1dedf4e0d14b3"
+                + "f4139f78aa65b6e8f32a3bd5afdaec4037a819b29c57cf5a4ec4db5d5db9f9895fd5de15"
+                + "64b089d4de5a0553b0ad357d41ade477b42025d1833e38703803085965cc8b108ede6704"
+                + "d85d0aab7a332b6f2670e4e63a8b524e4f03ea1f813e26774d00b87237edc6b454a416da"
+                + "4e1b55436493ef16a9e8a80acdd218b48e7a7de565fae8c3eaa25b50c0a3e7bc4ebd28a4"
+                + "ef3c5ead159c1cbe08b028b11149a0e9e655bed09002f6bbc2a33dcc61406d452985c10e"
+                + "3f516c562978033872e0aaa31874d435d2181d7b256a9485d785f67275bc482ca7964a9f"
+                + "5e98bef74e3380e193129b6ab7276844397b8ecbf47225d0b8067cc1e51f1fee76fcf3dc"
+                + "f5144f70de7651bf7e7ddf0eaf2d4b0bc01b9b661af8559b6e4e2b56dfe1094f415ab359"
+                + "4ec171d9f23de1177598cb7f1b3934f84456b45b41622329d7ff0c8dc9582bd88253376f"
+                + "0675a702ef6897278647f39af7e936e9c887dd476c3b62ba4cbf5066e4a4bf4938491018"
+                + "0e948646b79059d52884c8a80450ed3c45cd785a041eef5722d04c8a675f22d06ea05627"
+                + "902e598048e013a4cec723874239ade00b0bd884c7bf7bfc5df2cc45ee859ccc4ca64565"
+                + "06b7d347b60ee1153e90e74a005310a303c7990d8e268816bdabacc26632b353419afe70"
+                + "53f0bf6de2dc900ab5b3e4d0d11a459a395b6ec9b02d634ace8df35257a7ff24f011ad39"
+                + "094864755a016cddfd6391d267dd7761015b055add9a3e7d20e415367666f5130a9df4ff"
+                + "0ed9b2e1da5a2ac922c54e18e56da2478576d9a36f6dd37c0b90729d5082033cd9bcabe6"
+                + "7aa5d0751af9686baf80b537e699b53f88a95f98d787fa1e46a0d71d048b31afb0255e7f"
+                + "39872bf42dc8a331df5557899bdce1d0f12e1ee5b80355fdc738438a56bd68a26617db53"
+                + "a0980d67469b4068e7a1727409ea8a50983a3394890f4891a9ac4e126bfecb8be5dc285a"
+                + "86f1f7715bc214e716c5094c5cbd79ccc01eafb11d51bd83069f2479a20c5c939f839976"
+                + "b0395bc939e161cc24feaa44df0ddb23ef22f1c2fd7312bd39bf87839ea59e794883245f"
+                + "a2b1ce8a811de6313ff9e060cc389d98d0fbfd926192eba49e92b4f5be7c5292f37e2ad0"
+                + "476f7645367bf99dbea078f1c88d39e11cbea2a581d74df841ba456f8132bff3e6e69ceb"
+                + "ac4308b4ced30a33b3156a105b4c340e7ede5e33bea00fc2aa37ad48f5581cd17e74781c"
+                + "2d746dd33a49abc9ed52b2ff020381eee04fd9babb0af875ed80944880456d0bc700a717"
+                + "543a068639f464f2558febb47ab52d1329e08ca4e7dbd8ae67cabb8088d6d669542c0eb0"
+                + "31287b6403dd20dd1e1b88aa0dd6709bd2c8bb51d026270e3da08df9bbd376615b403adf"
+                + "25b8cbf1144e15a02b6fedc043846ebc31f759b272699a867883391b33cbdeed53c04c7e"
+                + "b1298729f25f91482d09fc05fafa69b208b1625739ae3864adbb1e5953866876ba1e10cb"
+                + "f350420717bc22263706f2e33a7f8ab22e004e45ac10aa4a9c983e931f8d201985e7fd12"
+                + "a40235cda42e4f0e417f610bdab1871fc6624c2d527fb1f5eaebe75c576c4eb3ddbf7184"
+                + "4f81f28afb7c91eaff940f469f8c30fe89d3ae8c23c377defc0f98dceac6a4e6975cd03b"
+                + "95861fd1355ac8fa4318f3e2cdf5606a8dd04f38c27b7d8794faee39346b6376c43a2d2c"
+                + "62cf01c11650878cca2001c2ec1857338b7333c102d1f2a74fba8eb432c8065dd219a92f"
+                + "5f222857813eb4cc82058fb38d7baf00c45cfbfbaae0328cf9cd7ece7be2699d15971683"
+                + "edf4f21475b2feb215e4676bb1cadfe48054c7961eb016132d9538926db6138dc73ed3eb"
+                + "b6a4d797a8f267609c9448b199136a61deae6a114ac71221e04052c9d840a84a59db18bd"
+                + "2fe91492a923ec442452b571838c05f8a09649762450d9cabe8b273464b5ca95e8bc9545"
+                + "6a9bc9557e30613dc8d14368c98333cd77c0a33c816e93d79ed5a5e95bcb40f619db2733"
+                + "33210ce8723507f0daa465ab0e01a12ec46a0df57178796e10af04a00c251e96f84ed137"
+                + "e81af57b647b7712127f6e03aefeec8bac9995ac1f9bc737ce92ee12d97333d1b083503c"
+                + "be6d9783c708b0ed5b02312c1bfbbf4e32fde1d5cec9d8b2fecd8f4478874b8a7fb6147b"
+                + "3f74aa6dce7fe07040c1e2b7d3dfae37209f727969ce700cfbd2a9de4d85a3c79c641ab4"
+                + "7581a80d6d10fbf4180e9ba656f885a49e7ec2058af7797474ca244269036bfb51c148a2"
+                + "1bf1b4ba1464a69595cad7444b9b0bceb1181e1f9d543ce126cb707ea9991c98ae203daa"
+                + "a07d6f7bafae26db8c71ddcae1fbf0d00d52f22a7a11797fe24e62f6e43e8d9ef46e1be8"
+                + "69cfff5542053b12c3ba183cd8785880e32cf99e48309ffcdc3b792a9e031c18a5ef137f"
+                + "f378e36313c1dc239bdc8eb978cc724ba513ac40a0b27684138265bc6f37412c46f188b8"
+                + "fd2698a9626fbeb2bd7517e3c2fc52bce52e76ca73e02e2364d2b00136180514aeb81363"
+                + "ccf51e4ee1a17d2c5f8e57ed1f510764565edb06b53a6332d67ff0250703a7d199a2c12a"
+                + "44d06cc27d1fc9b6d11bc3aebb153a032764f1a0bd664600e595e2c274b86b4247dbd182"
+                + "700f65910ad1b07df8e7097887269c1a8731f8489433ea83df61a800dc48d0eea8a00a12"
+                + "e3f1f31c155a7ac75bf39f7b4ab92bf3697a53062c9d8ee5a3510da10d8704858589a26b"
+                + "c64b2af69234a595e3725e838cac046cbc06fb80372f7e47afbb2468cf6dbd44ce2b3304"
+                + "b7b815d7ca8222966094d23ea394c1ee58e0fe88bb1a8d75f4de0575df43a2a9489d095c"
+                + "ba97a138e7b7f811e325cbeeacef1c75cb97d6141ed825b3963334635a1ea04cac48faa7"
+                + "6b016ee95227f090dde64f3d26ece554fc17bb20f212e7035ad243fcb1ae2d08ecb47fa3"
+                + "eafa000a30985688838c2d2bbaa46111db8bc0a4811af01a07e3b6596304e21a09bd3997"
+                + "86b38790c1a62c4cd1be5808e1869a794b899c9cec46a3503a06f5d3cd9a17788c7147d1"
+                + "b9ab562bf6d947c8458a215792e9b8fb0fc407b08bb176ad3383720cc9be5fead4956cc8"
+                + "3304f8f168183d5b15c56101011d716d92ed230a4b4f74e9f1f2193587bd8f99a8dc6ca9"
+                + "5d95c568bfadca8207e4aa0b59bb95dadf3992c26690959cad69928a4ed827339dd26614"
+                + "3f85cff2559e8b14c8db0f71b61416184277227cb45bb0d4f7af04556c64b08751103c1f"
+                + "f5d1256454349579df44fdee0b6c1af59cc73632528d11c7a3103292901cc248bf73c1a7"
+                + "eb623a5ebc6036673aaf95a7c8589df6402d81993ec97fdd2086999d8dc15e3d99113b27"
+                + "a8422d9084c77060261395ec1dad38f3f18f6bc688bc6fb4b3b442932ce7727be7130921"
+                + "f9adf5d177358686bcfa854e90683d0ad37c413bb52cf70375eda43a3cee589a3e84c65c"
+                + "bf4aec088d4d585c3665e12b37b5be0c0afcd0bc52faf10a7c853e93fe1d9bfba68dd4cf"
+                + "98fe0d5d8dcac15797ed172d8692343e8bf79195e0e5879ce468c604ce9ff765ba6c86c1"
+                + "653cdf3dec8d06442665bdacf55835f9c832d4ed06135f3d41d7ee8cd770dda6646a7a7e"
+                + "f5189a237600d3964e5a728824417588405e63283f97ebeb17fe49f64b253c18743d6794"
+                + "6ee18ef5ff96f1e3c55a165dd8f373464449464c447318b6bc95f2efcb15d1a627cca17c"
+                + "68cb0e3d7efaa38cf66fd562cecd3c63cbee3fbd462e281e1aa2bcd775931e88e9918447"
+                + "85af12d7d327a7383ff6bdde4f91ad31c9af84e153b9a0d0028f01c8a8d478a8d0bab543"
+                + "1e4c535b5251bcd9bd2f3d2fe6e2263e433979e867ad2f35c836d813e083487de56e9522"
+                + "d201c6c6570a94113b760b9bbcbc95bfe3de92344076ca89ce80b0f32fd26ebed3f47a50"
+                + "27e6b8aeb40c82c4affeceaec24b6f7993ab89dcbf7cfb83fecc211dff0cc0115b2af6a3"
+                + "0049e7b97d211a271def8f6925d146c43cc21db3e6942274c2bbff786be3ce85f292b8be"
+                + "a2830f9d052ce67c4decb39ca3c051b1bb004579aa4497b582e76b81168222918c9ae0df"
+                + "ecdbeda50d9674783cb50df55df4729d436697932f51f878db57e62b612f0bf631674096"
+                + "e0c7988695f8f6ffad459cc305263fd38604290d57127f8f0533305423a793c992230dda"
+                + "9d122a97c974c3d60a96b039189633bbcaecf3db47bf6c39742ddaa37e0ab91a281d2119"
+                + "57b3c0f9738ec90e2785202a9023b494f42480be4e81299cc75e2d560415378646cbb2d3"
+                + "641ed73a8b886c8909b13819aedd540b46ac97abea7e87dc122a1da614eaad8163f16dbe"
+                + "e37bb881ef1fd4cd0287316f130b5cfd65028cf2f7ef9f4ba7d15afc3192444afea93c35"
+                + "542309892d98208f61998684768a11fc771a3c7dfd544fc81a5c33352f01f763d920ba28"
+                + "7d7f3e3c57ec6b7ef78467e2efea39fc7dfe0a1b3d706751e157b0e183fdbc1c6684793c"
+                + "64d9bc8b5bb74ba345aa4b003a230923b648a21bf046df84e0075659f6b2d08f4cdbfa2e"
+                + "3b4088d00d49c6a3dde69fbebb909ff55f80bbf0f22846678481c013e0e851ef6323b176"
+                + "dea50e629d622b5c3f88c9fb161a34fb0a4da1358b0fbcad268f4f71ad6e294dd468fcc7"
+                + "f574f2087a748d34b416ba2002ccf3ebddfc45942fb8577def9624ddf80e880ace925d4b"
+                + "2c24e4bc64d34fec4b6fcbb093e3325b25360369a97efe4d76eb919f14466b2d421aab0c"
+                + "6bf49813c7a16633750439ec5fe14f39f4c86fdc47b0edc5480ab32e81e3f289d16206c6"
+                + "c8d496736792c6aeb3b5e5cd8c02454542300a6c03ff56cb0905b98834e082095b0728e9"
+                + "ff9519e5158759a329155bfb19760c1ad8c2d6260100e58cee0f1ac78e3ee3ff5bd104dd"
+                + "96ec6a690ea34785f0005c5637c3c3d0ccff229d8d1c444394f876a56a6385e03e502e3b"
+                + "5a0351d718fb4771f878020bec35c17860aa8566a7e7dffd63e1b25df90e0994721a41e0"
+                + "40c89f80c4bf131222e6d3d0371f67115d0fd446da933b31fde347d4d85b82bfc87f1e74"
+                + "2af9652a453f01d8849fee7705c9da3d5570aa4286a87f17ca27e748f8c114d160166176"
+                + "6a8b9c5c712102f747c66a8e1876406a26d9c3a9650684c8865de060dead429ec278e302"
+                + "cd2de2f103faa7ad31acdfd4ae3d3083bcc94f611247fcf6b53b6d5c4b4c0a1b527e84d0"
+                + "171315c1ea8247575fe6b0774b7e006f4bf3cecb09600e2cdc59b0d33b2445065d5778f6"
+                + "48077ce17a736993a15c2fb3c1c6d1e7f697081858ce2e8df63a6be0d0923ddc9daebed2"
+                + "2990477e09d57c18566d7f7bd43779c3bd14345121cd0be58c32774dc85a75d42686111c"
+                + "438908b982071424719999fb5088e05ff90035dcd69140a1289a1fd3bdd58d95a026ae16"
+                + "de49cb35c379fad7d38e730f2555ed790bd6a8e6e93ccaf74c5be15e95e0c352279ce9f7"
+                + "c814afecaf2560d93e169b3aa58ba4bf3d9ffce11c72b8ca98b38ee3c06d1ab4c633de55"
+                + "22f94efffc43e06cb090e119b76eb568cf15471c4701b68f426b19ae24a5917edbf1a4a1"
+                + "b0c583673b0ff7f98be901f17001f8aebb609850051799674f079d4397627fc65e5d3059"
+                + "0592d5d337c91f713607d47324f95d32955861b5aa1c6a9789ef75c412f27173891e26a4"
+                + "d418bdcfa3b6fc9adfd53db14d74885f3c4ca234f48fee094fe220d795d693ff44f2c2d0"
+                + "56a19d489951214bbf6b68604edc2bb3e7f8e1e871f5dcead73a9e4d452b8861b93db30e"
+                + "e372e11afd64fc40426ad84c439ad3eb66313d14917434857a4e14d6d51127d8180126cd"
+                + "c2a35c1ba658b5e4a953ef304eaa3500386c07678eed85a934458d469ba8b48ef4ccdfb8"
+                + "65181fc087686fd2949374e2eb5dbc1518437b2f60c2c0061619afe3db0b4e6bdee6ba39"
+                + "66b67160180baaf2ba58fde0d1f0631375f028daa91a63d87b37513c9173d74440de4ea9"
+                + "6dbdf332d67cf462cc532dba85f9bea52816f53c47d0aaeba206b32f21bb157d571f17ec"
+                + "bbdf5ddd0499d34816fda112852963433524631620df12c6b264fb53003ace63fa23880e"
+                + "afdbb7cd71c6b31a0979f683982bcd570314ebafc646c32f715c033e7c628b91fa4ae244"
+                + "06df0bfb4cf48f98fc5fdfdcc65b2db6ebb2e19d6b5b26144beb035ba5198c8223306f53"
+                + "10da1ddaf5a469209e3f11d6250f5e66d1b408d2515ccda5c68b691c16c2bb78483806fb"
+                + "4c1714ae5ed569202414b2297816ef1cde61478b75e82328acc76687fe6453f97611f609"
+                + "ae23f1956dfc83d44eb397de1c62ee7e3e222e87a3aec0881f479c87266ebd64b0b7c0e2"
+                + "a50b8650599f031f546b84c3160a522dae2c8b2b49d0aca76eac937b58cc4d0e3e935961"
+                + "d072c94c63a3e11b39c3409a49f8e9f7d3edf83dcc538352a38a348d2cd01a78412be64c"
+                + "46a12f67e1d41d50b60227c37c8c0043f798c87800afc85892710336172c40aa01c69baa"
+                + "4a20305751de64f4c77c0e4cfe2a360c61aba9fb0cd40339b7db00d3dbce9b5838c4b435"
+                + "fee5f30531d0eecd695aedd7156365fe3af959e36f2aa040504503560019a0c16361c233"
+                + "cb8de21449fc851b59752a1e32537402c38550204144028feb4078c967557877b0ad6e6a"
+                + "dedbdc3b366cf1082e18d83f6b5615ceffd5359cb793c04b51afce3510e55333bf3c5833"
+                + "28a2485a7a0ad65f64fc88cb40902e02ef437e112e3d0d54a27d2d08390bcbc62abfbf1d"
+                + "ab8ffd98f954de815218ea92c9cd9c93bf82d50cd1dbcf2d5cc07c21c126e93d67c95674"
+                + "31bec659e54cf95c2ce95a5a692f746a3778bf30986970697beb52a250179dffe83809ba"
+                + "2278fdb1ddca4b57b0a316d9982d62a85e3d33ad38ce7f47f9f397266c064fcc2ee39623"
+                + "e67faf48877821922e2676caa147a364d1c22d5643c38876ab1753b53ad37d25893bd20e"
+                + "294971ffa528e16cf742abc3702780dcc01f949583f3437ba10bedd1a98be015bb17673e"
+                + "ec5d40469c9ad1558e688fe5439f3943040ed37d8d1e21cb32a7e56edc06b83cd2d4a563"
+                + "fc142ccefd9590905240bacb9a552d6b777f8295737fe91f55c90540195d8f89777b9e07"
+                + "935e248fa7fcb7c2066bf748934b4bfc12f4d7d95a628984d27215bd5a477175347d0c90"
+                + "378e01d8497dad86558d45f8a458e96db26e35454e7e9002e47354d859ec45d5643969d9"
+                + "4dfa8e0a74bb9457576c096135fae2b81efb054b7a75f856b87a43c6806d76fe2327a0ab"
+                + "e2d42ce4855c2cf6b2b3fdd43cffb28d41a719d24e72162752139bb1ab00fcb6d28038c2"
+                + "994e7b8e8ac6730399b7acdc47bded773bd2cc15e60ce06601b21455bb4c0e8ecd02116c"
+                + "6d8403f43ebc2017fa421cc2863391be4d2a8fb545fdaafe6f80d7743157f3bd68ffb3dd"
+                + "3a82c7a08d29f5fe19d657d7495f3f7aed07a67444f2fd863c1af0d399de5bc3a71c1e6a"
+                + "211df69be3088fcd73998435a63611618ac4445ebb99f5df5daf02b45ee585c62ade53de"
+                + "945f3fd73d26c094aa1c3f0d0b19b0273ab3f0eb3d8a21639e4e7d6e99f63039639804c0"
+                + "bb014b1e6a83a522b274eaaf5f8d1921eb88720809d04a8fe91e241e2c030185b151e4fa"
+                + "56cd52aa2490b3891410f503646e362162fe619e942dee9d8fb11b601dae644bfa894c80"
+                + "9cf205a58fa4ac7ce5e472a05668f696240cf81acc7be6a1ed1934dbeed1e6fa50f41685"
+                + "e413a6e22768169644be1e2419e78ab0922d502934c5aef5a9212252c4c0bd46ebddade4"
+                + "02dffe970d8aecc535a102b5e8a01b1689f96e5cff5b9adb9104d88a7d55c8a694eccef5"
+                + "0dd58a662686a4e76e8724c133363a46e0cdbce85cde10adc415c1329fe366139fbb6e47"
+                + "fb3b37dc6bd030067d0eaca26d16ab22a8b2592f08bcc0451f3b848fd58576091e914881"
+                + "b82efeb0c57f85541f26e38f8e611f733e3b4bfbb02fde7fc43ddc05144be25e32108bbf"
+                + "90631f3b4e88c5df91fca2ab63db007eae705cb2e20b6039758f5478dadaa955d985c674"
+                + "44f8cadefee4d7929ee5109a6ba149ae48f166631848a2f1617bb1bc0e88da0308f8b2d6"
+                + "abb6c8a0c6f4dec77558477a85f207516b01d12ef9890506211e6d116d8ebb17c85b9e80"
+                + "23f3e56659d046f410fc4091a5a37214f551cc04f4f093c5f4284e441dbb8e7f96aa8be3"
+                + "9c40fc9332c3e7be5f9cad90320d018028d1df0554b92ee92268307f905d43362ba5702b"
+                + "074e6b213e604656cd7d7d57ab6a9ac0c4a9c9fbf0df4355ca75a0bd314933ffcd04ce8a"
+                + "9fa812a78ddfffbbd8240610a55f9a59222ed6a00d159c388783c07f1fee79a77abbbb02"
+                + "649128acf21abed0ad0fd8f3ede2c52b5316082429ba473b331a27c6db7e34c0bb81385c"
+                + "10e623d7e5865a8d3565547b3619621edf06d2b58b8b3d56c2dda975fedd0fbbb76f2dd6"
+                + "da507b4a04b13c8bdcbe00e987c2355711a47113bf7ff9de8fcc75f852b1939f858e9158"
+                + "ddace967bbadf9ad04ace7d93cefe42473143c13a71c31ad0e95dbcd096e27699c9a969c"
+                + "f5d5856ac16b1dba553b68a8a13e243c62709cf9d14350ee92ff10a62be843dc894f8a90"
+                + "1b38882066a0acf23338da38eba33012e287e2cc0010d4f8d07364bc75815ae49b0e2094"
+                + "ae26f2af67b0502ca11041b051de6f631aeb9c7105074db5562560ba830a2070d4a596fb"
+                + "b30632f221f59af16fbb017bc87427ceb24c5e1f6696ea423b98dd632db93f00c16d0b80"
+                + "211bd9a49edb23b79f039296aa6e0a8db61d9dadb06644e335670b0e74f51f786ab889df"
+                + "bc2e329f214efd9b26a97b45987bffc81ba39084aaefdede24903ac6a77cab13438b8c89"
+                + "5e27213f0b183de71b558ce6e05a269bbdbff6934a11898501e803ce1f0286133cec3d09"
+                + "482fba75c0b26d73af5275139d460e3657ba019ad9f565ed1324ad62a8c1cdfe6f925318"
+                + "096a9708f2b17ddc3503f86baadfa6d75725b4f54cfa34c714a4311ee5f1928505724665"
+                + "715ecb8836b4a5314192f490844658f7352568e83cd02125e7c589807f01aecbc8e33c38"
+                + "a19587f3c03db1daca9690ab6f806a096956b132b4f2d0074723563c7fd789dae10314b1"
+                + "4cd9de8a0a78d196651cd9bcf97cb840e6893a8cabe4aa5b0eb2c74b885ab7a64bec0b4e"
+                + "18219143fc1a59529b954adcc6082a199fe2e48395e21c5ce5535c8f9bf3ef72d2f0f89b"
+                + "6e338e338b8856757019ca35ca017761020b98b61b8a513f2cbf0f2e29aa04502de69a7d"
+                + "2eb78fb828599a2c9d11bff59d539ee43316e68227fc66fa6dbb8cde0066b44ad77aaa94"
+                + "86a733e79b6de689e71c7a5f7497743a267cb0e5bcefa0b359e44e9f2ff0d9b447871fd2"
+                + "6983f188395355030f2d176f420a94caa5ba283e53cb40ab787a6160d4157e38f2904599"
+                + "f7429deb9006584c9b929eb99711c5087ba7443d36a9e2bd8d2415f495bc5194fa59f167"
+                + "73883e144f84ce8483c85b1a3120a15c82b7b83558c5ad244fa48bdd7790a6da5c117658"
+                + "91b5da14391575a9f533acd8e3bc5b72ec3ab18fa4b775802150652d0d710f0c8b03e638"
+                + "81b00037d26163f38d4fd2e49154ea7c8cef79c51432ee0a9bb561689aa252d7f49bfcce"
+                + "d83101bda4cbaf702b4d6dc6fc390b8657dfe2b8f36db4adfef7cef9b3760fd522f154f6"
+                + "8889d6cb04dc399223df40ebba5e40167ec60e6ebf0b75bebb0df2368c7c7003deb3c184"
+                + "1796ebc178aa8ad6a34601edf623054680465e9bbebea2b5d80e473f40459fee240a73ee"
+                + "58fde1883f2436951dbf83f24ef567582fbcec21e5b7d02471fa9cab4a7868862f746781"
+                + "df633fe91a6a2d31bba32667c9e2b92d7f806c4de8ccb1b565aa3b0db9d94fc0d9408ba4"
+                + "35c4a151d6a3eb8f7e30bc5489ee34dac617f1d446caf2e13e662aee6e16ae7354773d1f"
+                + "5d5aedafa5ef517186840e5ab906e9f564a479f45c75867e1d7fda7ffaf0fa29fd3e784f"
+                + "471b6ebc1a0f16208fc0c96ee886955acf40d8335def23cf48b9be02b14db0c8d515e959"
+                + "84173f11137d096ba01c58c0d6520dd553e1f6a46855980dccbf868b44cfdae37fabe9c2"
+                + "b1e0ca078ca2bbc6a1ea37615c1f141aca739a16665ef359a786a9c9c2533c763a441a1c"
+                + "77f4e58ef2b528a5a6ab21c2c88ce7dd85de0e6ce5ee3e71656ebf5a5b2cea07f35ccf87"
+                + "2a037af6013576a814561353a666f11ba298b0206247dfa5488212268e846a66d6e277be"
+                + "652c013134338f064ab008f6b943b9e95183f6d84c545e4c5c2340cc4513c28bc40a3bcc"
+                + "41eea5a779f8d5585fe65377283dd152710b80f363499668c2283f26b840ca8265e2adf0"
+                + "7416003a46d80e769c4dd8f786a5703d7efbf2549e61dbf71a9d722e7af8ed20e8f2446f"
+                + "e5879f2571758df53e66778991fb1f5fae534edd006eaab3548f70c7eb87573ddd934823"
+                + "5f3289cd452a6f3ffada397f1e787db77094e5c13f8be924de471863260dc54f8ca78400"
+                + "0fa5f88dacf2ee8e0d237a6dbfbc4d2eb67293e2c26dd07114f4dd99a64cdd3c91f0b998"
+                + "b52087409ca442e3e0784eab4f0f2f256aaed526579a97c3ff87c5724475463ca1171088"
+                + "79796b7b7ab903edd5ff82c1f14f371649c9bbcac5e32497cf9703032d5559045124880d"
+                + "9c246905c312b92993ac12f6f030bc90e6d309286509b06c3800c78157b708c0603e2be4"
+                + "8015dbe6956f3d83e940eb97e5939c0e3e2e2e7d2e657eeaee23d54f4d849fb8227cea5a"
+                + "3301355b7867790c4f54ed36a3e143e466d18e3f3e4ca8b092a5c8c8f9f226c4d636bfc7"
+                + "728e0c1d41d206669fb3dcb1aa19db6b56de01befff4c6227764a0e9aa1949469fea46ab"
+                + "56e5acf340af4ab62fb05cfd87c3893177b77a1296a94e7a7069a690d6a48fa6940a8b3c"
+                + "405564d0dfdd4b8ab56e29acaae09e2cefcb4276226f4a2fc664a61b5d4aa76d0e37626c"
+                + "93edf16ed86b474779b9b33beb49db1b211d17244d23408adecdbf8aabf2152bd3b51f21"
+                + "50ca5542205965c4610d2571d5f566635529e755fd4b1a4aa033007a5f9676694fed2e6d"
+                + "c71ced63be0c39e3beb237da3b7f1463691942ede74158e2c03bb8c13580a609769bf082"
+                + "ad9256f385b131e2eec5cbde0ce761ba1db2d45bcf7b9ceffb2a488073b10c7ae01717f7"
+                + "6efbab5e3a57cdfc45e582e2a61fa23e889a9b80ea31951ab9d0cd185f98404324f55a04"
+                + "295050bb6859778fdd9ee5ec1306d8bd3e97a4b5249e2add51836da186b6f43c7bd00da3"
+                + "4d3032dad4dd6983f40adb7433b9b738b37ae3b1797273ccc8553a0429216fb803059b21"
+                + "a73173e87438b90f01450cb7066a630ba34f5a4fac6f565e95d1627285a38a3f4a295802"
+                + "0f636e5e1c2c9dae1c8bd2dfc5f94606fc8c197719ecaa5c809215dcf64d02fa0e544f57"
+                + "5666b9ad3072d93c076cc60bbdab9990c49732241bfc2c2655a2ba4eca53985a89a99549"
+                + "7e2c9a63ddbc7c292ac1d766e6dcd5d6b22ff967113a279ab42694bf7a01ea8df30eab75"
+                + "11edb677452a844a90608cc99445db0c1d0a3e32da45245dc7e51eba883fbbde7e7a3317"
+                + "567995d7a079d2773ae79f3d65d6e15aa7b317481bd16ec25e8a903dbc643504532b0624"
+                + "0206557be7629f6cc793a7278d1bb209a2222368e33c3315532dc2e3418ac5b660249620"
+                + "e31e8ebd7d2404ffd9111ee4b8f7fbf58b048b3bd68f2c5cf5e2cf759a69dce3dc4162cc"
+                + "1a722d94dba16cb81c511f686220f92e3d78f2018c85f35b21b0057c9dde2cafee32977f"
+                + "707719c42902aeec54ca6a909477958c2740083f1c84f923b1a539c7b6232c5898b248af"
+                + "67a5763c07dd934574af80e2e67043b7f03877d65a864182439644fc6d08a5ab7679283f"
+                + "72d0941033ace6e57b7088ec3e74f1d20c23f030879782b10e26ed41816be1bafee4089c"
+                + "3ecb1e363033f9e5e374967b94bd9d069844286164cb7c2e437f412c52456bb17446e07c"
+                + "d74fbf4d1dc1122c84a5a8204567b341313ea565957bf7741036ac7f77f4fcb51a070fbd"
+                + "1a72d27215f00b53a3eb228c8af66bf172d9ddd251a181d4e7427e231a3e0a655da22f25"
+                + "2baf974f79cf913f11c7f81e9b8b8e35c4b2485212d43c8b5828e8eaa2ec6c5f9af29c95"
+                + "876b360bb0f900bfdcb03b7665f3ce6d1b5e34b835dda0cb2b8e6799b07c1e5a3764bddb"
+                + "1d075309d4907e61e52e049fa57d365bc52a14b252e54d90ed79eaf83e6736f66787767b"
+                + "8fa6f7e583931581bf5dd115e7c47fbf9a625d9f0ce7bb817dafa9cce669056f2c11e003"
+                + "2624908c93811570f2c245ba61f6abcc15793420869c62a448ccc372ff0c0e98e528e71f"
+                + "d791e37464395e071ce23f6699fcf4a79f4c7ffbc341b8e950da3675094a7386693384ec"
+                + "6aeb71faaeea14e973888acde8f46be1b77184ce546570c9b6e07612c7c6e846c4803880"
+                + "0b433c6bfd1ea8d250958d2c78daf0ad8b4c4efd85cab7600fca914a22fc2735ba3b7768"
+                + "825e54f20f06a500c31d8ca2a557ca209c2324deca382b27cdf9b4b7d0ea21ed164cd900"
+                + "3971a7de731726d7db35b3ed7daeedf8ab151e484504158cb2eb090644de8309f837bd8e"
+                + "f5a633253a75fed6399b880e911513ee8b3bf56251ea548150eb0ca77fcbefba3b41bbf8"
+                + "68a97f0a5097466bb4963b24580640ec08fa10f666ee3591d9232606ec2e2e06a48fa6d1"
+                + "484eaaef5e7d64b8df9aa4aee033d5353de9e9053e6ceb9e1e59a4786169eaba14ad6be4"
+                + "97f62053e6b15bbf749c3fd5289efecf4cad5e451ac16e1e082cc9114cfe31835c25b764"
+                + "a5f0b6c66a6e3d59603e3a503bdaff870ce3b8fb4a014d6e8dc7eb066a7f8ff0c077680f"
+                + "cccc09e03069c3ae99b05aeebba06b54590810e11c7109ee420a7b4da2e3cfc18aad0d3c"
+                + "ca9faff93a1006d022a4da3f0799a85dcbc79d4cdbaf8d07996e2189a4a37768791a2b15"
+                + "a0d77afa56d0d12b1addfd49952cb7035832925b46859b3a726cd5b6da895ddf6b797799"
+                + "2eb9abd78746c6c4d034de1857408db162b0eaa521a4d8bc56323d67ce85735571fbfb83"
+                + "0a76ac93154764596cfdeaf6a9eb37df0394e324c7896b328a349251f415dae7dab73df1"
+                + "c4b4c443db8afe5b8e56706982fa5843bb77460d8a229808aa085b07b0aeb4301bb21301"
+                + "3db1a8ae76ee41edf189a9410c4de5fccca29d85e6fd6037d9a430837fff1c80c03f6b3b"
+                + "455a83c983b6289dd10ba05825f0b99b688c8abacd5eaa40bb104daecb06ed65ba25996f"
+                + "cbf0d545cd1c1f24361920fd8dedfa893a45fc7c7ab3a0ff3d29857c77d4af963033bf6e"
+                + "d5b3a14938654acf74743b597d01f25c31aa32c271945a46149fbdd819be1142ed959bb2"
+                + "f1f26c84219fa07e128b8aebd029efbab1a8962415928fce48f53cee74e8b0fc871ee49d"
+                + "52fffceec0a3182809332ca125a59893f540b693226816d9df294bbf8b470cc4e25f9ece"
+                + "111da040524329318bed4bb09ba07f77334f23e4334e23e2de9f3f34813ee2ea68b983ea"
+                + "aaccafa0e3d92e8ae4d507793d13a548cf6a25ed7524dca9c702c6fb68e97b3880861dac"
+                + "03a9ca1b63d5a7decb15e91958c7eaa8406291f39b4eae652d3ee22bb8ed9d2a9be9c97f"
+                + "393c5cf335d5b7775733df6f9adedff0e882a52f04079418fe0869157576e4f18cc7abcf"
+                + "a7fbc84629801b4b54af4dc3c2a9e475b5b4b2ab305ee0da237073df4e59ad3f11656912"
+                + "f3ae92574c07d41853244e91da13c98fd574c83fdde2b62a3e5a84d8904df8a21347ffcf"
+                + "9e1d3bf96c5d25c12087ef94977b91fbe6486d6efe219b5939a587643ebba464258d2ff9"
+                + "1e34225182a3f8281d43244860d740693ef88013982f41b4f8d4c971fda2b2386a84afaa"
+                + "37c5357f6555aaaf03b5e85878d917961eda027dc339e621e7852b143f0616f91fc7cf60"
+                + "cc178a1abd95b5303f4dc6bccfc32302f5c6c8b1ae6e4a532f2647394b720a233d5eb863"
+                + "bbd4eb9e74be2f802f7daaa9f76cfc1d9d0ddd675d3bb790673105b4acc2c9a6f2e95f2a"
+                + "d01a42eb63480f6a37b716cc76a24627e56b3d48ae79d858b03e1d31cecdaa95aaf96822"
+                + "bf355dad141d51b5e65d8d93e4623e1b0e459d005e4516539ddbb80bfad45306f7079f1d"
+                + "52aec4535af26ffdb3c1f49e4b436b3e98dbee3d5eeb89b97d9eea318f903d291d286cf3"
+                + "3eb88af7b7deecea0a0ae844142aad00a5cb6dd020cd5fa1adf890d21936a6c7add54005"
+                + "778e720e8002fce5ff01e891b97f00459d6e5c7995ba785b3300ec6dc65e90c5e0bb3f76"
+                + "abaad636e0e89fd8818a4dc647dd146b3f6b0a1c953c4cd9a8c98dfc502a733b0d8baacd"
+                + "9faab6dfcbfee2c82aba9bd0fa3922a8d8bf56979a460e63733c4834929d2c9d452e8b4e"
+                + "2e362a67ffacd43136a1086f3b1b1a91ef67b6f317b6b78a3d1f6d3c635444064faeb218"
+                + "3d591d0d042ab38ae354ef382bb44f8f97cdc3e02292d0e02ffd5111b39467570dcd5d75"
+                + "29ee20dbc33ad51cc9bee34767d9ccd642e846a896883cb79a9073b57f7bd72a3d23c2b9"
+                + "b98de0834d9a6b650ae902c1511276880a6265c5dca59423bb3079b593df17ee4b04cec8"
+                + "aaa44251ddbe150eb9e54a193d3164054c74674f037a7448d51bbd693cff4c42d160fcdf"
+                + "a9400ef8d6bd6647eb586306aa19e3d94979ed8a539f6a75cb5bf6942c9a22db8f752b6a"
+                + "743602ea946373eb0f6cf22c12851a56a6977b35a1334a1350cb4822c6b12de1d785a231"
+                + "9146e479d71fdde15958faae8a73f8cb2650818becc765d79250ff1f07a3c87005d035bd"
+                + "cc959a62aedd59d26a29f23a580a98af17e8c80ded13d12acb47ef78ca7b4d36e426afff"
+                + "a7c3a75724330e1d41be5a9e829ec1c284c50197210b3053a3241dbfbe737a906c4f4263"
+                + "66c6417693ea6c118bf699297bfeec84738acc3bbc26da53f6e102c1c1d3d688c732882b"
+                + "4f9a363e5b01d21913d2d7cd6dfe8ae667a4b4b6c9e5c78bc2ccceed96697c8d48434e8f"
+                + "b6b44006eac75fd65ac755386c6263c77a27f12002bb829e611c14ace578c2c563bb5df4"
+                + "c3dea110a8684c617a0533ce3539b8da44f5faf6a43d14e7ce71a31537817590aea1aedb"
+                + "f74e1cb6b2b3caf6b9297437d9c0974ab78d286e1bf6e4d63553c0df68bc11c81c8edccf"
+                + "989e4440f3de37cfabb2e84605954835f89512055281aa2cc60bafcea2de151aad2045b9"
+                + "6c2fa77a2a2f1b95e13d088da1566567b7e5f24539988a27337b37c767b702970f4214f5"
+                + "4be7c195ce3a7057672f0a372a0a3bd28a4029cb86a0",
+            "028de6552b83e136db31735a58f0ea3f8c39292ef79f24f3992401fc3e6d318a8c5a592a"
+                + "e5eaf0afb562a40ae39a2bbc29e76e194b841ca535a2585b1246cddcc715a7147112a72a"
+                + "530de4bf95b38910e2ba305b225614dbe868930e65b6d27639754ec9f5a97a37844bd5c9"
+                + "07779b32df0ef7dc5a42bf3b282f3cc3c13d73a9fbf1cd982c692df8d8a11fdd893ab923"
+                + "e0353360fc62ea5241baa68d686df794b3676a93380d963cb939aff9498cd9a60d7c4310"
+                + "44a3c49cc232114c8f23c38df8f1a876d39e2ca168589257315946edf9cf5b59f5cad21f"
+                + "99092c4a601836c70bd845276c7d5d08b8237c3a9180fd1053f60cdb8c0eb595e087e0b9"
+                + "e9a229917c74598967c48e665ede8053a978d96fbf3965fe597c08dc1f780b32ecb482e3"
+                + "8e0550a4d964fe2197abc9473075a8a83b2a901ba07b08c0f4964fe7f391d4717f7eb026"
+                + "28ef408033ed34ccae77f976fd00ab83d3b2561325b6b60e270f2c284a0080d29bdab05f"
+                + "e411d66aeaeb22cbebf9ee12a74a22f59448ce49b82de84b391277e61483a82e8a164ce4"
+                + "684360ac641f24e057c54ea65a19bc42b6477e871ff2cee37e42a6e2781da60387babc1a"
+                + "a1acdf99f51e0eae0004c9577d7b16e9a58426c2c73ec199509ff85740023538d1fbced2"
+                + "11c3f48aee7e0d5150a173c24dfbdf3cfaf4c16ef5f76b05b0fae7372cc0ed64a9d4b3de"
+                + "1199c9061f8c968a2fefa7e6eb04669d6ef88316c511617e8de4350a507dc337a725c3c7"
+                + "c605f1b829235ab9599b4a8aaa96408e3a76ea29988047fa25bdb25da193bdf019f16fbf"
+                + "6c6370bcb01e74127060b826335e7068879f636b359ed3de1c10676991c1cc9862893866"
+                + "139f0acbabe69b362bd4277962d567d19ceb42180c112c129771c46ed3ad810e1f73589a"
+                + "a9205e9898d82c9f26b3535301917cd0e8afa6a2b33763d3d5cde2b5ae2023b5bf257585"
+                + "7951398809dfc1b21d99aa492c223cb3b95991ddad59848318b4aa93685158ebab5529c4"
+                + "d2adcb55a4230e4d4b1206cc06f88f307d49a7e06590cd98b9cbf09ac1808ccbfb7d84bc"
+                + "0e779363df9b6e76ba60f5df8346f037669d4d59b19784223ec42db03dd76ea79aac36e2"
+                + "51a20a486fb629aa7576d5f34327564f3162e38691087b9465fb1dea92829375ccfd63db"
+                + "377825da9545d43101d85cc8cc5f559e82d57702c8c454eca11f172e657935ce269f4b2a"
+                + "86cfe028be25a8bea615a1f8768c7d770eb8d7813cacaf4258d3baec0f83c257249bd214"
+                + "f8572e69a3dbe92503b41d2cc873eea3e68eb649bbb609f5d22cf0450311efd92cd1d533"
+                + "dd27b92e79d4cb958a58a5792ddc710b094c80ce6cdb5f6c53dbd1ee4db9a14cd8a9d7d9"
+                + "5b213611fdb5706135c1c1adace4ffefc61a471abc08cd97f87b0a5609b3a41a788afba2"
+                + "53c5e75807be357efef9f39992cc1c978c38d8904b028b85d98371abff01037076f401ce"
+                + "63014354b11d0992abf65b46f525a14980d31e50f539289243e48a0e20725e81bbcc6683"
+                + "22d40c617c4ddece75dd571ea47cb13fdfe71ab55431ae55a0553b4db069427e5385352d"
+                + "ed2d43551f0dc6b62194c66f9a9350bab90a9155786e962f9f1783156e43cd52a049a7af"
+                + "6febd130e355867ab96060eabec3e9fd54b0409177c0c9de6d9f08fc52439f8b3e63f901"
+                + "78ddceb96976efbf56964536c87477f395db5cb0dc259170c01a8acdc80eedfedb0c1338"
+                + "4d2fb6b095ed528ef1fccf8f9cf2d040220d502828b677053c40a7b5138275c85aeaacc4"
+                + "e4ed3434c72acdb4076af2006dbe5557ba3a08c0a3a0270a0046a616565aa3cb69912b2f"
+                + "c1808369f45b3a9e5a1d90651a02c8c8072c40565dd294f86e9b53c301acc1ce5a19d49d"
+                + "caa6b5feb0ee046e06442431deed4dcd1eb184884d4d09bc1aee883e476f48cca3897297"
+                + "f3aecbab4bb2b98e9b542deca28deee98d0cefe17e313b5ae362e05ba4dd2110022525ac"
+                + "1f6bc3631bf2626662313ff9c698da06b036f987c943b3c0629a9de9b029221fd3982aa8"
+                + "2455fda07a2efc5871716d5bff85943d9f61a29c705afb729c9976b8f5a165267ce5783e"
+                + "b600ca1e1ed19a70ad47a98812048cda0bacda753a55339b9b411a44e2694b026e33035a"
+                + "a90232d9dae1ce06e8996785666bc4b6a82b9fe4fef1c1930ebedea590f8e1c1d82d25f1"
+                + "718b21a1e40fcdc931a2e911f1a0bc4895ed0bcc5987c8e58f12243bfaff873a11004d06"
+                + "4013c3e6c9306f06d78a0a260d7d0f008a7a3cb24fb14e9fb8f699f80a76d846662dcd12"
+                + "4a4cfe6ba2b39d08e6005119d50b82975e4041ce82448ed43bcb4263721280a07e446192"
+                + "1fb3f85d89f64db6630087f39d49eefea0671bedac17f2cd299dfeb9e7b307ede46c64fc"
+                + "6449c6e100cae977c34c0b79ab3f6fb89de4c9f4d12d6ac38fb7fb5b0150aa17138c2271"
+                + "7a099c8eb9246ecd6f9864c5932576560bdb9d436ab4f85befd00c42eb33f8083edeaaed"
+                + "e0d7626fa21191f17b2d29ffbf4cff4fdf83cc6361c4c59d6e23731f8684c608e0c2833d"
+                + "c2bcc3f1c2e335e9998b5fb85301bfcbf6a9d5bb4d7521735ef93afdc89ce4ce64f8f236"
+                + "5656d07969846d81c32b18c52655477c0d9fd996ca0c38f1173e1c776497722e74900ed2"
+                + "a0bbee050d14a6aacbbfeb8244f62eb3a438ddeff474ad6157b9b87af9730835aa3d39de"
+                + "834ae35a90846ed2aded25a98595b1319a5a7d68d632ded46cd390f21ccf9f47ad308ed2"
+                + "6e719703584d1cad159b017ba03b7a437b61c50019084a9bbbf546752fd6f14f28320603"
+                + "f6f1e2d8adfcbcbcd99d49419e9d6d862ca87b897d18cd53c3aa3ef0612f49edf3f5a2e6"
+                + "4d828b5ea9547274c5c4b61e09903e7942fe8f2bac74df96fe1072089bcb651ccd06a478"
+                + "438dc911f3a9f4d20c212cee27005767cf85eba9c790f484e03a177bed24a8db6c394f90"
+                + "222313535847518bebd9a27a22097d1f7d86732437cd5e6b7ef4f9745a01accad6e6809f"
+                + "94c95ae10f28da707c81ce9206b2e61e5f6fb6a7a199e042b2de99c259eaabc35f9c9f39"
+                + "ccb36684084ad105eaf74930a2439090b3e8c80e8c5ff631c9b36d32498d4946bedde9ce"
+                + "59dd929b48519ca6cc6eb36b489bbb39a649fb0f9504f68bb54096b3f8514ac1fb81b51c"
+                + "5bbcfa290b221a4ca065098eed104adde8e6f7a7c91fe6abb51f715e115bf121f25635bf"
+                + "e695a898de21c9d4f7d5604104ecd7fdbff317f7e769033f4dbe7663923b4a6c8c909c80"
+                + "7d2a1320bffa3314f40686b582dd82088681503fe91687d3d6e27d47cff720a29380176c"
+                + "1659b65559ef0afc37a914daf56c083671ef8668e06241369656656f916cb4e74459513b"
+                + "a7c0cdaf099ad6cb8ae5cb765419036886cbd7be1b394a845126e3a58f6c25063bd06198"
+                + "772ac2a536cbf8aaabe69b906836dfcdd5427e18189379dfb235a57d27be2a2de2190469"
+                + "b9df11a0be6452fc7187ec1ee5faf70a9b8ccf921255040f53c6b1c5fc31bf48aed2675c"
+                + "ce010d58c8b9067ece86c6a218a982d89411de3b4aab867c42b616d994ee50c3644b222a"
+                + "3e89c4f0d265de338e1c59c07be186139858c33146f95091ce41a88d79ebcf54ff6f4e98"
+                + "3ca2146b8f5dd62807b697f767bd3289795dedeb74f9bb361ac904a35c131a3e038307c0"
+                + "3802f93e9e7cb6fb5fc84571608af4939304e02fa3a884350a4d211cc3f62f3cdb1eb36d"
+                + "3e1e974110fac593153cff215a3b25c4f75c56ad7c92b1e4b14a0db4b27d780ee4eafe15"
+                + "3818775e208789c92706bb1994ded148e1858f20b97916edd21af5fc370a4238b971043c"
+                + "527d61527bc12fd86bdf99d393378cafd62ac57c3e8702990126e609928867dfb0e15060"
+                + "9be9f3cbbe22d15f77b6a72b05fdeda94af4bf531d726b0f90e652f9839f87576064282b"
+                + "3b44cbc9316d8ee5862c430feb936d89ef95b6ba3d29a9636e8508b130e88a563007517e"
+                + "f335fb6dbd3f0c382b011107fb4e59b673be6e3a36947e6940a4602b06d2161b12b94b7f"
+                + "8f0098fec17580ba843b0eda44a67b429f37ef19b056135c58f7630a140de52f70485957"
+                + "ddaebf4b9c73b535c7fd40243dfc620bddfb9f47b7b4f12cfb22c5d0028ba6a04c1c2754"
+                + "797737f4be2617de02fc92c283680740630fbcde2a77b1dd23ceb3b6b0d8473d8e03c222"
+                + "ef97c6008fb56a0661a6d1d2b040e54896b06fb5c3675869bda6a09718eee0ac77e0519b"
+                + "0461e9a1ea7b754869af7dfedc584d10449a3bdf3b95d4cb0e03f9eb48e3b1dca628fdc0"
+                + "a3c2cb3859d4ee4866450252604184ca08569375371725d6d14629a9df79670539a625f4"
+                + "ef39c2efdb54f8555926e18651a10b5280e25a94b5b93bbabdb3fbca8d697b74ecc8d15f"
+                + "5224af8ce1331d4c77552d9316ab05ff191fb2bc8323c7f1a5a49e8a16b9aa9413604a79"
+                + "41712e2a908f2adb4ccfdd0327584dd6c723a88e497da890489c4c2c36860f999cfd95f5"
+                + "a2f6ae99ec4174def7f36f3f877eac6fe6c74087372c8f936d161a2927199aa3918d3a2f"
+                + "c3f639139eeecc9cf58b61984f11b3e35a42de10eb88cd42843077962a1d50368613f1c4"
+                + "fc98b8ad20b7ed423feeb445aa54ea3b919a2afbf19f115c19c676ff33c8323419f4c292"
+                + "81a44066587e53045a94d2bf93248bd0a9b87d4eaaf3ac9eda29416b947696e25486f2b7"
+                + "0922fb34234aafed17d75ee3d61c70215e69662d1033010cbcd791cf5576fbb3a155f4bf"
+                + "6477632f7163f306c5e19c2b181d47e04a53d29f62c0087a23f9bdff588ef598dedca29b"
+                + "adc4f9cc7b17294129e217584303bc70a08ad9a5e5786a370dfec05b707c75ceedf2d035"
+                + "c677436a091466a3faf8699bba63f333e5398f8fb0a6a15630b2ab3ec084155d33a46b79"
+                + "51167d0ddd5350fd89fbae466ff9cb280192d015f956ca8b27907c7823c1f7e42ca321b9"
+                + "7c5b2e9e346ab008acdf8a5035232fbc6b0849ac620ea0f673630e1ad5929e237363eb14"
+                + "be422345e392cc5dc2948e4582bdf1e707970b021a026a40ddec7abd0e1bfe4d91f4b1d7"
+                + "b4987864d6a19182128c5864b234e22019eba178df05508840a724e08bb8ce83d4fd5516"
+                + "ef77ef2e5c82f8ba50e53bf4ede0400bdf4f7ea0a71409057a98f07084926778ef6d44ed"
+                + "be31fe2abdf0c18497e936e7aef40c62bb00389cadc533d138b792b978b2194d0538d0bc"
+                + "81f4b53db8d30fac0d7200187bc6ff06fa30da7848b1ff504dfc44ddee1fbdf1405a51f9"
+                + "515ea832ef7b20d3c28cc0ee0b1281f44798c8e0f2582056c925efd7ade4201a45f04087"
+                + "5eb5f1394109304805a3351e49b431a8e36b563d93d81db8f2658a1850bba23e01e3926b"
+                + "4762d92eeb11e31e6c0a716500cd285b2d5de1f96e3ea5b9e7d7534ce2c5e2ded8f43ea3"
+                + "641a1c2e2624fbf7cead517168a9e3b17bf7c797f0aa5e379e74e714d0f17b9d42f52539"
+                + "1b205755c841189e2aa1522714784064a0b8dae587993f904681bc832fcd99cef9acdb7b"
+                + "dd09015000a584a39dbe73878b9f0cc0d4d468b3d37cb5326c0492e54faccd96d5c22ca7"
+                + "d88fe6ad2a1b751ee7248d45fe79df967e8297a0f41fc278fa6f68ecf3b83b0756c7b50a"
+                + "e6265599997449095f6cc9916ffe6a2896e50e06dc4b0b3bd30617019b4fbc72b8af9394"
+                + "166c275a554abbfea02b8d75f50bbda22b5e5440cd8e2b94dc74418ce0481b8a2a9ef7e3"
+                + "6d4da37f2a1b4546e081dd1fe870f16172a7e1ff8af70ff31ed86b555ccaf6fdacfe28bd"
+                + "bf03e3b65134b7f855f9b82738e9506a46b5acc67c03fc0d10a8719e3db057b0eb08e05a"
+                + "33de1b1348c90296dce4ef155a325a99ffe6748d31bb44ce010cc585b618b23267cf637d"
+                + "128fb154d5184891f1a3564e9098a4335192dc472185c43b8a6019fef385618a8701f554"
+                + "cc4bb5b3148b72ca8cf855c0b43bcce4a4caded4ef349b7e9d6c8122b622a4bb1ba924d9"
+                + "dfc956e8020600e81561770212e8ae88379e617dea3186133b8cfc5d0700f83eeb49ede7"
+                + "71b7abafa0bc521d3deaf8d0ae4f39e818eb2f81f5200e7e5284301fc0074429eedb9849"
+                + "0168321d35ec66573de1cfe6262efd081adbd38a15de3cb6630f3f7573fd5a6abbf713e6"
+                + "f4661a18f2f24b5d2623c21443b7df1578c216de1563ab6b9f336cb2ddca8ad25abd2092"
+                + "96f6d0cb5f6a31103acbaaa03def743152b307148cf3512c7f9dd73553eef9c3ebe8afe9"
+                + "14588b4f8758c002af50de21532ccd5334cc54f667ab219ce2f33e9e74c19fd4c6bf3400"
+                + "f6243c160989acf899001f25be8c4fe9f07d00cbe12c1106d9208ce35203f69c480ada37"
+                + "8bfc9cb1d594b08072d1e7513f378bd5f9e63452fa1fc24383a54b43501238bc5bd18e26"
+                + "d05a85743ff819277f2db2ac2ef334b380dd02caf93bde7bd0b21c7c6d5993613bf96a98"
+                + "b900799ad7af1bc35697a577716882f5eebc401519643391259962b8736a1a2ebf7211e5"
+                + "6a5f64d55652f5e6d3ec6c935dae35f8e1aeec35e107f03fc5b6954ffb7bfed94ecb7c34"
+                + "61743436f8149c9e236a3c99ac05fc9d9539f3b3dd01b837c0880e75aa81ebcad5e1547b"
+                + "f9238bd733fea88af5a4a512fe68348c111f1beb08d634c604244d5fdfbc41c5dee448e8"
+                + "d2d0b1d636dc36404080627c45b8bb99f1440bf59e78234a0925b868787787aab0c0264e"
+                + "257747f7b08dc5bea6527adcdbae9d4296241b459c84a9bba22322f65bef658331ef0290"
+                + "bbf6c94890499f094d2f8abc23b201a50a81584c36d819a5fc8de9a3a1069f3e324ea3da"
+                + "97282157192cf42e99ad6af8d22a55d9fb13458195178e90fc9d180c7eefec1c7e78b007"
+                + "51c95f74073ec83b3155521ae6663ac3c9fb0ac646e35e3e10933c8a044796fdb75656aa"
+                + "b3ab4a5fa4a0d30eacc31da672f4c9f08a1dd34ffe51e6c954e9203dde766d0301db1757"
+                + "96ee6fb835708c6d836113e6e4ff40357f1a4186062c127124de0c89d5f726849a0d7f2e"
+                + "c7a397053febe5d19b38df1558b7fdc3b30a817016869b92b02ef81826908e4f313b499b"
+                + "1af66ad485b91f6bc1c50150327908fb7e2591152a3e18d17622a026012a616e33b54431"
+                + "2442797fdb42a54eeb88b61b17ad0572f02138470bfe18907dea0b30c41a65f1840cf340"
+                + "1ce41bf0e356dd4477d1b675f944cd17c823404270b76015191ae91f2eadee2fd0d05a07"
+                + "2d0d40b16931b9bbbd4d245f3c78000e3074a97f6b78c4477fb1bb40e8cd7b3fca65dbf0"
+                + "74704eba70b3090a2c292d8d5470ee355272a7203b047ca686fc4be6c65993ee11bb72c4"
+                + "672d94f40477a4d1dd36b771a4fe8803aa5961a2c7a9f77e9a4cdada37ff724e7992226b"
+                + "52de7495e0a505e84b638632be13bfab4421e11df0e7eb22e4c1512f972cd9982f52b508"
+                + "d7fa56bd4894ca67cafbb305caf686fe046be936ebdb99772ee68e8a612a4ce8d4d96f1e"
+                + "209c1ae03ea84153435e250de928503b7ff478aae74818665d7c64f96fbac8996c8369c6"
+                + "553f5c91340f98db47f41025c32336ceac70f5b713ee78a993d0eb15da54e55fa7b19649"
+                + "fdf115e20f770268751e0accb8de5af351c708d6ede259810d6586ef9c4d5ef04aa29685"
+                + "a3499d01ad532a781ca773af5e7499c6c409df418e3be271507ef63c46bc30673e14e50e"
+                + "d0f0d65a31fac3cbd5c73c0d66fa40bd4e55df54e4410a93caec29c78c5da94dc98628cb"
+                + "e26579269d76badaf5012a4c3737c8e0adc4575fb01dfeb0f1620b07232c03906125a0c8"
+                + "0718d9e9489e5aa8d459d9d284d49995f4c2b54a5c1db2cdf32bff2278ad1f247701aab4"
+                + "ec2cc244a88ab985443094081edc97a4275c2b67b764e1ddbc8a5094a5feb7f8c36dfa77"
+                + "bd661406689f60a7ca51ebe107b65a3d23170e6b705718d452ee69de2bed8b731b757253"
+                + "ebbbc465f1d928a6a74501dd81650e64c5fa1eecf2812856d7535624acce076250d23080"
+                + "0b1286183e91adeb41ef18d7b8bd9e43997676ba87ef61c14fb8cda6289eb1ffb158687d"
+                + "8dc869dc7f0c17cad34e6a4c3e4ddbd52d94b281830e405b609cfc47b69cc32bec309546"
+                + "dca47191fa1fc447607f2367d87ab33d5c0d88952ecc501e038236412c4f2bfa14180576"
+                + "6e6378bf1a40dc5db95fa300ac455e39ddb4938d28f22243c83ba5b05d1821126623bc7b"
+                + "da81df89f70bee28372e78a2b4f5e8bc2bd033327796ab6f25757de1f90bcd852cc1789c"
+                + "f4195463d83fb8a221baa258a4d8d884e557da0f37db6be8edaff4f585097d740564a400"
+                + "a64d060ffe2e94eca3f2c3484f5c5d649092672d7593e4402d2b42662cea65de6d291f66"
+                + "629fdbb9256bdf7537510080e17186135f2609605955fb3573d3fd1899b7358b91e5e70e"
+                + "d27f72dd779e153df6942f2eff0b57b71b9badbf75c585bc45970e3cc5e22c5da83615ca"
+                + "e553d59a066127bbbd490ccf52d1938d201ae12d8b7b97c754af65227dc518c1f0143ea9"
+                + "6f7b8db769170ff65fa15f102095664f6cee14e4b6886c1ac0dd03fdbde355b845183fdb"
+                + "3186b4824a74a657faa4ab7c86969825316b5e8044fc6e3e16898095b92b598b7bd20aa6"
+                + "6bdb70eb9e0c520fd89af2fca7ad65347e4cd480c609d167236c7347306e4e8636efd575"
+                + "6683a7a5fa7deec6b6c2cf0784e07898d22abc0c89357f315108cce33d8f0003e05c53bd"
+                + "b0fe673c55a25a8966e3843fb40dd5ed9169d519a4d83542e0a12a41fb9c0ec090df9407"
+                + "11a44cf4de84510fb66efbb5e7886f726d5b9539032a08d12d730528c786fa01d43490b8"
+                + "4acd99ba4c7ce8a1a5d9eba86125b297deb681ea7bd11a08c113087f7549b7e999ff7991"
+                + "84464f45c76caacf978a069c400884851bf734d97ac7a4381cb6be627575cd2135daf67a"
+                + "385bef686c342cad601a4354190aa3ae3caff33c764522b39a4eccbf7ffc2e67dff6459c"
+                + "8a10c235167b4ad4a70d9fe2815cbcfdc4dd303d0b37c6af62c9c0ffaae74dda5dd34995"
+                + "9b816f23efe5b993d30b6922885614ea33d34e15ee5254a852a15ea6c63f050075ad7f8b"
+                + "70cfef19d8b799a6536e3bb2d3ac2b9e67bf6b335c026a1bbf22c8383a7624c5dc97dd94"
+                + "4a3d3295fa3545739cb41496d880e8889e611025c03dd38c3f5779d4a9aa4e4b299231a6"
+                + "e2ce9983e18b30f4fcd4c84f5de461041acaa8fec0750dec1215231e63d520eb9092f121"
+                + "0180a3f97c2bf20219637cdb34feb5ea2ece17e0a16855e602ee494956a1965d0ce2cb44"
+                + "ee5ca7e2b4bae46ee9f4521140bd1d90c3a3510861b392d3ef902d1dd6b8698930356c88"
+                + "b9c9ea2209e5dacc48c409c5f1a4858544fe2ab35d9279a68627e965150d74b13cb220ec"
+                + "134a3acccc47925b9bc62dca979207d489e18582233f4d18febcec1cfaf69d703506f85a"
+                + "a5230accdd04080e700b78950e1d08a60709eb150a991adb9792be2ffc5b6b173a4b9086"
+                + "eef861f7d808120409d0ff76302ea42d9841eb3ac93b90159306e74bfd6372d2e0ad1092"
+                + "e42ba5256733da428d20eb9e42688d87013fd24eaac1f812b9f812fa591642b879182ae5"
+                + "4bdbb4c3fabd4cd2c7476ab3aada0d7c1867aab5b191d4632c67b3764688a4d8bb05aa49"
+                + "4b3eff086b759926404e5dc5cb96c688b87f66adda7169066f157b6a2331134bd551b128"
+                + "7d84c42b40173a0e81c50afb0815dbd5cf416f0090523b00194aad5acaaf06de0b003736"
+                + "38eac4efc7e45bda4a03dae71c8cd2eb9a4cdf7562181cd7308813e217ffd427c30115a2"
+                + "8604ebc1d05ac09678de01231f4c2051ae04b22c721415ff181af5dc5832aae4ecf72116"
+                + "fa2a144b08a47162ff178213165bf494f988f9f476d38d33c7a5881abb2094546796e411"
+                + "0892f9b36e60870f54c607eb8622e17a0da7c6513ba16254b41f90286506fd15248d149f"
+                + "ee19dbd8143b7a5c1b6979b749b32c537a3602463e1895b8b4bd71c845dbc4ee37fae482"
+                + "5e7d085b82b7769c2943761bf30541aa9d49493de74389c17704062ccb72ebdb1bf5afa0"
+                + "cabcffae8d9d5e33abe3619a948e60de163084723c881a315efca301eee2596015ecc1a2"
+                + "ddf6f9500e899228a177c61df945321860c48e03fa4654487b1e9a0fd76631ccf6ba77f7"
+                + "4fbcd4c82d1229683644ecee743f323ab34fe43f757aca147f06e64f51b0af623bfe92e1"
+                + "c7a518be1c371dcc14702301b4b9a1ae11336ec700d678422d670629ed13ded8ea4f5563"
+                + "9d1ed183110169107644b4118cefcb3000085a56f69b375eda4097f4498e9a56e97484b8"
+                + "900a5ee81d37e8270e01ad4149b0924f37140eacee17330a12d5355e43684d1c315edc76"
+                + "6295bea0fafa982ff6949d73f60b8bdd8631f9c51ed1fd689c9cc816e0a97f9b5ce8215e"
+                + "bcfe053e394755d0fde1c36fdaffc6cb734a67c95c67a09a78840de30f6beb0a64baa335"
+                + "1ea9cea12c1a95f1c7fa931fde769873e383ccbd254598051b766497f6ce8a3d7e7fc32e"
+                + "ad2aaf1f07ca55e1b1c64fd83730308c9924f8128f96c100a60b0f96649d2448f5c09e7f"
+                + "b1757530d2fda883fe329477049340c2178a7f8edcf57103a291b0468ae363800bc0209f"
+                + "d2ab5ceac81aaf9efee7c06af19567ad6b277e368e2576f04e329734f6fc5fba3111dc74"
+                + "7025f245ccdb541d8630194092475421de4fa0300702e2df210818f0aea39c36b0e0d654"
+                + "14cd514609561b26a4e7579d7d2096433ab549cc8c8438835f9f47118552e08f1a6b414d"
+                + "51e38c9d0348ad0f5fd38b834c0f26d835d4df70cf9500da3f78bb42377918296ef2ec81"
+                + "4c38f273e0609f0ba1f0d6cb6573e6620009a467626c7b7b41d12454bfa396bf7af88711"
+                + "b1ae1484df3df7c8a654697cf39b92c73b4a963fe941fa77b169f71fd4361f847ff951da"
+                + "10b7886061b0b5cc29fc93498ae668b7122b92fc6a64a8b36605251aa947bfbd4eb11a10"
+                + "d0e14f3bc62b8b4d4d6ea96fb0c33b6075e71a89fe390c537e63ff15ec9ea6d3bf4ecef6"
+                + "58cc0b793e3c4c2e0b07f6e7d984231eb27ab7ba77767ea9393d827262fd862c62fc3be4"
+                + "20739a9e48a73e1d21aa4f2d04fb0a2df2272d388911a8a6c457bd2ef86cad97e91203f7"
+                + "3d32334e2a25b1039f229a74faf02e51207aed3f4a3424b7538c3ca4f359a312243a6ff8"
+                + "40543dddd730d13a79eb2ce187d8d7e4e0df02a5721914520404457acc529a8b32946332"
+                + "da5d1dd943b32608293cad0312289fa603ea13c90b07425393be66816f0068f46eeec699"
+                + "125e446b521ad429b4314d4fb48d54896b3fc54e5a8682426fe09d0f371f56edf7737fe1"
+                + "844c2ea56ecb4e1560215f67da20b57a352bfe61408cafefd4327260ee27c8cc17931536"
+                + "8325d4ecbbd7077a69f9a5a793dbbd43e99d3ba6ede439ad2699230a5b7247166c8f23f4"
+                + "65b0831075ea0cf3319843f294a68c3eb5428d18edb9db480261550fcd602abc82e81558"
+                + "17df3bcc7a27da23f396f75fee8f04c925471381e2af11e0994ae2f37255f04df087a38a"
+                + "63f8fc6d254c64c4b8c2c361a19c3e7cf55850746e81814f8be6abb1882e37876b3a2dd6"
+                + "5f1be873ee5a4286a87e5674ada8a6835001777e0ec8cb4a6cc5397b3fdf304ca7b44d94"
+                + "acfa9db204460f80df0fe29cdcefc3a291e0b2b3bfb82a508ca774f7a0186b35798843c7"
+                + "6fb78bedb9629ed95fe342d68c80a6a404a53e040af129ba9251557f00596d3ea41fc235"
+                + "2b5e6f876bce685fd986eadab794b1f7053ce5a365c198da730cd694287bf0a087539c9f"
+                + "f1190f0ca7db74ccf8e7779f21b244efd7068dd6ad2b74553e8c4f49f16f65ef686348cc"
+                + "1bc17442cb3541931b7193b7715e87f07f955ba2e5ffb42e0ef8804e9296a3522dbc1c11"
+                + "db9eafe2a09e7430a00c09038e6bdfe7338da64de6bd508a9d4443e417e7d72db42bba1c"
+                + "29c791c979c0c3b7bc7ec11f840d382932f1098644a5fee47132d178622d17a34a593a25"
+                + "a319a4741f47b421f584e77942d18a7fcfd13fc66e90b190b1aed180c1301d8f126067e6"
+                + "cbdfb84d6ac9ff301c9e27ba27aa8dee8b74a6d23de64043f1e4eedc6d4ad1329b5e4647"
+                + "9591c83820e79b313c9dcb74fa78f2efc46554bac072e8fe4f28bb7dd04857a9e8a05175"
+                + "33d8ecc9b3887602918a8c565de2cd71fb7efa24e7a52847bcadd9be9141088c17cbad41"
+                + "8395679d946a76ff6b8af1f279c1a150d8a77f04b8e96750d1df6372b21152ec6d38ca8d"
+                + "5717dd4aed631503b6317852c4e9e1d8315142844632448a5300854b18f011b579978e2b"
+                + "4d255e6dadca6589289b142e76aee830c1818abb5bd977e065349a4a97290d0cd36f3427"
+                + "75e5604d08b1b58bf05595cbda5cc8d92453b0bf01f37c235a3b4f79ff30102ae8233d9d"
+                + "c60dbe67be424e183aed1595ac004d488035964d84f1deea00afe83ce4c959aa7d5e0052"
+                + "ebdd5a8d6d9503bbf5ada1dd38ee93308797f68dfac50ecaca81e5c8fad9fc488d9398bb"
+                + "f0031e63da258897c8e5375128fec5f91550a17d1c912f79ed0a9af60527fe2347ebf6f5"
+                + "5277e495e82d777c4baa19108adae8ba851395b8e2b4670721eb81d42315ae0f83a05ef1"
+                + "6c7a127d809be67de91c269b5e9008c37028a4e1c25224ba91f9552926dd180ae36367e6"
+                + "54b98d2019561dad9d3f03e7dd844793ec6f88ebf1257766da1906dd22d8a6160140fefa"
+                + "392610cf0869f132ab263c6b3c4f486fc5da399026d1852191c66d9fdadbfb997567dff5"
+                + "d52a8a28c3fa71b06186e8e2a55f764949165bdd5f8f4ff5195781c63ee737dce59cc643"
+                + "76e822f7b79a81d6779657090796f92347a3610b369ef281ada03e9fdf193a4dabc07299"
+                + "fbf5a2b0f6546843a6b5a7160cd991a1b40ccfab81f866e7d0ac937fa892d887f8615581"
+                + "44d1a0f79412f9a3953e637becb2e8ce7c5cfb22ae079e67fcf31638a139c0544fb279f1"
+                + "f554708123227c9eec14c933fa99c9b9302d7c60d2c3213e43964742dba721fcfa58c506"
+                + "f5c8df01dbed51405a9f1e93ff449fa6ef8f23ac9f89ffaa6d37eb09ee1701324799b656"
+                + "b713838142970777214871d5c763e4f0a3a5b1168a6d616f6e5479856fb3a5c798105f52"
+                + "5b532fb2858eef040c5426360e6dd5493d9e803a3fbc7cc24ef180725cc36267139812bb"
+                + "8e18d741493fde485a9b3db3f245927e836bb2c6fb95cbe549e787fff9bca888d8db51b5"
+                + "43b33999f49d7dc2e8b20cee59291eb5c5091a403aef17abe5358700d4368bc135b35ab9"
+                + "d7a5077ba571067e775b8be222ae968124174a86adbb357f1623e288c114e77a87c1af4c"
+                + "8e23344b10f879d48a7383eba4d363d2697321fc9eef3f9889b8f252b05764904841c837"
+                + "e400f6ba4cdea406090d56e085c486455dcb69cb7b2770808c2207bb50fe5cce6e9a9b50"
+                + "ab698f1ab77fc2cb7378d05460a649750536a0527dcc399b359fcdd2a9b93993ec1af024"
+                + "ec734d54f1827d705f754bcc5a4b8a0f1df86e0df57f29cfd332967a1a202a8964686e16"
+                + "17429c7b4eb6c5bbeefcb68059028d78fe283669a412dff2d651cebfcd6046a4350813f2"
+                + "01fe000bc988bdf6ac33c866111c70a010c432b5f54ea48a712ce244eec83d0ead83304f"
+                + "0c6b0162af07c44d4ea0769bbd5cf2b6857c38aa692a300290784d0582f3f739c581ae25"
+                + "54c590dd87660403fcce4df0f0a6622bfc3dd3fdce255380cc5e960c468335a69711da18"
+                + "d707c84e94b3dc77f1ecb55cf7f41da3fa009851319d9dcd7e83663f6bebff140a97bf7a"
+                + "2ecb6533257e1b6a1263708caa68200c4229c3ecb03616ae6606536fc26748c3b6890920"
+                + "4a837a0d7bc3371f6dabffdc0f238d63d929e46e6173beaa730051b6c12dfaa146a43946"
+                + "1e7a990e8dfbdfe08f79c81ea468234a43fa4d89784da1f1b87675f4a9c9ab5ea6b2985f"
+                + "3b8466fddc3a3bc26de8725080a26c85f420ed6ce46c303ba3dbd86805b7e2e007d1244c"
+                + "6c4f6a29549b053cfba0e0d00b0d6f18c8b5f5f9145b412469c84d7bbe28de69d009e11c"
+                + "66a7851cb46235a61972c42dedb064b93de58a2644627a650dc16c30704e780502742b81"
+                + "6661629fd19a898621bd2267cfd0caca65e291cf580f0afe8bc3f236c040db175940bf8c"
+                + "8d7483ce748f036871c087df3b2f982a28cd4b4d5f3f6b7da0dcc405770588ab09abf0b9"
+                + "87a699545c0d9ea51c51e2c4eb81d1fec6f98f38a4c41719c4bec0a681f90e3367ea7e8b"
+                + "fcc523155c98e3af0a855faba28b158f011fb2c99e5966e4652361e5370e2f0745202ece"
+                + "f16ad544d27061d29bf3e51f0ddb4da2da8d00570855bb889e24002dbe9d29f93e6d9348"
+                + "7226636892a1c6503020d7c56e21c1d904b0f70043ea401f05384db81b5155955b7c9044"
+                + "af10410bb29f0bf90e108b3cdd5bc00a1674474ab5870810c59711692f579b66d9a7463d"
+                + "9153edd9f2a1810253855ae75639520588f65c24bedb8c5bbf478fd176c7deb9d3683def"
+                + "e5d636bdbc1806a1f083ea71d64b1cd39c4f45c97bdf0a945f71df3b875196712650ad24"
+                + "e86f6387cc872cda22ffa218a99aa329ca170b44c7ffb3dc6b51a4fe192c501eb9cfe931"
+                + "690b14ff9e593ab9d81d67091c21a549323bf719c501c5d3471beb1dc0d31959baf91abc"
+                + "e216b213bacc3614dcf247ee76b3fde5c347dc3c3e40fa9fa31431d14d9d0b7533d9a155"
+                + "7ef7698b47a5fe5936431848417e90bc28108095de735bb165791c12b33868e2bb7cecad"
+                + "95c770ab90c92b4ca84c76c59eb5837186c379570a4054b0404f0df61e61312b2d0aab3b"
+                + "e939e8b581178b000a794735f2a39dcb0f232bff7baffd427823a6341af81d8359900562"
+                + "18fb628f72bd53cb83e2580ca4eeedaa0f904e902f0e60ab3fffac10cfe1fc09971f7b18"
+                + "ab467bdf0be77f7f314421a9918647ecc2eb45927562ab8c66cfd3b24b27558b2e71cf3c"
+                + "f521d81e178e07a355928e9f943f98e1b319cff9bd6aea16b94fb9695227dd46c9274217"
+                + "3ebb2bd63ed1231c57313a188a1ca5a1318aeaf925a3117d58ebf9654c0eb24d8f4b50b9"
+                + "2f00bf8f29b3559c1b6f233af6e61cc3fe3888ececd535299b2b5ff975925a253b291019"
+                + "0656e0e5866ed51646291a5182bab9878e1d620316bd664bd95205b3aa360d7dddb10cd3"
+                + "d360e15dccb3986258ceca8d5181c1862b92931a5682d527ea47364974c5a357877acdd2"
+                + "9f1589a30a0b4132cd1986fbc01d987784cc1d22ed1143bc02e84aabc569ee8f63a875b1"
+                + "a0f5a3412665ea2e01745e8c5ffb275e8374c88883e21ac05c02d2489e9362b771de1ebb"
+                + "14cc6b5f7574cdcb4b1b8676296837cf8aa7b1afbc294d416a6b931430e9457358394bfb"
+                + "56e6726211e008150321fd6d8a3f6b2a9626e9e0bd92a6665f66c1176e2cabb0fd672de1"
+                + "65d0d01c18515b78db38cfb171e3cc8cc6c8467b772c85fd26d283744d0094bb6745a920"
+                + "d972d33162baaa8906ab6e90bffbf0174ba3a557629da19fb71bece63cef94a71de2014a"
+                + "3fa2035c2893382229e725802a6e798d7862142caca8a2e177c676fcfa20b627d3ea4f81"
+                + "bafbc12f88bda9e89a19e7f9d235e1b05e182b437a19e9772afa959aa257ebf9399b1fbe"
+                + "c88ae8168f11fa65b2a98f4e1b7c6df6d1e27519f5e7b23e7537579b5eb9f052113659dc"
+                + "602cb013a3b7a7c9e60bfda8dba5a8488f300bed3bbe9a4cb7efbf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
+            "03208790dd4a7fa1b1d28a71622db4d9c619bb2a9a7464176cdf74fd4e2cbdf23a5c8ee5"
+                + "c49b4c2f7b9cd6ce9f41018f60ca1751d8c3678d0804075999340edd9f386d06c1cfffa9"
+                + "90e81fd3f6d9c464b238a3fac707c740a3648f60754d7cf404e4e2a334ffbddf9db6acd4"
+                + "3a14013472648ce61c6fd37dedff025b725ccc78521c9aff2642a17358bd628186eed036"
+                + "62f9bff62a413e9fff7bdd4b269aeecec34441dfcb44855c444b7ff8d59524af0b59ee25"
+                + "538518b224933503bf86849288ff387b1fb6c5c7f9391bf163e159b4979ade1e419fc7c1"
+                + "9e05be265ab6e9531dcfe9518b3e1220dfca9e5559ce05cdea4a70cad14eeab42ff11164"
+                + "1dd82a21e840707409047f4d1c904e9f22b28c8972f2106b366c45917b3b3538a0b1e925"
+                + "2ab2d5d3f7b4fa408d3e8ad510539055805a9cca3352eb8256ecb5cfbc05d8771fc55e8c"
+                + "8fbec7d242a2296c90dfa1d7e484be4fdb26cc27b0fdf2d3295cf9c259eec0db756e2e98"
+                + "d83d96d1913942cadcf2bd9f367ae14bdd5153ff84aee00f9617b20ee584f6d962d97b60"
+                + "439805fc1b5c4ee93b5e3ad422e8529a7462fe40709de559115e1a09feecc82d0bada613"
+                + "be56a3671f7ae9b84a2d468718f77c6bf1c3c20e7c9bae4a4fbaa37c919ffd40bbfb4026"
+                + "ddd40dd7bb9e536b23e7eae56d56a27a42214fb2819e82e8a16ca451885a55395f6eae63"
+                + "7c62d73e53cfc7c6a4ae12928134501f422edea0e2af4cf3fd9de014dfce198b34b4d6fa"
+                + "b7e4eb90acc2e40ab6030086e09fade8b53c4b15fe80e327e6c54070fee9cb4f77413463"
+                + "0e0c1b03d4b03083885788cff249c17597ce56ee74c5e03b5d7ecc2bf100f480b68c2123"
+                + "0d4f1932acc8635e001ea351319ce2f3f428bb42159cfcaaa65a9207b5367c00e2a8751d"
+                + "993349868e1765658f0e12e92cb79dc93f10ebcf30fb7057c7314f928e8494f8ab49f4ee"
+                + "25e158d8a0292ce3e06b939668e11cebcd460e6d0c90f45c6a8486fda1ddd7dec3af2d9c"
+                + "4a3d84a8c0c9e8d57f177b6982e645c903bc4a3d3a858ab298de4444cdcd7b6a8ee3c504"
+                + "a40b1113e7c9fe75a9877000e34b8f448e69871462622d67832d9e8f062a6e9d4b39441c"
+                + "06af8402bc1060f4ec15d73a6bd2f0c797813a5ab6406778323a5b248f0044031170d8fe"
+                + "c4ac9621c03a75410939025d42308db6898e82caac170a15a5c8a19af27f338f750df2c8"
+                + "cf822dd5f1f103be19cb58e61645f4fd754ff6dacff67b045980937b2b332f3b957414a7"
+                + "9d70df39e0811788c7a18e6830f4e5c5f05d79cbe4603c3929c505e226fa0fd03134a038"
+                + "36fbc7c2decbc41f5a9ebe4c3c33de7b4288fb364b3e4ebe72b79ddfde42658784bba26a"
+                + "a9b6e0108c1319a6e0bc87c2821e8cfdaffcf7a3fb350b7bbbf79e48993227c8b9bec8ff"
+                + "9dc2b7d7a349f20f8058e63de8374c4ac670951a9ee7ff15f4cbb7d6ad6c8e296b1e3830"
+                + "090dd48805ea3cc50dceff8080edc778b45f6d07f056d386ca9bc0f7e5b875d0b577f2b9"
+                + "7f8c5466d1f92d501dae49e3cf20c313c3a9e0e8d383a0046b0c35fa768ced57838be0fb"
+                + "289312f6db13f3be2965ec4cd4e1c3562bdbfd21a81cffd353ded065cf07c07b1feb9fea"
+                + "26156b77148638eb4960e1436ebdb4b9b00a3986ea86d243853e8a78b590d70d37f1c37f"
+                + "721687a26d99f6633a3177484a6701a0d62756e2000627dddf87a581fcf44fd21b1601a9"
+                + "9352ee4e414d3085512022bf695919eab97845889d232b4dfbc1d6e79d7cbaa670426533"
+                + "63fc47d202e380f24a5fb19124dab43b8225f6814994bf9effe76b823c28c33874873d10"
+                + "4f83e60778baa7ca5317879127677a3cd9045b1ab95a52bbebb63a20b1c132823561c1e6"
+                + "b71b15239349e15807888551a21996d5a2857d28cea48bb36bbe6c7a2577e8eefbc89f98"
+                + "3afc3f41a7dd29a060f24d80585ddeca6fe1cd24df4af0a0ea7fabd83747b5244ed05f90"
+                + "c5e5c8334c2a0b238e85550ddbc0168fa49e7e616e4d21157810b7f409e2fff79ebc1a0a"
+                + "07c105b2c75f2358a1240430e67c70078eb63b8df8cb572530e306c909c064232e23594f"
+                + "1c74e4e1104ce515de8b374002f8fecee12653132b134eb1584c36d2e4110c51bfe70bf0"
+                + "7e24ae024d17e29077f86790f1a16e465b5e07a3fbc8740b66a061293fbc681c0a255ad1"
+                + "deab590116a0b654dcc91f3f5ce172502463ec7ec9b89e9d525d442279d3fe71d63c4781"
+                + "fc39f1fcc0847ec47376eb565e9d1f4074ad8af5dfb55267022fef6555ef77a3decaa806"
+                + "0f94cb5202733c0f4fe0b7d16e2f68ca96eee1af31a8143d1671c008ab8150980acad5e2"
+                + "29abd8ead94bcaf949bee243e475d844c56c2a773fb0b8940b2473bae2295a1e280beba1"
+                + "b2018af12c172391768c65272caffe7b7264327d2cc58e058a2c4b058043f3cddcf5f1cf"
+                + "18bce4db04891175df0a4e4dd4598f705532c4e307cb9568a7fa86626fb8f0c1a47f8a2a"
+                + "b901419ae127d2cc3b82e46ddb174099dc9bf234eb49d0f7c054c2ac7051f163b4d0bf9d"
+                + "99c619e04f6a0f19715a41647661a74588a26ea78cd0d12ca1ecd335cf6ca84c51951c47"
+                + "6663179543af3b7b3d0329a87fe2c07d467ee11562e226f9096f62c40fe23803a850ed70"
+                + "e0dce12527c10876850bbd41b11048accf5bc02102add5136f6308353358d54d1fc2f5d3"
+                + "c8da06d0730c9f9e019b4cd70afa4fe964b0801f42b96a9049d200d0e0abcec0415af07f"
+                + "400dadbe7d11b2ea68bef4b33d4c8ece9c4913651384365d4072394d5c380f6665cbba44"
+                + "94a5bbbf77bffba41ac451a52364917b54267dd2859ef0df061023e91bfa7c41ac580456"
+                + "bb75d48f4b85c5bbbdeb75e3b6319cc3eefcd043d3d406a530bf4706add0d991a2850f85"
+                + "f91f5faae0fc64c4e165d626643136711f5ab8c302b9d34dd6ae14819dc7680e7d315199"
+                + "63ee5d5fe34220d2e34c012e27aa831b27d6cdf62e82eabd5347d2002694802a1d4568e1"
+                + "2a1d299279cd3d0dead1a71726eef58a6554129c3efdcdba4ece03c2564185d6acbd98cd"
+                + "1aeb3044a4b62a8c667ca50427cac84c48c201fb6b27117b9501896b62e63e660622a694"
+                + "9a8c7f879fea90b01db334625eb3bd709c4245bfb0b9214a885c4b63c88f1f61af7da1f8"
+                + "1c5f6f714e1b68abe2aca2c5c7bdc84e366f7928665c3b428350c8a1a3e272c8fe59b7ac"
+                + "1fe507f717df821ddfabdb0f8ddb4cb127480c2e92c1c8ffc360b7601665c901fafff944"
+                + "8495e69a8c265177ca0442c9ae7eb59a74ae190dd846ed49038eaadba282a32c8a06199b"
+                + "1bd03e007851a8c273f825d1c8adc9dded7d6cad75ea52cf6e3d4f68860ae3fd89971270"
+                + "e8eb2ba6f5bb5c6b68659aa3c8fdd0df5d9d2e279e6a1c4bfd4ba3baef599ef372287187"
+                + "589441f6b9ee870d716d240cee816abc659c1dc62f707479a32e802fdb9c9ef222a24c92"
+                + "cea9ef261aae42e6796b26836b99f7acf53c566f04c4505e919d2e827eff8592729f9fbb"
+                + "d02ceb5d865ee3a2a0e1a0a8b7edd601dd5cab09d15ab55332af6a593dc3f9cebf931239"
+                + "ff0525874c723c1006eac826c1d5f707a9013f433570b9c09c2d085dc06d287585f34678"
+                + "73f192206ac3aad489b10e453211ef9eb42a943f7686df1c83c645c9303a21b7ef9bdfcf"
+                + "cd7aa831b06d08426c0ea9a16577368b009a2c2871994fbfdb1a9ba66db667fb9ce84ba6"
+                + "2ef5969b2c6cc07a6803ed5952b8bce82edae2e5ca85e2eb98e1699c501f8e241a82b188"
+                + "8d7529d1adb6bc84f2f1fad9588f432fef2172632c08335d7bcfe34ee5d0eabb8750aa7c"
+                + "510064f389cbf6d56c149cdb3eced412d22ef17cee7dc043d4cd424d706fdd50176a1803"
+                + "73aa5582a80bfc3f5bbda8c1a72e0d82b0a295ffc3d4f9f2c01f3404a0155c1743b69d60"
+                + "7c30f0bcf32bcc1a45810d88d7b87d380a331126a6b1f56e27ad09fef4aed79c30f9e9be"
+                + "6ac8cd1e4ea69c8e01f35315a0f2b6d957034303733ecf59b2a60cb5cbb875d83fd7a84e"
+                + "40fdb04c647101ad1a7808d79ebad1c6a4000e9b45b2b92584b9dac9827d8fd6cba39dcb"
+                + "8a248e81c52b8ab9f062b2664df9b665fb9f324cc0341813989d830f5b04d09c409b808b"
+                + "bd29004b3c92bf4c31951937dfe0e3b53fc55446f3aed937c2c09e9b0550fad0c353d9b9"
+                + "425be9dbcd2f5b45f9486c1603887f4cf5481e1b16656d5fba59bac29ffcee578e574d0d"
+                + "b062289bc21ccbe558793522aae371beec11571787ce0b6993d1fd8ce2cf97b91488a82b"
+                + "cfcc79ca776745315db1971ad913aaf11c89846c790539af6e89936fdc6096e9cce57c31"
+                + "28acd30f1e6bfd18533e5b6e499f85d015f940bc77d4e4f26c3fdf6985dc8a8dd28a3427"
+                + "9b920487feba903f903101bbad2715cef2a377f1a674fad5eb65a06cc762a959601cd2b2"
+                + "9fdaf240fa9b8764a07cce995a447afcc956d1ad8eed4b72073ae5c5cd61a8e76a5214f7"
+                + "ee60c577227c609251d9475f21ab244546bc1e63e167d8f47c9fe13a20a0cfa5e17c572c"
+                + "c5535932223cf0768a1ff4d8ea80cd4b43cf11abcf9039d6a7afb15516206cd96cf1f425"
+                + "02985d027d42eaa560801dcf1495f124370640a1b2235b260a89235107714265fb0ace06"
+                + "2812d48096e6378cac9abe673b3cce4dab696f435010ffe857c5bcccadd9683cd82151fa"
+                + "011ee7a6311529496b188c930493640d27e2000527cc451550359eb28aedbb9108a80403"
+                + "64c16c65788ba22f3a33ca37009734edc7d3d9a6b5aba33fc389f459116326dde1277a1d"
+                + "db81eaedded2db8886ae2c6fe166ff4cb3d7860c59834f9fcd4321bc2345a136045b699f"
+                + "7f8463ae9eaa1bd3d443ff92ec8e2fe311066303218ab7b1768bc304d8d0ef0b5e7f42aa"
+                + "7fc6aa182338923f5bdc9b679b882029d8bba536ad3c9b6d1cabc112ddfa27c9c10f5022"
+                + "d4b8cb9b5166ac2ffd67bbc47cf093f4af9719ead9fa28a4bbb958f7a0c884358029bff1"
+                + "d52ba249099739211ceae1a00c9be48fb12fce6f6a6de170646774e67c4bb8c2a4901409"
+                + "17136519aab7c96040545821cc1ab6ffab210830ff9b5e4eae8a96a8c042a427ef7b9a41"
+                + "e39108a8fb680197c252696c3ed6843b3fc596bdcd2ddbe4e80d03ffa4607ce5b77fe7cb"
+                + "0464472ee14b311a06bfaadb0dfbd6cb8605852c491acec561b381e32d4b5191b69aa8b3"
+                + "1bcc849e31927a806eceb18d0c3ad463927e196f097b829afc2d1c4d35e700174a3ca3e4"
+                + "c8e308df6ca519ef74bcb712f08657d79bcd0e86ac37809a28f368a58117aa10d96895dc"
+                + "ce0711d70bd0fddedd74a728b85e6123533fc52d077121cd7b78026e4fbb289c0f21123a"
+                + "e505e578c7423512e56629bc69e8f06b5addb1f72a2ee2a8a9224de18f509f3968730ac9"
+                + "6a7dbddf5868e51b1fcfbfb1bb8d8cb038663d5fb2df7085d6d671f654cc9733087b3205"
+                + "7b7c5b37e7855cd6e9c9dd15ed0ecf9f6619f36ecfd1415bf1154cfb37188ffa9922b64f"
+                + "5a457c8236d7ade468f178a23009a1a0798ec11d572984000cf20c4f62b3628f9a086a8a"
+                + "aa345d62a5c84e5028db67ce280afae26e77e0663d5c414e4d4a2bd68e66f5db03003510"
+                + "abd45f51d5775a100415842a597631f779188f4be15b1f74f0cc96520f97dff782373049"
+                + "85e11f0730bb92e9c46a4b767f42a44dfc1279da95ddad0abb0412bb9e623246dd7f26ac"
+                + "6a0c47d95badcf38f29679e5ed86611a6b7b015adf20c9635990560ee4aa2dd04d3dc6ac"
+                + "77855a9c973fa927fb2f0f8dec5bce26599ba7b1ed6d3968b7a618cbbb62b46d103813b6"
+                + "a297c710d8e5dde8aff9f1464f596f947bbcb0e674391bfceca767c9e782509f1bcd7d41"
+                + "3e86620688c55dc5bf25a63c7ef45f53b48adbd2f3280771c861291e77f9eeb196808cbe"
+                + "7355c7e9272613523b230253d23c68319d8f5f366ba51a64876a93939b9c11384cf9be76"
+                + "6b332e974df21bd205bbcceaebd6892d390753182ac0bac0fc28ac8b7025f915467cac56"
+                + "6ba1dfcfb1d816de813b69057f666949e24ad77e5726755a7ecf9c88ef691ab1d0caf1a2"
+                + "b6a6abe508c8b18b5f6083cbd5f0197a2f6f32c110693b741f1a26bf2b855ad737e36147"
+                + "309fbe0d705b83de6883ce9aba460b43a04b24290ba90ad03e0bb22cb7367b4258d7392c"
+                + "7f01c4035dcb0ed22c0e93c7ed6c6cecd21449b349438df5fb2138d71704cc5a7c29576c"
+                + "7bf6f90967e7c9039386752d2319d6319279213d7f2625aa40835126fa6030edd820b2ec"
+                + "c54bbe1091e5a807bf3089cc92bbc5fa58adfc18b768716d6d68335140e083453ba5ce6f"
+                + "2a4b6d84a605d74f226da3aac4009a540e5130629313665e7bdd3b72087c864571951f6b"
+                + "ae85f01f46498f366aced9fdd40b570e70a04d1d4ab6f1e9f80f20a3ed5aab8b9c4c4b94"
+                + "fe4ea5223d7247ba44b389132d3b44b2be83d0305c3c4904722ceb094baeddb60aef2125"
+                + "8ca61452c6cc6a06aa47f61c9916ffd51a8ad46516afeb6e55496cec4ce7d96f7052914d"
+                + "2014e54095b18c4fdc3045abd6be098169cc95a9566466eec78dcb605facd120bbe865bf"
+                + "cecd14a7fa496e2c2877d5c185b15c62c9c3c93c8b191e32410bc350d2b40a2f868a86af"
+                + "01e0d665bafd8cd7484d4e0ffc41a67e46a05a4f62af7e658557d8d25d8c489c7fd6bec2"
+                + "ca2cd7e1487039fecc136ebb2e8f9434468cbbcd2b1e3e90ea190f88f363e3ad57382320"
+                + "9f8b440c459fb2cc6474605ea4881608f5715a8be11292369cf764c75f2fb71690685331"
+                + "533347f435d55d2dac7bed0bba2e4189e255a75c1769d175514f65aa230139b052f54297"
+                + "49075c04f144dfb2aeeb369137f29b07c61334449ff6046ecfe5467d201e7d98a4095476"
+                + "cb4397018c3e3bb9f4f0bed0d1645e4cf4b4b74df589d9c76e03ac2dd1fa0ba08bed5158"
+                + "5f1e5a65de19d1a2f14b6bd7b0dc6679c2832dd4c93b1b613cefadf232d1199b5e83424d"
+                + "4b48ee73bc00b3026cea2f75240d8de829114beaf85ccd520695bebba66666022fae1ca4"
+                + "501e1c1f5916fff287b13d65eb8ccbdb80e3dea217235680d16051784dcd8965e35c754e"
+                + "fcbce50486d9930b1d53f7a67ce7215bc9b3989528c54831a7f2f05d407fca5ce9db47de"
+                + "de1f1aa13d575971af42b23d56df381c1fd3d82c7a150c972dd779429fb1a9d6613198a2"
+                + "b4df2269597fc4dcb483a66841bbc679d7ecdb1fcdd0b53a1a6e1781199e974eb18ae05a"
+                + "4c0e146af19a60bbf9a05c78850abd7c03a4ebd50b7f4395ab086a6613da80bfaa7416b5"
+                + "276c2573e105752daeb44e933934abcab1e1c0f9ade80edcaf10f635c9577b53e358f494"
+                + "58743d5cf50e117ef8f98525a0f889cbfd94f432c54524b14861e3cdb71829657b0e01bb"
+                + "9110f4d6b700193a042ccefb75af33309a64d5fe9ab1a2aff76c77886fc8f0caefd52cf9"
+                + "1d759d26e687f59c3865d5d05ce397a1ddcaf8e159abca91c2d3232d812dcc7fe4a33c36"
+                + "abd65aab04102577ed312d5ebd9c951f41369100d6879aac685d365844ebb159e9967c6a"
+                + "ebff20a554bfe4197de19b108dc7abe79bdaa54153e51eeb1ff1176e1a33328453f94157"
+                + "d0da210bd5f5fe3f2fe5ba83ccc74a75ac75525daa38d98a73a8c2981c16c78459212dc8"
+                + "8a8e46328b5deab19bcae7464551597109ad84717b76e155955e93d6f57d9236f507f8c7"
+                + "12eaf0e0709f931ef4c8b6cf3eb96296bf8532d2213b27836f89bc98cd7b0b8d4af82af3"
+                + "7f3825bbe559fb99818c3407b7be60c1ba4402c1c0b2dcebd1597891aa7210ceb0df81fd"
+                + "65ef10f66b491a220437f3e3c20bb6418d7fae5a72d3e1a9e98c9663d40ffc3b48ba549f"
+                + "49d2414e8363dded41d780c98ccbb58e9adf1287bdf92204e0a7a5d763f28876249f5b72"
+                + "468dce06ef490839956ba6d9f175201268cb54768702f58ad0dccab6e7209ef1d133d60c"
+                + "b5c73dacbaf7fd73ee4e18ccdb9a02cc5687170718dff35be84b4f350d5c3625ca389e2f"
+                + "2741dc409816754e551cc3b8d598ae5c776ae6075c5ed6510d4f5538fe24ed8ed1ce052c"
+                + "0b62d61e83b1ecd11a89e6668b9ac98f18294d1fd6740edd7cccdb83545f2c15f4198216"
+                + "bd2a4e3606c2ae731ff9074bae103fb74f4931fd079140a4fb23e0d326bdca3a8fd1499d"
+                + "6fae594bad25ce17e01766cf0668eecdcd62c72955ede5401f4085e1ae4ed7c95c54d18c"
+                + "e6e07a1477c50c8b1d227cd6535537fafc6f7887f0e672faa46d629947489005bdc36434"
+                + "c363f8991db0ede78673ffe52036f4e687a8d96cb4ffcc14505fd43fe3a747b45f973dd9"
+                + "d7a0733ec4a86be65ec2d16f2c9282630c29eccf467119d6cf01cf78b010e8c5cfc38035"
+                + "038d9d6ed1925ac15be70592ed6be4f0ae178f9be23df3f17c7c1de2e285c6d57e19af4f"
+                + "d7e34cbf956a737800b6eb80272b1d58d10773fc0a45a74e45a9fb33950955d20f119152"
+                + "203e3e09d5ceda392deea24bbe84bd08adede976f39c95beb33bd4716506d779dd2ec0bf"
+                + "66d08d48e40920e86314f4441c5a1eaa56c2e21fe0c10ee5d88bbbb77f2f9faf03d13186"
+                + "1069a3d4bd0a188df337e8430b20be94e94a130ca6a4a708b22da811dbc52cb886a7ef52"
+                + "909409eb2542ee0262105abda6995374e9eb6f8224c2409dc78a256fad06ec4676c716ae"
+                + "160c150fe88ef57e5e47966660f8dc83689acb820f86df125bc3390b005109102ec8d7f2"
+                + "5c6e7ff8b59c5082ddf297c875224287ce613f3ec64c08131c9ae19169ba16bfeee55269"
+                + "a1b14beb874d0f46f8713bf8a0ea34f50ca38b4f6da0981eaf928f129ad551d97cb2d41b"
+                + "cc3699e3969529f33ac27029be91a9693af0cc3a201d192c66d7b86caa7e4d7ce16d7695"
+                + "90f9e4060f8194bec8d220e835480c90745227ac00bcd583df1d32dc18a29b6a0a89128d"
+                + "988216972f90da16602714be090c585b469536f202dbb8a66b03a81cfd19abb0ebdc5cf5"
+                + "7047361e65ae34be1ae98fa6e9ebf8969b93ee4dd251169a08aa991baeca0b107a3f2611"
+                + "dcb03d6b13e2b744dc4152d9769421d040529f270c537ce220a1af2113715fad7b5f5cb7"
+                + "5f783bffaab75e05efbff0c3b20925ab251e53bf3f79f8de691edf79d277e1eda7a58403"
+                + "d4fed819523561efdce90ecc57d23c925a4eba6083cabe1c9481f10666317586472f5bb2"
+                + "d760dc2d6c7018b9d1259efbaf1b2909c4e58f1975a10c59f8ee64022ca5cddf8273b4fd"
+                + "0533e44b1acdadaf30b5e17293c444205db4b858f6929c1109623f09938c8d7458a5a057"
+                + "8779f7cb24e974c0fdb9a181d47d407a4e92621be668d3c860de808b0d1662394c039df1"
+                + "2384ccb8a292e2105c283de4b263b24e045e50b54817f69906436463b7560ec1d3123591"
+                + "66cedeaf6d2461c8e48e186a76239ffa3e7141b3e901384b2c37bbf55080edb7f155ed07"
+                + "f73bb4575c5d657c040f90658336d5b47ac25d808e9c15b2ee589efbe8700eacd36ebdce"
+                + "4e05fde768de50dd2274ae41c60d480e44921fa9281a65cd462198758865d1c82d2d5c8b"
+                + "13ec635b18e56a62d2db9d2416f0c7b0ec9934d06dc61670344c502dfd13a1f24ef1c3f8"
+                + "b1640fbb9da42868a5cee4512ad3726260bbe0c80d42b542059e0e007b1064f6e2409b3b"
+                + "99fa6134db2995fa89fc89daec591f49e56363911c79eadfe51499f54cf72bbde3360289"
+                + "e10284d23ce2e028f23560dfa4cfbe58cceffa234f25308fd1d28967d93e30a343691fc4"
+                + "d4046fea1147c9acbf0b7dd11a1469437a61a3eee9932dca81a2fb50a35672ba7f5b8b25"
+                + "77182e604ff5a75d279af4233684c2c58bfd99de5a28a62fa8f07f5d765097a79641c785"
+                + "4c834cc499db3da7d317117ea70afb50026ad4a6f0e8d1f270e5de83ed8f086f10929643"
+                + "e114bde2344b9baabb379b86156622ebd35080462cb493d19dde8a04b455a8a6c67c9466"
+                + "d4aacc642d808598ac362b4888267b46d1c7d91d7d17c3b5b09b3040f566be91e1675a9e"
+                + "d6a7f59e7e54594de27775a8906761c6cf52661c8b6235d45b117233a1dcdb8ccec28eb9"
+                + "fb969d785a4e829ee6be6299e070f51a7fb73f6242a6c3d5e41c6c3dec176ce66ffb6403"
+                + "50b4bf8bc489298092e9d27e44e181fe10d50f86593df008f8ba9e521b549b884933e124"
+                + "d6d3f265be66e9986ac9e86c6c903f60ecb2c62acbc1cb6cc478c54d9d5f5734625bfb89"
+                + "e8610c86a8fe5ab45a688fd1af9bfcb56928faa722f82a85e233462c0072f488f44e2d91"
+                + "994fe54d509d444f5bd1a8c0d3bc1d88d9711b7c447e30059b4438b83a048787d6bc919c"
+                + "6299bcd663e6088b27073399486927ab85bd7b8f60cee514ac276086c90ee2cb375d37ab"
+                + "823b9a425d217eb3a8d2ba2a0059101df8dcc9e4d3a82c58d2b3f96a3e0c4dc00b706bcb"
+                + "8eb2ececb209f759c9b6f674eeb8a752ff8e66c16410cae20a6b02dc505717ef5ffef576"
+                + "1510da67367e53ba0274c1379010c4d18db1bfce4d6b681bf1a66b8e695a419f6d134d5e"
+                + "c59826665e10daddbeb4a80f83c78fe92c117c9466c7c8837f66f4c57c379eaa407ca9b4"
+                + "e43fa477466067d5eef8049d72eb7c2b493ad0b7ce0c494ff1f53c5690aa65c75b3fb3d0"
+                + "27d1790a89142a79f8c39cd9f015a52bbda5fc07935f2328c46dae67ce5d5c65a10e9198"
+                + "0a71ce2910519ecf8c018f5b17029f79f735c8bf0d9c044f2455d0bb40a132029186b989"
+                + "291608e21badd68160f48dd8fec5b95b5b26d975456421502cf91c56fb5ac4cdd489c643"
+                + "e72272dde4a35d6f062a10f384ebd9a71a487ec5959cde04630678eb54294bdb835d7ac0"
+                + "ebe404a1e8edd28c5a41b4622188511bc530fbc81d26661f594d825b701085905d925eb9"
+                + "76af765ea89f93079af888d49593f913d78365fdc290698f69a3d5fd1ad2d2e5c2ae59af"
+                + "407863faacd4536beac7f8515350810940e10a92c8c9c97153e9ce088d11ec43952af719"
+                + "caebd15e02fdafee286e7a7e9e0dd027ec77cb7404bb562d5c5320c9bba867ce3c974192"
+                + "b1301f6afb6e03dc9c5be7de3e5673b1147db2eb2301f91b48995d145df254698f0c6049"
+                + "8547c55f7ef62de081ddeb42505b62d6b18d37269814db597c75fb3c6d8c2a062b391995"
+                + "c7d332a9b6f6f66ba502bdbf69b73e0ff54e8337db6f65a14d58ff2d223703cc68b3c04b"
+                + "c10ba48c32f29ba763167de9cad171eb5cc65b7247178c99c35774adbcd241b102914ce2"
+                + "895a220b50b5d7a9776280f3d4b898d3ddcb6b1e14f2702ae71c20ff29c849d874323aaf"
+                + "6bea30ae2d31b151d96f9d7bd786cbdb5db7d766afcf31cce6c03240d4db2edb7dc3810c"
+                + "364e203d233b65fcd16d4236d0048b4e6ad9994f83fc294cf4a8594cf0f696f4ac6c7b37"
+                + "8a89527119c2937a942e001d523d2d08f301cf9f060531dde931fafad8aa348ce6cae1b3"
+                + "9ecbdb1d6ea0d111ee75e98e307f60be639b8dc3f9b71a1a21ef4063b4fcdfb9ebc4ba59"
+                + "b109b0254e383d814adf65456e93f59bf4570bd4adc97242229dbe96e5464b63ddc192c2"
+                + "65d47b6f96134be0180e659c897127579d9fc90d6d34a3b720c8d340e29a9aa250984fdf"
+                + "17e9fd71d863f88e11b2262c31e913a6aa60b8445ad6ed0ef654474886a4bab7d08bfef2"
+                + "1cc19acaf41550b6b5b3202ce3eb8116ccf5992ccd19497e76dfe35fa5f6f87c48d404ab"
+                + "2a08960a65003496126cd22aeb2baa4d6ea6f2846e90533538f6697df745cc491b87ac5c"
+                + "18ac24cd8724d6b7b0aa6ef6720e9d8e937c24d096b493c28606327769f865c44426cb47"
+                + "8d993d1044b6a7ab991dd58eabb02a0de80c6b2e29520ef4d31e3a46a7ed0e5887e1db02"
+                + "2fc4fca42e90e99775ce915ce26bc518cc2b25a121501ac0694ba81d34f7991d4a96f500"
+                + "09139f77d5a114e10d35a11cc7f7a41d80ab56e366c7d6cab81c6e7281afad5232734a0a"
+                + "d7c1621136879ac9288b72baf86a55488c77f0bc557cb76b3ceb64d042192f29aa5d9276"
+                + "7cdb820a4523f768a3ed4ce8593360f454f9a471159e2b4f89018153131216a81a2bc85e"
+                + "96992e0a437e66ec25c331127e6cb578223d7efdcc84f6be2c2e93a1f93713b58c5e2372"
+                + "2c727021b56d0d3af80b312e82f10d16cb068e3825d380bb0347856e7a2357177b9c3def"
+                + "6177904c498d3a519583e32985bd521a6fda487bac15edfd74a1ace1ef8af95730b014d9"
+                + "bb9c9547490895321ec794cab19e87d2af92ef157a2d6a1b9a76f4a11cd63902fb16ee93"
+                + "29a624808218d5215d11da4e95abf6ffb6ed68055b94a0babcf43701d5537c67e9a0ee5a"
+                + "42a53046bdaacdfc7980196d570ca8df8cf00a727c37b3f83203660c13716d24da6b3676"
+                + "14019195982c4970bc5c3889bb3cc9b7ba6ed788ced4c4b132b3617ac8038902ce0fd566"
+                + "c52435e8793578b93594c8aa08882875e8c1909d48ab967fa1edec3ea22322ceb0088a36"
+                + "62138bc23a4fbb682d49e949b38dc6bc08ac75054c268e667eabb1d93ac57a951c89ddf2"
+                + "1a2bd66bb3296dc012d17256e3a013d651430b7172d4d92d047e13c11284aad7086b3a70"
+                + "9a0e4a95e5a2d7640a1aca146aac6b2c57bb6cbc933635312776b292beccdf275326652e"
+                + "3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80"
+                + "e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528"
+                + "aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d0134"
+                + "6112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225"
+                + "f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade"
+                + "7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc"
+                + "30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd9"
+                + "8d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd3669"
+                + "9e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebed"
+                + "eeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4"
+                + "fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954"
+                + "105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc"
+                + "51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26a"
+                + "df774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935"
+                + "fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885"
+                + "e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f9"
+                + "4a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a"
+                + "07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55"
+                + "d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d"
+                + "1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a30"
+                + "1bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0"
+                + "341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df486123"
+                + "44be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca"
+                + "184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb"
+                + "35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed7"
+                + "86ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab5"
+                + "73d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d5"
+                + "03f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a9"
+                + "4f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786"
+                + "229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1ee"
+                + "e5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e1"
+                + "1e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a8"
+                + "9f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945"
+                + "f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd"
+                + "293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2"
+                + "027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8"
+                + "d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca8874"
+                + "2d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c"
+                + "6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2"
+                + "bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea"
+                + "49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec"
+                + "7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e"
+                + "16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e1093"
+                + "8b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd"
+                + "7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e498"
+                + "2242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13f"
+                + "b108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e613162"
+                + "59942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787b"
+                + "ee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977"
+                + "979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d2454602"
+                + "67be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f392551816"
+                + "5cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda"
+                + "99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735e"
+                + "cb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a"
+                + "8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c5"
+                + "66dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf6"
+                + "7a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe"
+                + "012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e45"
+                + "44be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af"
+                + "4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
+            "03383f9085f61df71557736df9d4e3d54184f8bec63f85442a8d7a1381d8305b3993f0a9"
+                + "514cdab55458973e3f98af9a1be1375a8b52c2dfa0cd5a9bcfb40650861eb7428d34c39f"
+                + "633e8afc60088a9ccb93bb2116084799162e5471ec390a84178cdd09e51b5aeddcb44516"
+                + "5142e4b33be4c1c5395cda0fc61befd0e42411f0998c42ebcf55be0a8c94e7c2c5d6e8b6"
+                + "038adfae681a32c6b977cefa022610a738d9b4bcd7f0a330a5cb7bf34b2dd1c7feaf6a6f"
+                + "278384a174c9e5df1fba86878292d5d1f43f4049fcd04c796a2ade88b35e3dcddf64eaf1"
+                + "1968cf12b95c6b1a329678eb05f4bedf014d9b6f5d7180cfdd221cd44aadc58eaa04c946"
+                + "1a3f0dc03df985b9e10c6860c80f28b3fecc46cd0ce20d0c2cec18f8107c4a96fee6f2bb"
+                + "ade46b94aa0103177ffe5b45be5a94164728b8ee9b5643cf9665ef973279f10c706888bf"
+                + "319d5e8db370a42085a09b7bd5c067492b1576d67e3a587a1982be2fbbf9bfe7abe3f48d"
+                + "2a50d4e102cafa67d95f7d9239bccd78df918628668163f7b933fe13754f056c8952f3b1"
+                + "381a5dd276d1a72773a565437a3247e36731bdbfdaf4f3167550454c59e963817a5dd0f6"
+                + "a3f438f564b23ea6576c9b6debc9b61c0538387566448d2d186d11efd51c9c51cfcbc89c"
+                + "bc90229a018902da5e370b8752f4044b5836df062f74c26a562328f39eee8789cd4d52b7"
+                + "e66a507fb6f90737cb278dc8414eca01c620d8bf9dfba8cc5fbc6db10eac9aaa47b0bfd0"
+                + "2baa02524fdb36ec42da68029941aacf82d29f5b1323a0cf6a5746766736d02ad0c20348"
+                + "98e9fa73159188f2c352372226db02308225d1a427204df705576fc8c499b7e3cecbc8f3"
+                + "07a7fb019d4b405f9492deee4ac3ccd51862217923ac0acc0bdd3f7ad86093471eb796cc"
+                + "35bd92dbdb1dcf70b44ea3a4b0e9cce2314717ab181108ee8a63276ee84f4df21fb715f3"
+                + "a4cc73f196cee7b756ec996b14e0113d23c677839df5e5843599f86c29a43a6a0ecb73bb"
+                + "020151a98da2352079357d4acd66442f699ff7de56ec214e4f0846bb4555c29b76d9d8c8"
+                + "ffc83831b038bb232875a1354fe99058aa2f8d03f7e86bf510e48030234194917c1ec691"
+                + "87c5fa65d17d96ce3580ab0ac863a7ab6ef207497b3f63ce2ef08d7e1a9d0f6fc5b53eea"
+                + "20c789677f930245131f952eae43ac98345ef8c0dc2a491b08e194a06593893ab1e4f180"
+                + "51e0fe498fd882f5ceb30a3ddaa234bc2d54bc3f121f510dfb72bff123241408500a65e7"
+                + "c4b3957cb94d5135d01a95c95e644858ce9f4891764c0baf68eac8dede086e5911f21c74"
+                + "297c35ed8c7fb734858690446be0ece986e35c5ec690370a07ec582e2d00f9569298bac4"
+                + "a295fe8fb3652b83207fdfb513df2c24e29e1d8051d4b7080327a57ab21a15c877d594f9"
+                + "b93369f732d5d205717a2dc4329a262724086a051b9992168a99dc556bc8e4af70e3ccd4"
+                + "c2e3fc171100698e80e05cc2627697661ddda7f90a9a50df7432850a47fa5c28088b8fb5"
+                + "3ea8258b2cbc0aafe2af9e567f43225f91f0f830dc5a3a861052654501aabf4a9d584b0f"
+                + "77f93abb215e680fd5f27902f1ea10ff880a39bb8002a95160b1f53db9f7a7386e22a6de"
+                + "47b259844841710a656c8fcb67a95bc326852027b5779b492390ae7e8f2c75468942f2b5"
+                + "ef8606e5aa6415057781054410d639b866e2a820ab14081b936e21bec7c1118752762e22"
+                + "e0f188c268ea6ab625763946cdbc2e3ea8b37e207d58b2276f716af10169cfbb5bf653a7"
+                + "3fe6de0f568ffa1aabcc8fead9f3c1c38d4ee0dd13725ae3e10ac40d1a52ed1d8986f35e"
+                + "36370bb7141e4ffb8a81f56bab0429b887550fd86f0e537d308098a1866d82461063ea2c"
+                + "d1a6236d942000f44624d2c0b1ccc08d30b547a04cc6e55fbf860f0bf4b638e213a16e17"
+                + "5a952efce2e7c614418d24a36c46f7ce35437791790e278fadfcac4e0c4bbd332a0daf17"
+                + "c72368fc61fb046662275d7aa87ade5a7c7fc8384c90b9889c0f04724d07c3d94eb47b19"
+                + "10144b9328d770fdf14c75046360fe17fe1a4d07625389ec7d7a2b9e97c592961ff84afc"
+                + "662885172ac5443464fc23c968faf39d8cee0d24292fd261b1f1e5a79e3df1b5b52ed6d3"
+                + "f7ab0db8822b9e44ac72d21fe242050b3d72d15f31bbf88d3570e1162cb595d079914cfe"
+                + "2933ffd8f6a01fe0ac7dfa4993b281d2c998cd80ac679f508f4b6073f4f1608f9d67d26c"
+                + "1fc2e021506490e973c54d07a54f5bc204c01151e09ad0f1ae30aefc95821083f235eab7"
+                + "e2a9450c5602ce71c5e197e742fae10baf56c161096c2f1ce2fa5a793334cd97932929fc"
+                + "e2b040cdc1027484a74f901189d4b4f5deb5ddf86c9cf4d722b1888673e8150d8cfc5978"
+                + "60796430be61852d37df213623b179f8667576f690770db4343c201cd2e3ed8d1602a270"
+                + "96d2ab6d5b65f8b0900407b787fbeb51176e3dc84ed2bd0d4fbe02051c422aa346f351db"
+                + "5d1e02a2fa2e50a6993ef8c71b3e4c72c2d283b53c423b4609c91f4d0bc432d81756f5b5"
+                + "48a6de883f82a4278a7d1be74cc7e46cf524a78c50ff0b507aad48985a8eca93173c257b"
+                + "56030a8a9e07f8bef29a991ea0ee677f0b45cbac244c7a937a78872e4f4e4892b7f7e261"
+                + "3d36a668bbd96490662c9b153953c588650e6ef83dd829aa759f10683dc7997215172e01"
+                + "e135c66752e447b4a68d5ad06183047e2468abfe74aeb24f2eab6be72dc61b8b901673d2"
+                + "26d4162e1ce738f20043e3346d9861a16f6f9cd25af0d0726fcdc642da7cec294880adb3"
+                + "aa4d8cd56aa8a9e9a31feda62d931fc8a2f8a18d39ce71cceb39a0a2022046b5e5701100"
+                + "b94c8e82c4112d3eefd01292002f1ec038328834f10672ced0aa7b3f94a093050890d8cf"
+                + "ab9e2b3f9349fe61dfc90d0c1419bfc63235bb060c6ca9907162c9f2c0c1f090cf0fa258"
+                + "24dd159beed051e667f7c9471a8203abc635fe0ba97039a3216e9e044f02b32a0b0b0f73"
+                + "a5b94467a04e012172d0158d0980dbf4eeb5747235738953e4f8d3e4fa3634164b5ae6cd"
+                + "a6f1784a7fac24d37e9fa1890b26a770d150ad72822159ef1942087a7a227dee5b016349"
+                + "24eb86b7f7d610eca5ed16138ab1cade501585409d6b30bd1c18d4b097af6571d1134736"
+                + "18ed8784151df96d9031cc1f570ca36bd85a5953da2a34056a88fd7321bf89f7b939ac8f"
+                + "4a27bb0d6946f1ac24d3a4614193c8bc7ac172acee5788312f3e8848759dad00556949b9"
+                + "6e79f09945392f208eb5c4bcaae26d77b03070331b420e09d726ce9fcba4062c7986ede7"
+                + "4aef2f7692106dc4178945e33f10c42a3edcb28ea2fa1c36de05245f7de7bd8eacbf5667"
+                + "cc23d7fb5456f23862404c784a5a6612f7ac98065d9a66448b01cf8cb5e2a2ad2ab0e79f"
+                + "c6e2db723dc1a738319e7cef78c4d7ac37d5da1763aae117cc4c0afa928ab6a56ae61f5c"
+                + "f38a9cc2b203a9d50fcce22671a441ec0ea8e8dc0aac0d429f61e6fe1f2f66e7603c05eb"
+                + "c81b19d5d7c95c0c85fd61567f40e9b075105c914ddd930cca4288ce9628aace17b5f945"
+                + "31688f5dd61222c21b4bb0b486bfed796d841b056d2e931175f81574ad34aba3cd1d6d7e"
+                + "494c1e1cb78995039bd592edcdf2a23888b635857ae6211a1bce16d899a74cc365ef5a0c"
+                + "015e99308c1137ab97a3b1ad0eeee68a56d4317ed9439003901ce8c83dd30e7a5449814f"
+                + "50fc8905b7a79e1dde18e403b36b6b2ab18a4423cc740a2d24d8322d15500efe7b0c6f76"
+                + "c0d81a1bdc2a7127b327a080e00171c357ca4a2a44241d998e9fe7a9b1071d231c83596f"
+                + "0cec9d1281c8d093cf2a090dc3578bf6e5fc13c1208ce68cda1d9674cc02e582d1e894ce"
+                + "2140c69bf5fcc5523afcafb5aec4133d2fe80cb5360ee73e3b791dce7e07ba731caf7b50"
+                + "5bdd01d207ab2635ca25004002cde4b581aae39cf5b8d2469185d78c36e2b8948d82fbc3"
+                + "42fd7b654fc52cb35bce4ad269f434fe24d3f9dd54983a6c4c06f0db338b73850eef29e9"
+                + "9f2f5e1feb70de9bd2f73fb705e2f154e818e6a56096e36901307dd22011796be9f1d05e"
+                + "d5d5751aff6a1bc2ed731d5505b5d6305f5509192bcc17659d10de8d22d272082b17544d"
+                + "b30d833f149c4a0b3544d5a478ce56bae38e22b9044af4f9c997e6b9d004edb43cf9dbe6"
+                + "b49a20755d58f575f70431f083b151f1bcbcb5713c572e2bf7e0c8b611ae6d2051816c85"
+                + "00c751a29352d4eb0b077754c8879e4968774aa20442bc12359e6ef1209a23d25df34364"
+                + "1e857c7c4031c025309867d91041034b9a9ceb620768bee81e2809f04c49baf0d6179e95"
+                + "2d257fbd5a053938de5756b82c552a4a95aae87ed2f31579219598b7635942c724886f0f"
+                + "7dad53c218ec32e36170cef879d571d843ebd0e14e92272649cb0a647660667f18fb4151"
+                + "55c5ac92ab432d376fae7ef7073d8df8000202fb80b1ba261b9275eaa5289242809b4990"
+                + "038bf9d837f903030a932c17e1bb4d95f93df6f349575e061f1ee8b367d5825a55c5e432"
+                + "37054739f20fc3638734333ece03cda62dc5aa239deaecd18ae4a52a9f24d2dfe7c50fa0"
+                + "e14146537a4c155c7036dd180ca72a82927a47406e2b0684505e220e4e0f08374f1b0d34"
+                + "26fc31398c2717c5f4b6f004705187f5e1dbe0c477a84347679cc4919b560a5317e55c2e"
+                + "649481e2d3b96146758448b255e0efa9cc16e8d2f674a9267a6ef91960e995335db3933e"
+                + "4e8e42e6f6eafbeebc9fc1d4846855e2f74f1e0d97b74c66edcc0c3a0c67a412875e8d79"
+                + "bf76cfd018f10e3751aa7916275af1f96500d2d6857be7c0860916525997a4cd8daf2f44"
+                + "e44046dca7cdc9d8f0ab579cec870a1bea5ef5a3502fc99271d8b2a826475be3ea0ec3e4"
+                + "fb79b4ea0b35efeb8dd4a25fdd490356fd9ae74b377e51789ac77c2c1d180db2c24687eb"
+                + "bd9451efcf60b16dcf79e4f29be606f4c7e373eb1a240011d8c3567a1a2b7cde05e4b69b"
+                + "9487a5af41fc510da0130bd556011c06d9b4e1595c69ff9e6d822cb6a3de3c782bba90f0"
+                + "2d6cbf338b6e6636892d5cb4b10e0f29f357714c0e444c370a8402ad2615d430b0ba04ee"
+                + "857c7266ed0bbfcab0ff79bbfa1811190e03c07df9343b9009a74d711a9129df639f4a89"
+                + "83ad793933c612eecaba19939f0ad02e54f20362c92b95e9b28ae914c731dab52995335d"
+                + "fa8d5c659d8c2ffb51bba4a63fdc008a965ee2f5d2358c75799b09086882c9f0380c3228"
+                + "c8dab6be4021ae1f92599f33afe9372900298ce637c58860e3b08069fecf2aee8b03854b"
+                + "7c903625cf2e9c1268229c511fb2e932bb3952ab024085bad064a6886f13cbc265ea9453"
+                + "de9e23bba112482d512c3247b98be799ddc28ee6eef80af7d869e790f635bcaa43b9e9d7"
+                + "d53d208c97b8dcfd2a6f32ae397d73fe3d36f83dadc47d682737c851625de5bb33370e7b"
+                + "073753415a77af0b547dfe869606988deda3bef30d58b1c2cb4c9cf39a198af96be9e1e0"
+                + "5b572fcd55226bdda65b3b360561eb2a1425603dd467370c75124a162e609e8a847b1711"
+                + "a14cf1ff36b8fb7ddbbe346b096612e77c5c3a042b0bc51aae1b25e479e13e1f76d2c6c8"
+                + "3f7d383600c4f5514214c04da4884f3a767cd8945ad3434624fc120a076f3e806d95a5b4"
+                + "11272e86ddd8fcfca95b3748fc7550cbdf9dcd38f51d2efa576324c22cec800e81ed55bd"
+                + "7364d1dc88398e4cab0c6e7a2f4aba779455efb1a00c6366c151695495691c658f713cd3"
+                + "b37046d48137a6c3f92dc1e4839f4d53c0fa8addfc23fe13c6170edd2a5560b8e85768b5"
+                + "43089be7febc19ddc56847d4cfd5c7122cf5e3fb5d5f2cabc18121017077199b7680940e"
+                + "a3a309946aecec3c3d922c2186f2c6fe7fc3e520e7a3714efcd86a435dc224f01cad9bd5"
+                + "eef5c5ba1bae06de04aaa78a4629c8263427dafb02c71ac6230d45aa88751db537dd5746"
+                + "03c87f263cf297bafbaba7ed07dea7b01395128f837f17660bc95eb9715493e7ca756012"
+                + "429f8aec9758fc9e6fa8a08fbf4f2c98a5efafe0584c9fcc9f69f81981af0a301831b72b"
+                + "f8e4efacfe4e7a8578a8ac1cdcfdce4c4cb046b7c42c648ce9fbdea35ec1d3c20c710488"
+                + "276d2556d42a96c86cc5c5c3b49b3421184c94e4f03e6dd711b86a32a73770501033ff50"
+                + "d0e67490adccd4c72b186a97a4314d4e93d1f90b8cde6c4fe80d1e4cc1c48a200e41705a"
+                + "fc20c78fffab6ed403fac59266fbf51090caf65e4cb4c12c51e0d7b49af1bd1f77bb5bb2"
+                + "7d9befe1b3dd5abff9d3e82ed765166aeca221287980ff92ecde3243f44807faeb4d8229"
+                + "fa64d5f58ad699a604197503fe640823c151dc0b7cab58800c3230f8db81115963b7acad"
+                + "68faef46e0328912c86ffa5053fa878cf93345a226c149765a9d2e261e13347ba9d545d7"
+                + "c2bfde966293f37c59e3363015742938bbb756dac645215bc13419d26a12c07ab8188173"
+                + "38666d246a8276a113caffca7f24fed0a09408b69693643b8d0a4d35fb555da096f8f1f2"
+                + "93cd127c5f3cb6aa405f611eee1aada3db7102c783dc1e1a13f90edaa1cce7a9365d10e6"
+                + "6115e84b7f6386f0e57384e5cd941836c2b5e670f648a043c359cf70d3d02aa7c073e71c"
+                + "1881b4f2095754848c5274f7d0b2c7ae35c3faeb40de8337829158ed6cf22c2b9d51240b"
+                + "f57f5c3a81092aa01fb31d297b6b1ab4a6c54ab303ea3c07db6eefd05e3677cad9dd0fe0"
+                + "d23d2aba63c232bf3b6b0731e7898da46291e7ee8ccd05b56f948f03bf1170eeec714d2d"
+                + "4850c228c96fb48a969ae64cc7b324e69eda6df389728c1eaf673eb512b59f7442ad2177"
+                + "31b346da582a8b25c97494700cd4b7f326dd6bb5b8de03e0afe68c12ccd924cc5ac1d14d"
+                + "ae95eb5c276b47325bcd1627da150f9e9a1cd31c15eb42c674782cc3d4e189908173cd5e"
+                + "267cacab7f476999f47929384b6f5f87d0e02debf42b4ff57f531555b4ad81649237e027"
+                + "070f38c0b849db7107f05046a0a30900bdf3cf626112afb6f540cee9da095f4e4d7f690e"
+                + "d0c615b233e17f650ec9141a6721ae9415947fb2e444277061c1fe9f3f796b13e88a510b"
+                + "51ad0b5548e73e7d29ecd198f20cf4f6d3c250ce1678ecdce90911ceab85ecd992f98ad8"
+                + "ae7de1514d710817984b20e896fb2ee5613e91892f233843242054edffba1f958c0a16d0"
+                + "8556be15546f1e3e4f6434101ab0b067bf95f94e0d50c0cf25bea487467ac4f10f8b980c"
+                + "08ef0ac11c8263239bf8a79a0bc6e2908fa4b682fe5e462e9d5cbaeaa3a8de6b232a646d"
+                + "f34bb6830cb1329622d243d76cf471add577fa7d0ae041a43f9598a0e179af57768ee386"
+                + "12d92b1ae8655772db2a05a6825f11e6ba874de5b326410f232e17bddd09290819ed932d"
+                + "da2d1260292296c4cc9bbf8d3db26754e515b0ec8088f0438d0b58fb7e18d7443d7d9760"
+                + "f6d739e7ff13d868ac27d165c962de749b8db879bac47b33d50dd721ceff662b8de95a70"
+                + "697b3f754ce0d0d3a45c1dd65fb2d7ca0187559244fed5de0676c5646a6623f4a1e875ac"
+                + "d6f5742358edc71dc64cc9c028931298239648d83fd9a801646539927ecaf506be54a72b"
+                + "9d4477696dd65e11205c69f61b82c55a2f9ba37235e8169cddf7db311ae41ec9f1f32f2d"
+                + "46ee21a032e0ae1218761045959523c10aa2e3091a3de6b517033deb3c7f3ed98f594169"
+                + "08db14d847cc5c5184e5d3174d60b18faf07b7c2f4c803d323fc5166038cb1bb8715f417"
+                + "0e609e37a1cfd9678a64957c5878660a7d67fa88b6db1516d04292fa61130172788c759e"
+                + "150262c694dffd0e4a54004d3f7fa2863d5269ceb10d53bd04147d04dceb830578a46d5a"
+                + "617b9db3363401c5545a9ef1b969720069d5b710cd8d2dae0f16e3a41a4931062ee8d43b"
+                + "0513305a175f85ce429f5ea2f36416f8769877d996b39d8145f7abe2cc062cd1254fb8b5"
+                + "434ad5e89b42b48e60c8bdecac12b73d36f6884b1b91dcbddaaad07ba63299a1580b9cef"
+                + "d8099aacc652d632f8d08b88375d96d61644adb4652202c862b75b17b61f7510e3bdf074"
+                + "616312435b6b54e93c48c5788d74544ba195b4636eaca38a9a13456fcbd9690613c57cfd"
+                + "b97adaf95fd30234893776e559b6830adf385064e2a5631006981e1ac848e95769114289"
+                + "473df1a87bbd4d501e3a882a7b483ed518b5e5317efc31e90e2b595d4852a0c2f843c872"
+                + "d3d526792cd6e3fa4bfc9f873bed7687c463a165469e706380e537383b5ed534157a127c"
+                + "77239d0b2470e333dbd129fd2194629d707f5954e444b6a2b7d07c7b7a2c168c71c47121"
+                + "b3b38b41ce76b18781ce37bee76eea812afe9f35e29e2b3b1c40e8a15ff1a6a675742384"
+                + "08e7d1a8bc3728937c9cb0da73b57fb3be153818c73a41db6971872024ae94acb00e68cc"
+                + "9a85469b1944e7e4599e12cc0cf8dedfb6b2d8916f2101323769ef7f1d0e0eed52acea1c"
+                + "2809ea3050782e5a52a768413606ed07212afe6ee16cac04ffbffbc11536811c81187a2a"
+                + "d350be5eb06d82ef17a75df9a3d746f37fcb3bddb7401ed05e3fca749e0def202df03325"
+                + "1a2db806e7367525891a4f2a5a7d6c5de122723691893fa264a6a61783c3aafa70c09f71"
+                + "bd72176799530c65ae2225ab6334e422eb52a01ad7ac028abff2d53fc7f9604e58602326"
+                + "60a0580fbe75288eebb6ae15f78e70d1f06e56eb438b79a3acbf714b994f3900d8886fe4"
+                + "dd92a2d6087bfbe7b3a16f001ca2cf4d519fcc48720d667610a3d7598b586760e0f51193"
+                + "dad94c5bfc24d686fd02bfef706d0b3329c113196612989f0da22339cc0262cb6a464337"
+                + "a1765e9b33949b94a1feab43f3bf7083a89a2f09936206cd7bf7c6f4a32f96a75e57fbfc"
+                + "3be9fa3da3b316aec6c50c54966c8ebb848224bfa0bbc68e171a592dc0acc7ce2e6753b5"
+                + "4eefd0b11701d881a987ad4e51209118678eaa376fc1e47767b0af8e159e07bfd823586e"
+                + "c508db573c855110d3fa75df0d70b9a524220186230aa84ac2927e65b0326b54f2d7e133"
+                + "42113ba82ce070bf7fb322e58659f0447024d3e2d8e72232f8f7ca8ea99b19e92814e5c8"
+                + "1bd2c8e3f64eb89d425bc79e09e110b0af1e644e767e30bc7cc6a4231570a085460b8699"
+                + "933b9a4caae915a29be50638f16e6969faee3e4ec6b2d6b53f84af3c6be55049c9b2ac41"
+                + "2462b3886a696eeb2c249c9265bdda8d323adb1e40134f58ae1f64defc20facbe0ae8f29"
+                + "24fe564236fb1004a9604e90293418c74c2cde15af0c01512c77368233d1759f4dd02f16"
+                + "6e2dcf6d4ad04cd91161a775abf279f775cf419fe140cd4a01c00598f47eb761af1a3331"
+                + "99a476f5fb26fcc6c0489d88a0eb357e031b6c1f8fba99f449e3d33cbe61a16cb6630c50"
+                + "4d14deaf6d2461c8e48e186a76239ffa3e7141b3e901384b2c37bbf55080edb7f155ed07"
+                + "f73bb4575c5d657c040f90658336d5b47ac25d808e9c15b2ee589efbe8700eacd36ebdce"
+                + "4e05fde768de50dd2274ae41c60d480e44921fa9281a65cd462198758865d1c82d2d5c8b"
+                + "13ec635b18e56a62d2db9d2416f0c7b0ec9934d06dc61670344c502dfd13a1f24ef1c3f8"
+                + "b1640fbb9da42868a5cee4512ad3726260bbe0c80d42b542059e0e007b1064f6e2409b3b"
+                + "99fa6134db2995fa89fc89daec591f49e56363911c79eadfe51499f54cf72bbde3360289"
+                + "e10284d23ce2e028f23560dfa4cfbe58cceffa234f25308fd1d28967d93e30a343691fc4"
+                + "d4046fea1147c9acbf0b7dd11a1469437a61a3eee9932dca81a2fb50a35672ba7f5b8b25"
+                + "77182e604ff5a75d279af4233684c2c58bfd99de5a28a62fa8f07f5d765097a79641c785"
+                + "4c834cc499db3da7d317117ea70afb50026ad4a6f0e8d1f270e5de83ed8f086f10929643"
+                + "e114bde2344b9baabb379b86156622ebd35080462cb493d19dde8a04b455a8a6c67c9466"
+                + "d4aacc642d808598ac362b4888267b46d1c7d91d7d17c3b5b09b3040f566be91e1675a9e"
+                + "d6a7f59e7e54594de27775a8906761c6cf52661c8b6235d45b117233a1dcdb8ccec28eb9"
+                + "fb969d785a4e829ee6be6299e070f51a7fb73f6242a6c3d5e41c6c3dec176ce66ffb6403"
+                + "50b4bf8bc489298092e9d27e44e181fe10d50f86593df008f8ba9e521b549b884933e124"
+                + "d6d3f265be66e9986ac9e86c6c903f60ecb2c62acbc1cb6cc478c54d9d5f5734625bfb89"
+                + "e8610c86a8fe5ab45a688fd1af9bfcb56928faa722f82a85e233462c0072f488f44e2d91"
+                + "994fe54d509d444f5bd1a8c0d3bc1d88d9711b7c447e30059b4438b83a048787d6bc919c"
+                + "6299bcd663e6088b27073399486927ab85bd7b8f60cee514ac276086c90ee2cb375d37ab"
+                + "823b9a425d217eb3a8d2ba2a0059101df8dcc9e4d3a82c58d2b3f96a3e0c4dc00b706bcb"
+                + "8eb2ececb209f759c9b6f674eeb8a752ff8e66c16410cae20a6b02dc505717ef5ffef576"
+                + "1510da67367e53ba0274c1379010c4d18db1bfce4d6b681bf1a66b8e695a419f6d134d5e"
+                + "c59826665e10daddbeb4a80f83c78fe92c117c9466c7c8837f66f4c57c379eaa407ca9b4"
+                + "e43fa477466067d5eef8049d72eb7c2b493ad0b7ce0c494ff1f53c5690aa65c75b3fb3d0"
+                + "27d1790a89142a79f8c39cd9f015a52bbda5fc07935f2328c46dae67ce5d5c65a10e9198"
+                + "0a71ce2910519ecf8c018f5b17029f79f735c8bf0d9c044f2455d0bb40a132029186b989"
+                + "291608e21badd68160f48dd8fec5b95b5b26d975456421502cf91c56fb5ac4cdd489c643"
+                + "e72272dde4a35d6f062a10f384ebd9a71a487ec5959cde04630678eb54294bdb835d7ac0"
+                + "ebe404a1e8edd28c5a41b4622188511bc530fbc81d26661f594d825b701085905d925eb9"
+                + "76af765ea89f93079af888d49593f913d78365fdc290698f69a3d5fd1ad2d2e5c2ae59af"
+                + "407863faacd4536beac7f8515350810940e10a92c8c9c97153e9ce088d11ec43952af719"
+                + "caebd15e02fdafee286e7a7e9e0dd027ec77cb7404bb562d5c5320c9bba867ce3c974192"
+                + "b1301f6afb6e03dc9c5be7de3e5673b1147db2eb2301f91b48995d145df254698f0c6049"
+                + "8547c55f7ef62de081ddeb42505b62d6b18d37269814db597c75fb3c6d8c2a062b391995"
+                + "c7d332a9b6f6f66ba502bdbf69b73e0ff54e8337db6f65a14d58ff2d223703cc68b3c04b"
+                + "c10ba48c32f29ba763167de9cad171eb5cc65b7247178c99c35774adbcd241b102914ce2"
+                + "895a220b50b5d7a9776280f3d4b898d3ddcb6b1e14f2702ae71c20ff29c849d874323aaf"
+                + "6bea30ae2d31b151d96f9d7bd786cbdb5db7d766afcf31cce6c03240d4db2edb7dc3810c"
+                + "364e203d233b65fcd16d4236d0048b4e6ad9994f83fc294cf4a8594cf0f696f4ac6c7b37"
+                + "8a89527119c2937a942e001d523d2d08f301cf9f060531dde931fafad8aa348ce6cae1b3"
+                + "9ecbdb1d6ea0d111ee75e98e307f60be639b8dc3f9b71a1a21ef4063b4fcdfb9ebc4ba59"
+                + "b109b0254e383d814adf65456e93f59bf4570bd4adc97242229dbe96e5464b63ddc192c2"
+                + "65d47b6f96134be0180e659c897127579d9fc90d6d34a3b720c8d340e29a9aa250984fdf"
+                + "17e9fd71d863f88e11b2262c31e913a6aa60b8445ad6ed0ef654474886a4bab7d08bfef2"
+                + "1cc19acaf41550b6b5b3202ce3eb8116ccf5992ccd19497e76dfe35fa5f6f87c48d404ab"
+                + "2a08960a65003496126cd22aeb2baa4d6ea6f2846e90533538f6697df745cc491b87ac5c"
+                + "18ac24cd8724d6b7b0aa6ef6720e9d8e937c24d096b493c28606327769f865c44426cb47"
+                + "8d993d1044b6a7ab991dd58eabb02a0de80c6b2e29520ef4d31e3a46a7ed0e5887e1db02"
+                + "2fc4fca42e90e99775ce915ce26bc518cc2b25a121501ac0694ba81d34f7991d4a96f500"
+                + "09139f77d5a114e10d35a11cc7f7a41d80ab56e366c7d6cab81c6e7281afad5232734a0a"
+                + "d7c1621136879ac9288b72baf86a55488c77f0bc557cb76b3ceb64d042192f29aa5d9276"
+                + "7cdb820a4523f768a3ed4ce8593360f454f9a471159e2b4f89018153131216a81a2bc85e"
+                + "96992e0a437e66ec25c331127e6cb578223d7efdcc84f6be2c2e93a1f93713b58c5e2372"
+                + "2c727021b56d0d3af80b312e82f10d16cb068e3825d380bb0347856e7a2357177b9c3def"
+                + "6177904c498d3a519583e32985bd521a6fda487bac15edfd74a1ace1ef8af95730b014d9"
+                + "bb9c9547490895321ec794cab19e87d2af92ef157a2d6a1b9a76f4a11cd63902fb16ee93"
+                + "29a624808218d5215d11da4e95abf6ffb6ed68055b94a0babcf43701d5537c67e9a0ee5a"
+                + "42a53046bdaacdfc7980196d570ca8df8cf00a727c37b3f83203660c13716d24da6b3676"
+                + "14019195982c4970bc5c3889bb3cc9b7ba6ed788ced4c4b132b3617ac8038902ce0fd566"
+                + "c52435e8793578b93594c8aa08882875e8c1909d48ab967fa1edec3ea22322ceb0088a36"
+                + "62138bc23a4fbb682d49e949b38dc6bc08ac75054c268e667eabb1d93ac57a951c89ddf2"
+                + "1a2bd66bb3296dc012d17256e3a013d651430b7172d4d92d047e13c11284aad7086b3a70"
+                + "9a0e4a95e5a2d7640a1aca146aac6b2c57bb6cbc933635312776b292beccdf275326652e"
+                + "3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80"
+                + "e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528"
+                + "aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d0134"
+                + "6112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225"
+                + "f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade"
+                + "7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc"
+                + "30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd9"
+                + "8d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd3669"
+                + "9e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebed"
+                + "eeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4"
+                + "fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954"
+                + "105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc"
+                + "51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26a"
+                + "df774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935"
+                + "fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885"
+                + "e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f9"
+                + "4a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a"
+                + "07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55"
+                + "d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d"
+                + "1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a30"
+                + "1bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0"
+                + "341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df486123"
+                + "44be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca"
+                + "184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb"
+                + "35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed7"
+                + "86ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab5"
+                + "73d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d5"
+                + "03f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a9"
+                + "4f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786"
+                + "229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1ee"
+                + "e5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e1"
+                + "1e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a8"
+                + "9f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945"
+                + "f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd"
+                + "293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2"
+                + "027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8"
+                + "d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca8874"
+                + "2d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c"
+                + "6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2"
+                + "bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea"
+                + "49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec"
+                + "7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e"
+                + "16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e1093"
+                + "8b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd"
+                + "7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e498"
+                + "2242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13f"
+                + "b108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e613162"
+                + "59942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787b"
+                + "ee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977"
+                + "979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d2454602"
+                + "67be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f392551816"
+                + "5cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda"
+                + "99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735e"
+                + "cb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a"
+                + "8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c5"
+                + "66dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf6"
+                + "7a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe"
+                + "012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e45"
+                + "44be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af"
+                + "4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
+            "03e9f01ffde735a486ffaf4a881d32ba494cc7ae5e55b4756a6f6f640981835e55634459"
+                + "f96d9b0216eea50e56d1558c0c53be1b3e108589eb39c0136594742a0a7802b20c349173"
+                + "434a863e645eac92dcddccb3db0bd69370dc9a2253afa72d97df378f7a66f882aa695d4b"
+                + "4b561e185bd527910e710cfc62f52fdf53d3dc881d3f6815ddb5c4b49a327d2f69198e62"
+                + "74d19a61a666d9aa2f0c2b035b91f584910f33f9a538115c17ed80d4870e16f14e5520e2"
+                + "7abafc4f37fa1c872d0b5a9b47037a5fb7fe6e754ca05f33232c69403b71f62d97ffa39f"
+                + "f2657e829a7b9d3bf095ad1a363d50f0d37a72c6e26036a68492288b9bdced8ffdacaecf"
+                + "b46475876ec3a28b8fdd34f2e1df486b321c26d2b3775b284cd937ae1648cb5def50872a"
+                + "e97ce40c499d4687c226a7db5697260d34a2fdfe2c652ca4fcd4ae40e0aaf7bec33e44a9"
+                + "6b65f50457b332f8d5c5948bfa352eded0cfd439bb6c25fc03d10eaa281cb1dfcf771fce"
+                + "536d0ac7712bf53f84a656073716b610eba64562e51d55efef7c297a56e149b2fb2eb428"
+                + "011a6039104fb169cfbb5798d536d715c6e106f72bb88aa9428e69a2b0b6a7bd1fba54c9"
+                + "757af27a139a275b3550fa4b06f1e8e95b380be1c4db016f93896b9753b0eb660a05c987"
+                + "6c031516e1e0274c370f9ec349680a8050f783ce9852164267de5ec5ca1b4c2aa49749a3"
+                + "92abe83051897675317d5ad6bbe7a2ed5470f312b40310aff5639daf1e44b0d6add11d15"
+                + "51c1ff76f345b4d890b1ad56aad8da87a25997ba07d1c6ce6f3e4070e679310ab122a246"
+                + "5bef28685686da89612d56b0324484ae64f641cdc3ff4ce0d2c1090365dd874b6b865977"
+                + "a03d7772ff592da05f94cc7b19257d5904196daa986f764f04f46640c11d2a00f6009cb8"
+                + "9077270caf3a699e386e2a679f3f8c44ee9a62e0a5eadeb7c3fbb032440b63af69623bf4"
+                + "be092192470a8b42fcea8e3e0de7bfa257fc4c95b8ca4e2f4d3c1367a7de83a428a36de7"
+                + "328b3dd6d8c84802cc6966bb8802a12a16c1dc877713ebfb1ab653aeccbd875bbe8652b6"
+                + "47345087097d04d8577f790e599c4e091c764097036a4eebde3eb370adf2f4458e192def"
+                + "fec3555a2c11c5a568e988cbf5d2b02bb9f1898ea283013cb42fbac2f501feffa738c08d"
+                + "3fff3d3eaead37eb688e097b5c0c0018bf9f032d25ec4641fb831874be16fdac57338ac1"
+                + "822a9340753ad0e536213a87ee6384c49a14c11cbe83280db9aa37ae07a9dca0a590cea3"
+                + "99c4e23beecc6e1e1ff5ccc4d4fc05bb79f8409a1d2331bc5a31b859a41532c413132e9f"
+                + "388c8933562bcc61d27c9ab9689ce8cc821f019842bb546d651f2a68cea349f36dc630b2"
+                + "9a68f43fa127c80262314738fbdfc043ba517cc2c015be56794a8b7862325226b077b612"
+                + "a4b3b75e144c85b1d123f41634fed56b6792bb7c5c5efb0f466ca4e2b05b2a07b170548f"
+                + "7324ccf9382ca8db4aff755e6cae95e4d1c748a028a6611168169e49ce988742ef552230"
+                + "635b49c815fea87595770173c8f4a442276f070786a8f5df6c9da04645b084da2247281e"
+                + "30ad7402fa54c8d0d9283b98f04c6ff097ccf5381dd9efcba98bedaf58b0c311d136401b"
+                + "edf7ace4ce27614028a1268fcdbd4d9045bd17cf799c92896bb487e7ef79c227e3302f33"
+                + "9f1d7b6152d9257c43f2d14c37842d001fce1d8952e04cb84f7e2888d14158fdcc7f1488"
+                + "abdd392cd76bd0d9298118d8a8e6bf492bfc63e2b52265d0fa7178f4830fe56c0446f373"
+                + "43f91d89489f1394a62a5fd0d732c07905d32084912a1ed2e0ef9ff79115cee167c040f3"
+                + "9fd238090dbad4e64af6639452ab5bbeef89af933d08cff92a03d2b8a9e2baf582288577"
+                + "55e3553f8bac70b738946d11ea8a40a6820d8b9d9e0a534dea54f4338bb304438ff465db"
+                + "f8d105ed28cbd65af70e98e75cef4548128effa5f75a123051274f26663d78ba29ead801"
+                + "d8647ba13a1a3f95af2eacc559df2d220f923781b8a00695bd9a4ea4b471ce417799b840"
+                + "d713e1a34edfcdb2846136cf14a5cd98ab098fc27191857e95ac395f79b797edd9aa1ad5"
+                + "515f2ecf28bd4e4a888b4cb0912beddd1f40ce7f2ec22622710f3daf16a812381610a2b7"
+                + "595abde77cd480ec457accd8eafbf6aa1477e1f99f3498b161ce54d9d1991c08ec473e46"
+                + "c86628103399cd414e2cb2fa8d78f0c56dd0b6b242cb806232a5232853b11defa9864337"
+                + "5dfc149db9622c46d95c428b53243e4a35ec53afa845dc791fb04e6e96faf66da30b3e92"
+                + "f7425f4dcfb8eb78546c9e1149b8e2527d57c3bc0e481593e1f439d5457e2433317b0cdb"
+                + "2fbc80dded260d4b0613e88fa82a176912552c0ad6945f4d26c2e29d2aaf9879a359cfcb"
+                + "6f88412e0d2595f1158a750bcb899ecfedb07f42d59cfd4f36a3242860f017bd211dd796"
+                + "e8b455bc19b87ecb0fe9dd52c3cfc342b6a9e86365a939179df9778a4b50b2dda4227d49"
+                + "f773e60e1d81a52d5b9940903318869e5e916bd3d69a52f6e895e58b69ce6748094884fd"
+                + "f10bf6437e6ce5e4813ff37d1d8a127c4cfce3818b549a40180e9e0010d9ee5fed140c67"
+                + "6bb0c1a2c618c484864e900cf5e3e46a44da97fa60b2f7e17c33c1d9d95bfd8ee2454036"
+                + "734d16dcb731bba606354758d3e87b3cfe8c3a105e86888a90b766e7a3daea80b360c256"
+                + "e31a10f2bcf420187eeaa6b033bdcf7bca460b0f446956eb8499f3e516a26e378d40b927"
+                + "589f9d5532b88b90d79168f7e9dfc81b7cb9d0c3bc83688a43e4438866a3709332216993"
+                + "f0a6ab88864f26538151f91c8ee274685af3f4f858d83f7057a12d301a7d2d771c87f5e9"
+                + "ab889380d1916f63972e130baca2b364c5b4a680753faeab7af876234e475c56171a8218"
+                + "789d93202fbf7e05a9b72801a3bd22e55c6309f1f02c24dbd49691b95d38c7e5df3dde50"
+                + "b56010b65caf9380d05460735f2b91e18d8bcd5c086a2fe54b6bf4f97cc812490d7d0b09"
+                + "2f470bdfec096168ba1cadda094876caab9aff1541e447bd9c3323416e1606345f839d3b"
+                + "c866f8e633fdecb0af6be9bd5e2adf32eb0f50b948b68df87305ba0579183410d98da4e8"
+                + "3e6df5f88ece5703426d22c44454d304d99b451f8024631b0ddd5d51616b2c72f5a56980"
+                + "c060596192e2463a1e0288b0e6ad089a46f1a962c1bdf8052950381987957336fd1f5017"
+                + "ff37475f0f71ad39885428e617aaaffa0e61600c0bc40b6167f13ea2334f18f73e4e322b"
+                + "38b555c4dba014182e8319b80a38ec2d48f1b48b1bec2fe334d8dd44d6b053ee2a2768e0"
+                + "cda48c707a4ba95967fe32922063eed5285a538b83d5daea206a412eb97fc732446094d8"
+                + "242657631751b67a54bd7c88a1ecfbf8ecf42b6e38e56a6f03aaf3dd65b2ea405d18bce8"
+                + "b94ba3f196d60552bf525d61abf4cb1371af226415aaa7834080dbfa7b93e7a206895f83"
+                + "9cf3ea0c0ea64cd8b9811136bf226fa4003b3be34e16e28d8b11c9386f53743b3b715625"
+                + "b1a6a5549edec026c600c096e06a853dca5a5d44247812b6a0d3a8dfc59b2336cbb0ff3a"
+                + "b7edb7d72d93de31dc7c28ded2a0a5a483552d85f35b9aa48c0db2c36aca2fe4f8ab362e"
+                + "7f4602740f3062d5911c921b3b258a435bf6be26565d3793f46bb6f4c6dee2213f19036d"
+                + "510367fca6d699134709de72811af07b04c00b55d55d6670b5b6ddaa6ce44a56d5da3d6c"
+                + "c5c0b8d7b95581cb9f0d7dae5e6f6da29da60ade836d35160554593d7855713c8e7cafc7"
+                + "38dd4e803aeb45440ab66740461ddbc606cd8059d4374a99ffd59a0c5e27d61756b1174e"
+                + "c7ee1a0dc106edc04e30ce8a7a026a6445359502d112350ff9ce10f2b5e3fadc4abaaca3"
+                + "eeaec544f7d9794107d5107db86b8a87345507a3589fe1d8ffd6bac075b2823cf86a529b"
+                + "8c0f3f0a31edeb8c1c946ba9fb211d22a76ed52c25b816630b9aa01ce1d6897db26807f5"
+                + "7b82b8faacb9330b5e00c0e4ad00645306f3d299efffd2676b831db296911fd1bc506cc1"
+                + "dbf536093446253a48b52f3eec3c821fef533b23d4dea3c99e08e278236cca2bea104491"
+                + "88bbbe98c8ac1d8eee5d5e6db7a366974b3908699ff21a2b3c219db06e4d74b8e8ddae47"
+                + "904bedc3b524b5786601f9a880fd898a171681515224f3d968fc67aaae50e961c10b54a8"
+                + "e7e1fea0b0a357032477c8cb4ed556d267937ac47fe410cd341f6020b415027238961d38"
+                + "4d703f8b54576185c5d70296431f30383292ec14b3942aa880706988092b87c2c617e053"
+                + "558b1cf2bd6c7610f4728acf5d622858b85e8ed385689cb11595033bd2f511bba01254a2"
+                + "d4855dc988143d2bbdad24e115a6de729199d889fa3e61b3b64541d45957e7ca9e0c320f"
+                + "9572f7dbb901fc5f94b2d82555f3138c96bc3754edfc8d5e8f664ddd16af9d87baccd875"
+                + "6fda09eab4f5cc29fda1f55900cc1af62bf09e9de43ad2bec74746509637e82fd1ebf38e"
+                + "80344ad229644490f8a1b9c0b9abc3761da42e164b955b59c9210415425e25ea174a0d92"
+                + "c1796a7abdc62f7cabb5627ff533912c771840c578d44aaefc84214d5f42a9f00d3537f2"
+                + "d302006a116d7fed10bf8745759b4b1cb75afc76dec9dcabf1634abec8ab0c0333e65eb9"
+                + "937afc72e9a709c97f0fe7464344aca7fffc480c1ed1a676072c5248e29f203e0db8f5c5"
+                + "03fbcc19c223f448902a0b6cd7f49d61ba9597388c1e9ea88ff06ec5b7cea2d295825fa9"
+                + "4b5d8c2ab17e057477184d009149620bd8f4eac656a31633dfb444417fafb6b38c9d3823"
+                + "d30620eca4eb5809ee3cc6937f456ce7cf8af8781246af65c32badcbd1fdfd1673bebdaa"
+                + "e56e817816d5642b86ed8b0a7cda7cb6a5e576be60a2e2e211f3eac7a84c723869e9167a"
+                + "eefbeb720c25d6832b07a9b359f8436bdf482cc33957a427e005bed32b0d7dae5f7d0827"
+                + "f5581bcc14a22c73b0dbcf8e98d546052554a95f80bac9e21c177f2c70f3abb1378664e2"
+                + "5a7e206e8d24a59c6d8e0f2093d6f73baa92fe75eba36f975aa420e70384c298df7d3845"
+                + "3a35b884cd3ea34d4f60fa9f60ee11d48ce3a6e1292162f67d9030a049f27c4c0f69d773"
+                + "717d66c51bf37d8fb56f7c810cb5c1667cf3c13a7a72bba24cf3482e0fef98e1969c6af5"
+                + "8de6c16fcc5be2a8908e57f87b3bdb93fd692ff4b823dd5ca77f9a4798061f80eb392bf8"
+                + "8d12cde02706598f6d986a84c28c275424c0fc5baa7e9848b738a6658ef77402b94e5bda"
+                + "b4cfa069ee6d83bcf790967ea84a8bb2832f4e56185338713837bf486014e3d30f199c96"
+                + "01600259cab114bfbcec350347ed05a4d2ad960680cf0d2ba7a9c5367769395ada176a45"
+                + "e1efdee836a9a1a5d6440924a274dac36ec7a7389de2be0c69de33d0ea2aabc9f996aa95"
+                + "32250d141d41b0c9273b2dbcfe5417bbd1bd7937661a46e5ae3483e5adb6080443a1f086"
+                + "7036b176d4b4fdcdbe2cc410f4e591c6458b7c7d9a6a0a67c351ed8705a2e9708725aa67"
+                + "a5d58bee734cbe03fab9979f9590f3b2964d0b843eb65da51bfa3ee876042a1a61cfeb7a"
+                + "8249854179426b2d8940baa85f5fc9d77ad1ca70d95ee31843732b3a4b1912658ba3b78f"
+                + "89e944e8940b310eac6dd71f232ada67b99ad61956b6541af0a27c1368383f0a00abbb14"
+                + "e84afda650119661aff626470fde7975175d1e74d81e6adf9cfbe08821c94e438d6e7e4c"
+                + "8aa45bbecdf47cd7a623068c0d18d0b92100ad72815995264ce8c5deff5f3e970fb6fa0c"
+                + "45fe0f403d8debdb87df83ff86e121e230998f4f6358721ee6c6138573eb386df2a43dbf"
+                + "a64f9048ac470bea6294893904d42a2e4ce3cdd53b29f685b0e2bd88942d08fae980e782"
+                + "21bb1bbbcbb4f81edf4cd44ec8ac306c94901520c4b0d9b2688d3d5185e591163ead98ad"
+                + "9a8d3f721f0254fbabfd863d290ece83c1bd38084440e973a178935a4cae3effab1f79f5"
+                + "749661cccb515660cc8b3b9c30c5b7a69aa6e248322b9c6ae4d19152760267afce57703f"
+                + "090ab2ae4f37722cb520a7b67d935ae6bd8d5860fdeff97f56f14a33980bca09896a53ce"
+                + "b8c89660037ec8009fc7c5a6f2621d0c0fc4c54f307757a80d5445b3632d657055b8fff0"
+                + "eb1945e910d9559a5376c08b680b0bc1f9ac81cfd00faab560a45717d4c81137abaea91b"
+                + "244a2aa150deb507b9b2b1afeec0b14577a5653b4b51748ba07d262aef675fe091073bde"
+                + "9ec47424f55c44147f2f682b071f73c354bf47e9fc52cbf33c5d87dc6f21a4ff4cabe228"
+                + "1b3aea26659380296906e0187804f601a4065268f7ca4beb50199f1e93d6c239a4271177"
+                + "2f3f10e7395e929d1c5e3eea50781a3f58e8c49fafb519cfc352ea3ac5c43893fd36ac70"
+                + "b83ff0446c0ff2cc49eb59d833217828b3b1b2741b68a484960479b3789357b04dd36a42"
+                + "d2d8aad9a1b0c4113ef70208a36fe4e55f1c06e6ccfefb243c768bc004c1454edb67adfb"
+                + "9615ee05aa933145e69fa96b3a0aa770ee045001e03ae8393a158e69683946337c5b1633"
+                + "1581701136168235dc380e8653c6c61e060c18f70cb71ca3fa461817b5e36c0eab8389f8"
+                + "11f4bec0f65649b9074676c74343bf1584e869841497fb4a3726775c2a17f649c1f413b1"
+                + "f4ce6b897e0e1f89460a7089afe015211a16e2126b5a2189997b06effc58306e399ac62d"
+                + "b822b7724ab09711fb2fd1e348f4dbd6a544ae6a9297039da254c081eb351206187b1699"
+                + "d2adf4b240d8e415ff5500f3b6fc9f86d98fb5c1f3392beffbfe9686b78ec162f1ba5c75"
+                + "23bc95b8e35eee142d001c8d4a0bd0365dc4645c15310328dbd20330a64005e3ba9e3396"
+                + "bd80673191374558d1070edaff3238e68c9ae615e2f7b1ff32c5cbb6b70a3eb4c39f2dee"
+                + "2ded2f90359213cd99e048e87a9799f418551e5de54e764ba39e971925ba17309f25e9ce"
+                + "8bdc6926f0f26939ef35b274db38344f29ab57c21baaa0bf91f0b410b8410f0f03b5525d"
+                + "309358abe004e2e41d49140f8be0c12f945f9202fa77c9e2b5d98b535870295f99380f4e"
+                + "103954d119d561180db88d04d40cfdfc1fdda3d8546ebf828b21dd3664369fb5d46c645e"
+                + "43c7d4c144862ba370b5d5e51970db6a28488d5e3c629c788fe634fb5668baa6a6ddca5b"
+                + "3852ba0009012a562d36cec07d09b8db71ecaafa0493234edfc68e3d54ecad2821efd3b0"
+                + "0026ab1924bb943fa845340ea45f754a595998e4d22cc2057711f175929779cc58555862"
+                + "548c4a39f79440a1aee4301bfbc9cb9a0b7ab7c2f1dbd31233157b270441249a11e599a2"
+                + "07abb9e79ba35d076b2186a96497c65a12ac3453cc3cd50470525fe6c62ebbca59434878"
+                + "672edd76f4042d8e9164afef105d838966201cafbfbca21b8bc1781fa29e56029a2d3a30"
+                + "d74c32237e57b618cab48cd4f319afbdacdc074b57eedf3875e47c95c2833933b21616ed"
+                + "e97b2f48befeea2fb87b2b3ec4e29fb24c3e29b43a2047402ae1d194abc978b2ec118f7a"
+                + "ea1122f4f1d52b7fc3c942481ff3e7eada5bab8c0d0ab9599cf43105262e3b4a692efd91"
+                + "74b50725d1332475c9c7cf1342062402e7d43ebc633e8174b92df853799423cfdf189ff4"
+                + "45fb908fc16b2d7349282ea58bcc0bf28ef6fe35fcd264c139a2d174768bea8e471153a5"
+                + "63a4861c1b303c75b7ff66a646f0d2f0766949236af140985b45a18ae336c51d3f671fa8"
+                + "f56895c6aebb03d7f5ae0cb53b7fb80fe0e0aca45f2a362a3f9fb36f77e3acc715618b84"
+                + "a0fd8d42aec0dcabb4a20870b233501971dcb6e8b7930ac66489498aee4f72f462503c29"
+                + "57c43f7c01a65ff15ef36ba65167d61ae2d8a9fc9a45a3dafacd1b3b3071b78524b6cd18"
+                + "5dad49c5c27b2edde567c5fc4e085b1c68d969b0492d6cd2688154ea35f82aa1c39d1fb0"
+                + "9d097b5369c5d39973902ee47e5b267302f9923b90fad9e128cfe8613350d744a2731a28"
+                + "49d9bbba4458443ee2891016f8a4fc8e871ece944eb164c0c76263ae8080e5a04eebbc39"
+                + "5bba456939e5b82f44cef4fac69054e29d04320228371947e3156ddd0e3f702988700375"
+                + "1e3283c4d72b5fa8e0d32d3efc02928b25321070f14d9a15e2f16c2a828e3107941c5ea8"
+                + "1a3d7022def7408e55e4f3b85a2c453e2913792681d9fb3edc778014c7fca681ba60b1b3"
+                + "716f7c1e5066c9994dce7468980780ae924aacce0896fa81c571d1a69f9f822d9ccd9f9c"
+                + "1230b3a3f50933de67bbc79f1e4c5dd62996f2cd301d147e38e8279625d52e96815bec69"
+                + "7fbb12c1613dc5485ecf6dec4bcdb3b8a92e6e1736b1b72f5404fbfe065b88eba775ca46"
+                + "4036062b83c306df767b367f863725c3676b9841bc29a88e503f00cd1326d2137b9839b2"
+                + "7ba258b6923acf3b01b9eab50f76542c610136fe3d144dc9c5a035948e01bdde41eaeb0d"
+                + "e454dd699be116468c049815e492e6a2788439208ba190ac482ae033c10f44aecfabae9b"
+                + "96acd1bccbc174c2bc23483acbeb74f5fe5e57104054cb71b29ea94c1b77414f817409ab"
+                + "dda8b443e2326f26b26f4e07748d15e37ee46220227df3d290d29ad0932b5bdbfcaa9e53"
+                + "a19f6e2af9c000f9bd523ad7f770d139f023738432d14d9add2d8ad498a3cc5b6141e1a0"
+                + "4f5bda3243f7edad8b06c7298f0bd15378a1dfc514917319c0bd019beece3bf4dda245ab"
+                + "b829dfccfe6e210a20fe621fe609b32013c211dc44aea9f7cfc127c95f17e043a1b94a34"
+                + "426d1b8e2716675c0f19cc85cd0113229c86e9a7e18bc258f90517da68d0cf1625489237"
+                + "ebf9d2c46818b408fabf3dc48df77de7957e27bfbce31b4dff5385dac72c1158fe7dc2cd"
+                + "3afd9b9d8db11159942e6cb6ba68f712545e4a8c50bcd8de1650576f41b1952bfedf3a76"
+                + "a5cc931f2c42780e32062083d6a2147ad4b8f4440ff58bf1aef47daebc99f42dc497fb3e"
+                + "dd04dba05ee6a7a5327d2a1f6ef43a57dfe1655baeed25b9dcf7ae9527604cbebb753dae"
+                + "f99b12f5b1d527f79b8d9f0c4089db8dde23867ff37bd8fa776cbe85d7b5798af7229daa"
+                + "117b096de137cde04dc0a37ebb78165539569c7f2a3ecd39d14c5dab57691d73542f9e53"
+                + "53af42a115fee8e16955ead438332429874b983ca4ef5a03c7cd0f1a874cb0db95eb73a6"
+                + "31e5723859fe1a1a27bf01f198a1432b28bb0eda40034d2e5c21cb13d9d7b1588563f53d"
+                + "b2054fe32a54d4573beaeba77a62bbec0a5153702c47a2a9422b375230b52847390340ec"
+                + "05dbe18e4108ce7f57b370fe4c2519994383efed53b0ba254a80182589fa58d5201c11f2"
+                + "74f05d34cc77d3bea22359c47f1677cf12a21e72de3c53f028f4daddb5fdf7c1497ad441"
+                + "2f1c2f1dcbad6d84956d336e0922f4fdb36f7b8be9e48c19d5c4b57ca5bb85060772aca2"
+                + "62e6d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae"
+                + "890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c8"
+                + "3799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2"
+                + "716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148"
+                + "bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463b"
+                + "fa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34"
+                + "f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf"
+                + "5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc"
+                + "9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c"
+                + "86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540"
+                + "d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b2"
+                + "1bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb8"
+                + "5e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d"
+                + "8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f72976"
+                + "05d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d1"
+                + "3cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751"
+                + "475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad"
+                + "69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fa"
+                + "db2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d679342142969780"
+                + "9fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f0577"
+                + "04b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6e"
+                + "f44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a846"
+                + "39e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9"
+                + "c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139"
+                + "f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b46"
+                + "79dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865"
+                + "625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f92"
+                + "7993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956"
+                + "b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684b"
+                + "bf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2"
+                + "c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea6"
+                + "2f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9e"
+                + "b96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35af"
+                + "d8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a"
+                + "3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd"
+                + "2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c45"
+                + "6eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9"
+                + "b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d"
+                + "0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5a"
+                + "b8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca"
+                + "515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e"
+                + "4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6"
+                + "b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8"
+                + "c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792a"
+                + "f6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc"
+                + "18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34"
+                + "ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d757"
+                + "27ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590e"
+                + "f40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8"
+                + "c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c07"
+                + "23db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f4"
+                + "12fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd5"
+                + "6e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e"
+                + "3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559"
+                + "367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21"
+                + "f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520"
+                + "c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d"
+                + "47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d"
+                + "2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f"
+                + "7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b"
+                + "71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c"
+                + "87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f924"
+                + "4bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e"
+                + "3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80"
+                + "e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528"
+                + "aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d0134"
+                + "6112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225"
+                + "f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade"
+                + "7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc"
+                + "30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd9"
+                + "8d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd3669"
+                + "9e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebed"
+                + "eeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4"
+                + "fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954"
+                + "105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc"
+                + "51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26a"
+                + "df774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935"
+                + "fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885"
+                + "e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f9"
+                + "4a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a"
+                + "07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55"
+                + "d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d"
+                + "1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a30"
+                + "1bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0"
+                + "341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df486123"
+                + "44be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca"
+                + "184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb"
+                + "35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed7"
+                + "86ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab5"
+                + "73d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d5"
+                + "03f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a9"
+                + "4f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786"
+                + "229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1ee"
+                + "e5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e1"
+                + "1e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a8"
+                + "9f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945"
+                + "f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd"
+                + "293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2"
+                + "027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8"
+                + "d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca8874"
+                + "2d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c"
+                + "6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2"
+                + "bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea"
+                + "49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec"
+                + "7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e"
+                + "16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e1093"
+                + "8b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd"
+                + "7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e498"
+                + "2242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13f"
+                + "b108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e613162"
+                + "59942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787b"
+                + "ee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977"
+                + "979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d2454602"
+                + "67be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f392551816"
+                + "5cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda"
+                + "99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735e"
+                + "cb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a"
+                + "8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c5"
+                + "66dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf6"
+                + "7a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe"
+                + "012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e45"
+                + "44be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af"
+                + "4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
+            "03fe19e89df20c2fd4b7227948357829765b81d88c2212de4197d1700a69b3abdcb5b02c"
+                + "adf7e2df91958c5994a9a68b5b47158b77dce08b3419a746561e3eaef652b628c20a173e"
+                + "f1a21560c8384725bc7f3ec9abf6a6a433ea54451f15a7fd5ed08c2a53d95e4b51eaf54c"
+                + "ac4260e11b98cee8cb185af54122f699321acf0b9535edcbc757c71bd5c74f4fd754614c"
+                + "cb4a948fcdcce6e0aa8d590baad1f812b2f794c84ab36039430d42203d0a423866ba1d06"
+                + "c3e48ac789c4162574529e5282b10616181571cbf8c7904b3d34f449c6ba934bc1d9de8e"
+                + "29afd383a32ec5e6198c2405aa4c9370d7907ad29301d0a5decae9055a2f374afd48bdd6"
+                + "6d642889a553ae29bdc7fce2497af7c9dbe20b453e21efb07fe08a4f9f7019fab8ea8433"
+                + "ae68953a9810234313b9cba7f87c13df3b3c7cd80d0078d9943734cd8ed1dae65beef615"
+                + "295d4cf35723485293a551c98917a5a923d5049e803e9b2c403c1db00853c320bb5f0bc0"
+                + "0de1a41567d9ed94549a4130c05cf159b0d48b9d81dd1091df8c25c3797d485a688accc2"
+                + "17a33c216480ab323f43fcb9d6df4476ce5bb875ab9c870842518566833c847d47106d77"
+                + "5155c79edb6636be4b35020cab53cce6fb026a5b38f4ed3d427c4244d8acae573f1e01aa"
+                + "b8a406f6ffe361bad6a028c7c0229766e8ffbd45e83e4e880580c03aefcb40f0fe7d20e0"
+                + "5d4df51b0a8cfba618f94fe297b6d0a8a7a1e19cf8ef19f06a96273186fcb72ea0049ad7"
+                + "febce5585e20ac75e008d75d91bcd2cec85489989dce68dd3114a0ec3f9d67a9e6157c28"
+                + "f39d072c5a0f9be55bfbec17f1427e15a9ea71f0b707ca6a28512072216b463d6e144752"
+                + "eb6a25a2a74ea86231f41af41b8dd9890173229f1a0f5687223848139f385568bbb71a04"
+                + "266552ffcfdfe5b8f4871e2ae905054e0ba08e0b1de71044ee96ec46b8ce9bd1dc567077"
+                + "821322f7b11172484ec498eba2e9016cb20c2418deaf5c3d72216a976cc591b19ee0c502"
+                + "0b650fddbabe931c1965ee53e2f8fd8e0e849085460ede1c5d180861cfe61c4b1e708d05"
+                + "90e0ef7dbb25434a619d036124044a6c4ffcfff3c5cb1d02b9b47acc518bba187b8f604a"
+                + "a91dfd0424446a2a693f995128ea9bf003d83fc034cb951a697ad596b5f9e1d84dd74a37"
+                + "ba043d6a2f623fb1689a0f714b89cd6cab5d715fddc97b1338afd6536306debecf023585"
+                + "9417213af2bc7db2a91cce582f2f9d1a1fcfc9031425d111c35da1f3deecdee8b3f7aa0d"
+                + "9bb32459cbc93513d964b33035348ab816e82e9ab5990ce4b2db8eee3e65ee355aa5a8c9"
+                + "e0499589b226c5f93621ed189b395b5b5f639d4c74218a73992f763c6ceefd4636fec088"
+                + "929479e154b6d5db7fc236061d0240c743176c0accbfe7bdc44eba4cf65ce0e07015ed10"
+                + "9b2146fe37bfdaacad4fdce3e01ca41fe25037e8e5a10e88da6b8fc56742cda818526b47"
+                + "ce3240c2bfcef3699e2af5a01ae6f42265b83aa76015e6b5e3c657afea16c25000895081"
+                + "f43a2e7b6a2e0de9c6c70993ae3ce31d9a7474260ec37ed5ac3f54aeaec54d635e7724b7"
+                + "d1c894f2e631d0df9d3da5937731ae29b056f2b84a9833fd2e04217f56862ead24eb80ce"
+                + "72a4f127fcbd0087cce69ae0b84cf1897629cc9b41fb1643e0642ba0eba008f783ca91ec"
+                + "82af80d533c3f1c0b73957ea77511610ee70e0708762791c1c3ad7aa2d8033fa54be97fa"
+                + "e1559e3e0271b036129d62c3e694790a5a6120ed2ac3f99ecbd76f4a76fec84219620dc0"
+                + "025825924ee4d24fa590f6fc29a16d3546f3e71080b5847a6ff2bde3cda414463e7cb847"
+                + "897853e9bcbd33ed46bd01535245dd584e1a17e75265973bf25b2b8e098f5c0a42215d68"
+                + "b65fb357de4e76ab795a39c3b8350fbbe7bed8967a615076b1b36b96c0cb791479fa3a79"
+                + "8664370795ab793d69aaa654346c7b914b6572b34b6f4a053c655c41e02b626d4736e2ae"
+                + "d36f0878935f46578600009e2d5e0fccff5d46d551806cc1b802184513234325188a3a67"
+                + "86b2519a6013aa2d8ac9c776118b9000d1502fc058eb7ebcc86091815f4724589050bdd8"
+                + "2cc249211545d2228b38503cc486a2c4ba224752063b315f49c9974245e6870ae369da4e"
+                + "572ca850be421c8fe0ebe7da05eb453f0f258b295b7d8e8fbdac0caefcfbe84134a4ac9f"
+                + "4d8fdd596df2e18081e9fe5128a71eb8ccc79df036c2380941d0cfe6907c354d579a2855"
+                + "b45724992f18d5ee277fd2dd9e48251130640e175817135f895e4e752193554bcccdd82e"
+                + "08fcfc4f33269c2a33621471ac102766932b8f78fdf3a21c318eb2d9ee59ab7fa204d725"
+                + "8219ec7e92617f9ceedefdfcff4c9c5692c498ba0549f578402405f651ed4dccaa786cba"
+                + "95366e41ab2708a05c39339493518671e2411fa1c641eac239a4ccca80ecafa879930902"
+                + "a6f221dc77977b6621914988930beb5f69d2c031208b13f0bf44d0a82deb0520ef738b17"
+                + "2a5f663998837439e595985e782e69e41371bc5d6600f14f8b0a180bdcef74b56149dfb8"
+                + "d2224357c46e486fc52492ae7718514a26d5d9acdf15dfa990d265900afb3d3f0ccb548e"
+                + "fe5a9bb39208cbbdd172ee5a2330aec8574b894f2d8d4a473ef4a3b21aec96ea9669d23f"
+                + "c72773fa9d7c2bb9052131d056e9841f3215fd66422107e2c3fe0ddea1dab6fc76bcf5ee"
+                + "8e90305020a8439a9b867c7a09475b3fb4515e685b506fe498c547acb659dc862e27e270"
+                + "41c9ab69c055d483fd7f88a5c715df32b1e7258e7007c744e0356bacccf93ebc3cdbfbea"
+                + "f9af0d6bf224995bb4ea8640a1f7f7472de5aad1645746b37b227dd84b806331c9c91aa4"
+                + "4d6724f6cea9f59b3489b136fbd7fb9fdc7bd3919f40738f0215896f0e57fc3c6cc4b07f"
+                + "3eb18ef7121a3a51506a57fc37578c7c982dd031cbdabbba993c4bfbe5f95634544f5f08"
+                + "b46f3c7fd5c3585e66363da541b95fed20f316f746319fb7b6a0a4f2fa70ddf3943b5652"
+                + "2a244856cede10aaa813d924dc0984df05260b2eeb33fc14d82ac096bd3a84564c129883"
+                + "595210a196bd3312e8d38ec808d5009992decea8ad2165597cdb27430ab3ab595e2d51f2"
+                + "68bb406e2e013ab1b7fb48fb47c753a7a01a257523bb59d378365713e189956f37f035d3"
+                + "b0865cf0d28ec65685d4e700f585fd2a9679c22f4028e892a05e374b768077da7a050802"
+                + "7d37ea278fd43e8a6c40218e00b47a8bfcf14d1d3db7df57b5c6cf69259d87773a3345c4"
+                + "5cb8f6a7e91f23f5a6566bc9a3125598ee90c3d3f5d814597e0d87a54c6c3e883a70e5c2"
+                + "49efbd57cc8494e9c56e088c355a49b512f09908df78a900169fd3ea8de67b521b826826"
+                + "4c7597c2900e8bb986e648f8b69990956e8ac069d811fd9755235260aa1eccaf25b6ce48"
+                + "46d33f7ff6f0291d6cca010d3be72ff3f885b9252138a801788f6661ab2040866cdd776e"
+                + "1616ae57ab588231c748656e955746986766494a6a460c4352f8a843bfbbd6199822ef85"
+                + "34ac1c436389c7227f4b0626670c8aa2e40c650bc5d27e6668b8bc7794c852e66f98ea3d"
+                + "b47bf906a2dc47fbc17468a1a06b4c559db922fb0649ad3fff2dd9302eb79b9b242750ee"
+                + "b95e0985296bdba72aca0bd2c33d48b68eb89c8f4c4ddb9c36387f8cc521698eddbbb60e"
+                + "bbf356145c811cc136579d0246eb9c6fa224438bdffbfd74c138302ade0307003c04b881"
+                + "32fa21c6119e1c4fe48c4133dab830acde6d9d050b74e1e7a05466e314208a4a148a7a62"
+                + "23530f18aeb1a6e65f0680402c879bfb914525132f99c185c96c26b394d3b69e3d96ce16"
+                + "7ba9edde0cf6b689d065f853b49839d868b4fc0e879f0e2aba5053c5746630ae06a80b5f"
+                + "4cf424c1ec3812dcd16b2ad79bf342a7e84535940b254941e942a1da02394dc19978307e"
+                + "a5122e9607f598c5c6f9c64d9100f9971a3e7b8e4da6b789b41aafe6fd66d69b4ec7af94"
+                + "6b5abb71c8213367b45eb67200b6f3b293f5d5fbe9c37b84b641c6418eea1cd07b0c63d5"
+                + "1505249a0c253e19c4c6071fe6aaaff1de33a96e6c7e40a8bc647bec54b4798a0076fe2d"
+                + "11507334c12896123de599d0712488f056d5aeadaf66dae5f46009b9d2621e399e3eb08a"
+                + "9e2cfbecb82396fa3610c35e1f6464e326b0f969609fea857a5b59913d4a82f08dca0fbb"
+                + "1821751a1a9c4993429f715f30e6a2bdb102b1d8264ef83eae3009431f228f1c2b5c3e29"
+                + "23f564bcf2ebc2862b5733b5ec22bf9f7d0dcfbe418fcb989b3adbb2ab1af9a161daa4c2"
+                + "e2920cec9ccebafb9d3a241d8e7c659d7caddcc20dd787b9707c32dc5a34261e2d18e85a"
+                + "9a1e7a709ea161e1d12869a490c8a451443ba53347d3ec0b9df4b644634f12dbe36dd950"
+                + "a11b633f0d1bb3c4e6caeb735b6a393a9676a7ec9eae6cb916c5636c9a57a84b919e9cff"
+                + "9451e0695dfa7a8b46e36a72d40397decb7a8405537cd8fc42cba1f11f34d5e10fc02929"
+                + "c8830d941798a52d79be307a535b04dcd73da1734e696e2b075de7d43b211983fba7d3eb"
+                + "6758aa23ab87dd338ae5b7d4943755ec9999558ed1ff4d59e7e0fc57304b4e0cdd98bc8f"
+                + "6dc528ac55c6951045a04a6ad2ff813ecfc1c08bb11c27f8cbd93ee4d99b07e56f61fb89"
+                + "5bd9724c052ec7ef148b496e8093b6fb9ba3c236e43f575e19d728bfd2405f252014cd79"
+                + "984d14cc2a12f0434a826712f299a85a6c80edd30dda8f0df1639afdc1217d7536ba6aea"
+                + "69059bcd1b909e01ba516206b24b37ef8ee329ee88a4539f9d336209abfc823c9f223231"
+                + "27b10a135dcd9cee68d62f0a43510408e4f1676072beacc6fcd646093aa736284c67264d"
+                + "387fa3ff0f76e14e86ebb12c518940fae86d4749bbcbc0da42e2334bb0fa284efdf970f6"
+                + "9eaf55c58be039269e69c6e857f824b0a4aef81cc4b0cd39cbe821e2b9325745c6426487"
+                + "31f5edc1e35a2b41bd5f07b8c7b48207264426d8203ba45d6191ccf280e475112d80b66a"
+                + "467419596da008c13ff2c7a59d20e07c45444b8366e37d21a2ea0c435c74cee05cc9360c"
+                + "3750a512ba03dbd9ee1342da85c50a10d6668215c48f1395ab4a88af64810137cd1a94f1"
+                + "5a080c321af1b269203416caaaa53d3314a867ad16d72f7b6e3b2cd9ca1deff124eaa103"
+                + "f033a76f9dfae79543adbf24bf5d0bcaaea2b468d11a779d53564c7f131363af96bc2003"
+                + "ccb0636fc219a0f59cc0eaf48ba3750801bb916b78220dcf99fe5fde841d4084e47519a1"
+                + "76dce6a1bbd65c2162f923336b13b045a4bbc19337c71afd351b1af0ccd5642f5ca62df6"
+                + "374d5c1c3f636d8e5169b2e9582607efd5b3925d584bbe8b19a29a06d8f0ee958011f984"
+                + "4d24d6652ea8f21de11ebce37e3c64ff24bc986a5ffd3c2ea59d17dc8d3cc7e5ee820bf0"
+                + "fce9755249b520bded1eef46d08204b39fcb6bf5a9667f9f07b2d630e5cb5fd56136faf8"
+                + "5a2efe82efd22914a0f32c3ac226d793a0b323c71c8e91b27e45650b61de65e13a0179fa"
+                + "af98bdff8031c1fb1775c824be05bcfce16a10fa21d5e2e3fe06b4b17b02fa19a76df8d9"
+                + "c3d5417c1eea28aa8de67e8d6d29eced26c07c9361c9896372fd451754d8204f44bd903c"
+                + "e7ff5d0dae69b5bea66692e0e8160452095b509f85ded03e0d195929849923cd28e5ef4a"
+                + "0b969b25a34e80873ab0e49eef5d0f532d05a6acc13d92b4ffcb6fcf80212a5a744f11f3"
+                + "bfc93eae1f19c2e66a8a799868f47d4bb3873b88ed7bd73e01ab27738657fe7ded82d5d8"
+                + "47d3b05851c55a2ec2dee3587b96a07f6bc8bcdda75e1acd6af8b85c1ffa6bece522773d"
+                + "30e0fcebcd09da2d6d9130961571db1546cdb405566b3b80eff0139e1c5f5fcc1cdc1646"
+                + "da134b24bfd6cf0f3cf790214f505a349911c15f6356eac315eeed27981b5d780ae493f3"
+                + "fccc17f93fe13466abbbd2096f170cfc69748e6fbc8c243e61da0aee285ca2f795bb5c03"
+                + "762b0fcabdfd8c422dc8625d18feaad2b7ad31bdf6641401d509393701642eed48f37878"
+                + "80ec1e355760ad6d095139c5af12da8a1af32f7078ceaddd5db21c8561d18098ab29d95e"
+                + "1337bc2a1f6b7372e298dd7f7dd52613b2e057a103179977ad5e4718e0fde0ec6e21b4af"
+                + "9fa01b110280ca29adbc776cd275a46115f42b566c9cf530203e221ab39dc3484a96e06c"
+                + "65821edadbfc50ddbd5702ab563eb4fbafae513735c93178aef68486e78776c8be66b141"
+                + "35d8d04f2af39442e0f3c264495530c554b85b06d59e722ce1542ca428d6bc724bc97b97"
+                + "4c10e69f6db0a1e854c6cee309a109374fa3d39f5c780cac40295702217833867220751d"
+                + "ee6fa2e5badd335c788a267dd9cc6a2ddcda372a2b25097eedd139ffaae7147ee593da74"
+                + "de201f959fe86f9166dd59a2dc36b04625c804cbe8e96023f2eeacdc8140d22d0fe9e16f"
+                + "a807021891f772505dab861088b492154acde10a1539c3466c0f40111a902bb15d37fddc"
+                + "4f1b9c53e651f8657360c636ca65407df9a537c7f2a3cf8cc69310bf6f2b00bbd6e04a17"
+                + "2bac7de33c77554ae4255bf71f437b2fb976b8295346c7d8746536db31a0edcdb2e0743f"
+                + "4c4cdf1152df1c65d11b4f98b35da958fcf6a41a813878c4d3625f3856f8b7d85a428a29"
+                + "526452ad1374ae59cb3f70329960644aeed4604c54fdb9c89b507a46c3471d70ea84d4b9"
+                + "ae08bf43d206571f8d85a2594f7b1f23bc705822461f46d0683313f98ad4de25fdf96e4e"
+                + "612ac233db75fe8f7d55b7d7c1ebaa5a19308c4d35a9f7841bbc2d78aeaca3dbfe51eab8"
+                + "d38b60d1f4a1afdf5bd5144c332efc29cebfffe3d0a8b3ca8e065f13c143d2489e8f26cb"
+                + "d806eec413d904b96ed1f7b44e48a540921a9542c451389f7a7fc15b540d30e6d4275808"
+                + "2a680e308789f99cb9b4fe0405880bc6a1fdc2ea25895b109f7b406fa69d0971bd056a61"
+                + "ba1d0672289aca060073740b985f0883805f75b54c492465ca237bda765dac097f069a8e"
+                + "c735779eb71db82465dc257b9e501fa2fb9b7ef69b64e59fd763e8baa8fa4037af84c979"
+                + "f9464cb70a80772da62d293e70a39c572d3a2c3108e6e188294f360c8e3c319ddd3017b5"
+                + "b0fec885f2ad5d412cea1c73635e9b141c3360e41a40b715db5780323c17b10864d1b45b"
+                + "61781fe487592858ecb11c99ccf7300bf4d0e3fbae7939f922a989d464fdeccbd3b00149"
+                + "4998cacf4e38229b7ba49b851d09c8adf900a3d0abed61442919fe1973ab44a40e3cc2b6"
+                + "ed8b5c501dc9ffb37b68d3f58ff4811bdadc6464ac2b37a3bdb39de4e18a6a9b15e9f318"
+                + "9b8010df6250da8f18ba514ed50eb979dde95836f0505c58c90028849573562dd78e24a5"
+                + "6a0c42f4d533023910097d8e73f2ee5bb4ec173f3b8a4b467bcaafe2896454979e753bb3"
+                + "dfc2f4092d889e6e7a123994e2f7fd835188f8bb668afdee1bddb8cb3076c6a72a013c50"
+                + "cce90e922370bb484e419b22d86a0abad1ce1af95f26aa14af36542ee25274f90ab22845"
+                + "67db373f1c395f0d90f09924613ff8f20b46947baa8a5e88f48b63f52251e9889aa149f3"
+                + "3efc6cc653a4f6c7888cc2c493c0637f7c46664b1a41e82be785c080e5e81999e63d0b02"
+                + "f71cfb93afae9f1d64fa723a515dbbbf0e84f48876c902bf0fd5a24e21c5934454778295"
+                + "9f72e855933087e3142d7407b0e9224ba2b5976d43b2d228f9368374512d5f59eb2d1394"
+                + "c879dd392a1401085f6443231fb8f208b9e3143bbe5488f185fc1eaa96cf3562cbc5bb56"
+                + "08f7bad8250fb1fc28f72e9fa04a4bb5819c00c00ff702d0be366bdc1f2f91d65a605a88"
+                + "7bde087bb0d5f4a608e1d2418b33e49e04dd706357389e94dbc6ba7d7c1aa1ec0ce2ceaf"
+                + "d5ea409c7b3a435b2a4f5bc3209c3d5bc9e0bf60fc96f75038aedd82ee23a6b6aad794f3"
+                + "0c6ddd0fc73224f531ebc0334401741d6c009bce65dfe56432e34d5b24c0acce727982be"
+                + "1c01b4d02d96ad4297c8e17bd550b4dd586a3f4f2a2783790927d220dc254f020efce565"
+                + "d0fedf01125edf2098808bd8e42db499a8bf603f70e45620f7b86df3e5cd27e8bf6ff8b3"
+                + "f4d5a5403d0dd6304579447bb8aee092629efdd5b8229946cda41104fe063f2e5ef5599c"
+                + "9f409e8332939f48abdff0bf90fbc13b34b462e7725f8656197b97b97a437c3fa82ee97c"
+                + "217c0b4e4d63681f6fb9d9746823231f27b19fcc6f15a65e8453546f9594d76546febf8b"
+                + "f7e189fa36676d04a3c4f13277f20a58df88a285321624d0ac173fb8376af9cb224091ed"
+                + "86269a7d6911e7d031141c118725a9cec27523ccae6aaa92293b247b6e6d68c52a873032"
+                + "d69f09e32510b3a3a2d9112f09fb35b54ef7bdaad0e71781faf246283127ceba1f137678"
+                + "30eeaa766e5487ae2d90a07d6f3e1164da65c1459e85e5ac935d15a62951a33d08b1741a"
+                + "e03515e38f29b945e4a5d4bb65c54a735ccaceb626bdd373c330f74844a6ba0e781cdf8c"
+                + "ebe829d1ad02bab96405afc68253265a88187d920415414fa1af0bdd01cc395353f8eab2"
+                + "1e5d30660c329ec6b83a77714739f6ea24c0ad78fd538b06139318c9aa84e3d6e52b7db6"
+                + "d08372ad91695de5ec6f1d738a46b8672b12af6c63427eb7593be449c52be0963bee1282"
+                + "b615673863494c5102aa3c3370022af20ab9e9ec9a7025f14850709d08a175844d9e837a"
+                + "611b3a70fc03a6c1fc191b4e3536c844722596ed8a697afef863d3ead8f35337cfbb1e1b"
+                + "3fb23034699777bb2a21cecd31120d4dbc6326b5404766ed53f275122ba02d2ec7035c58"
+                + "4b79387aeb772f233d524e329f7f9031e6c9f5a18cf76873fd6a875d131bea71fc1fab6b"
+                + "1e869bd0dc5fcbc6a6cf1fd1a1ed204938ba3b496a68fac70a948af119141bdc0d92d1fa"
+                + "f7d1f46d08062f87b8b2b12b522a7afb73426462677b915ed1dd2e096c174fa23eb7fc71"
+                + "0e2123998105a177fc55a90b2cb9f3f7f13c86eb4f169a63f0ca454a54f90dd16a8245bc"
+                + "7b9034b2a6b6374d8ccdbe1862feeecef9be224b8448b3e80c76c57b63465d386ffff26e"
+                + "3f14d0e7c6f2a31f37a9b94293bab1b5d5d2ab46b7279b99dc466eb4b04918a84d1385fd"
+                + "c74fce86698f9dcab3e3ccae7e94d8cec8bca18fac444db03f37853f416cb5037cb0ce5b"
+                + "75a24e7d2e52b5beefaba941c907c03f000fdd667096cfe68357b8173b62095e9c312d98"
+                + "a5cbc3ad6cbafdcce98f4a52634623a010814344b6de5e7c9347f8dfe0d1cf52ae1d7443"
+                + "ecd12805f877012abd49cae1453b4c689e47e6877850beb657efcaef5623fc99215ef89a"
+                + "c33b9cf32126865299829478e23a19f1321dbb05814bb263ed59b01ca72949afca6abe11"
+                + "6c449598dddeb71c51f5cf1098bb24db154a29a0aca63cb89719b2ec7ae2097b41d9734f"
+                + "bf89d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae"
+                + "890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c8"
+                + "3799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2"
+                + "716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148"
+                + "bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463b"
+                + "fa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34"
+                + "f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf"
+                + "5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc"
+                + "9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c"
+                + "86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540"
+                + "d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b2"
+                + "1bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb8"
+                + "5e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d"
+                + "8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f72976"
+                + "05d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d1"
+                + "3cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751"
+                + "475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad"
+                + "69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fa"
+                + "db2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d679342142969780"
+                + "9fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f0577"
+                + "04b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6e"
+                + "f44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a846"
+                + "39e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9"
+                + "c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139"
+                + "f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b46"
+                + "79dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865"
+                + "625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f92"
+                + "7993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956"
+                + "b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684b"
+                + "bf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2"
+                + "c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea6"
+                + "2f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9e"
+                + "b96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35af"
+                + "d8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a"
+                + "3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd"
+                + "2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c45"
+                + "6eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9"
+                + "b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d"
+                + "0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5a"
+                + "b8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca"
+                + "515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e"
+                + "4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6"
+                + "b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8"
+                + "c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792a"
+                + "f6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc"
+                + "18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34"
+                + "ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d757"
+                + "27ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590e"
+                + "f40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8"
+                + "c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c07"
+                + "23db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f4"
+                + "12fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd5"
+                + "6e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e"
+                + "3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559"
+                + "367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21"
+                + "f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520"
+                + "c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d"
+                + "47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d"
+                + "2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f"
+                + "7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b"
+                + "71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c"
+                + "87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f924"
+                + "4bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e"
+                + "3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80"
+                + "e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528"
+                + "aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d0134"
+                + "6112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225"
+                + "f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade"
+                + "7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc"
+                + "30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd9"
+                + "8d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd3669"
+                + "9e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebed"
+                + "eeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4"
+                + "fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954"
+                + "105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc"
+                + "51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26a"
+                + "df774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935"
+                + "fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885"
+                + "e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f9"
+                + "4a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a"
+                + "07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55"
+                + "d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d"
+                + "1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a30"
+                + "1bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0"
+                + "341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df486123"
+                + "44be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca"
+                + "184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb"
+                + "35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed7"
+                + "86ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab5"
+                + "73d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d5"
+                + "03f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a9"
+                + "4f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786"
+                + "229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1ee"
+                + "e5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e1"
+                + "1e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a8"
+                + "9f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945"
+                + "f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd"
+                + "293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2"
+                + "027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8"
+                + "d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca8874"
+                + "2d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c"
+                + "6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2"
+                + "bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea"
+                + "49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec"
+                + "7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e"
+                + "16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e1093"
+                + "8b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd"
+                + "7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e498"
+                + "2242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13f"
+                + "b108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e613162"
+                + "59942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787b"
+                + "ee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977"
+                + "979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d2454602"
+                + "67be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f392551816"
+                + "5cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda"
+                + "99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735e"
+                + "cb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a"
+                + "8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c5"
+                + "66dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf6"
+                + "7a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe"
+                + "012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e45"
+                + "44be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af"
+                + "4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6",
+            "03ff3c180f191eb9214326a3df038f5b168f96539b2af6bac89088a2c521534b4ac1c555"
+                + "e3a754af50c012e38bf5e5127c9f5204fe4976ad8b2b6b2a8936cd3a6506ae1095ea348f"
+                + "0aa6686646b5a0dfed9ed5d91a9d5d6b74e8c5164656d5ad426301efe4d19682970cbd17"
+                + "0ebcb2f829911cffe487f8838600a277585805e2dd575bd385d2f18c9f15877bb5601f9e"
+                + "f672f5523ac369daebd31ea15cbdfebae36e8b3dfe2f09237855202c28bfd059c1a2d700"
+                + "c2ba2c5ca97d59693c73ba073f852dcb41b4e19a6d9f352367a252039afdc7c76ddd0ffc"
+                + "a9b0e77d97a87722e9f8f1b63f63aae3ae1957482f1dc753dad460af43e078acb9497512"
+                + "a97ba6a858b3986d608d1eff4302133c0ef86fe2b123bb39cbebb0954a94dedf81f5f9a2"
+                + "12549cbb3d09a42b2323bc1ce86241ae30596e5cbbcabacad1d594a7d6bc3bae235a040a"
+                + "d7955323b18f2357993846f2f2a90d3cdfda2271fbaa94269c564d99023780cd6097aba4"
+                + "d2f76610b4350345f340e5d43beb12ef5d09c27268b50a4d7ec7428d3e008ff6c4f0de50"
+                + "21fb85c8861444ebb42cb49d2e34af19452c6685ffb5a7c6e36af83e1d1b1c770a05b9b0"
+                + "e5cdbb4127db85f88f95c0e197bb1fdf2cdaac23c6525aa5880341123f1ed83c10b8c77a"
+                + "1a0717fbc5c42ae6d65b5bdbd986e58538c7abff89f26be07e612f6036ce3b196105bf62"
+                + "22795f109e3bbb14ab9e4b6dcba8fddc6c273b8004454a75fac6819b13fa9ab3f3508e65"
+                + "9a2a413590057cf8d577493ecc055c2d88f403f03d2785deeed1a99481d91777777a0a80"
+                + "c49822f0fc0dcff9a4f384bf0eaf35cf1f0c8726abedb956166c07dd167918acdb6b0f86"
+                + "53989c9566284028ed49875c443b4ca9be2d101dac23a248ee96973a76219194a1626097"
+                + "897877ac151bb67741d49ddb08dc1054c822e9b63dc4cf71ae49a8e6c3cd1b5bdd48ecce"
+                + "768e6d9630cdf86c7aada5c9485f5b026ed55fb29925cffa23b81654ca9662efc811b6ea"
+                + "4aff0cf2da79943078198f133353daaa1beba45e8f4ba2a8d82dab553504c3615b6c4b22"
+                + "a92e7cbd0b5ddc3ab88a29cf555c5635351be42881c685463ccf09977514ace0603e467d"
+                + "62ece7f65e25b2425dfe5cce51a7b836b45292be6f9de3c760a62126881ff7c910caea8f"
+                + "c7701be59a47031821a87267e6d3eab4589db9220b17d636a23e7d685d98e5d49ae86b33"
+                + "18ae3709af2814a5800c2962f373a71e249e9c26de27f1a1a08fb3be26a6384102fa71b8"
+                + "4e94c0ac8952df997682840cf8cea302f14ff872de49bf975f1b281354eed33571c51dbf"
+                + "a70ae3497eedf53a23ca84f53976f0db4bea237c96bcca420dedac8ef6397a14def1a18b"
+                + "063f48a802a159b0715cd241a1a844a988e299467ce2847e7e1d3d9b1d5bf8951852403d"
+                + "f4b74a01fbb123b4a3544cfbf2daf3c6a4eb9c08e300735d230c5657f29efd1edf56215f"
+                + "c6fd1de693027096bcb708e3cbe8083450bdd76cf1a031d4a49f0c565f2e91442ccc4f2d"
+                + "8b24c535084ec23e4bb9ea21821c77b615f964dd7f28bae609fb4d263978dc5c81df2415"
+                + "be8c9ae320d3a2c366743d6a108f4a8cdcafc1592a3312e6051bedc031e93e5df4c129c1"
+                + "379bbfacea92e24e99e1bed384bd5f33f2438305bffe54c54ccc78586d3ae92fcfc53ff7"
+                + "e357285367b6503a972ac951508cad8d7ac52296631317fa418e8fd42d8c15d69fc42497"
+                + "2efb9d39f7231b3a32ecfd0036f09ad7c23ed98eb83e43705f1e73b69c36ca46603478ea"
+                + "d74a375744217f0db23e67f801ff606622547cc964022fcf7ee81dc122742b1ade2fcc3d"
+                + "0013dfc961e365a21c396877e04e65f3128a496f00965df4ebeca73e32e88946fa5141e2"
+                + "425e0484ca8c23fbc29e115209a8a7750c2b73e107c89eb914d7a4b54911a44e64604f53"
+                + "368f4c65a228cc9bc0ba8d67d45d84972cd4c9a7f9594b3659d60cac3e3630d1e8cf8e62"
+                + "5f321b492a6eff590da2efb0ab7ed84b9cd9483f427b75bd0195245e9f686bf5add057c6"
+                + "aecf38690662dcf5704c76cc5c5ae275a403ed42e740e7fdb8fd054435c6b34643d1136c"
+                + "6ecfd06aa375a886574e30b13260f01ec4fb79313617ecb2c8a5a39e8ed92652796424b1"
+                + "b118910ffc50d28046d68705737f5b4a8a4011c773d335b228413f3fab3690230c4ecd7c"
+                + "7622da87f3d0d6b51c018a2deaf8a4302d4788e9b634d62248193b45771833082ca986ab"
+                + "376db3bb612f530d5cdfe26138a1fec23f9267651099adec9c8f1ca9dc38f579fa631408"
+                + "ccfe1dd998dbb16a8691c7b2e62fad3426d9a1260e4b4b87e3703fd13b8f348da313a489"
+                + "5bdc2470007fb7d455469dccfd88ffbd54543cfe176143d088a3694702e0e0fea21171ca"
+                + "0573385689b42f8005ea79bb8550fc796dbb96904d80299f5d5d65dd81ec4bf21f5fd0ea"
+                + "ec09c5850c6af5d043e9c968f696324957a2a2869bc65499a834e3abc232a3b3737885b4"
+                + "a580b0ee5e1438a126bffe9e16130b9c57a3b42818a3f92bf6f83bd77d54fe21af108621"
+                + "d6f33114d8a7e3ed9c55b500301d646822cfe22bd6f1dc2e71594d9c8942a8ee8948ac83"
+                + "b40b0a2d18d2b2e3c7040d85592bb2bebf70a50a0a42d71c6a40a18e3332519288a05127"
+                + "b599b258f97f840c7d3646e1fdde9e9121852ab2d4744f9aca249c45881f62d41792518f"
+                + "d51b6dc85a41e31ff700dac42da2bbafa44df6d0abe12defb39b066b62c88cfec1aec5d5"
+                + "7b5648fe7c4df944b63a39be63b05d8219e65a506cedb414c3d2c0ed3d0122b8a799ecca"
+                + "ab77a93c27a68c4d74868ba1f1125dfe18c871552560e69e05d5dc48254f41dee7c413ad"
+                + "03ea54e12fca064865a8e328894100136eef64c74e18a957b13e0d6761c0953bac3c38d6"
+                + "2fdcee2ad5d39b01deb3d3b8a4921baa7efbf699b008934cccbdb396a9db0027ba312524"
+                + "eb193df16a8e345cf58bb95c889978e38aea27c88601d658f8bc9aaabd342ee9d4b26a83"
+                + "db62d9dac58f4499e9c4c473e73f69206e20d8e6987ac1297c331537e0a525956fede028"
+                + "baa478daa0e35106f74d0100ed6a293dd40ccd56e7ff33a6d0b8ae9ced954fe6ff837b2a"
+                + "64f3da224e9a81779664870c209953a7a01a257523bb59d378365713e189956f37f035d3"
+                + "b0865cf0d28ec65685d4e700f585fd2a9679c22f4028e892a05e374b768077da7a050802"
+                + "7d37ea278fd43e8a6c40218e00b47a8bfcf14d1d3db7df57b5c6cf69259d87773a3345c4"
+                + "5cb8f6a7e91f23f5a6566bc9a3125598ee90c3d3f5d814597e0d87a54c6c3e883a70e5c2"
+                + "49efbd57cc8494e9c56e088c355a49b512f09908df78a900169fd3ea8de67b521b826826"
+                + "4c7597c2900e8bb986e648f8b69990956e8ac069d811fd9755235260aa1eccaf25b6ce48"
+                + "46d33f7ff6f0291d6cca010d3be72ff3f885b9252138a801788f6661ab2040866cdd776e"
+                + "1616ae57ab588231c748656e955746986766494a6a460c4352f8a843bfbbd6199822ef85"
+                + "34ac1c436389c7227f4b0626670c8aa2e40c650bc5d27e6668b8bc7794c852e66f98ea3d"
+                + "b47bf906a2dc47fbc17468a1a06b4c559db922fb0649ad3fff2dd9302eb79b9b242750ee"
+                + "b95e0985296bdba72aca0bd2c33d48b68eb89c8f4c4ddb9c36387f8cc521698eddbbb60e"
+                + "bbf356145c811cc136579d0246eb9c6fa224438bdffbfd74c138302ade0307003c04b881"
+                + "32fa21c6119e1c4fe48c4133dab830acde6d9d050b74e1e7a05466e314208a4a148a7a62"
+                + "23530f18aeb1a6e65f0680402c879bfb914525132f99c185c96c26b394d3b69e3d96ce16"
+                + "7ba9edde0cf6b689d065f853b49839d868b4fc0e879f0e2aba5053c5746630ae06a80b5f"
+                + "4cf424c1ec3812dcd16b2ad79bf342a7e84535940b254941e942a1da02394dc19978307e"
+                + "a5122e9607f598c5c6f9c64d9100f9971a3e7b8e4da6b789b41aafe6fd66d69b4ec7af94"
+                + "6b5abb71c8213367b45eb67200b6f3b293f5d5fbe9c37b84b641c6418eea1cd07b0c63d5"
+                + "1505249a0c253e19c4c6071fe6aaaff1de33a96e6c7e40a8bc647bec54b4798a0076fe2d"
+                + "11507334c12896123de599d0712488f056d5aeadaf66dae5f46009b9d2621e399e3eb08a"
+                + "9e2cfbecb82396fa3610c35e1f6464e326b0f969609fea857a5b59913d4a82f08dca0fbb"
+                + "1821751a1a9c4993429f715f30e6a2bdb102b1d8264ef83eae3009431f228f1c2b5c3e29"
+                + "23f564bcf2ebc2862b5733b5ec22bf9f7d0dcfbe418fcb989b3adbb2ab1af9a161daa4c2"
+                + "e2920cec9ccebafb9d3a241d8e7c659d7caddcc20dd787b9707c32dc5a34261e2d18e85a"
+                + "9a1e7a709ea161e1d12869a490c8a451443ba53347d3ec0b9df4b644634f12dbe36dd950"
+                + "a11b633f0d1bb3c4e6caeb735b6a393a9676a7ec9eae6cb916c5636c9a57a84b919e9cff"
+                + "9451e0695dfa7a8b46e36a72d40397decb7a8405537cd8fc42cba1f11f34d5e10fc02929"
+                + "c8830d941798a52d79be307a535b04dcd73da1734e696e2b075de7d43b211983fba7d3eb"
+                + "6758aa23ab87dd338ae5b7d4943755ec9999558ed1ff4d59e7e0fc57304b4e0cdd98bc8f"
+                + "6dc528ac55c6951045a04a6ad2ff813ecfc1c08bb11c27f8cbd93ee4d99b07e56f61fb89"
+                + "5bd9724c052ec7ef148b496e8093b6fb9ba3c236e43f575e19d728bfd2405f252014cd79"
+                + "984d14cc2a12f0434a826712f299a85a6c80edd30dda8f0df1639afdc1217d7536ba6aea"
+                + "69059bcd1b909e01ba516206b24b37ef8ee329ee88a4539f9d336209abfc823c9f223231"
+                + "27b10a135dcd9cee68d62f0a43510408e4f1676072beacc6fcd646093aa736284c67264d"
+                + "387fa3ff0f76e14e86ebb12c518940fae86d4749bbcbc0da42e2334bb0fa284efdf970f6"
+                + "9eaf55c58be039269e69c6e857f824b0a4aef81cc4b0cd39cbe821e2b9325745c6426487"
+                + "31f5edc1e35a2b41bd5f07b8c7b48207264426d8203ba45d6191ccf280e475112d80b66a"
+                + "467419596da008c13ff2c7a59d20e07c45444b8366e37d21a2ea0c435c74cee05cc9360c"
+                + "3750a512ba03dbd9ee1342da85c50a10d6668215c48f1395ab4a88af64810137cd1a94f1"
+                + "5a080c321af1b269203416caaaa53d3314a867ad16d72f7b6e3b2cd9ca1deff124eaa103"
+                + "f033a76f9dfae79543adbf24bf5d0bcaaea2b468d11a779d53564c7f131363af96bc2003"
+                + "ccb0636fc219a0f59cc0eaf48ba3750801bb916b78220dcf99fe5fde841d4084e47519a1"
+                + "76dce6a1bbd65c2162f923336b13b045a4bbc19337c71afd351b1af0ccd5642f5ca62df6"
+                + "374d5c1c3f636d8e5169b2e9582607efd5b3925d584bbe8b19a29a06d8f0ee958011f984"
+                + "4d24d6652ea8f21de11ebce37e3c64ff24bc986a5ffd3c2ea59d17dc8d3cc7e5ee820bf0"
+                + "fce9755249b520bded1eef46d08204b39fcb6bf5a9667f9f07b2d630e5cb5fd56136faf8"
+                + "5a2efe82efd22914a0f32c3ac226d793a0b323c71c8e91b27e45650b61de65e13a0179fa"
+                + "af98bdff8031c1fb1775c824be05bcfce16a10fa21d5e2e3fe06b4b17b02fa19a76df8d9"
+                + "c3d5417c1eea28aa8de67e8d6d29eced26c07c9361c9896372fd451754d8204f44bd903c"
+                + "e7ff5d0dae69b5bea66692e0e8160452095b509f85ded03e0d195929849923cd28e5ef4a"
+                + "0b969b25a34e80873ab0e49eef5d0f532d05a6acc13d92b4ffcb6fcf80212a5a744f11f3"
+                + "bfc93eae1f19c2e66a8a799868f47d4bb3873b88ed7bd73e01ab27738657fe7ded82d5d8"
+                + "47d3b05851c55a2ec2dee3587b96a07f6bc8bcdda75e1acd6af8b85c1ffa6bece522773d"
+                + "30e0fcebcd09da2d6d9130961571db1546cdb405566b3b80eff0139e1c5f5fcc1cdc1646"
+                + "da134b24bfd6cf0f3cf790214f505a349911c15f6356eac315eeed27981b5d780ae493f3"
+                + "fccc17f93fe13466abbbd2096f170cfc69748e6fbc8c243e61da0aee285ca2f795bb5c03"
+                + "762b0fcabdfd8c422dc8625d18feaad2b7ad31bdf6641401d509393701642eed48f37878"
+                + "80ec1e355760ad6d095139c5af12da8a1af32f7078ceaddd5db21c8561d18098ab29d95e"
+                + "1337bc2a1f6b7372e298dd7f7dd52613b2e057a103179977ad5e4718e0fde0ec6e21b4af"
+                + "9fa01b110280ca29adbc776cd275a46115f42b566c9cf530203e221ab39dc3484a96e06c"
+                + "65821edadbfc50ddbd5702ab563eb4fbafae513735c93178aef68486e78776c8be66b141"
+                + "35d8d04f2af39442e0f3c264495530c554b85b06d59e722ce1542ca428d6bc724bc97b97"
+                + "4c10e69f6db0a1e854c6cee309a109374fa3d39f5c780cac40295702217833867220751d"
+                + "ee6fa2e5badd335c788a267dd9cc6a2ddcda372a2b25097eedd139ffaae7147ee593da74"
+                + "de201f959fe86f9166dd59a2dc36b04625c804cbe8e96023f2eeacdc8140d22d0fe9e16f"
+                + "a807021891f772505dab861088b492154acde10a1539c3466c0f40111a902bb15d37fddc"
+                + "4f1b9c53e651f8657360c636ca65407df9a537c7f2a3cf8cc69310bf6f2b00bbd6e04a17"
+                + "2bac7de33c77554ae4255bf71f437b2fb976b8295346c7d8746536db31a0edcdb2e0743f"
+                + "4c4cdf1152df1c65d11b4f98b35da958fcf6a41a813878c4d3625f3856f8b7d85a428a29"
+                + "526452ad1374ae59cb3f70329960644aeed4604c54fdb9c89b507a46c3471d70ea84d4b9"
+                + "ae08bf43d206571f8d85a2594f7b1f23bc705822461f46d0683313f98ad4de25fdf96e4e"
+                + "612ac233db75fe8f7d55b7d7c1ebaa5a19308c4d35a9f7841bbc2d78aeaca3dbfe51eab8"
+                + "d38b60d1f4a1afdf5bd5144c332efc29cebfffe3d0a8b3ca8e065f13c143d2489e8f26cb"
+                + "d806eec413d904b96ed1f7b44e48a540921a9542c451389f7a7fc15b540d30e6d4275808"
+                + "2a680e308789f99cb9b4fe0405880bc6a1fdc2ea25895b109f7b406fa69d0971bd056a61"
+                + "ba1d0672289aca060073740b985f0883805f75b54c492465ca237bda765dac097f069a8e"
+                + "c735779eb71db82465dc257b9e501fa2fb9b7ef69b64e59fd763e8baa8fa4037af84c979"
+                + "f9464cb70a80772da62d293e70a39c572d3a2c3108e6e188294f360c8e3c319ddd3017b5"
+                + "b0fec885f2ad5d412cea1c73635e9b141c3360e41a40b715db5780323c17b10864d1b45b"
+                + "61781fe487592858ecb11c99ccf7300bf4d0e3fbae7939f922a989d464fdeccbd3b00149"
+                + "4998cacf4e38229b7ba49b851d09c8adf900a3d0abed61442919fe1973ab44a40e3cc2b6"
+                + "ed8b5c501dc9ffb37b68d3f58ff4811bdadc6464ac2b37a3bdb39de4e18a6a9b15e9f318"
+                + "9b8010df6250da8f18ba514ed50eb979dde95836f0505c58c90028849573562dd78e24a5"
+                + "6a0c42f4d533023910097d8e73f2ee5bb4ec173f3b8a4b467bcaafe2896454979e753bb3"
+                + "dfc2f4092d889e6e7a123994e2f7fd835188f8bb668afdee1bddb8cb3076c6a72a013c50"
+                + "cce90e922370bb484e419b22d86a0abad1ce1af95f26aa14af36542ee25274f90ab22845"
+                + "67db373f1c395f0d90f09924613ff8f20b46947baa8a5e88f48b63f52251e9889aa149f3"
+                + "3efc6cc653a4f6c7888cc2c493c0637f7c46664b1a41e82be785c080e5e81999e63d0b02"
+                + "f71cfb93afae9f1d64fa723a515dbbbf0e84f48876c902bf0fd5a24e21c5934454778295"
+                + "9f72e855933087e3142d7407b0e9224ba2b5976d43b2d228f9368374512d5f59eb2d1394"
+                + "c879dd392a1401085f6443231fb8f208b9e3143bbe5488f185fc1eaa96cf3562cbc5bb56"
+                + "08f7bad8250fb1fc28f72e9fa04a4bb5819c00c00ff702d0be366bdc1f2f91d65a605a88"
+                + "7bde087bb0d5f4a608e1d2418b33e49e04dd706357389e94dbc6ba7d7c1aa1ec0ce2ceaf"
+                + "d5ea409c7b3a435b2a4f5bc3209c3d5bc9e0bf60fc96f75038aedd82ee23a6b6aad794f3"
+                + "0c6ddd0fc73224f531ebc0334401741d6c009bce65dfe56432e34d5b24c0acce727982be"
+                + "1c01b4d02d96ad4297c8e17bd550b4dd586a3f4f2a2783790927d220dc254f020efce565"
+                + "d0fedf01125edf2098808bd8e42db499a8bf603f70e45620f7b86df3e5cd27e8bf6ff8b3"
+                + "f4d5a5403d0dd6304579447bb8aee092629efdd5b8229946cda41104fe063f2e5ef5599c"
+                + "9f409e8332939f48abdff0bf90fbc13b34b462e7725f8656197b97b97a437c3fa82ee97c"
+                + "217c0b4e4d63681f6fb9d9746823231f27b19fcc6f15a65e8453546f9594d76546febf8b"
+                + "f7e189fa36676d04a3c4f13277f20a58df88a285321624d0ac173fb8376af9cb224091ed"
+                + "86269a7d6911e7d031141c118725a9cec27523ccae6aaa92293b247b6e6d68c52a873032"
+                + "d69f09e32510b3a3a2d9112f09fb35b54ef7bdaad0e71781faf246283127ceba1f137678"
+                + "30eeaa766e5487ae2d90a07d6f3e1164da65c1459e85e5ac935d15a62951a33d08b1741a"
+                + "e03515e38f29b945e4a5d4bb65c54a735ccaceb626bdd373c330f74844a6ba0e781cdf8c"
+                + "ebe829d1ad02bab96405afc68253265a88187d920415414fa1af0bdd01cc395353f8eab2"
+                + "1e5d30660c329ec6b83a77714739f6ea24c0ad78fd538b06139318c9aa84e3d6e52b7db6"
+                + "d08372ad91695de5ec6f1d738a46b8672b12af6c63427eb7593be449c52be0963bee1282"
+                + "b615673863494c5102aa3c3370022af20ab9e9ec9a7025f14850709d08a175844d9e837a"
+                + "611b3a70fc03a6c1fc191b4e3536c844722596ed8a697afef863d3ead8f35337cfbb1e1b"
+                + "3fb23034699777bb2a21cecd31120d4dbc6326b5404766ed53f275122ba02d2ec7035c58"
+                + "4b79387aeb772f233d524e329f7f9031e6c9f5a18cf76873fd6a875d131bea71fc1fab6b"
+                + "1e869bd0dc5fcbc6a6cf1fd1a1ed204938ba3b496a68fac70a948af119141bdc0d92d1fa"
+                + "f7d1f46d08062f87b8b2b12b522a7afb73426462677b915ed1dd2e096c174fa23eb7fc71"
+                + "0e2123998105a177fc55a90b2cb9f3f7f13c86eb4f169a63f0ca454a54f90dd16a8245bc"
+                + "7b9034b2a6b6374d8ccdbe1862feeecef9be224b8448b3e80c76c57b63465d386ffff26e"
+                + "3f14d0e7c6f2a31f37a9b94293bab1b5d5d2ab46b7279b99dc466eb4b04918a84d1385fd"
+                + "c74fce86698f9dcab3e3ccae7e94d8cec8bca18fac444db03f37853f416cb5037cb0ce5b"
+                + "75a24e7d2e52b5beefaba941c907c03f000fdd667096cfe68357b8173b62095e9c312d98"
+                + "a5cbc3ad6cbafdcce98f4a52634623a010814344b6de5e7c9347f8dfe0d1cf52ae1d7443"
+                + "ecd12805f877012abd49cae1453b4c689e47e6877850beb657efcaef5623fc99215ef89a"
+                + "c33b9cf32126865299829478e23a19f1321dbb05814bb263ed59b01ca72949afca6abe11"
+                + "6c449598dddeb71c51f5cf1098bb24db154a29a0aca63cb89719b2ec7ae2097b41d9734f"
+                + "bf89d185f39756ebe4b51ec0f68e3648ba709c74872ae4250a8e985060c01e294d641dae"
+                + "890c6445b9ea48253967a7462d34159ccfd0452c82086249efda8a2e56559df2f21db1c8"
+                + "3799d33b2ee9763b5f4b3189d529009f9ac23c9ff9b0da6ffbd3eb9ffd73cee02fad62c2"
+                + "716d2ca27436f44956c0338adcbe99feeea400c165e80d51b519fa659ce38ab0e2361148"
+                + "bedb613db78a50f77398a1350d3cf6c7dd4905a64f06c7837b197221e239c401d697463b"
+                + "fa2ada91b26392da391a132657bdabd08a23989ef4cd2a9cc93d7408faed4f3cd5b37c34"
+                + "f7b5113342b7dd75ca5a474ad2e1fef2e1b5503049cf879a1a361ec195fb33e14dd1a7cf"
+                + "5a6365e470cbc797fcdf3f1b2550637a7eb9ae04e2e5b45ecbfd441df03046da55b000bc"
+                + "9e58ad71e569fe9c3e8501746939e85ea4231b56bb99390d2ec16281049dc0ce4e4ab29c"
+                + "86197ef9e78f259db9fbd6a3af4aba9c4ee4b42ea72efd92d0ef6a8a902a017bdc9e7540"
+                + "d428a3a9b54f0081288998e31eb1247fb21749420911fb5f11b73a70cd7203fb039a25b2"
+                + "1bace7ed56492ce4bd11b189f8f08a5d8cca30ecff25f1b75ac4c6d99b33abdc82032eb8"
+                + "5e8d7e21e410f1dccb22430868a64bcd3fe564135623cdfc69f6aa2d3ca65fa5884aca7d"
+                + "8c7787fbe6a36d79291456972ea8a1beeb31b493753bf93aab46aeda348ff677d0f72976"
+                + "05d00fdaa3619e022b2e53b8d5323a966f3d8e45ef0431f09086e2627d66c04dd44215d1"
+                + "3cbe2e004ec13f87081f8c6ec10ef027f10c1be4af9002c8baf24ed38b2420eb94037751"
+                + "475e1e62d3f6f0e9f4aa152f2429cd7493ebd5235c72a82b3874a0d1864a70b42fe07aad"
+                + "69de5187284baff925d972842eda9ab6411b6c37607ad9eb3d0929741514607e84e5c6fa"
+                + "db2063832a87480935bf35fc1d58ec626e6b18747bec5e8cb4db8c39d679342142969780"
+                + "9fcd62f3d68d81e1ee88c6ee2e709e5f9290179ffae3058784c467184c1dc6e48c3f0577"
+                + "04b7aafa1667a0d7a4eed2e08074e4d7c72dc9977eaf5669c502f363c7b1930b36feeb6e"
+                + "f44b60591bbb961bc0020cda4d52d51f184498ebff010dfa6ca4dd388d59b6c89b44a846"
+                + "39e864de7b129abb35439291e3b45fa69f120a8b9df8151c012aae3e1c5f78f3375cdfd9"
+                + "c2280bad9c3261c8f6a9d30f83dc671be94cfc8782da4961155425e47180ef9c7be81139"
+                + "f28e5e548ca95935d74eda74c3c54feb90cc3dfaae8ee0b06c1710e00c8e893954ba8b46"
+                + "79dab007d96c8d0b347951b9f29993a420862cc2bffc7bd6d56f4bd3f3cff871c10d5865"
+                + "625967338c8a7c1d613e4de2e2712ce3a8ee12c0da16cb65788d4b17303d2667f3529f92"
+                + "7993ccea6f96571cc86f3264979c891025f22318d17f21a06fbbe7a1cfaa2ea5d6c8d956"
+                + "b39d4afdd4a76e11f17baef8d230cc82da6d5416a66e907ed8c68b0b8636f03c3d22684b"
+                + "bf0dad9c86812ba8d454bd1a0d515c8b2e8dbe62639e9da15c5ae87cc9a1e195da8f81c2"
+                + "c4c44329f7e7bb4baa08caddaee9aac3ca93982082781b3c03dcf9bc27fe31d12c677ea6"
+                + "2f0f2d00710bc5c753eb8affc6618741263290d7e62a26ecace63407f64ac853ed15eb9e"
+                + "b96735523653e2650088b43c776ab089329c906cdd67ee5f53c2b5ffb2a42c175cac35af"
+                + "d8b254f8ccd8aaa771417a2b0d58818f4ff7af1bcbd47ef1904dd49ecde3d95f67407b9a"
+                + "3002c1180355a89e886ea28dde0b53f0c0554ec680419b8de6276c71e59bf7f6eee722fd"
+                + "2f18df08c5afa4b4f1c4c2c36c4acc9d8e6a408352fd095746f7b548dfa89727a3938c45"
+                + "6eea71718df1aadbfb3a9d579193bb1a1d3ef12c0e4673b58cdc42df747c47e8f7d3cde9"
+                + "b57e56b4bdc7582ad947d161946357773a5eab31b8c296782bed4dbf50c1aca02898470d"
+                + "0925794031163ea5cb25de4e6496c0cc166a13808a7e1642349122db3836086de627ac5a"
+                + "b8fc3c596433e2ccd7a59d5e5b974fda712c0acde6bc86e2db0f448d91fd4cf4666ecfca"
+                + "515f7692328bfc5adce4e57b1c20d34024f474278d654ee4793adc7c68818a53078bed1e"
+                + "4288cfb9991b3ae340d64abe8bcead6882ac26a7a680a6bb1da708dc0269b232d87ee0a6"
+                + "b492613b76bf8431f48f732bfec0233ad3b1e3bfcfd18004e4c3f9b4926e9e59c4313ae8"
+                + "c40de20450f84e31d57e0251c14ad8f1042f5be60537c36e1d6212feea378a57bb1f792a"
+                + "f6a0ac4fe989b7741b88c7e59c1624cb1f2a3fd84241f28cc82057fb4e9c5dd88908facc"
+                + "18283f607003b9dddbdacb9bc2e55d8622d21703f1a044079a7c30c5a055817305dc2a34"
+                + "ed6564fe1dbe354b83e78979234ae3474487dc8ab00d7f071d37f886371e95b044f4d757"
+                + "27ab7febd6d91315747335c2f2af8be2c1bfc36962b168d2338a9be2ad53063a0495590e"
+                + "f40a5a72c51b257ef600d6ce930112a4401cdc9b5ae5a2ad9eee4ca9a7b35ee98d12e5d8"
+                + "c122b7080c8f2528ce132ca3a546b0b96ade3c3314a61209b62d1e33979602292d0d1c07"
+                + "23db69c345b8cdc4eb21b40b37dff85f347f199ac17edba640dde8167f91a8396f1824f4"
+                + "12fc7d6ce5fb76abaa4884b953b37ceecb191a99451d62fef490aa10b20fa75545060dd5"
+                + "6e64242da12ea90f60a6130bbfe703ac9653038a61db80c5dbc81474516fbaa5630a588e"
+                + "3ccf0c0d2b4fbc0955d69e1c5b72cde82c79ee92137c82e11dcdb0739f7a84987a0e9559"
+                + "367e4dbf9096976e67d44086002fc3899c532ba9c2b8064852bd4bd2c2b60b23badb1b21"
+                + "f2158b9ffca70f02bc00cdaacbd15602da1edf6530a59088d9bca500935b92c3eb5ce520"
+                + "c372e1ec3b75882f39a3f902f223332eaaea86cb608a6206fb8a62d1349cd8d906bd405d"
+                + "47cb6a3f65c72b0bcd2f0e3aacdfad9b25c7f61282a9e8c7021f36c274247cbfe21c810d"
+                + "2ad66b2ec18510a7acce809954d4c8357f5037b6ce5dac8a40295d6f451c9889a73adb6f"
+                + "7d0a88c55ee7796512eda8de569e088a027c3e86dfc33df4307e26235c9c446769b0fe4b"
+                + "71a9103b728cf43f9ad70f9cd3ca934d3d96861612c16fc84d22feb1eccbd10668e8fd5c"
+                + "87408ab90e8834770c866c664f4bb5661bba8fe44fe8a0062f000ee92e5b8f62fe11f924"
+                + "4bb7a337dd6d8e9158ecca146aac6b2c57bb6cbc933635312776b292beccdf275326652e"
+                + "3660d8fbb3a8452a6f1cc2632d8f5834108238c31539dff7b296037f4b0d04209dc8db80"
+                + "e4d377733b5a53c63e1d63a488640ff77648c569c994099c96b87fefbfee4a8a8f65a528"
+                + "aac35dc268c8981c3496fe2894189637f772ed0d9dda215a726ca744e66c019ed61d0134"
+                + "6112dccfdbf0c88b2af1f2c7ace4a7825eeda571608bd921c924013e1bf31c2bf8434225"
+                + "f781672658401d0371abee59877f942125f5290180849ca0f9659b3112965509453f6ade"
+                + "7a80a5d902c68f11e22475ea5282fe5cd61fbda9b1175b9253667adbb344162c3f3987cc"
+                + "30135ac2852e4b9090c0e0d0cc114da680183fa1064bf5bc25a617c2295edce9421efbd9"
+                + "8d6a2c04aedcc05ae63cc0d7a851f7d55230d48fdf0f7365dae70d271e46377a75bd3669"
+                + "9e89da8fc6db50c03098e895ce6a0cfc67c11a687d987a34128168e243e13708272aebed"
+                + "eeae6d0a05ad8854cd10aa77a33369fce485e6556b085fd253a8bbe3e29c9fcd7d546ba4"
+                + "fe7e8c7942b64491dd54c31d0bb5701ce510181847a13f30294060ccd547ca6c4d195954"
+                + "105929088e2d86530b3dca1978642e8b31bd9cde0730f65cac15353c3084997a948228fc"
+                + "51618fed3e97bdd8aac99b9ac606a1f915cb17bbf0db9e4c4cb9458d98a9beca762ea26a"
+                + "df774bb6667d4b388e84d8fb39999a7a023dbe0fb7cecde785e6f0e0344b447c2f3d0935"
+                + "fd93679bcd00204cd7ec4b9aee4410d1f3308021ec8cf9bc51a19e2b9250d6564e0d4885"
+                + "e93e414a872511518a77c1cea82c816538fad83f98e889b725909ce5583b0f7694e612f9"
+                + "4a43516fc2a9e73f9aa59b65ff3cac8ea14b05545fee544b84ff22a8308e44b89019c29a"
+                + "07e8b82709e5d7090f7c59062f0b5561e452b0349662450d8f68acf32e67a57de5852c55"
+                + "d5378923d07d37d4e0e5ba4356cfddbaf2bdd2fb6592adbdec5964a95ad433fc0ba01c1d"
+                + "1365391d98dfd4fbac0d3b26a92465bbcf197cce5e098f3ca0e5a58a83b17b918c1d1a30"
+                + "1bca3918da53b18a118f333b92fbd1889cf0c647ee1695f236bdfae2aa8ef2fc8773b0a0"
+                + "341739f229ef8a53bed9043cd12d7d9cc041c19493158f25f6b4193199707468df486123"
+                + "44be8e34a56e82f3064d141f1637e490e1bcc51fe2691b828f0276605551a70f86780fca"
+                + "184caf1cca34bffb679ed1d1c416b0c1320e8ee3401bcb148deb953f9d15032022ce6acb"
+                + "35a973c3e7827f24bcaf0b74333fde8211750af5b799f1e2f13c4e507b7703f7812f8ed7"
+                + "86ed51a71f758eead7153e3ece581cad12a60672d5c099b361a17ccad9c7ae638f5cbab5"
+                + "73d5a132f157f637420e9b3fc83b7941dd8e4418a16fc166f2d43c49a60c116cfba371d5"
+                + "03f195ebed43066af11e08c8bc4dc44b77243469ab823be815ae48004800f36a975f42a9"
+                + "4f1ce45ce8e0bb5d0ef55f9e8aff3bfd529677c232f039646acc8ad75698a8de52d26786"
+                + "229bc15da4e6f097c327a9bb641b796921c4583bdea9537f84a15da277b8caf6b292a1ee"
+                + "e5951233344b9997d0747a390ea0b37cd3a20aa7a04c137b17fc8051a4fe1c5a163186e1"
+                + "1e4ee74ce1c26bd2452ea4639fb7d7f875b3a2553f256f568dd7b9b4e6d84d4d454034a8"
+                + "9f2b1174e8fe8ccfbf145919f7b141cd0d0594b47c228ae4989f4b4e63aa2c3f77336945"
+                + "f4c18118242974d861baecb9967a909cb4b1413778e3c4f6ace4cb46f6e8ec5a959b2afd"
+                + "293fb5bc43b14a99ba4a50c8ca429b448157bea59f5deeeef84f7a19cfef734f6062e8d2"
+                + "027cc3761c0c5f30ca3f89018c595ff87dd17d49b3b1ad36228742f70948a7ed5ccc85d8"
+                + "d63a9b2ba4f68f064ee7b9850216b7963dc79b30f58d50c5d3537863a5a1eb3f12ca8874"
+                + "2d66dab69c74c86de129b9f2c811cccf8091a30e058a71edcce014a0ad042b266ff6ac8c"
+                + "6550112f496ced59f95ee04910d803f4c6c662e2069c7d6f6bf03b4b141b41c67600cde2"
+                + "bf1bd98578f769f65b11588863dd69977337bf7f206e9afe6cf544e4e45ce310832ef9ea"
+                + "49d7dbca3918c22a9fb448a75113eaff1d0ecab031bc0617fb0be2033a20b4d4cc324bec"
+                + "7980a32a2a7ca66d82b565477958af80b4e410af1c241feb46660bbb2c5d7dbf08e47b6e"
+                + "16b6b03a685aed47cc52a07896bed4e278934a6cb863c0a5e7ce0f7170dca309b80e1093"
+                + "8b59aeab3f78f70aa631ac6e73285dfbc65cd61629720084b0782595487d92a8d21feabd"
+                + "7bee3218ce484ef6d3ad284e355ffdc1c907a831ac7ec0a92ad51b654b999ecb5646e498"
+                + "2242cd7a49ca7deb11d5688a52f1a237afb80cb6fe3b46fa4c59baf8548f7ae7af69c13f"
+                + "b108a4d6ab0550b8a4b32ab95562b3ae70105c46f8fa9b759af68c79981805887e613162"
+                + "59942d76cf02eccb838b82d6da47694ec2d00fc01496c417b60919d22b8b302a2dd4787b"
+                + "ee2f581e0f2269b53bbe8e1562af0fa65e1729ac04b88e45526c0d57828de8ad791e6977"
+                + "979452cce714fccc752c5f30a96aabea1ca73d0399bdccd8c9ac6c0757585b47d2454602"
+                + "67be1ff3793dad5e2f97400e44f0da442dd82d9329976a838f20b747f8f615f392551816"
+                + "5cd2196a10b433b1614b785eead6c6cbb3678e1a7afde8e14e2f85b8418bab1c7e477fda"
+                + "99f7de5844249b31c8f1f329bcfbf91707911ae74ecc2c5f014b78fbfb26f106a5fa735e"
+                + "cb9121138c9b3d84b350433d4835558a0da74f0df8a61f598a219c54d7b89fef107a943a"
+                + "8a345a33b223d70974c79bc2f1af9d5d1e237a2505d407b2c957b7bc65abc76145b473c5"
+                + "66dabd3db721cbf4a71fb574f4046fb927ff0a076f3f466abda2b5182bec623fa0f1fcf6"
+                + "7a8ba62cb30647388cf9c2a9ab146db9855c80baa1743a15e402766b3910314219d8d7fe"
+                + "012d3750f5d36ed8b9a522d8aff3c6e9c660fea9be58584624b84055fe0a8edf9f182e45"
+                + "44be653c9d052b14583289d1720dffb7f833f0cc7dd36eaf8a48894a742d4328113073af"
+                + "4db5bd60a342086f0f78a69f2a7f1f190609f83be2e45daad123bf37b387fcea41eb8586"
+                + "4ca2192c10eb1c47201fdaf2f493267d285d82a1bad6"
+        };
+        int height = 10;
+        int layers = 5;
+        XMSSMTParameters params = new XMSSMTParameters(height, layers, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        for (int i = 0; i < (1 << height); i++)
+        {
+            byte[] signature = xmssMT.sign(new byte[1024]);
+            switch (i)
+            {
+            case 0x0006:
+                assertEquals(signatures[0], Hex.toHexString(signature));
+                break;
+            case 0x0023:
+                assertEquals(signatures[1], Hex.toHexString(signature));
+                break;
+            case 0x014c:
+                assertEquals(signatures[2], Hex.toHexString(signature));
+                break;
+            case 0x01dd:
+                assertEquals(signatures[3], Hex.toHexString(signature));
+                break;
+            case 0x028d:
+                assertEquals(signatures[4], Hex.toHexString(signature));
+                break;
+            case 0x0320:
+                assertEquals(signatures[5], Hex.toHexString(signature));
+                break;
+            case 0x0338:
+                assertEquals(signatures[6], Hex.toHexString(signature));
+                break;
+            case 0x03e9:
+                assertEquals(signatures[7], Hex.toHexString(signature));
+                break;
+            case 0x03fe:
+                assertEquals(signatures[8], Hex.toHexString(signature));
+                break;
+            case 0x03ff:
+                assertEquals(signatures[9], Hex.toHexString(signature));
+                break;
+            }
+        }
+        try
+        {
+            xmssMT.sign(new byte[1024]);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+    public void testSignSHA512()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(6, 3, new SHA512Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmssMT.sign(message);
+        byte[] sig2 = xmssMT.sign(message);
+        byte[] sig3 = xmssMT.sign(message);
+        String expectedSig1 = "0025fc9eb157c443b49dcaf5b76d21086c79dd06fa474fd2b1046bc975855484b9618a44"
+            + "2b4f2377a549eaa657c4a2a0dc9b7ea329a93382ef777a2ed402c889733bede15dcf75b0"
+            + "d6559ae6842167d500c5f277e1b107bb5125e2a5bbe6e1cb0a6052acb8696b3934c03770"
+            + "4d78c579b41480c3a0a487f335d1658685813c83a1d31b31369fe0064bc6700fad59f3cc"
+            + "8e381c0eb645037f72598a31bd2288cbd60e3e6c40bbc804005724c026036922e8b0faa7"
+            + "319537cdded6f5ee98a07e47697ed4d9b8991daa6798562af0929455f18b7bb45246e992"
+            + "d06398f1d0b8f31eace1bad63c2c0597a3ebd641bfd0af19d639ae745a945b6dd0dea2ae"
+            + "fd84edf1d2e209b02912950836a6b88c0a3947c4922eb93f3cfe69b769539ec98bb4933c"
+            + "8da996561a4a59149e3bd3b4565e2607df047dea7ac67eab33f4fcf4eaaac947a8e76ecb"
+            + "0fd86deac88282cd86cd84209e794494520218edfde74222f75864364616a5717eab7e09"
+            + "d61346faefe0c5f65ac1e4fe673155c1bdc2f9dd5c55ec17cad7861fe5af3510333b4117"
+            + "a84c56f5965babe4ed06c813ba1bcac6abfb41de59697c828399bdb2d68dbd67f490fc48"
+            + "741a8e7a618f3a73b1c9c2b13f8d9676c5c9d3a17d468f543593a20d8903ea34e825399d"
+            + "464472d208be89b26f0c3ad2b40416c310921c43c242fba58a145d7a6486678b6e4f67db"
+            + "688134d1fbad29394acad511b535ae9c7c0d894dd343268a080a4e032746507da33e61ca"
+            + "4444ffdc8fdfc61f00762af22e6e4b9bc449613e42b610930209fce2eef8f2473df2123f"
+            + "02b905e0cfba8255304dcd9eb4e7d4cf9ee92f0b1a4406e01d1c0754d56f6e14b74da737"
+            + "3eeafc687a4820623c240999ad143d9ceeec76cb600406a8f4eb2bb196959862e1e11270"
+            + "29fd818811dd97f8ab09e3ecedcbac04d7922ec4f2f76b7f3c053a447c63fd9aedf6e765"
+            + "a0731fd4ee3b3a0a794d0f22437b0e2ae88a71d277e4333528bd373217c98acdfe952247"
+            + "4f7255c8089e6422de79c65112c6d6ddd98d53d306436bca504f8603fa6beb9925d84c10"
+            + "4d374cb4c681dc2b304ccff8f1b1b3b10f58fa701445d83810b853f17834017ba9a21206"
+            + "fbbb066b1563a606f1c844800712db2cb64b07f9fc834f2242d8145e6144c6096cdfd1cf"
+            + "5a2c467e9685f457d64ae6c05fdd8275da0e94a62b99350f3528a103a6b6bb44d5d96a55"
+            + "138508f407f26da2f9ca56c57defd98b0ce6cb9cfa75598ca0424130c3c250a0d0da68f9"
+            + "c5483defbc4227bfa7332466a98c4bf1c83ae317f4485a6f49ba70b5957dc69113937a64"
+            + "e031023f4d98b7c03362f2b31f64bfcef0952438576f3ed4002e41c99396cafe08a3d684"
+            + "43a643aa55ff7853330bdd80e1260953aadaa30d1e1a1342674a29813fba0bb5774fb371"
+            + "4a4fa8b170569cd735d36a053399f73d0551f946da0724d57c056c064335d05c620af0fd"
+            + "6014197b629d90b4ec2bc26f9848662bd5d4864405db8963e83b68885e10fe99bf394e1b"
+            + "0ac0f2b7df872f69e272aba3eab5c4bc529fa4fda71d15f421b459f48bd3c38cd93a5ffe"
+            + "4949b90ba8aa902cfd63d70e70fc8e810f16abc7b98c613b0bdbbe8ce713800b8b25c5e8"
+            + "753e413bf23439e6857559c2f244ad11d5d004e7cfe3e9893e6e69621e8dba2288e4d040"
+            + "43b034eb52ca68accf3b32eae0ad55e81228974c50ab7f51767a26f914e0d647ae361961"
+            + "04a6cbe5327c19519324be2bb86c8c77bd18d27e858c69be862cbd050918880d2109e204"
+            + "a0aeac4a94aa45c6686712da0ff97038c879a3681255191562c75a2c2cad5c817ddff956"
+            + "8bf594120e5bf3692969aba1ab5e5071253afe049f8a664c2c7cc9f814163a998ee8b92d"
+            + "dcdacc094e68b7a18d60e3b49e356f948e7771eb27be3bbc07e53dc09388cbda7affb39d"
+            + "5a7644f66f3d2b9c5e453519532a7655bbe80859ee939e7e0f82061bb88d00293fcff3e0"
+            + "1622500f2e8f01821edb5d452f45681de52cc4e54e26da8fbbaddb59a379a27a31861967"
+            + "fc256d8734917ed71dfa6643aa3fa7cd90fc7dc631ce6a5bef6a4267dd32fe0f192d4ba3"
+            + "faba611389e707ef27f306860be42dde9048faea9153f2acd0565bb8115ca39e5af4a412"
+            + "466927d84074232eab6cb6966a7a3eff29921083c80d7d9905588aa0108f5fc318565c32"
+            + "0b5d17f3ab131d85c8af27a891b8892a02e426250a27a228bbc64ba31d16db2a182628cd"
+            + "afb4779b8f0e48e068755a4a41d4b43c838533c82f8b9f4015975fa013b5708726eec8bc"
+            + "1b684d179ebb7f91698e3ad80739252cf030983843e355af77f82e956ba60fe494878762"
+            + "dd1f2736878f5c6f6b303a43cb46a94c2297b18bc3fb9c1d35951cc603be084021aca25e"
+            + "8fccd15844efdf5ae367b71fd7ba298e034ed98f9527db0586156206ed6a4ab012209bba"
+            + "2e8f0c15355efa24162b1c0ead2a26718b120b880324a690639f0798f2e7203b41faac1e"
+            + "bbf6337afdca8fa5dcbe73daa5f6322871dfe2e5f5816afa5c75e7df33660bf79610aa18"
+            + "2aaa59d0f9a113bbc58ed58c02226516ecd977286f8d58fec91ae665ffeb7c0b449b024a"
+            + "1adb3d3ed33438aa60724bdedc8c7d847c704798c2724db12a50d5744ecc772f80b952e0"
+            + "2ca29eea671732fbe1705edd36e9a942f64483c6bf5a9a5dcdc6470a362330c935446024"
+            + "a36a7491d54dd81d7677ef762f5056f50c04ddd35aa86e16d3a4b7766b785c15741c82d6"
+            + "0fc14449571cf9dd0dc55ecbe8e5d4ca8f52fd284cfedc93ea39ef75cd34664cc2507638"
+            + "0fc474a03949754024e92308583ea204517657ec51f35697784dbbe0846b93a6600d8e9b"
+            + "ab5ed1573e1f0f700d864a043ed9d686b330a28d10314112bb5768be82deba3e15054898"
+            + "f7b7ac3a6daaf5bf9097433f7170115266caa7d8d853c11fdfc48d500c477d0639b18cd6"
+            + "04e16f25bed87933546987568b3d2c42b0c4e10b70c05d7a16030fe311a9e20ce7593489"
+            + "ab3826a0c0707222ae8d31be6e52b7d9f49c153877f61ecf9d1b6dfb20ca5d385588a16c"
+            + "366db0797bc924b9364e1f717570ada26a1e6b7134e3d8bf87ef72d9eb277103699e079f"
+            + "67e5959ccce0cf604b1ea999de077dce656c993d9a3f460770c4735daeab2cae084ff264"
+            + "29bd2aa4f3a533cf19ec00aee08a7db5dbdc56fab1007a4ea0d135d2d436dff5ea012d0f"
+            + "8e78637cc10f197fd6bfd6174305c167ebd74d63cdcfb21c8492845a90434e45a6ce73a3"
+            + "10e0dbc9b78bd5fdc4c35d2ba31475cd246692fa13a40228e730e655fdd8dda53dd9be1f"
+            + "9f2cb3adaad4f7157c522496140b708d24e69f6eba0a47bd7aeb6be2131d35ce15fc18ef"
+            + "361cdce67903d5070b7f493fbf4fa0de2d77b36499888dd3efc3958e22d14b362b09d4fe"
+            + "50c751707de6a471cc69b9717e6cae556a1672d6b7ca3569975957cf13999076c9830982"
+            + "1549c132eadcf1f0f627ce7755affab11b5ed63c2c9b3b9c07bfc7bcdb29d764579275c4"
+            + "dad26b646107813c10bd3eb93fb742574f561f3db752d8fd23aecb1e5b15fe1f41a6ac4d"
+            + "c0bfed115344d6ea942a45582ca5a309682cf41d1fcb31d5e02ec8505f92f642fbcd02cf"
+            + "d4152da4e890904d031b22bf0f3e752820e92b410af4e366d1eb490acaa10f849da3ff77"
+            + "43b900b02701405e50c7dbe444c86544ab4209b4f8c4e83edab7aff5824fcdfb5b5350f1"
+            + "cf484bc1b2abb62c8003591726c17a537fda146ed575c55e37804f672d64694e48bc9b36"
+            + "dfd475514ae4e98dd5984a9aad24aa34b45e75068b5b139f99cbda09776975fbca448f0d"
+            + "e92ddb0ce444ac9079d6be43bff2a996d645bcb59e7bd467236d1476e7866308aef87fd7"
+            + "fd236d1967569290fc613e285cb33ae657f342b56f6809d5b66a880b02cc76b6affa2516"
+            + "cb8a19453f2b3dd17f45542c7526d6520f3da91809d3ddeead5d65a1710b579970fb61a3"
+            + "974ef4b49408157bd34515cc188c3d6de8450be783349e112a620ae4b0b7c51af068e190"
+            + "cf6628927cd8808523e23e029941dc4e3293c9938d50fc634e979a5367aa213a40334aac"
+            + "f545467b954efa70fe2398df5a345b942e779bfe0f21e8e2d6c0a67d7dcdb7954335d4fd"
+            + "b3b4cbfaa6331bc33bc81f5f9fda1dc0c947ceeb7c56fad107bd925c78c87223a28a8565"
+            + "b90a18ce927186c8556a39adac746137c6e6b4628735fa2c2aea88d15b9a8e91508bec39"
+            + "41c70207112f862e6dd4b4521c430c4764fea490b9db36c31bd6839d62c0bdaad428e0fd"
+            + "3ebe9ea2567da61c0e5315eb3d6f2aa76f87cb6203f6bcb1d341d4d5c58c6a458f45d2ca"
+            + "4692f932f09a685069cac657621860855c15a14f0aa937e7cea46d6098a05854f86a6ef5"
+            + "daebf4b440fda7da440785d6d9636a6158eb93ef74c218e0c581b2a2be29b47dee78c155"
+            + "306bf1e34ba1974532c17d6d65f275a9682e0517d8239e364966eeb69a6bd48db6b1501e"
+            + "8a201a2f532794f56644b94e0384217fb655d5d13ed121804632d882fde00fae774d3308"
+            + "cb2f8bab09dd9b84c2e7cfee4330934bf915c5c9c14e0828ed4a7921c1aac99638830b6d"
+            + "50e302f22645706f62db2d0d053533bb0ead4e9e571b1ece49d5d9f69e0a9781594235cd"
+            + "4a3aab5286f006453c140767f3989bb993f299c09eec6012c36048c7009575eecb691256"
+            + "7a502b656dcdb1811754052474edb971edb06d47160a35c6ec032e9b1874762cabc766e1"
+            + "a082b6140fb88ddedbb490a5f8c965e3c8fc6983d0c4e4df4fed46e7a90713e68e032f36"
+            + "3dfb76c7cda8e9b8d58967a5ea3c2120ac0056d923a894071c8a5e88aa5c27cc23acd4b5"
+            + "d4a6b10983767b615bdf4dde4cd3d633cde44dae5ed87d90aeb83f768fe2570e7c432821"
+            + "6a38a692d854b44da9a9c88939c0deb7ae004822bd7683a4a3fa5ab89c4aa02188bfa4f8"
+            + "6fcb04a607795c61fd2f7c0fdc86a2b6e3ceca97d9bb9e8a2055171916e1dbaa6d20caef"
+            + "d445288bec13fda5f7afb9b798ac3667a4b85808bc8000e29d4234ca0b9fa2c1dd301cf4"
+            + "b50dc543259ec84e85c8c967325d4cfce573b356a5a1927e57c07d286bc155c559486e77"
+            + "ebfa3909d8ec69de049e7e7883edab3b7fecd44de737ea39eee21a83cbf46afbd1d6e915"
+            + "c9ba8dfcc96575d0a3d53b6afeaee707dff733d6b901e363e2748e1124f56db70869d11b"
+            + "87f878c269737a0d2ca113e00ad29745647ea3057fdf5c57f61ace002b12c2990654a943"
+            + "5a4ae6637fb00db78d2ec84ab0965cbb6d0cdeeb186fbfd48a9cac70e8685e0ce3fe0eaa"
+            + "ab80ecba1780e42eb6c3ad850b10dc3b576c1749095f7aead7f04a36d3770b645af5ec7f"
+            + "4b3b071c77a4a5ef94fd9233a9c8bf6ebca70d63cfd8b7ab6c8f743073611c992dbe1147"
+            + "8195fb08044dd3be994d9029d9c7be90022edc5dacf67f0f47ce8cf8de7a71fea93185d7"
+            + "d7a6444ba3fccc767b2164d21155c9d84552a99d51de0f552316e43a6e28f0b3bb066930"
+            + "0530c7386e429cd8486fb0d717722970194189962037bed66eee52fabd9e7a3bbe7ee38b"
+            + "7277fd82941b0358ff8e24ceb278445956788c6c91475317267b270036048b6d1c1fab15"
+            + "f14afccf255a55c1e5fbae75b17653fbea4afbadfbb46935b90e956e4497a16ef8304c5a"
+            + "43603a9cb080aff118e2a2429a595a6292c0251d32b28528e40386bbeadcd7e0b357cf34"
+            + "4b3bd71c5cbff64f15bc47dca42b76004b1d2567bf7fa9eb873fa84ecab14e8993d8dfe6"
+            + "60c48c77b7ac42105ffef587b49c314925fbe5071d6a32ab0165f15271e8e6c7d423bd97"
+            + "f3c9b113fdaf3c6df38ff626a5ad11cc00f58e751c8e3480471133f8bcae4fd9078b1732"
+            + "67a60320dcfb44bcded141d29affbe6fdd9e7e350de7199857c1e560c2b2ef767f5144f9"
+            + "90cdfd6e34978d17611d1d1bc955391d4f339050d7893c76488bdb13b2031a9be5e369ce"
+            + "cfb0a4d41a2a06c3788d3f38f3306d99d54ed737408bbe202301f1728e04b78651490b4a"
+            + "26edae95610ca437a40e54f457d5449a54cebe1fc73630cea63b869d3e0d7a268ab86a92"
+            + "577cce888c02ccbd2253e89dc661107f3c167ac1164de224b91f1a6765db3b20705c7088"
+            + "53822a825b87194c5f098a7323abe3d41634bd592d09414578091ed56ea9e70fc17092fd"
+            + "4254e3cbc8c0e302eae5fa705707b5174b1c84159927f5520c4f36a61ea3840f14f4311d"
+            + "9e9a49d0ed241cfbc8e14bf7fd39130cb571cac23a39044751915cad4d09e3264a2987b3"
+            + "5807b4893ae01426132a77fc5650721d4adc96a376e258ae5c4c57b10fc4ad593ec47779"
+            + "d34cb35b6ff63a789c0d7b67e9f5a2f698dec4891aa17f48d33ba688a8e06b48dd00a4bc"
+            + "d1ebb0ba86aac18c994889a6bf1f94fcd69c0c9176866c155314c54b0c9fe237d2ec770f"
+            + "801ce6a4f3231c48b68c53d629b222e5f540bb99d971a57b10e20ce8f9053c1844661bcf"
+            + "1f8d6a6f6ac3989f7f03e78bcee4af0958a4f59b828bc50e9c5b77fbebbb9dae09371f89"
+            + "f9ece5f84f7c37e9a2d58046e469d74088f2c6dd82da5238ef9c049b87dfc2ec438adda4"
+            + "eddc01fb19458737c7c594dc49b0cb28b7eb7164defb561099c7203bce1615042548eeeb"
+            + "5bcaeaedd5e6e96a29423e373d7da23be3594b9665796cc7fca206f894eafa335d0e5c55"
+            + "765ae7eb793221d792b2d9f996fa425a79d66456bc49372fe2c70069541624edf6ff50e8"
+            + "473b80ad51dbea7b55d778757801bb450ee44fe678d354c421940d60545a0ae44c0a108b"
+            + "7cf52545f0e1d4a5d55d4960d6f1d1e4b6edd72dedbe68d0892c8e706dedfc5c1ea81480"
+            + "d2e7fe39105f28e15ad585f57e089b4ac7ea973c560252351ef591bf2c1f0d89cc2853c9"
+            + "78f0118bb854394a91306e1241b069315e90bf7f5d0b7a8dc835d9c00462307f61405893"
+            + "b55adbe6212ab2bf6f47a6099c00218655faf2a590e89cf61ad58910b156b9fc5a398c6e"
+            + "29cf01341d9e443e2cd68288e4fd34bb2641accafdfcdf916c5853a326adae083836ccac"
+            + "374a186b07ccd0dcee2e53df4aed0751ada40c649b7b3f42c6542f6568feb378eb3792da"
+            + "325abdcb03cd42e629e6f74cffabab29d6a15ffc6f0c3131269808cd03e0068628d5a4ea"
+            + "6fd0980ef276aa772d82168cf0636df000fd1a9c9cc63f98d7ab2783531c8481d21f8529"
+            + "bd93e815b78249ebdf9f910e89ea0b8d7ff1e1a4c594cd2c4b15878f186fd33fea9a0b72"
+            + "f6c3cc0dd1ac67e2560d37c886f29e80a1dc3e4ab131882b5f058424f02e67d922e8782c"
+            + "926a2f09b596888329d36866f4296aef1f84b75893eacaa51283fc8ea3d27f0a029b70d4"
+            + "76a623a9125afdb70ab50bb5494faa5c8c2ffcacdcce931aeb2acc2a8e5d35af2331c950"
+            + "6e2f1b4ea9afaedd48644c0c642946b31330a140e59c59a7e53de994abfadda7b7e041d1"
+            + "ef27bc8ad33db485cb20726edacb43f9e92d8601fb8f1b65f00b75d5631c34564f8464b4"
+            + "3c69312fd30612586fbc0f4a5bb9002ed134519632c9bd3f1b2c4631b184a052ea8b2750"
+            + "034de21b301617372e89a17561ed8fe4c1941b79e80df64f8854d1544f8a4f8ecb83120f"
+            + "f06f5c9fe5593d4aa6513a98d1eff773d3da712b9b417926e333cb6cdcfe1a3cdb5a95b9"
+            + "41f784f6b07fdc2f5039a545a38ef038e3a549258cc1013c4fc4eb828f84691b9ff6892f"
+            + "121b057c7114c225fb558faaa81840e0fb888a1f65332b59c61e54e762a735364adca5b3"
+            + "f2ba4da825673961e8e37ce69aeec9694d9379e51397ccbc56f19645a72cf775c043dcb8"
+            + "b0c4d6825f85956dee9bd5b050042a97f1fb7b6060b9447c680e0594bb750aad68e81b8b"
+            + "6fb4a6778fb48aaa34c9a09c29fa1755739e352344961bc2899a56c8fb1058c0ae7e8788"
+            + "eb5a8e9c9b2f95b24e09e5b8de10c371ad281a922163e54938f0b5864b0fa5151973863c"
+            + "77f31864140a6e2a6daecca9af0f2f4afe43cc29d579d7a9b6d8a45dcdc373482bb50109"
+            + "da06d89b4d64341f89331c9cda84bc024ec8869f4b4a186f598e97625abe7e7fda7bf94e"
+            + "f5c067856ea0bb3528603d4a30f15140859a33706dbfb52b257e46ea29d860e62116e0d6"
+            + "1e00fd65529bd1ed291ed8592e3a804720cb48836fdd351c6d36647e254cc0c15f47fb87"
+            + "d2b5f5cd7f85e93091ba68de7a282b5ec5dfb32e272780db19f77bc36c0c4f45ab9f5779"
+            + "de0839765015eea5c41f8b9c618bab2ce24122a571462045e0df89082a23d708d6d74163"
+            + "bbb53ab360896698cc77204478befe31d47e5e09bae329ae6fa4eb77765fdf630806a756"
+            + "1389988fd3a40a48817fdf36bed7cc9c427592dbba3512df2fd0364290e07d284baf8442"
+            + "3dee7bb7db241d127bf172c99560de1196f860026b9f622347f6f1780de232248bbea8a4"
+            + "41f3a081b59f4a490072f19b8f9261d30501ee02a4bff98670cdc65d5ceee9f755897e03"
+            + "f6a77d787515baabca946172a2689dbc0faee12a3e8c16b5e1e18b94d843221d5edae807"
+            + "4dccc044aa54d46eb183d3ab727c57bf2ad2fe00be3cf07e3f3e5e0e5d1795f90301f059"
+            + "9f1d88ffca82732ebeaeda0dd3768041c5ea392edf584b743b72e7b17254df6e11dbd48c"
+            + "bc1086874dbcad4459babd65f00b63cce75b96aeb17f9ff234ce3c6fd61bd9581362d36e"
+            + "b418794975591ebf743eba08b0a2a684561442aabc0bdb5585aab31c9e880cde3784f4d4"
+            + "5105bca0ced46225cbf95fad0f3e368f2425a4aeb90f86f3c3d3af8db4ae2f15b3e7b0e7"
+            + "a8cdfaf781e1bddabf09ed3c4225652000f9ed5dcfaed66b67c050a078fa9a703eeec48f"
+            + "b9ee6e75b8a8d7c7dcae4fd8455b66fc46ed758caedb8c58f4a319f4ce35f4519b9082b3"
+            + "32384ed7767e77b3350015956f7ab84b9f6b94034e7a8a875079eda90129de0f8d964343"
+            + "9172ac3c7b533d43e2178782282a8104d60db119dc86197b65564aaae90491fc75873ac0"
+            + "89c237234cad8544525dfc1ed44be421622721ee7abdff3dd5e5dbfa27849244eb3ce832"
+            + "e7fa0bd8a57ef8d2e0dd322089abacf3a06793c1ebf9932b4fa886d813fd5b2d77788787"
+            + "d04150a2fca196147b06a508d7d0b0e1ef23f364f42af78536f398a27c2cad8545a66b5a"
+            + "16d068e5628e4b5c40b1eec63d145c15d94e67eeb3fa9e42089e1be6267ef2852b6b4bc6"
+            + "2340a3e79de9e34455d015c35f66bedd1bc734b499ebc87acaffd769d720400e828f4ed5"
+            + "292e27d18b69112fd48988a8b95dd1191e2e85125148532768f4e5d97907158b7fe11748"
+            + "f847c13ddd8b1a56d0c63937700c15a91c8836265635f85f6a5d8a15e45eeb772773f1e3"
+            + "06317e44d664628a19656814b01711e63a74a8051a4d8f3f7f6eed9a074b560bbcf99c2e"
+            + "768f4dcece3b61211c41818af61a919d487b26b32333bab43e020ee5354ea215932dd076"
+            + "114294c26d6c9eb00efe50fd61a6da7d4751c98bd48e0e1fcc8795521b1f12186c6beab4"
+            + "44b108e42cc4e7b936fb2c124746c9d754abca301f9a393d3e87944c0291a9e56ab3c68c"
+            + "1aee545109bcbb7a5f1aee4a6d8dc9886882fa125cd60d72ad595f2b5ce68ce6f2fa1b8c"
+            + "58d46508dea96fc67bb666337e13bb159c2582ab400ac6f385610776515a130798efdf15"
+            + "112f0b1b911712ccf5f5479c89beec0b1352aefb09c919b9f300936caac5b82408d1ad6b"
+            + "30bfd734c3b57417db7e9724f60434a91f9650dbb1d1d220f836a7c8bd143669e053ca91"
+            + "a5cd594bda857e29f937f653dad0ad5452df94ba675e09fb8952eb267334a240dfcdbab9"
+            + "7ff5558c3e4130c7472526192c97f58900e82995881cfb23b553f4ce6a8a4b5a83c63eb7"
+            + "42036c96c004a05349b5e5a07526bd52564fd9c37d2805a052ea589d5570dd551f2d8bfd"
+            + "578a95203e76b7ca8becb55cc3b8b4e2d23b105ec5cc171919ded2512ef2e45a80af0aa9"
+            + "8e3124e22688184d1182a9c323b140d8768c5762ed1c9b7225691e18ec48ec20db28b74c"
+            + "b0460c7184dbdd1ed4f61320088cb8c88e215d5c8447ecfcaa1949e652270a9222afce00"
+            + "61a370dfc2deab446467bdb615e1e19bd631ef24e15379d5b1f5b66e2061761f806a0cc8"
+            + "982f79a1396eb3e06782384ebc852b647c7b71c15b7e6a156ac0032499c399b131ebf9fc"
+            + "4624bcfeb9bef9c63ffe49ddba20f4d8b5f24114ba7f3fabe4146712e0a742c60acfe22e"
+            + "7c9caab22630c7c8916f075a3f9edb63654f49d7a6f7d366f0b7974035d1ad2fcac42582"
+            + "a8ac076471eff16dba3f3377b7332f316d386ebee955d1be778f89aa9e1cfa12ec453157"
+            + "84d2611294a8f6bc643e3faca8c8e0e154d24f13c9d3f9806356a4471444aef4d4268986"
+            + "854490354207ff745b28bfeeca5340beae75717d528d449dd79a9bd88a12f98e07afce60"
+            + "c1b238385b7b37a1b93d59ec5b8105d40942c1e2cfe7bebd6c44e4827f669a12ab0b6087"
+            + "20cb99c1d3d7942d7ce599283d2ae628e6b670c0c162b3aee8531dfd4a9ffb61fdf882e0"
+            + "a66eb0c866f7fb0b3b779b031e0eeb062703bb6605837f8e957a17c58404c73143b5ee87"
+            + "63e7c0e74c4d4107feb1c743dd39e491c69e271fec9565e81ab282c5465fc5d0d98a9425"
+            + "94ee17e4557bc04c14a1e9b3d6f997bedf3c9854f21334f67c30ec17e0a15e27116bf057"
+            + "53123ae537cf2a735b49fc90fc3b60f99ac5f5b353427a97d411699b4e93f36a3ed02734"
+            + "5e228c32078a97d69c5c18d1d791b83deb2865ef0e9e9a3b9d5b0f25192ba7c96ee05f78"
+            + "913a9f04e317082ff28b13b75e81e7fbf8c1eaedc4a2c3dd4230fbeb1ba300aee40c9eee"
+            + "8c0894c45051fed1c2f7c419b219bd845df48582733c4e818811b26baf6d133ed12bae7d"
+            + "ea0f3bbd23c599d62efac4d175eafcf0c3c3c797981df399ea62ad6e2095913d86a74bc8"
+            + "8a6a676b3a447422ea3178cb3722d7e05f2175cb67ede648512491391586f5c99654839c"
+            + "b126129ad7e59241f20223d78667a6fc2762b0e14dd732c62b549c2b22705b2b405f02eb"
+            + "2a08869c7dc35941362c37d64d2ddf8b5237fe789999ad6818648aef6b12476cba89095b"
+            + "66afa4d1bc3491396315007848b807019ed74e9e033179954af97c5745cb943dcb469d93"
+            + "3094c52d7a4163c80880e6fa24fdddbd7fcbb19fdad8a17e75e809a1ce08b6be377c3501"
+            + "e16676f837109c16e7ceeb67199fbedbdd5d78483370d0f61f4f941da4d77247256ce3e2"
+            + "aed312d09fdce505da883170c926bc1b459563775f1a5aeb409efdbf74e419caf7cc6323"
+            + "f319a2a0c29c63f8286f0d63bca4dbf42deb1d1a101fa86a0eb14182219fb5d013a87457"
+            + "2b4cb3bfe7567c1399e0b55bd2d3d050123cdc342dc18ef23707c27f3508d3f6fb423642"
+            + "d9b96cc1d4ab6427ea98aba621434d117a202b4670a8151e7cea68656dd90c4a7b44a38c"
+            + "cdf5dc6dfaf72f4c9455d0d0b690af8b478b23b4e61c886cf76b397287609658fc729d03"
+            + "a1ecf3774edaff7e6b5607cbb15abf4176dd5fd55dc19971f4aecc865b101955686ec3fc"
+            + "ba9f60cba95b3b1da8f0ba657a199735f83b5f41d99838245bef486f20952cd307d6a684"
+            + "e5f2a4741cd39e9e73529d55debaa5acd9a1c55405f4b0f04c164474dc99b810f5cb3bd2"
+            + "eaf17d65efee7e1893185e02e09c7b12b111fc0f1d2c9b64c95956556a02c5a4fc064e26"
+            + "18157f85f5ef469795f8b638461c7feea655c5061002c027fdc405acc396306627c97041"
+            + "abb1dbf46613ea28daa3898f466f02b2865b9f952d62f5c216250a0cbdd0495034718785"
+            + "4a273b03f9e983c17b6a064c78477c159043e29d7e02ac9dcae2ae40e25eeb65b177aa86"
+            + "461c2cfe6c0f22cd11d1c495a073a52ae2844701bf4a113687701a4ed1a7d6f19f9a86a8"
+            + "4a8ee7858b8ea0da61eae32eb4dffb2abb07315004c483e88743cd966faae60aa51820fb"
+            + "35ca298d83310e360603162d027e2041659bb43b719dbbcd741fa8c9d6227d24d31b7ed3"
+            + "fa792988f1dce1259d721a5926fafd81578b4376fcb7476ba565db4e685a2d6be116b07d"
+            + "5b44ea9981a5e580c55d3e07202619ceae9daf5e49fb5b13bee641f639ce0ebbb3286363"
+            + "aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d"
+            + "1291e925731a4bfd508698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267"
+            + "877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b55"
+            + "0406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc3"
+            + "7542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787"
+            + "d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d"
+            + "42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207b"
+            + "b7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf812"
+            + "5c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800"
+            + "b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd"
+            + "3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5"
+            + "ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462"
+            + "eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012"
+            + "c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc5673"
+            + "8c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520"
+            + "af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8"
+            + "b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0"
+            + "dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bd"
+            + "c2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d22"
+            + "79c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d"
+            + "870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb2"
+            + "74e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c87889667"
+            + "50673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991"
+            + "dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d9"
+            + "8aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa9"
+            + "34e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f"
+            + "985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d5"
+            + "90999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369b"
+            + "b2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba384"
+            + "48bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c"
+            + "414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853"
+            + "e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8b"
+            + "bd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1e"
+            + "bda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f841"
+            + "1421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61"
+            + "246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681"
+            + "595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d"
+            + "69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e313"
+            + "72d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208e"
+            + "cf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e"
+            + "4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a"
+            + "34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a"
+            + "63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786"
+            + "d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40b"
+            + "ca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a8"
+            + "3b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3"
+            + "392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e3"
+            + "87113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c1"
+            + "38f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b"
+            + "7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e0"
+            + "6ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f335"
+            + "1bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6df"
+            + "a0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591c"
+            + "cc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d4"
+            + "14a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd"
+            + "8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93"
+            + "018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e6174"
+            + "6892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a"
+            + "9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c"
+            + "1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b"
+            + "359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c5"
+            + "5f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595"
+            + "e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae"
+            + "816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013"
+            + "e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514"
+            + "bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8fe"
+            + "ebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a78"
+            + "7e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a94"
+            + "0dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea2"
+            + "3d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe"
+            + "878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fde"
+            + "fb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430c"
+            + "cd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe05"
+            + "0616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc"
+            + "26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8a"
+            + "d1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d4509796"
+            + "9f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc24"
+            + "7a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d3934"
+            + "7e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9"
+            + "e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6e"
+            + "d91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a65178"
+            + "8a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44"
+            + "252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a004"
+            + "9c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc"
+            + "1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c522"
+            + "35f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4"
+            + "a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1"
+            + "c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579e"
+            + "eb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979"
+            + "b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a0"
+            + "9ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf"
+            + "4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848"
+            + "ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36"
+            + "c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e3591"
+            + "28fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec"
+            + "1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd91"
+            + "2619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f"
+            + "2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b"
+            + "855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9"
+            + "fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa33983"
+            + "3289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b"
+            + "6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d1"
+            + "32c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a"
+            + "36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a2"
+            + "7abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec8"
+            + "2d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5"
+            + "f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e"
+            + "627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9c"
+            + "dd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad"
+            + "2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb"
+            + "979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb71"
+            + "1ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c6"
+            + "26e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6"
+            + "a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fa"
+            + "c6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd"
+            + "9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7"
+            + "b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a9"
+            + "44dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f18"
+            + "6bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0e"
+            + "ecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78c"
+            + "d4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730c"
+            + "f1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d"
+            + "1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb134"
+            + "45b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a3"
+            + "76bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2e"
+            + "afd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd"
+            + "58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43"
+            + "797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204d"
+            + "cc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973"
+            + "272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2"
+            + "e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40"
+            + "da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38"
+            + "a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b4947"
+            + "2ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff"
+            + "3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e"
+            + "9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91ea"
+            + "a357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e"
+            + "136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e2"
+            + "0a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc"
+            + "3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db"
+            + "857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d"
+            + "599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff"
+            + "56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaab"
+            + "fa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023"
+            + "a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd1165781"
+            + "08d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b2"
+            + "7eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9"
+            + "b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad"
+            + "4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db6"
+            + "28a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83"
+            + "688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07"
+            + "c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea"
+            + "9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e35589"
+            + "7f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264"
+            + "b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf914"
+            + "5d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57"
+            + "da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd"
+            + "9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff34"
+            + "68d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf812"
+            + "4324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc"
+            + "2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201f"
+            + "d9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6"
+            + "d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba"
+            + "35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06"
+            + "d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d"
+            + "8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f228"
+            + "1af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e"
+            + "54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa"
+            + "880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d4"
+            + "32820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242"
+            + "023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7"
+            + "fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417d"
+            + "cb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b"
+            + "12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b61"
+            + "2a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122"
+            + "df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcb"
+            + "f0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d"
+            + "474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82"
+            + "f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b0801"
+            + "99167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebe"
+            + "bac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df"
+            + "04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ad"
+            + "a17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7"
+            + "af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace293"
+            + "26792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8"
+            + "284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be68225"
+            + "67ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50a"
+            + "fda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d868"
+            + "6bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d96"
+            + "9147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2"
+            + "c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860"
+            + "f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f"
+            + "6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a86"
+            + "69d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e70"
+            + "4f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce1"
+            + "74b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734e"
+            + "d4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c3"
+            + "1d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662"
+            + "adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e"
+            + "22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690"
+            + "a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b06"
+            + "83921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3"
+            + "ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d354"
+            + "05cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52"
+            + "d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f3"
+            + "04f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096"
+            + "f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc25"
+            + "19ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e72"
+            + "59a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91f"
+            + "ed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce"
+            + "1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca"
+            + "722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850e"
+            + "f76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7"
+            + "ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c8"
+            + "85dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a"
+            + "32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb"
+            + "862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f2"
+            + "2b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c3"
+            + "63f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb"
+            + "7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad"
+            + "691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c16"
+            + "9776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c"
+            + "1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006f"
+            + "d4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660bea"
+            + "f946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5"
+            + "694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673"
+            + "826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97"
+            + "609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87db"
+            + "cc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03"
+            + "fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc371"
+            + "62b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda"
+            + "9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3"
+            + "dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b"
+            + "855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a"
+            + "8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0"
+            + "ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db2018"
+            + "86e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea8"
+            + "5a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab"
+            + "5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137ae"
+            + "f78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3cc"
+            + "fff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bd"
+            + "f14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385"
+            + "ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e"
+            + "960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedf"
+            + "f10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce8"
+            + "7b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef976764"
+            + "86866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15"
+            + "a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a2"
+            + "46a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2"
+            + "a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a"
+            + "9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d506062"
+            + "60186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880"
+            + "113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b8"
+            + "21ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d"
+            + "4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ec"
+            + "f33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c"
+            + "05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b"
+            + "2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422"
+            + "e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05"
+            + "d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c"
+            + "0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede"
+            + "0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f6"
+            + "31391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a"
+            + "6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9d"
+            + "d48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f7"
+            + "0a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6e"
+            + "bcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2"
+            + "acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b"
+            + "67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed32"
+            + "8437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1"
+            + "b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52"
+            + "243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a216"
+            + "2bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01"
+            + "027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af"
+            + "6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c"
+            + "9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b76353"
+            + "75888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d4"
+            + "5e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a38178981"
+            + "94f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f63"
+            + "41fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff637143"
+            + "3bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a4"
+            + "5e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4"
+            + "d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f"
+            + "91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33"
+            + "af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492"
+            + "983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba"
+            + "51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6"
+            + "c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561b"
+            + "ead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a"
+            + "7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ff"
+            + "b552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87"
+            + "a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7"
+            + "bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d"
+            + "2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213"
+            + "d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087"
+            + "ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea2"
+            + "1da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7"
+            + "c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37"
+            + "638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ce"
+            + "ed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee"
+            + "56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261ede"
+            + "f43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6"
+            + "f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676b"
+            + "e20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2df"
+            + "a21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc"
+            + "57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e"
+            + "4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec224"
+            + "4b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54"
+            + "383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fef"
+            + "df7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb"
+            + "3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4"
+            + "aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b4"
+            + "5134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b219"
+            + "0ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453"
+            + "b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b9"
+            + "2c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a"
+            + "688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103"
+            + "afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f60162"
+            + "04080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da09679509"
+            + "2edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb"
+            + "9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1"
+            + "849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b"
+            + "5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3f"
+            + "c9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7d"
+            + "c2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd865"
+            + "9b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa"
+            + "085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20"
+            + "f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669c"
+            + "ac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101a"
+            + "aa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355"
+            + "ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aa"
+            + "d7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf50169"
+            + "75c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79f"
+            + "b125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d"
+            + "12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e2"
+            + "91ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b"
+            + "9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c24"
+            + "47d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381"
+            + "e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59"
+            + "e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b"
+            + "63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b38"
+            + "1606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0e"
+            + "e7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b8"
+            + "55152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6"
+            + "440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee"
+            + "38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da"
+            + "83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853"
+            + "f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b"
+            + "765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743"
+            + "f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681"
+            + "ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f50"
+            + "11f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7"
+            + "cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e"
+            + "43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49deb"
+            + "ef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee"
+            + "94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b"
+            + "9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266"
+            + "290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1"
+            + "553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e"
+            + "7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc37"
+            + "19d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e"
+            + "92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b9"
+            + "5b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c43"
+            + "67f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c2535972"
+            + "4bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463"
+            + "fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b"
+            + "79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7"
+            + "edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfb"
+            + "d72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13"
+            + "c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e759770"
+            + "4084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c"
+            + "93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9"
+            + "a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901"
+            + "d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9"
+            + "b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933"
+            + "b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138f"
+            + "be029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3"
+            + "fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce65"
+            + "3e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c1"
+            + "0ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c"
+            + "9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4e"
+            + "dc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac4621"
+            + "7944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf0"
+            + "5fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec81"
+            + "1b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572"
+            + "b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c"
+            + "388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c0"
+            + "7924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de241"
+            + "1e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70"
+            + "b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a20859"
+            + "0eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de019"
+            + "99fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c72"
+            + "6ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333"
+            + "e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a733345"
+            + "3f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc"
+            + "8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb0"
+            + "11eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a"
+            + "96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430"
+            + "c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1"
+            + "e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9e"
+            + "b4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050"
+            + "168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf6"
+            + "6d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7"
+            + "274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce5"
+            + "60260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e9826"
+            + "67311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa6768"
+            + "3191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc7"
+            + "13109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0ae"
+            + "f43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80"
+            + "e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f34"
+            + "5532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19"
+            + "dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f"
+            + "4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2b"
+            + "ed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db6"
+            + "1e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6"
+            + "b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d"
+            + "5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562b"
+            + "e1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfc"
+            + "f54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20"
+            + "c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c"
+            + "06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369"
+            + "bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bf"
+            + "dc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c47667"
+            + "4d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b"
+            + "7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c5"
+            + "5dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e4"
+            + "8b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b3151"
+            + "4ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a46"
+            + "2632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b53"
+            + "5bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a"
+            + "3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda"
+            + "1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117"
+            + "fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9"
+            + "d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345d"
+            + "ef5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06d"
+            + "f734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bb"
+            + "ecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865f"
+            + "d73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544d"
+            + "de24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0"
+            + "a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f3"
+            + "0e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572"
+            + "733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a23"
+            + "2473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459d"
+            + "afe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f"
+            + "128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0"
+            + "e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658"
+            + "e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c728"
+            + "4f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95"
+            + "030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6d"
+            + "ae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651"
+            + "b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe"
+            + "414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342"
+            + "abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87"
+            + "260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a"
+            + "5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb83"
+            + "3f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80"
+            + "f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc"
+            + "77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6d"
+            + "ac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6"
+            + "b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314"
+            + "a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e7"
+            + "8ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d220"
+            + "31248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338"
+            + "f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe"
+            + "9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabeb"
+            + "fb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390"
+            + "950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34"
+            + "999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59ea"
+            + "ae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf4"
+            + "3bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7"
+            + "071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810b"
+            + "ce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e0076"
+            + "9878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f"
+            + "14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd83"
+            + "78d8ac445c";
+        String expectedSig2 = "01e40f5b447267c994bdc775ce6d98b0e559cba26c4261d70927adf1bb11b3336858fe06"
+            + "4267d0514a6d971b69939256b8ce8b3d8ba0d796413f561c6825e720e613aa3ae2415241"
+            + "50c17b9f49ac4cdbf986bbdbd5fe9367eccc50c1c92b961cedbd701216db313b8d85b938"
+            + "35ab94d1ac85d9fa186e7a99d294f43e7c68d867ec746ab79f2b3a523eab1fd65cc4c1c0"
+            + "54823f2bf93a7e535697919cd9b8d1edd375057a0654a2c7cac9e8296dc440e760a6b9e2"
+            + "79abd066782e07edc8f73608aed1d9a96b3ac55268171f26e75fff39f50cd72a7f5e8610"
+            + "3b8dd311193e858be1a56492deb2588b1b5ef1a361b88c67af7797d2692d5fd38006580b"
+            + "e6874775b1316a52c00b8b1c582e9f37c8967474520d6c557d9969a020f78f424bfbfe76"
+            + "190e34d56f0fac45fe1e550ff9e234d9a8bbe44520a18f228bb5b0c8f51d041134dd9e4d"
+            + "c28bb25e0afefd311c68355753be9f3c12f85458eede1cf39442c8d51a4f9297a91864cd"
+            + "98ee8ce77584174e594072df3c776dfddaf36cb1c14161965b3406d4fada8ba2e47e25fd"
+            + "90edc05b8d42c0d3cb326524986f1b3266cbaac2f3340efed4a103d79c34b4bdeda0dfff"
+            + "1f24eb9d4d41cbd19022d1cb2079ebd11e376e7b03161cf1a3190e507839e2a862905271"
+            + "4c57ef4cdbb2bf7ec7077d3727c5dc4642f2bc03bd2cb0dbfbd4bdf0e241d3b083913e00"
+            + "a1b0f4f42a549a407a74219d5c5be4e9ed2efe64619aeac7df02422ff85bd1e84b827b6d"
+            + "efd7f47015b268b4e0f11f03fb3fef025c06e7befd627ef03ae70ca72b99e4d6ab5efdd0"
+            + "3edf3ffda079a8866843425c2929550dac4f59e1ec47a4a0b25b651f6613ba08e31aff45"
+            + "a365e5d77c8b7bd8b88bb10fc4ca0b2d236cf36ccfe9fc95fecfd627957b2ccc3709fa7f"
+            + "503a332ac669f7a4e6867f298826853975cf6fcf3d22b57247b9939d5c12f9888b3d24e2"
+            + "1c26e8c081e8885a094bb3082ccb9493aadffc3ff600c5717067a41b74b175036cce1728"
+            + "ad47c38bfac77fbdd483ced51316c5810efe97c2af137c79a534cc309dbb6f91d31b52f7"
+            + "e173c6eff81569ae075d396a2f4a37ea71d5a14d910b5dd82cae41121b62cb496b0e797e"
+            + "66d6fbf9674e86f3fec5fdb6775048272fea37a6f956353a21e38237e0fe7334f4e5cf85"
+            + "abb817adee590602ce599d8c2d338384301855f239c9438537800cb102ccd6352232fedd"
+            + "2bd5d72a036786f4f291b0486061d56d1da2ef2392a373b6170179f236cf7cddead70620"
+            + "940013aa7ccad75feb73f7a2cbc74528c0e8be8146f282db2e1966d032f77855c6902473"
+            + "64416f07fab1e9ddcc49e45a20c846f2b83ad7ec9a89883c57cc9205ac197a2380329981"
+            + "c2d772a4698d19fa9651d6ab63163a3547fb3fa796ede999e61f8792fa28f5561c206925"
+            + "fe9ab9a74f91409585770515ae68d46c0eabfa6e4bb937db3beccc371cc7aecf1e0106ad"
+            + "4d5821eae4097d8ab10923c80cd81982f2fc742b48418c9af8cdcacb29ba1591b949d075"
+            + "1ebad228ec7d793dec4eb2df56a69c452a879a13882a23fd1a849aa718d9eacb04d90800"
+            + "202de9c64fe35d77847889ea566bb8a0a0736354f405fdbf0438c89dbd3fe97259858ae4"
+            + "db96289ce5496183080aeb24a45377d8e04e223e1549e72d6e3a85ffb5a049f60c017b69"
+            + "d0b60b2b6913b384cb88d02cefe09aa17244c12cdc881adaa1e62c903fcda166048c66fa"
+            + "db7aaff22cd26211a78950e29ba6f2fb66fec26bf9986e843771f873e84b5048d73ccd04"
+            + "abdd40e4a5dfa3ac9d0dae0ed58c9bd79243d44d5fdbb8fb682ee716075e04dddb07f415"
+            + "2f841cd50bd42f9c11466f57be633886c9d1c825f5cea5f7ae6d43af348dfb8a1b2d0cdd"
+            + "2186bc6edd46ec73bb15e684d54bc6ec7e1af48d5d52b9520d8cdbc8e57f9f00df895143"
+            + "70dfbb5692eec74525c7da5b5a056b47cde5eb100d50affb1c89a36aef92cbb6411ab3e0"
+            + "2846a7c7c58acfdbad52d5252a0cfb8516aaf51d2c1881c1a250fffd2091d014bc924365"
+            + "1cbb89c3d07078c355c098a60d72e00acb82b7b6594f259082d2ee3ed46d103a9d0e5cc2"
+            + "d0a557b975238190c6d44d6f4e88c0410bcf641082836fb6f766977ae623cd908ebb8f72"
+            + "dc6a42b21e12ebf4e1315e3422848f32600fee5698fbf1d6546cdef918d70b5144ae032e"
+            + "12f9c30ef0b9de7bbfcbb6e0d0f04b63443678936a2124e08f90af9bbab707ba5e3fe7fd"
+            + "60afbfc4a5be3855b2e62312d99f7f1f22512c4217ad36e82a3cb66d536b44d593f6dcdc"
+            + "df0010b40da4d6d865e2def1bf2b6b6916d7cd4865b4ef45ee9b3c799f4cd3f558bad618"
+            + "4d1761bfe0539f9dc88267f1e7dec04ebd423d8b68eadd5a65d29ad681e36973443c8df5"
+            + "f93d472d8b05524486b42758e523a04a9a2e534ad4293a5f558f2388609854a3df975881"
+            + "380f9bac5c22a70be11ff53a14cd69a1fb740b2c2dcfdcd2361f73bff5fdf7139ce51bdc"
+            + "7ee5e1f10d8bdb743c80e4d08cf74b07b034f70cb76cdd9215c43dfd10e4c825d94d8e94"
+            + "4745760441b223c915ffdda08c22c585c7209d414322a595dd04aa3aaaaed42260d0c6e8"
+            + "07cf4dd84621b8f27581834fe8a4423a51ba66b00c08389271416a82877f43201fe3aba5"
+            + "6319373cc64cb8aee9cddc393df49037d78c5de8026ff149fce50aa50d9207c842ed8532"
+            + "2ac391786af1a011722db75a9a3eee022ce8a39d59d587bfa2f7afa707dfc9a36f60e0b0"
+            + "df2775d32548e33a91929ded1f4b5b61b66fb193be72dd575ba68ddcbc62e271aa507dcb"
+            + "03f08cfdc4c34c62d6b858920d2f13b32c27b4a4675dda223d255cfb4d45cb24906e2c3b"
+            + "d8811adc3136c6483b689a0662b184e5fd1fa4b25f6bf100d6609ab1b5bcde4e2767e4ee"
+            + "36bb8a11bd3c771337941deef7676ec15ff3314a29b4ad0a679ed4bf90264401a71fdfde"
+            + "ada3666604fb05958fd46be35c63f1729efbd18ee60a3173efe26ccfc47f77d3082b9bf4"
+            + "7ea7495875f3c00bd920abb6745de0aa4b2a773cb2045deaabd16411efa5e9420993cf9a"
+            + "af90259c1c6971055b56a452ad07b3d50f7adae206b3663178b91193c18ec07c651a4e28"
+            + "7a805d73f26f16298f01a6d999cf309f44705d4a60d0bac9cb6de4b7c710d94260313ce9"
+            + "5fccd213a6c05f920e8324a17e9ea8389e828cd7cbc35b5a8b5875bd8ff61146e3786070"
+            + "0ba2be69e3dd75219d7b395579052d9f3812c99a2b3323486808dd33568a43f8f6e75c2c"
+            + "b50c6200df42a775caa87d419de00273e6971001f384c2e6a789ebeabdfd524dfb33330a"
+            + "4995cc1c37ff80532a69c7ba2e18a0a2ab116f3ae0cb874fa23de6c87fae3d7f0a194cc4"
+            + "59874ee7ef96fcedee8cb3f261d01b3059709f99fe6a248fd6dbcb882504600d4711de90"
+            + "f807ea6c88dbcaca6b91703148b2d1fc58e6c574affd9177f44542cb339169cbc0480a77"
+            + "6ae20c79cd04f62b848d7505f5caed8c85baafa9a8b46a2773490e652e48e4c58357cc00"
+            + "8ebb9ced1a8f6288d23faf884cdb03507bc403b88ff0dc2305789bf05e12039536a502dd"
+            + "fe7b0733008e7301763468030da26bd446e87072624bbaee9e859dedffa01efa0e3a9a2e"
+            + "e09198d004847c61efd522e8f6210778500a01905c64585ae70f1d7694fd284fd91b98d0"
+            + "ff9fec913c77e166113a59f7e8140699e6593242565683e9a75c77ad30aaa6aeb3e84dd2"
+            + "9200d17063a63baa798c8dc4f1f1cfb1d35ea9f68023346f25b502dfa48317fb3970e58b"
+            + "9eba6e64c5070bc93fb6679a42132312e650616c05f120f365529eda556a2092736ad9ad"
+            + "bbe6674d3dbaf0a4b1801202a8a9fe715b002643e0db247140cadd24e8a330b32c80e914"
+            + "c7ff0e9496907fd66e76905547c7c3aab6d11bd53066c932ff5a8fbe72370748b9745fb6"
+            + "a27b477287522ff13af06fb59b309a14d266cdfaa379198761a1388b79288107b91b34d3"
+            + "5d301addb4ffd345f2816284dc03289d6721fb867f78488035357cfa9d4dc7fda336f943"
+            + "b613565e3db190d9c1110e695c74e8966f634206277d8c380fc44182e6689208821f972c"
+            + "1e4f355f4599e6a9b151881ca3e64e752726e5118738894884344db8b83835d6ecb5af9e"
+            + "d3215001801ced0a549ca4093eeb858198e1cd0956420cce95ee41a1158aea1dfa99b7f9"
+            + "144b6dc27fe9a5086a4599a1e90b7e5a39caf467106b4dda79cc990643e0754217cd8482"
+            + "2265ae6f45011f0a1662bf8b4977948ade6db8ff47a0843d224121b6933bbd77a6a4c241"
+            + "3e58fd5e9d325a1d24b039d7374588bb1651478caffc77ef54d52c6bc1d3cb8c0e198308"
+            + "8f9d00fd4d20fc335a37dcfd93a10f2e417015d51a2851ac79dea91e23f43d88ce2a3dd1"
+            + "44481f17f073ac06bdba3edf7f8b5539bac47432736ecb4823885d9b49740586d0f98451"
+            + "3a2797fb0b24d160ec7f31022f501af3466ed0be36a73f8e05b23a9f319f7f61e3a8011f"
+            + "8318d9dc037210d67a85b584ab48ce078ed01416721bfbe082ea5edcb5299e695961d3bc"
+            + "37801715fb1e5e7d89fcdae270f8f680ce1a4db609d93c3585e7121dc93b136959524b80"
+            + "79d9e325b3427160d6f7217dd412ef31a0cbf9426007779ce2714045ea61eee710fb156b"
+            + "d482d71accf8789b17dd333bd7965b72dfba9cc794c1ced4cb0bcd551ba70f980797defd"
+            + "43b06f12350d047a45c7003c61bce4760357ae904e34811a714fc80c31407edeca492787"
+            + "fac40e8062ca6d392a413902eeeec47660379ae96d273aa5bbfefbf5d6d2f32ca1ee7a78"
+            + "c13e37a8cbde35bd5cb2652c2b13a0c1081d4807ef801246f2e76d8db95fed0ddf56a219"
+            + "e0e2a9567b7b8a25f42bd328a6bf4b82c289338b5eac473cdbaf250be2e3d8b17d55022e"
+            + "79b16a13b13b031ffdd15a22668a11ee6266d2dd38335ad6456b9acb03e27bcf62c20a44"
+            + "239daa46ad539697ecb05af10eee730873c95abb33e7f89984b26a6acc76bd49b23176e6"
+            + "54afc1eb2c48f9e079fa9d88c465bc4b13a7a6e9d44ed123d74f67a00834427d58627ac3"
+            + "48b8d213217ddd932ff9865323028a0889edabfa5c21d48f337bc83c0ab2ab84eeec89e0"
+            + "60b54ead106d42db1c9dc77d756494e078bad641e2522f2030c00ebe184f633baf34c0cb"
+            + "9a2ad6775b7a4a6b966b14a24ff6155f8aa6a598e814368f0523631b5a2b3ec845f86f87"
+            + "69a33ee69336ba833880d1c80c9844379d4f40183a7f37662f6d1bb02b90c65d83949fec"
+            + "61944fdcd7d450d7ccf870e1c69185e5a7e2dfe56d6d44c3811801002cc5289a82a5df34"
+            + "5ed2774acd24de65c096bb45866e104c7388ac6661ea667548146c890bc6c44bb1824b2d"
+            + "9dbfd885c969bc8ccebab308da78fa2b2538a9d7a10a3ce2a2d6662c6b2eb60dba7fd8bb"
+            + "0ae8623c7eb0f09eb9f24f5f5db49285fbe583507080710a0fd22120d11d3b5d53de4b6c"
+            + "695fdfc095cdea56128f9728cc71d13f0332e6f597d8a87c7d52140eb847dc76c1d6aa93"
+            + "4f83682567b1cc0c848a883c44c1af09bc97980e83dce964293353126beb1b7a7a8ab505"
+            + "62da1db75ffb9e1cde3b2c458cfac0d13e4b2ceb247ab60c189ce54ac0046ca20d15756a"
+            + "1397b3a206dbdb0fb5a3f7309e0f7025e1113ecdcb4d2d18074e8230f990edfbdd4eedb2"
+            + "4bd2e82a17f228a7896ca854121f43c3d039ba38ae6198ce8f57ff91196c6427905181ac"
+            + "22e885ada2a91818ebba779af1d4ee4a4edfa0399c5d6c418bd702f1ef0588d5bb3653b3"
+            + "d7eb8786cc90b8d6ef6ab9b7a4898dc6681d4f000708a558ef157149e3826eeb136c9cff"
+            + "8ebdb849f02dd560942c1db73b2de5ea3515b064ef57cc877df7a7874aff2723b1764d41"
+            + "a68f9e6d234066da64fed9ad0bf64c8680befbce0594e1f63e4da7cd33afa09c69b47e2d"
+            + "ebc17a88d3c45196f2fbaded16af67d93c173523093964675f41a4f8b26c02c856176222"
+            + "2cf3b329198df795c4692c6e571a672aa5b48ccb74510871c17a506116398328aca6d229"
+            + "24025e1803a5410e726bbaf11f999642a5f72624e9e379d30f743ff707d03828e43030df"
+            + "3392e83120e8d0a1d03053831283c72736c0864a21b53c6976fe4bba9c0152370d6e5a7c"
+            + "04a780240f3b17956a9320d878d1d852ebacf4e9e32c8e5a078446a3abf918f7b43d28aa"
+            + "777b90169a6bb1c9d0aa1066f66698ce9487c8ab85f993ab0fbe43e44061d5a6be6c018a"
+            + "da8c9a2b5593fb4c7e81cb57ac489ab4740ef499388651af6cf791fd70980c80a8cfca4d"
+            + "efa0651b39975a73ee4ada7ec9c54ae479eeab35a86b91ce28942334833c8f3b7a97b7eb"
+            + "3a206ea0e781f801b39e28bc970bee35584c67f3fe85d66ea34b5442a3d3badca19e52fd"
+            + "c7f8f86f5755b43b1807360432bf0c6722df99a2c9a2f1f9c05f9d1d9a358e5598172619"
+            + "404c532e490da33d2178404797eb64e0132b031ba00c0e34b36eacf35cc579a2c4e335a6"
+            + "21a1511187c81bd424e6829a92763fab1dc10b88626137a5f147fe822a20d78a06c8ea79"
+            + "d352c8083ae8174723f8838e1fd14ff0c7cfcaaa5edca09c7825a0a3c94cf0e2fb112186"
+            + "1c4f66b2bc7e9e5bb9c0450f947510e920a95e6a241b9feee4015947aae1693a2a097ed1"
+            + "a291646e1a85c03c94c932eceb06085a2930c71b9063637650bf7e09ad7fbb5f5e7e429a"
+            + "273fd4f1a7c3d288a8d53330f2d3f0f7c9100f36cfbadd24ead6354177ddd29a5c41d5f9"
+            + "5a68885850b5fe8c00004ddaa68ba6b6b212e14d97daab317e8d8722de72ec762de36bf7"
+            + "c2f1501f623b2f89620ef1db90050582a9882cdbd30971c2b0dd858a9296dd32607eb183"
+            + "3eb9651135133cc5aecc087ed28b49cebb25068be6d962df9afcbfc570d0cf8e0b30c2cc"
+            + "cd65c3e1e451c4cab9b23851ef91d364a3743115033d81ff84a2fa63b02fe8716df87aec"
+            + "a899889f5b8e6db5000055cdf12cae1614dbb23488af69265ea4b9457f39c588dc1a57b1"
+            + "6c5bc5eaab01646023dcad347e31ce1c3e19b5dab935daf129c8fa10ca341c246c1df7a1"
+            + "cefc357b34b45365104d207b648eef34a4eda691f9f2515d306e63f01612c6971d0b60d0"
+            + "50550d185c1c56d96bd5e653b895e001187ff47a43841773786f3a2ff840865181ac9231"
+            + "4b6f4ac538e9c0436bbfcdd674dcc6fd0512eedaf51c272b1c843da8612b5fbc7049960d"
+            + "63ff64cebf203591a538c8c85a351338f7d66730b7c2e37b7a630c81047ff827c1945a07"
+            + "d6bad7b2fbdf2d483378e1582d26084633d730c1bd1863719cf248382ea44bd4e821961e"
+            + "2e072f33ba12649d41ecf5044f24d25db2caa1bf7df0de20ee38de3287afc8136df44d0a"
+            + "10ff06d22243a88857201e111d6aacf8536273a77535e161be2d398e53a73eba229ac791"
+            + "3ddcfa280ef946c92987eed9c100d96139c190ec0b6c0d2688d050673e78ac3b91f40aaf"
+            + "c4a92df24861707e02a7e3c890395039c361967cfaddc7c6dfcee4abe8c0d8ee642943bd"
+            + "da0bda6fc71a632eb35656f1f889e42745860f72cb3a5cbf2bca16d1bf5474da3dac6b26"
+            + "dc79a8ea042640a3e5b54b4ff7ff161c4f2e679d237f0f097b876ebba828586b0b788899"
+            + "7578f8c396b86b7b208ba86eee119cc4933f7a8dd62ad337390fb92e69c8fdb8676580b5"
+            + "9464946536789416aa65b074a793bd44cf6896f5cdf4429a702662a4e7b8594454909b17"
+            + "cb1adc60c33f68e04ffdf71f36add212f2209e50a4af87e90211e0080d5332ae8b02894a"
+            + "9f708fc7bd85d76eb02e1a09e7bbc584fd28704fb5068c99416415ff47c0b97da37e267f"
+            + "dd597a831f81a5e1afdd70848967b9cee40668af36525098d7f62fa1a08afa180cc5ff4d"
+            + "cfcab005a407dba9f7ea9f3c11674f0f84a597db00de9cec80d96fa5798ad709b22938c0"
+            + "afe67ff07326c900a96c0958e06cf92dc9783d85deee989f7bd4fadb395c9dfe9c8310ed"
+            + "dcb494a6824226ca0650166349146ae3af96c81803dc54dd4b9f7c255ae6a92a7ce0d095"
+            + "204cd309e8ef5e00c41df0dc1244ec5fa07b7e31f628af0fa40fef97556ba1c6b67e736e"
+            + "52a06eb8857094f9b005e218a85f9184ed41108927d09a8facb0aa6d520a232ba53140d3"
+            + "b649d872463c9c8e5fb771d154381a47116612593f06751cc43329475dfd33a2dec1e639"
+            + "39be98b5f00a9c0d3699479cf6368f634a2e5a7d1ed516c79ccb4dd1f4a738bb44e8bde1"
+            + "8a1c4401fdfe5c7f04d7aa6ddece79de6f8fe8c021542f2f4f0f4bcad0699f49902ffef2"
+            + "5dbb16889258fb32e8b77503d99e3c7a625ffd9a4f969c2d0961f85295704ea85a7de450"
+            + "f97e3a4e43f3ac86f16be8a35b3a49ab12e26480dfc6c8938a9ceddb0aa8226cdfa1cf02"
+            + "602495279e8bc85f37eea24fa1e5a5cfc145b1b893af67349cd8d5971dd902d0d2b8a2d6"
+            + "3b59b62cc747e8861d6cf288188cd000be9a4caaeb1dcb3e8aaefb6c722953235d66f881"
+            + "fb9ab34e30343bd8fdb125f084b53abf40bbc0dac12a781d3cba457ea0c7b0fb7fea40f4"
+            + "3072cfd2873903cddf1ef824ed2abc77a1ee7322a8d9968fadc3764ef441db4e91f87427"
+            + "c38c5b5cfb2b0bd95a2bc382f6ae19dcb2522c7a3b637ba584c6004151e688199bfabf64"
+            + "570be2f66bfdfc48d574b15fc38b536d21050bcdc466cea678d85fa965c29fb20e3e3877"
+            + "713a0c8352b7f51fcbb43ba9b93a43e33b006094a30b309010cc501b66b85ed3ab3bb251"
+            + "f793bf65cad1dd1d1b3de8eb397a1cc06b546f318d8ac0c9304049c2ea5cd14cd74a42c2"
+            + "5110fee893392f3e69e9f83b4c05776c396983a3466ca23ce2b337063e93f5ebc2a6cf39"
+            + "3653b531bd20a0ed0b104d358c04cd553804dedfd1887f66999202a1b530732667dfb61a"
+            + "1a984b40a09a7af20062daafdb1b583b602e96d42f77c74c77e12350fd328f6f34a86893"
+            + "bdec687c03455747a62953027dae4e927ad64f8e4da6b9a1991307ee8f02366585ede811"
+            + "a0a87b0945620c0165b1da30cc781726173835720ed74af663decd7d971e127d7e922aa8"
+            + "44df09e7f0b6cfd7b10980ffd8395072d21a937b5cdf03d0c0b4d9b4f2c618587afec976"
+            + "35987709762fdfeaa5ac2b7dd8de83b12fd40866a0bab512a2fe4345f540a3ab92517fc7"
+            + "8db310d2af63ce38467d34ec541958977857b6c5ee228baa095e65cc25d651d244012887"
+            + "99cd38cf2a0441aaa8c26fc45b0a10e3dbf14da8156fa02a054c60181314eb4ab9addb37"
+            + "5940a1802e9bdd878427af72744d949796f61dd15f93c5c3d2fe791bb696d45ff5193a92"
+            + "9a2a2f8ca18d661cfd7cd8768539e38c24317545d2a0cc642874ff3a46e5039e5d7ac411"
+            + "4614b5f77fffb37e28367ef081d39041d27fb29002d48426b685211e61ddf348010c8595"
+            + "db8a0488d08e58435f43c7c29f5e8ef62d402e8c8337bf5f44ed6f227968679081d70bec"
+            + "6239c7c9d3a780c6110bf0d99033caf4ecb2bc563a1bc7aa3e6708f5ab16d96fb1dcaee9"
+            + "cbb79cf361392d99fe300b973d94f3eb56beec73fe060db2b77b7f1315c62ebb3a22cd26"
+            + "4f56b78b186f06a398f1194a2f7596a48adb387d050b1e012cada908ba81f3a73b77cef2"
+            + "809b957b08487a599f20c2de4a9d3c4692d57392ee59bbf5dd7e1998e3fee488c437355a"
+            + "9c91d32c9582a841453dc3c22b02c3400af2264f0242116af905e212a6abcc37052d5e68"
+            + "4b6c1344279dde536caac3b4830a97a31be1abc900d304911db62b188444b6afa5e6cfc7"
+            + "38ba4384a0c01add2131841e8eed06f7071703fb097de0cfcd88d35a9ed3a1a9e43cb479"
+            + "5815535c31187cb5b00e696b1496e433e68a1720d069ac42904669f572dd9adc334e112a"
+            + "58c5f61cdec7e5ceb985adc70acedab53b39b819a434d4960f29393fa27dcc2d4307d5b9"
+            + "744b7a29668bef7e0b2bd25302a935bad30318a47675f276af5e0c4e353def3fd38f8cd5"
+            + "cc8f7c6de3c9d117e9f77e0d34df0362cbde715a2e4f0bc39332b33668a3ac20b956f808"
+            + "481632f725059c1245482d14ce0aa87fb3eed61821383d23ca2c3f8c08e26e984c7db4e9"
+            + "5f03763bdc7fa012a8f8d83153a9dd191660f003be85ba59a1d3bda02919feb8b3dd1b60"
+            + "07bfb5b58873bbc2ba9e06903445211a0f3deffb474bef06f96898935dd54112185f966e"
+            + "b09b88fc2a4c1b4921068d5a29114c33314c028d95039e6431be1f7541765f0aa4b85426"
+            + "39147e5cea89a1ee77d05dc9de90d306bed011d98826b54aaf7363e2a219db086ade6719"
+            + "fdf1ef4d93203d2354c3c8d3ce9eb8e66d59f2c295e5711025f76896c23e3e7cfb6e1d62"
+            + "3ffc418fcd2a1dbab57fe0389da900d65bd054eb5514578b61aa4b89c291e3db90136758"
+            + "d66d3c1e244ed69ca676b288cf8aea0a17f487360a91a4e01c217860bc6bdb26b6dcd61c"
+            + "72ce271deaa0c9997cf1f4f75dd7865ddbd648dd6fb18cf6f462bddb46257f87c58191de"
+            + "0a6dc7ff59c89b353e7c78982efd64fd5df24c2e2cc63db48368702ca55df3ab8787a76f"
+            + "373fe33e72df268beb3ed61b91392258a64efe324286ffe80de31a1f5916a7b80bf2f24e"
+            + "4374a721a30eb9d5b95edd5501e2b80ac3ac2ee384aa01f0b80d38a462032e901586682f"
+            + "3eb6f26b5c52adf355fa44cacc80e96114c133b381c2b79226f2f5c6f120926afc4ba016"
+            + "1c9b643932c2aae72a54dc680034851051e7b78681be9f2f23da8f8612ab85f07ad4788f"
+            + "fe4bff5c18a1a17064de1de14be917f6cedc21027d4c3614aa5afa1c1cc64053458ebca5"
+            + "6db5b677f613ff148d3610ac6943fb1c210280d83e05dbfae0a1c523f16c08452a7603a3"
+            + "00038359a1c5ebc7ebf7487c9980d205f30c964d4eeea3f266ee493cc6dca41544f29bab"
+            + "45fcb5de8a7dcddd9330cbcda955670a8f92a938adee32ef40a20cff0117f840b60241b8"
+            + "4556e0295e854780a86240df42ea75c9a33331f920610b0e18c5a152a59ede4b40ca40fb"
+            + "569165ecdb93fde3d44c0e1927b929d60ad80c4e295bbc6a1a67596d14dfd80fa2bd38cf"
+            + "8ec639fffabdd725ee1d34aeb54856ab844a37efee8c4df9b6a4474e1fbe6f0f5ea8fc72"
+            + "5a2a78b3ae6386f1ee8daf49087bd573bcffb617809bab28220c2d5a3e9ddd316f7b4bba"
+            + "8de4022a30c0b27d9232df1f25ea199ea7d26c1f935e54d9c49cda958b761eb43bd452ad"
+            + "b0e7a30e3ebe87aa05ab1cfaa1485dcd7634775c66df3a17bd04aba333d6dc73bc9d52a0"
+            + "eeb78dccf81951adb6187578010f9db56571afac84acdf6936f101409f6f3e8c038dbcd0"
+            + "54422f28c1aafd0929bd30f5151b27d03e11c9259d225f72e066da74bed6caa6fb758483"
+            + "49610a48f4dc2827942596d54f1743765729a9453789ca01b2f19194e4bc74fd239138be"
+            + "66956eda707f9eb7215e4b377c0486df98b23d8af13578ca2565fb56461abc1a492615dd"
+            + "f5d38c1200d3c644543f08079482e014ec02028b7836c2c6ded978cf5dff4180d0c62a8c"
+            + "de12e4a7cec10a1da309ac9f787314bc2b5e83ed3a46031a258caf653f46a78ac6d7f35e"
+            + "079fbb3f28d356a6568d9776487a4dc4dba86fcb6d03674c596cc7da17bbaac12514d557"
+            + "5b406d9dfde9e598bce564a503a38f96a7d70cf60938bea798d363a69edc64a9b1ac9fbe"
+            + "a1863be31ece7a857d47819f89c6b03c14d062d78bd7542c712bd231e5b771e72d1dfcff"
+            + "aed9c4bcc53e1a33bb60f9e40c6a57c5c5674214782d1823ea0470f9a399d7dde6a14d66"
+            + "e44010e3538ef8ec7bb31b06ffd4226b278b710518f1fedab126e5521f30f6a2d0b62e89"
+            + "dadee66deec949f85bc76a518decc5d2feacd924dc18e06f8915194e82c9136073c8aade"
+            + "c284032cc250555b825d79018c0fe4e85a155109403bc7d081fe49b9cae484833690936a"
+            + "f43f966804b434fbcf17e7d2527d2f061db98e0541e23bebf3024023694ef4fa925cf8ac"
+            + "44026d0e3ba5663c9792e80ed6038cfbe5549092f63c6a60b8a88181a9745ab291b8a25f"
+            + "0aa2cb7ee40efb4e5eb802df9b4e24509c75be717a10b45fa4404c6fe7b31d23aaebbbda"
+            + "44dcc2cc3928b620aabcc4f771dc7947b59daf5e49fb5b13bee641f639ce0ebbb3286363"
+            + "aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d"
+            + "1291e925731a4bfd508698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267"
+            + "877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b55"
+            + "0406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc3"
+            + "7542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787"
+            + "d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d"
+            + "42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207b"
+            + "b7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf812"
+            + "5c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800"
+            + "b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd"
+            + "3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5"
+            + "ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462"
+            + "eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012"
+            + "c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc5673"
+            + "8c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520"
+            + "af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8"
+            + "b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0"
+            + "dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bd"
+            + "c2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d22"
+            + "79c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d"
+            + "870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb2"
+            + "74e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c87889667"
+            + "50673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991"
+            + "dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d9"
+            + "8aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa9"
+            + "34e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f"
+            + "985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d5"
+            + "90999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369b"
+            + "b2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba384"
+            + "48bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c"
+            + "414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853"
+            + "e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8b"
+            + "bd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1e"
+            + "bda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f841"
+            + "1421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61"
+            + "246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681"
+            + "595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d"
+            + "69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e313"
+            + "72d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208e"
+            + "cf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e"
+            + "4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a"
+            + "34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a"
+            + "63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786"
+            + "d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40b"
+            + "ca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a8"
+            + "3b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3"
+            + "392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e3"
+            + "87113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c1"
+            + "38f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b"
+            + "7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e0"
+            + "6ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f335"
+            + "1bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6df"
+            + "a0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591c"
+            + "cc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d4"
+            + "14a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd"
+            + "8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93"
+            + "018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e6174"
+            + "6892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a"
+            + "9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c"
+            + "1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b"
+            + "359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c5"
+            + "5f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595"
+            + "e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae"
+            + "816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013"
+            + "e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514"
+            + "bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8fe"
+            + "ebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a78"
+            + "7e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a94"
+            + "0dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea2"
+            + "3d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe"
+            + "878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fde"
+            + "fb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430c"
+            + "cd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe05"
+            + "0616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc"
+            + "26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8a"
+            + "d1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d4509796"
+            + "9f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc24"
+            + "7a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d3934"
+            + "7e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9"
+            + "e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6e"
+            + "d91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a65178"
+            + "8a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44"
+            + "252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a004"
+            + "9c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc"
+            + "1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c522"
+            + "35f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4"
+            + "a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1"
+            + "c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579e"
+            + "eb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979"
+            + "b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a0"
+            + "9ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf"
+            + "4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848"
+            + "ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36"
+            + "c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e3591"
+            + "28fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec"
+            + "1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd91"
+            + "2619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f"
+            + "2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b"
+            + "855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9"
+            + "fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa33983"
+            + "3289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b"
+            + "6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d1"
+            + "32c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a"
+            + "36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a2"
+            + "7abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec8"
+            + "2d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5"
+            + "f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e"
+            + "627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9c"
+            + "dd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad"
+            + "2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb"
+            + "979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb71"
+            + "1ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c6"
+            + "26e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6"
+            + "a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fa"
+            + "c6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd"
+            + "9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7"
+            + "b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a9"
+            + "44dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f18"
+            + "6bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0e"
+            + "ecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78c"
+            + "d4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730c"
+            + "f1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d"
+            + "1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb134"
+            + "45b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a3"
+            + "76bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2e"
+            + "afd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd"
+            + "58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43"
+            + "797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204d"
+            + "cc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973"
+            + "272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2"
+            + "e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40"
+            + "da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38"
+            + "a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b4947"
+            + "2ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff"
+            + "3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e"
+            + "9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91ea"
+            + "a357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e"
+            + "136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e2"
+            + "0a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc"
+            + "3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db"
+            + "857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d"
+            + "599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff"
+            + "56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaab"
+            + "fa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023"
+            + "a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd1165781"
+            + "08d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b2"
+            + "7eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9"
+            + "b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad"
+            + "4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db6"
+            + "28a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83"
+            + "688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07"
+            + "c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea"
+            + "9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e35589"
+            + "7f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264"
+            + "b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf914"
+            + "5d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57"
+            + "da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd"
+            + "9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff34"
+            + "68d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf812"
+            + "4324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc"
+            + "2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201f"
+            + "d9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6"
+            + "d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba"
+            + "35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06"
+            + "d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d"
+            + "8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f228"
+            + "1af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e"
+            + "54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa"
+            + "880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d4"
+            + "32820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242"
+            + "023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7"
+            + "fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417d"
+            + "cb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b"
+            + "12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b61"
+            + "2a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122"
+            + "df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcb"
+            + "f0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d"
+            + "474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82"
+            + "f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b0801"
+            + "99167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebe"
+            + "bac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df"
+            + "04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ad"
+            + "a17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7"
+            + "af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace293"
+            + "26792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8"
+            + "284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be68225"
+            + "67ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50a"
+            + "fda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d868"
+            + "6bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d96"
+            + "9147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2"
+            + "c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860"
+            + "f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f"
+            + "6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a86"
+            + "69d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e70"
+            + "4f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce1"
+            + "74b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734e"
+            + "d4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c3"
+            + "1d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662"
+            + "adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e"
+            + "22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690"
+            + "a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b06"
+            + "83921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3"
+            + "ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d354"
+            + "05cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52"
+            + "d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f3"
+            + "04f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096"
+            + "f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc25"
+            + "19ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e72"
+            + "59a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91f"
+            + "ed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce"
+            + "1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca"
+            + "722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850e"
+            + "f76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7"
+            + "ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c8"
+            + "85dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a"
+            + "32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb"
+            + "862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f2"
+            + "2b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c3"
+            + "63f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb"
+            + "7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad"
+            + "691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c16"
+            + "9776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c"
+            + "1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006f"
+            + "d4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660bea"
+            + "f946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5"
+            + "694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673"
+            + "826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97"
+            + "609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87db"
+            + "cc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03"
+            + "fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc371"
+            + "62b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda"
+            + "9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3"
+            + "dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b"
+            + "855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a"
+            + "8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0"
+            + "ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db2018"
+            + "86e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea8"
+            + "5a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab"
+            + "5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137ae"
+            + "f78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3cc"
+            + "fff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bd"
+            + "f14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385"
+            + "ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e"
+            + "960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedf"
+            + "f10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce8"
+            + "7b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef976764"
+            + "86866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15"
+            + "a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a2"
+            + "46a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2"
+            + "a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a"
+            + "9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d506062"
+            + "60186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880"
+            + "113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b8"
+            + "21ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d"
+            + "4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ec"
+            + "f33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c"
+            + "05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b"
+            + "2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422"
+            + "e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05"
+            + "d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c"
+            + "0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede"
+            + "0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f6"
+            + "31391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a"
+            + "6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9d"
+            + "d48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f7"
+            + "0a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6e"
+            + "bcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2"
+            + "acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b"
+            + "67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed32"
+            + "8437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1"
+            + "b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52"
+            + "243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a216"
+            + "2bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01"
+            + "027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af"
+            + "6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c"
+            + "9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b76353"
+            + "75888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d4"
+            + "5e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a38178981"
+            + "94f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f63"
+            + "41fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff637143"
+            + "3bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a4"
+            + "5e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4"
+            + "d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f"
+            + "91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33"
+            + "af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492"
+            + "983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba"
+            + "51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6"
+            + "c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561b"
+            + "ead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a"
+            + "7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ff"
+            + "b552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87"
+            + "a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7"
+            + "bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d"
+            + "2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213"
+            + "d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087"
+            + "ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea2"
+            + "1da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7"
+            + "c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37"
+            + "638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ce"
+            + "ed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee"
+            + "56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261ede"
+            + "f43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6"
+            + "f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676b"
+            + "e20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2df"
+            + "a21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc"
+            + "57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e"
+            + "4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec224"
+            + "4b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54"
+            + "383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fef"
+            + "df7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb"
+            + "3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4"
+            + "aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b4"
+            + "5134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b219"
+            + "0ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453"
+            + "b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b9"
+            + "2c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a"
+            + "688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103"
+            + "afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f60162"
+            + "04080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da09679509"
+            + "2edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb"
+            + "9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1"
+            + "849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b"
+            + "5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3f"
+            + "c9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7d"
+            + "c2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd865"
+            + "9b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa"
+            + "085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20"
+            + "f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669c"
+            + "ac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101a"
+            + "aa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355"
+            + "ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aa"
+            + "d7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf50169"
+            + "75c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79f"
+            + "b125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d"
+            + "12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e2"
+            + "91ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b"
+            + "9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c24"
+            + "47d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381"
+            + "e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59"
+            + "e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b"
+            + "63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b38"
+            + "1606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0e"
+            + "e7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b8"
+            + "55152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6"
+            + "440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee"
+            + "38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da"
+            + "83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853"
+            + "f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b"
+            + "765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743"
+            + "f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681"
+            + "ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f50"
+            + "11f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7"
+            + "cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e"
+            + "43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49deb"
+            + "ef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee"
+            + "94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b"
+            + "9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266"
+            + "290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1"
+            + "553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e"
+            + "7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc37"
+            + "19d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e"
+            + "92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b9"
+            + "5b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c43"
+            + "67f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c2535972"
+            + "4bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463"
+            + "fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b"
+            + "79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7"
+            + "edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfb"
+            + "d72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13"
+            + "c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e759770"
+            + "4084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c"
+            + "93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9"
+            + "a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901"
+            + "d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9"
+            + "b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933"
+            + "b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138f"
+            + "be029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3"
+            + "fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce65"
+            + "3e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c1"
+            + "0ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c"
+            + "9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4e"
+            + "dc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac4621"
+            + "7944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf0"
+            + "5fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec81"
+            + "1b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572"
+            + "b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c"
+            + "388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c0"
+            + "7924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de241"
+            + "1e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70"
+            + "b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a20859"
+            + "0eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de019"
+            + "99fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c72"
+            + "6ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333"
+            + "e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a733345"
+            + "3f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc"
+            + "8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb0"
+            + "11eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a"
+            + "96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430"
+            + "c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1"
+            + "e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9e"
+            + "b4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050"
+            + "168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf6"
+            + "6d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7"
+            + "274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce5"
+            + "60260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e9826"
+            + "67311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa6768"
+            + "3191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc7"
+            + "13109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0ae"
+            + "f43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80"
+            + "e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f34"
+            + "5532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19"
+            + "dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f"
+            + "4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2b"
+            + "ed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db6"
+            + "1e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6"
+            + "b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d"
+            + "5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562b"
+            + "e1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfc"
+            + "f54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20"
+            + "c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c"
+            + "06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369"
+            + "bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bf"
+            + "dc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c47667"
+            + "4d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b"
+            + "7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c5"
+            + "5dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e4"
+            + "8b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b3151"
+            + "4ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a46"
+            + "2632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b53"
+            + "5bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a"
+            + "3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda"
+            + "1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117"
+            + "fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9"
+            + "d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345d"
+            + "ef5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06d"
+            + "f734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bb"
+            + "ecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865f"
+            + "d73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544d"
+            + "de24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0"
+            + "a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f3"
+            + "0e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572"
+            + "733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a23"
+            + "2473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459d"
+            + "afe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f"
+            + "128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0"
+            + "e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658"
+            + "e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c728"
+            + "4f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95"
+            + "030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6d"
+            + "ae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651"
+            + "b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe"
+            + "414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342"
+            + "abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87"
+            + "260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a"
+            + "5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb83"
+            + "3f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80"
+            + "f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc"
+            + "77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6d"
+            + "ac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6"
+            + "b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314"
+            + "a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e7"
+            + "8ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d220"
+            + "31248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338"
+            + "f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe"
+            + "9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabeb"
+            + "fb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390"
+            + "950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34"
+            + "999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59ea"
+            + "ae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf4"
+            + "3bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7"
+            + "071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810b"
+            + "ce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e0076"
+            + "9878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f"
+            + "14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd83"
+            + "78d8ac445c";
+        String expectedSig3 = "02e562f6fc4309848d94edc37e66c4fa704a494576668583b3325e859eb7c67c9f74ad76"
+            + "223a4a61b8f9d6bf9f9d64b74b6a8f41f64ceb5c9166787bec71f1c5513b9113bd1b2f30"
+            + "b524a18e34f1d88c54ce45de04ff4ec383ec2bcbe910670c1a8c23f49e3da5d162e76578"
+            + "295f912a532a2da4948a4c110df93ddc5d03d97a5522097181934ec58ec3d871bb206f40"
+            + "09857a2bfb810d4005b9dd5b32da5a0df79c8d895a97e163947fc96792b806041dbe480a"
+            + "83b0f6c015967961173ccff05eaa59284736ef2309792c04acf3eed3b9840c45cc03ebc3"
+            + "37e9a3c7a853f23f9488c91f27fe7440fba480e6955fc32ed4002aed645a61713b0569bb"
+            + "b40e17b64840e9ef40911164cbecb72ac515d6f9b836896fc5ac94d10e97cdb0baa6771e"
+            + "5e8752151db53d32f7b8a46d8e7daf86e47b8366a627e719f5046675f6fcc12e90c1ce63"
+            + "525b98dd9134e0e543e35c3762da18c092e5fca540e302f5025ef55584ce76b4f389763e"
+            + "6b81ca675587e84645b683895aaa9ffe1b3edab4b1e74d9bcdab466950b8e07d837e2d42"
+            + "da273fb714db20162111e7fb76f919f3cb559c02b2631e61dfe3744e712d3331ff28b638"
+            + "cafb76d9fddc339911b7efc4c707d2cdd2c0dca6d503398f304c86ae055ad644f69d382e"
+            + "d7f1e9297f6aa6d8b522931d86cb82ef64de2839abb1d38709ffbc51ccc1b8e1abb8c8fa"
+            + "533ea900a3795c9070d469bf4dd7d570e5e2231295be081ac60c291ab9a2cfbcbb20b032"
+            + "d03beb5d0eead5d5c5c5d6b16efe3dc95a4fe687cad911fb1e67053dae986e80ffd25c8e"
+            + "b17dec708c01d63e279648a9f3ef878a450b60e67b9db042b26685ea4c0781bbb50e22e4"
+            + "e83acd6646afcb50eb11823aab3df92676dcd95c1f75a85eee631f20e8109aa76ad00a52"
+            + "f196e97b0107dc0345970eca562d8cda0df19241de07423a59c139768fd79f62f77d4646"
+            + "e23946b37c1df31217ef5b943ad40aa913fb8b6934921d21136fc25269e047f8f8068d4d"
+            + "226d7e9b20e2bf59fddd2c59ae61acec5fd8645630725ea446803c3404284dffb6882339"
+            + "bd657c1bbf92e959a4d0bc254110e4d0a8d6c83cf94b8e2369c4482686c8c7ffe56ad168"
+            + "c40f769f99c326205cc84ff15ac73356194640633e0ef08a062ed373188593a3f24594d8"
+            + "bb94a2e434f36ae5e14d417276a549ce352bc22911147ec791cbddf2a3f4191bf79b3622"
+            + "1da26d7114e3c167f6d872a80ee39e5820adc14e41e25cef9300df3b93624a183ce0542e"
+            + "c0e4fbfefa2935e036760e7be47ddb99c10373e09615b6201b6806cadb60f5de7e232267"
+            + "7552272a67c45d909bfeee119811612ad05697786df4a56206f69011a3e0865254f1f0ec"
+            + "1826fe0fa3f618045775c5f14153f18f0d07b05df0b08a9adf7426f511435e6aed54b3f6"
+            + "88b9747389e7be5e9ae82d148fdf94f2b6e8469aede5c68452bed66ccd180e806c3f6e04"
+            + "a31cdd6d5b01796ab6d92929a4d553954ffd20c14ed1f0fc133ea7764a9e55a2773b9cb1"
+            + "2fc8f699869c6ad2072ebfa2cbb155182b0d6caba65dc772c394d40f1554b3dd043f0b25"
+            + "e698e3ad1d75ef5a40e3430bc0676bfce6e58dc55a60b16df3a7b1e63d588781f7ccedea"
+            + "79ae8e867d54429b55f3c0fe4975210a6daa5cfd4e83454eb836f23bc009890b74de521e"
+            + "467fb4ac600acf838d933be8ca8b3b31706a034e54d3a2d884eba3181add9a37dafe6fed"
+            + "5b9b1d30869ce814656d5f65f6ffb90b6cde8eb45b1c3e81fbef381581b3c0d6204e5e1c"
+            + "666297e7633c7e1a7559466647919c21702d423ec7b55fe442854223ed8c11e6f138ca94"
+            + "a227a6ddbd969a8ece746013f581e2132baa71c80344470d5370a4ed95b4af0344b9ee45"
+            + "ac4e346bf9ef5464abe155925eb3b6890f865403f01a46974c17eba5d2abac79ec5baa6f"
+            + "ab5729fdf90510498bf2d21cff46fae99dcee62f6820e22fc2b725cece209e3b32920a2b"
+            + "3b7a3a821b52f132e1b4c9539ac2b0cf6dd9e7184f0aa0af804b57a9ee46448b0b7997ce"
+            + "78d346750c288f06a6a7b2a25492405e6395e037f811d724489ed8037cb18b99c2939656"
+            + "eecf759b06033f245586470ee62adeb58aff6dbba819b26b45bfb837f2c2d418892e8e28"
+            + "676d890a4c01afbc78ec47bf56d7d8013aea05ec056b368b5f540fbff2e6a9210a1b234a"
+            + "cbe66934d50b3bf187e9f37e6693a6d701cf038d5c53f45f0675e5112b34f69d7ab34f8c"
+            + "69f569c38acef4e666a5ff9c8b9f4bf5a842e1b9e0889101be22840439ef3493068b0f76"
+            + "2b56f8b1832e22586ba7101c146ce3ae884b61063a39a4e589f655a33b1c1eb5cf304b04"
+            + "92798ac7f4780dc94969798d6812f2d43cfc7b15efaa706cd8995368b1d73b86551cca83"
+            + "5aa6808f56fe42e34df175d653a619a3825ac15f1bab5e4139f73cd555a7319c8221dd94"
+            + "88b0607e9c73320968872c1ef68117f083fc54d4dc4ad87f4d3e1016db6d70dfdc427df4"
+            + "ec80a04ef264e5273615ba83932cbf0805f6bb31fb0d8293912fac764432946f4841c120"
+            + "c32c970672c4d5d47cc2de7cfc0d0ec38304c7dccb04c84ee52781e343ff05a3453646b9"
+            + "d169620c6510b24d1b0084fef985235444eca06459fb3973f4b2c99da58d550adec73139"
+            + "8371bc5262c8097a14f976b56720a51ade182549f082d56d04c8a3ed6c93719b3e817a45"
+            + "8f97fca4f355e6ff07ab38b3ea1f973bce0c94f46d2630309697aacad93b2b252a5cdf4f"
+            + "b4395d457afb5ff490909a915f82943c30a13a108a84767408a4da91c800cf598f5c56ef"
+            + "c62ede9b49db68b8dbba67970a5faba8017ba51ba86d5a006bc3fa9e5a47bf74d9346fa3"
+            + "e6d892816b1f9d2e4bb2336e56403b15dbbfaba890aec7752b7e458f9572bc0aaac604ec"
+            + "19e6205fd07bc212f83c3842a309fb567663cec5b523e045c00df3038c68e212c2e6dcac"
+            + "e900fa2c157c0106f3ac85ba1570cfb92628b4eb2c1c84ef146072e8bf5c41b37e1c60b8"
+            + "09e17272976cbf0e92acd631257c694130a41abd1bc2f55011b2205ba8881d975ef28a9e"
+            + "c080457381753b74ec9fe1175fa5c806e39762836246fbad8ced9bc0d5b8e01ad844fcbb"
+            + "ca79829041b7d09f8f35866204e6d399d905765fb5418c8d3c63fcdf9f289128f2bc09f9"
+            + "20d30c299b5bc47a3d8dea63d74c8071ee8bf96fb535e16e2607d554d6dc6e07cfc9b8df"
+            + "397c0075e7489c9015f8a677ae491220ff0dcd4c700395d0e6e1402e6f093437d58c80e6"
+            + "016fac57f839d1aca29a0f00b9c8d20ef546fe739f12e3023c751daa2e697254b003f31b"
+            + "50e40ffc7e58f42b5d21e2dfafa0a597581a6d7b63a3c739281757d3eca5293cc3c1b79d"
+            + "62c780225cb43be7a2aedfb3c5c4b542a35850c30750d1f7c3d540540df1d59463c64bbf"
+            + "e3c3ca113638f1fb2ac990d9d5feac436883167965d400ab27841b6d7d46be336d9791cc"
+            + "8d11d014191ab9f34938bfc216e1550db88bac59d74329fb28d8341316d27f4d57f866a8"
+            + "b2e0328d4c2aaae9b5c959834c6aea11ab16213281264fc5a174ad929188b1935e022ad6"
+            + "127be1b63de723aaa71c839fc3aa8055cb56bf2d37730e2e93491504de6544eab819f27b"
+            + "62d336c6c66f02308c311afffb8cc0fa4f6008336a1b8481ba9bc381d511cbff3e8d2172"
+            + "fed876045ce5067dd1ec159fb74f3d6f40e7199e4944cb8cd1e3317ab53fbee1f7881ba9"
+            + "a8f1a5f4f878ef38cd3d99f6a1efd556d56eb272aa07e32d1486e3acbfa72a1e83a839fd"
+            + "eafb1f7a5ba244b3f352dd8bf626751fa78f6a55abd4843d034132de619fb5196567da0d"
+            + "9516c98bb3c25f58d72bf63c9456bc49f1a47eb471a17abac729e1a8c8741857e4e206ea"
+            + "91ff3ce7b57aaf4abea35dc23e0bac069401c206b74f8f927b5cee864661fc2033e21a42"
+            + "f40c70c31622923a7946ba1edf74550661f2b52a85c573cdc336aa9b956e9af10fd1d94b"
+            + "15773c08ee50c9ee6cc3d6d3af4c928542308e0a211b06c21866cc922b787df5cabb5469"
+            + "820d731040ee2a674cf3a41a94dd6c13db9080f7165b71645a6ad30e1a6566b0d52896b5"
+            + "aa57461b746dbf6651a9a27dc7ff850d25137821769a16520e248cdcdc914c7d9e655b66"
+            + "821cb2769b76a434b6c8d26bf624fcf346d2b66d95d9ed45e6e5e93d1df6641a08f304d6"
+            + "cd2a6a1e842325f68dd903f480fc0808614fc2f89bb1bbf4d37fbef451980217f35282f2"
+            + "01e41196af2d5829ba07fdc67d6f62edb11bef2d970001d43a69d62cb2bdf76c5e9e5fb1"
+            + "4857d9297e908f5fb901a4b4d8b43fbfcbfd48ec93dc8b9b6feab6c1af6661d7a06e4cb3"
+            + "e7e4ef6ddc77561119cb08bfbb164c795438a95ad526b5d115f62c500ebd906938aa5caa"
+            + "7f5cb604d7a6c6e0ef360d57583cf55bba5fe162059f58f8623bc3f25ef4f9d63a4dccbb"
+            + "6e33485f89bdfaeb0ee58f5c0fe53c39ea04547631c85d4ad4fd76bbaae5f81c926f5d1a"
+            + "2d70354eb59b8e5401da6372e614d8ae43b8e91adcbaa8819e68a56894f23673902a8dd5"
+            + "317c22ee42ed2e8d53d0447798beb319a4a3a37a9b7a32c80c068244c24fb5ccbc9838b7"
+            + "7158895d663fd71e3777a56112a59817d88462807bae4f3407b227189f6ccf5aa33f8b87"
+            + "18f8a7b400ddad605aaf9b2cd8ea7ed6d4da4e411bc73e6eb4a9563f32b2b68f788faace"
+            + "8eb18f1cd8cb7436b5b1dc764f275f5e8fe2bae94b4a7ef7d2b6d526b1b86eeeedca6de4"
+            + "4441f69da5ba6918b75517d0ae6989a34ccaef091da7ea08436848662e7cf53eff68ca51"
+            + "10248046bbda2c0c058deb6d53f7b5682adb72b9f36ab7e02b7650641acbf4f571c0f4ea"
+            + "e57d0ab3b38fac344f9491dfc3632b7eb5926dc183ed88efd1eb2808e07ef33dc9839dc0"
+            + "313dd494048b02a08fcf227abaeb42c136e5d487c1a5e5b62b00ecff9e5311df58b27983"
+            + "52fa900f2b50b4fdbedeba8041f321e460617b8db4205631fe147c36c51a39f26268b7d3"
+            + "6f89ef7de30ad718b85970ece7cff36a611c7a04becbfa2f1e7b73060519e5d68c63ee51"
+            + "a4b096921962fe28de115cfc60cffee4d2d5db32cef1c44a8d6b929595c737b5022db55c"
+            + "ec9b49c1c07063b544f59ecf5f4e2360776025f63238faccff61921593dd6b1c04fa92c5"
+            + "1451a76a80c17f8d39d81cfea5a14474c88c315870963575c2bc52b274abd0021365e635"
+            + "125d80997c4a7e92ae41cf9c24cff337473118c4aea7d8e9a00d420e620c47e2a79e9694"
+            + "bf709c7b3771bc71d0c1f1e788c8c3cfe07ed0a75c423734f7a4c2716366d0d705c115c5"
+            + "4bcaa94727876e25fac31eb0b4d24b4a2b610d54a594f3ca0737b7891c1bbc7e131932fa"
+            + "f316133c3bc0c8c024942f118a5e45a834a62c3c3666461aad4fa6367297176af16d97de"
+            + "5843f98a87e41d09934b7f4365c4413b861396c7e94dfcf2e33b232a2b96a10628da7cc3"
+            + "550d332eff6314b8424d6b8df2c83d7396d961f07ea4c401998b471be18dd99966abddb4"
+            + "bf5a37c125192d25778ef66329f1f086c83bf5dd7ddf1e4dba7b7aa6130da4eca58b0f9a"
+            + "07eebcb4019055caded4e32616b13c6b041d459bfb4c7670abf8100c5e30dd001db88f01"
+            + "7f7fde72ed9da6928fcc4a73c8ba622b8650555f532f83d8c0266a79e42df4d9ab092ff2"
+            + "034e1abb081b25e9616c3b7f0f9913ed780eb1140f3196420de3721afa4c5d9d3aa43eed"
+            + "6e8e03403119dc23f479a0dfaf42c71021f995ac6d4f65dd3e3f97f8961a6524ccd9f6dd"
+            + "0a14bfc7ce0ba0b8e840e4ba1c37857e791183483dfa73d9297dce10ef9524a15235513d"
+            + "8a77bbbb9b33d69cdc89b3679cbc6c6e2d75613741c91f2ebae035135681300c64e19355"
+            + "dbbff4f29196fbe0a6a993db8744c8271b487f1fc6bb85379da5ac8ca59e216477308554"
+            + "6c535298c9f6391e0062f92caef958dd6824a688085793169fd8422d5abbc0d65acddde6"
+            + "51f0f3ac2c69daf9729daaba52a87f46252a7aa7a70cc3cb16f855ee31e686310fc58496"
+            + "3e849bb348a0ae5853525b6bbfc23d34296cde2306ceb099174a5a42bb2a8b614a774af6"
+            + "740c50afb984ba2b6f33dd66156b219a4429c79e3ce04cf3ea6c5849c4e7aab55d8b0be3"
+            + "7567f7c74c0e2bd4820de4e4b0c16d2433186a246c651a9bed77b82f4fa606a2f20f2a8f"
+            + "f95f835a389a8b3565065eb0a67307921dc2f9952386f5f0b52c22365e1b9e2b06eb59e2"
+            + "b35cd988eea585e6813d09de62f8a4bf3609558043e7dd80558fba0af188dedd6e92bb6f"
+            + "9cc3590d541d344c7dee221bf3194e7f3ac21e94a8ce44c98c7ac24e3fe8ae0d6d8c7d20"
+            + "9bbda73f1ac7e93752362ddfa1a1dd3fce76d69e29bef53004c24561eb5db28d38d38e92"
+            + "2099c91e2cb12a24237c22ac0e60611c64ac58f561fedee4b965d7a99414971a0e2d3d6b"
+            + "ef20e3cd67d637ca2bc82e8d4e47e2c48032a215dc26a664d5b7b800b6da6d6322560f7f"
+            + "a3ab57058ceaadcfc50e91d1581d6a8aef99325967c3f32e58782dac661c6d1c4402d654"
+            + "0065126ce4eaf1f0cb1687348087ae082d615c3ad21ea77b0275cbd95eea7f211bc56bb1"
+            + "ccf58ae837f7b31335302d28f93031c91fe5ffbc8a5833ea61191fb0bd8a5fca16232d79"
+            + "cd4357363937db9acfe729f7f3187ec59b5ffe6b518005a034da590c4b5906f4631348e4"
+            + "46f98c689796cea0d51943f3ce527f4960cbbe5280bd11dd4d461c68e9d73a5bb69fae32"
+            + "edd855f8badb7775e01cd25328ec11a3d272357a5984413fd9a2328c67318655815e21ef"
+            + "44dd05d9cbb0cacaf1e44f2917ab7bb1d058320cc85e786f27257b0dce3ffe26d38267ec"
+            + "8a2a3d3742bfa0bc66925e6e6e18ea232ca426334dcfbd832b71ab3ec51eef47fe88929f"
+            + "f0baa4f519cd3e6a08be80d5c74e5fd31b532749701bec45a129989b8b6bc8ee4d36deb7"
+            + "95dd5df06a1d5eea53405910c263fb17ade6d2b244a3c03c5895ab085f705c2d3439e958"
+            + "5206db3960b0252d12eea0892fcd7bc706026d108c765eec62bfac281bfd717ee2994511"
+            + "b1b86f83ba99bc1712b1ff3f8594e2f47ce78f4ec2373806c0d9ca9ed217a708a6674316"
+            + "618e77d4d936484df25963df0247aeb8bb466850e57bc21ba1526e086e41279299f163ef"
+            + "4ec8a8331241fec26eb001641fd5ee8bdeb0082ec013b019cf90782676d9b8e5b5ec63f9"
+            + "22f1fb489a57d593e4f93afc0fa725aaa06feec21b3d81e38f57a30fe473a90efd043957"
+            + "dafa3f121a9f98181036adb5d4fc56635680d8b271ff01a948f6bf9d924e11dc6ab8e341"
+            + "ee750a68d9c6e46f92edbcb4b665770980772d12da6c409286245142fa55c974f3b6cc35"
+            + "776504558e8307b1a0b5b9b68052a19a7e7aa9a4d7032f1173937453554b8a192115ef77"
+            + "b8bd7951c10889726c5bae9cb355692dd30376267a36c1c50bf854379529e68e530dec37"
+            + "39a185dc56c28f0e8f77b016588c4bd4106f2c445f340c9ec4d8f618f7dc1741be067c56"
+            + "89c79bc0b2f8a73eec36ab8fe4d69a9f12c179cb657783f379b4fc2be9b036171ec7fe03"
+            + "a539eb18c3455b2a118db7c02e44b12093a45f227539094d2c9e6ea94234dbc6e8c393b2"
+            + "454f32365534d5bf4d300308f22b8c04ca1f9d9a7c3a96a5c38b054990d7aa92fbd3cf9b"
+            + "894c2002dea5297fe0dfd03528ff32517d70693c4858a3a50b15663ff1690642b6d612a2"
+            + "645772b9e1a28f1f2a254211e86bc05f0856aeda8aa1a304dc499a2ffdb70ac23631ec74"
+            + "8f11f0ee1dca2f4a865e9936d42a6ddd7a9346b7b622dbc009f226dd85f42973e7db0ee5"
+            + "ef58d88b9cdb7668431d48824a97e58e0c32c6f2c459b33ff437e05186d3554ffd1f2fe2"
+            + "02623b86606be20603b805e57f288fcfa054eca0b9bc1f94070682a34acc5f4e714797bf"
+            + "4a3fc4bd2cc1b4db3fb2bd4f3d9ed4d52d9ddc72b69e213bbfda25915fb945a0e919e68b"
+            + "d0ef372891ce2f44d07b8562e6db1ea216c2f66dc5a34a7ffbe0fea4b3c432fec29ae5d3"
+            + "ada4cb48b8c3bf29e4f61fb41a030bdbd16e48ff48d01c1c418ffefba8d2939f2347c9ee"
+            + "0884672ce8b6b06062ae5b6353c3a082d793a4dd993fb1e5948702b637e0c0564cffdba7"
+            + "cb9b5798ac3220834412cd349330364602348ea399cc7f636a5aae57570459915346ed28"
+            + "601bfa3cfdd64f48ee66ae70af1a0aa3379447d598fe14e3edfd3db7cd36693ed93add7d"
+            + "c16059e312f273ef5f5b39b9a51948a03dedbdc055d772239d28de8f82db2df7b606a2af"
+            + "895b214666a47fa522a953521660bfa82783ba3879ec47ffa77a45c5ee3a244501478652"
+            + "a99279a15de8b60976cd222c5d12c8da95ad3a4cb72f44442fea4978bf92b6866b030e2f"
+            + "c2820a970ebf751d9b94f17c0a7bcc761d2962e481d87742139d4902ceb6e5f96fa91fe6"
+            + "fe58d1a7a2b15b766c3bdfa4e5b381e291148bd7daed8fb74ab5d088794fe231b65b1ca2"
+            + "066f272615bcf50e6b1621b32b6b1ca016e6fd3428799ed92591e35fb60ac7523830a4f1"
+            + "3ff88865549560a87a9f56622d9695533b412e63bae0394e031b41e2cbdc600056ae22c5"
+            + "9ec3e82e84b80633d73dcddabb92787c71f11d157deddcebe71818ba6afd3de15430903c"
+            + "bad44cb00fadcd7c9eb08a201ef938c8ffb6a211bea47cd93a73694247b3cb1f2dfe90e9"
+            + "d25a9c3023a6ef3eaba820c94cf2c518fd3176af9649f424b810706d4117d81b63229dea"
+            + "e1d8d2a0793c1a0dbb26ed4fc36a46dfce85947437aa9873cc3397d9b87b882784ad5f6c"
+            + "7af1c5e70783565b739932b75773bfcdd9007893b85619f5d1b4dd98b7646b3af994c9da"
+            + "b794e02e941c2fd8162a72ba2a77b88f0f6081e0df4846a38b49c80f5d479c8103e9ac98"
+            + "7c9321696e83ee8e1621383502fcddd7886998d58c1136a675700b521930608e4c8878ea"
+            + "fd1126f8f5fd99398ced83f84b76eb2d9c84f748d316707642c428b8dde842bfa5668f55"
+            + "b1873ebc42e97c45fd21bc753a890b67b4b064c99cb6d52685321a30b427b34772053ea5"
+            + "3d76336bcb0541e36c98fe45e9635bcea279a68ee5ee2243b30b506fb367ab2e634b1eb5"
+            + "2d91fe1e68a9fe083a0af00b58cf9de567f39c10f80cbdde4948f945ccfcfd5f086c7f62"
+            + "914450d35286da0b5393d65c07c551f873e163ee3d0839840e748c6a6214ac4b0bfd8804"
+            + "b783132d0860be5b88a366430e4b3af76a39beffd8880bcfe081f5d8a1476a87a2567969"
+            + "e67b195f065b56890681fabb5ba902aebb141fe6e7e254e95ffd3d5801dc4d049b899d66"
+            + "e35fa46e08cd19af79e06ef8a00e211a39c7119c2a9b8a3cc06b8fc97a0b933ed8c719f5"
+            + "d7d21c8e8f4edee86bd00d15198494c22d9279163d8e0aff8e261f88cc5619c1ea165073"
+            + "6882056b15ffe757ef223686aac96c57ad09733f36b4234eb7656c684ee0e124a828ba41"
+            + "3e3e4b2ead074268a5ce14bbb711700661aa79e9c4d8fe419e03c246a578dd42ff880f7f"
+            + "8bdfe1922dcf9fab29456cb76d31920e697384fcf7e986fef1f2554e046a120cedb0da59"
+            + "136a5d316bebd3401a91041d6d3503209e43746b9a3e5ea2dd2e64a15183f5921710659d"
+            + "c615545bead57d72e6336d393dd9a94c8ebfee9de0e1cb30cbb116e770f0464a2972e7eb"
+            + "2a8fda7dbf9acdb4bf7cb6890bf4dd2afaade4cb50e7d51fe4d95235cabb34eb63ebaff1"
+            + "9f242834943945a4c5571edc442808bf49c03e5a8020951b68a430a9eef7f0d660f0921c"
+            + "388ef5f8b0f9874e7ac43d70333de0445f785f7acf090ace53d99301a1a6b773dda709a8"
+            + "a6093ffe2ce787e1806c8803246d6872340793cca8181b8ff93ce29904aade06bbe3efb9"
+            + "5e929df548ed92b0258c7eeb1f851062538351cf6b160e57a69120c0c85755427d4c616a"
+            + "f08878cfd3f96bd52fd400d7cb30aa0a798c0c04112ae5ea6ad9ad9e4bc815dbd01ca392"
+            + "5af21fc2a058e024a91f4f46a4063163c6409eeb5e4f6c2a1e859ef427a0a29e8ac06a0d"
+            + "6dbddd7785034cef0cb492b1f91b296db493e884f887ea31caadf6159f318edf82d63d12"
+            + "923cb7972830c512833a67454da2e9d372d41f5121684ae8b0de6202f109ab1460e81a55"
+            + "30759323300964b794c86c160d49f1297410981b4d963986aa4478ae2374342246369428"
+            + "2a44bc0ed67ecd53b3a6a83fe89478695626f44c2720f8001d9619f72b6bec314d0c1b78"
+            + "d8e9e7db00f937eea2253ca6216e71bef9cc3cdf502f6f99378e024b99fb141dd460f39d"
+            + "669ffc0c51b02ef7a28215b9e42fd1c2fb088c7caf850931e4ea871737ed3d4aa3858243"
+            + "674e593b504f85a200c7207778fb0360ed5b44862bdca1fb6c0ec9cb66114f0436031982"
+            + "61df02104bdc7b112f8d290297f6173f47c2197f91c28d21dc830d9dc0f20cb878d633b9"
+            + "9bc538bc97da8ed9ec80896f52139f7b805fbd1835141e37cabf79241232e60f5a2b8d09"
+            + "64b1f576326cce3cfa5ca53e53108b77e629b65e5359b3ad5a41342eaa58db0a019cb434"
+            + "40d0c9e9a9292d4f7464ead646449b439b4bb2a85454889879993a5145b47202a1782838"
+            + "930e9497ff7829d0c696770c2d4e8701c5afddf3f696d3a9518e9c9b2dc5da018f0aad76"
+            + "39f365c815e003bdad234a172568b2b6b3959bb70c9793fb285cf44fd6f3a41622d64bd0"
+            + "695b01c628985408901d85006fd53448c86a565268ab09cbfa74c7048c66e2ee1a45184d"
+            + "b03d533064887542f553a242d81e3ad1728d05f5d5573e91f758e5a33ff78a34c81ffa3b"
+            + "0fe122f6fd5c64b1c8846b66c171398c042e3622c3d440091552962b95f4dd464a87812e"
+            + "d9e67b6168d7ccf78501a21aed1ee44396ce8b76229bc0933b39d01e8a08d9a46aecaaa9"
+            + "34cd168481ec93ad16f2aa4e381fd750b0242d94d459c6b6b352299de736f604040b0f95"
+            + "2a272b5bb299f1ae9e29f0b38d451c0da39053472ac03c116ea1991b5f287fbb1966f844"
+            + "ccbed288ed5b5643ecb92f8a01c734245d148378cfaae4d858301cf77f5c137b4ec50fad"
+            + "a61f827d926eceeacdae1dc99d01473b322c8b2fb4468f11066634e92865001d46230cc9"
+            + "393554d0cd45b2aa6efdc3ed35cdd84c24ca75dc91f3e7fb1446ec6b0014ac26da82c26b"
+            + "ea7592b9cf83ac4a8e51a5a9281b888ab1d4066e81bfd0376e5ce45549fadec88ca405e8"
+            + "2ac13343afd598cfcbf2b68edece4602f9c3070bdd0fc711bbf8897462f2634e2d74e03c"
+            + "7add73fd31aef47b5ecf50d0c349c046ca3a91019204c9476addb871ab1ab6db4f60c04a"
+            + "31e2765525961889032e5f5ada2cbcaba0ea9025a04fa5e41f0e5c1d27d49be67e4f80a6"
+            + "be0f68402dff4960921ee4c791e689e04ddeea57d896e76fb206440cfdc83cf240d50dab"
+            + "f22906e4c1e276a8ba0fed2241d2b7fff51a8c802873587ef387cff3e75ebd8009d2910b"
+            + "210d07316355fb340dd5e0994bd40f4c71691f25b71865102dd7cfcf3f154cd0a9d25b3f"
+            + "dd802a5d81ae448d284f0979bcaf72f3f5904a19081f369e422510f23c3f22ede785775b"
+            + "89002daca5c8f00e31c0f9c4b2716481d5830269172e3596907aac87f4e81ef66cfd976e"
+            + "523905534c13fd9f006342d4ee39584f7c196f8c6ddf7fc71a7236fd039b8e29c6fa1de4"
+            + "ae6288ae82175d939269e07393cc09f1f502a3099246582d4a2184d9109651bac475b431"
+            + "6e48509c97bfd72f97d66e119f7cf6d475bfa42d0df7d0fc5b3b1bf9621066c1f32a4506"
+            + "1363c3175074fed91377556c63e104186f7609b5a06e4c3902a8404e295929d4d786c15a"
+            + "48be408d8745d9674873c5a7ad4756e41e64188016191e8da12aa4b30238b29e4d784e89"
+            + "50e808931f12424fe4dad6d8cb0bb55e8c0440e0148f04d50b06acd17af3a47854129cb4"
+            + "64dcb3cc60c6ce8bc8649ed48e36c4a10162cd2b3073ba21aca3844ebaf0304ee1627490"
+            + "5f6d85a42276e84113ec09262a267d9ffc08f38b93e244f860d08e6aeca55193b23b0e74"
+            + "8b9b358aadb3ed7d1e2affac941f7be7fa35005ccfeb02929320d8db088245cf16f5bf81"
+            + "1a90b37f8e30c7ee4c8ff0aa018a109e544f5c51e844152315da766f4c19033796bc9e21"
+            + "e981264f9f118947f1775acf373800c77a063be4473994d511c1e4919047f00e988c8912"
+            + "a8647fdde6c3a8a6328698c2619a8dffce9aeb17141ac8262053897bda1e034826ca7267"
+            + "877e2b16d810e9ec95c7f29e3cf9e80ca9b75c3c4b4660b3add09933a6e1589cb1b85b55"
+            + "0406d08acd49a2b2d280801ee3f7a6cc4ba6a2eedc58802e84145c0a530cae752708fbc3"
+            + "7542f0e5021325d4f8766cfd46432fc3a4aaa97e802b36ff090cf1a8a27a4baf64843787"
+            + "d633c52ab63fdb9744e354c0b91d815aaa3e33431d7a7a68a5cf45f6b4359dd96603d31d"
+            + "42f84976daad0f13ed2e0006ac7cba863d082709422694a783ae311101baa0055015207b"
+            + "b7e8ef5aff677af5a1bcaf62a7a485c389cd0c70655868e73013e9776dc0bb57c9dcf812"
+            + "5c0e2d8ff6939f24741f9e8a39a9dfc3cc44da1fa6b997c84b7bf1ca4fb3712760ae4800"
+            + "b3c2fca8ed4b6586c45ee17c8af8ce4cff5ff1e9faf3075128c0f70f7633c3d35c238dbd"
+            + "3e1f83be14a1cc48ee50756ac69fdf87ee0466b0a33b4e385fab0722fefeac403f1694f5"
+            + "ab8b9462da5fb196ac8b0d82414f34c1a8a2cb1e15e5ab7f07dc2cd163fc1444d35ce462"
+            + "eeb62b480a29088da252b8535ec521d41998d616b1ba6e83e347ce0837c805fd81821012"
+            + "c3685d52e4cb0fe3fe9de48fac7d45a76be4d03af4409ae66ee49d31e3d9974f06dc5673"
+            + "8c3cad522ba3b9bb1ec8701fc99c9fe7fdc327e2f6f7b140711ae3601bf1386f83fa7520"
+            + "af94c94c92b2d920c9da0f0389acd4a668e4955fa8cfbbad588c2595378e45e80c5705a8"
+            + "b7e77a605f99ed13b022ed7acadb66a49f64ee4524c8e8be49573c857d5d3266be5223a0"
+            + "dcfb116ef0cac02d03e98c3f4400cbcbb199266cdd358714d30261816a755f176f41d9bd"
+            + "c2b306c4aab7ade8dd3f95939480e4962784531ed583640014829e93f063ccfd345a7d22"
+            + "79c34e461dd0db2fe57929442565ce221b1e1bfaa2276b991b0863333b146f411ddcfd4d"
+            + "870b333845762df5d2b7b8c2b8d41706cedd70dd5a8252b8041046b063785af91e251bb2"
+            + "74e26c27eac5d0946997b425e3bc43f67a88342ac42f3572f887f705ba470b8c87889667"
+            + "50673a1b21d14a3b740cfb2375b8330b6095dc8b527fa65fa0d7c672f1b37a0fe6728991"
+            + "dbcf2350d0612ca3960ea0dacd334b9cb8585665e2aa463829debe1b17137d6e3e9d05d9"
+            + "8aed08974f51cef6cfdb3c4844f5de518cd3b99154f2dbd5be2be4b509e39b59b5cc5aa9"
+            + "34e51df7e9de203b58859ab715abcde6cb29c48b14717f171d3fbe0d2ea6efa9ced3e63f"
+            + "985703fecda75af4244faf58a9af20a927f3c1d51b75255a679cf85dd3595be24b0ff0d5"
+            + "90999a0dede6b14253ea218fa037a688db053372eacece54566aa866f4d62aa97db3369b"
+            + "b2fd7922f384bc9ede485118d70c4d759aa0e6f90ec42201d53ba35bb8da09cad48ba384"
+            + "48bfd10a8454d4bcb275310a1401f1cc0f228242681583d124018b7cc1bc96d4fe32938c"
+            + "414fce8f007fc359064271a8c6aeee66352f2bc28bf3b1fcd74a18276b338b30de3eb853"
+            + "e9599b5178794c10ef864c55f769a7407dd6f782526b2c930f4933d4f225dea3fe947b8b"
+            + "bd1a017dcd28803ff223f3b1008a407cb1f50dfacb264a69a9bc5b3429e0dd5071669f1e"
+            + "bda62428baa18ba9d943e262b74c0b1906afd1b7cac1e8faae2957f0b549870ab284f841"
+            + "1421e97d83f18c2f63e36f8e153fbd657ddecd258ae17692c9dff1710f5f3204bcce2d61"
+            + "246a793157401820abf0c00eb031020a00a639fff7f5715235871204ba234ec53b71d681"
+            + "595abe6ca3c89b9a3b1f8dd808fb427753336d2029437a528b6d4f7011bcefdba9f0b82d"
+            + "69fd792d57fb094edc563310f8819d65981d9ac7dad665b9fdeb73a85c38ea1b6e16e313"
+            + "72d4fe9cd5ef2e17351e2669957d6538288f8825519a7b88601a90d580c006f3995a208e"
+            + "cf6426cd11a1acebeb8878d1883ec284b710eb50ada27ee74238836e75a4fe211356368e"
+            + "4956032362c1b9d4e204d31277066a21e85c30f010ea63668cdaf5ce59759473e167398a"
+            + "34f1cdc7dcfa3b8356d96bb414684a11f24ba9c48f59a1ec578e67a37f80d4ac9414e84a"
+            + "63ea1c8a982e12a7314e24ff97dfa291ab53c24e4f754be45358c2f57c24fde8bf77b786"
+            + "d28ffdf9214018dd255c17be56607440071e3f11b31757af93f9020410dc95308d62e40b"
+            + "ca702c58127b727abdf27271f8a249e85785e50de507421d3c5e37801a250f4cef6568a8"
+            + "3b7dfcf3302d6aa5b4bd14c2a9aa103bf016aa9bf5633a72a4205417fa92202e89a62ce3"
+            + "392005c7c54efcfee818b72ed5b75d2f498842d6a677824b40e9690f495833762b6096e3"
+            + "87113ca6614f94381b0a8f8261c046ec3d904a01fa32b4246a3857128bd8ef35c41498c1"
+            + "38f476efd47d5c93a618d0f801cadb3e1fdf8e5d468e26552b20605a15d9a69e13df789b"
+            + "7423689e1cc4c2db339be4d25c7524abc1044eaf7a45f030edcce4b0fd79059668d964e0"
+            + "6ea23d9bfa8c6aec6f6843e3d6a312b2bd0510111e3d44a7af93705cfba39448ebe7f335"
+            + "1bac301341777fe89823703e3d942c929d2ff567a3e68b7f92032244347444b6ce9ff6df"
+            + "a0b15443b1190f5e182895ac4cc59982a5568aa5af8193dd9f8dc47af44171555a70591c"
+            + "cc43290c52d8634d2e812f733ee6ba9cddc5d81f7aa0cad99a647f5bbe074c6e8bcbd6d4"
+            + "14a594edb11eb9dd46938e38aaba6c679b49d2c0a5abee144f9b56726c86e45b0b3d3dcd"
+            + "8932cb58164d2af6015fea5de32826803eb6dc99a3cf3f7139474f45bbf51238738c5a93"
+            + "018ccb14d3b4a318803813eec0c8027bba8d101e504f5edd3235664e48483236f37e6174"
+            + "6892842d5aeefd92c243fb7c405a3e936b60878b77e342b21de08126d7b4e02144e4644a"
+            + "9321394dbfb85b855afb015dd98eed3446ae00e621390f10758b08055294ec4c3b71cb9c"
+            + "1403351a4cb40d61a35f50b7919a2c21ecc8bd484dd9072ae95883149a1f82ca7365081b"
+            + "359e138d25fdae799648e4b02a1b95a81e7e058681985ee96aba5e069a1b918f333fa2c5"
+            + "5f7b1f1d1bf8bd9d7d12060211c2635cc8b69acabc7ac02689cfec63b2d25414ac388595"
+            + "e701f4e1da96e13c4ca37981005870b34c9b4bed1ebcad29310b69b498b31c3ad8cbccae"
+            + "816d853014f774dd22e1229802e3c7980023d0c5e37f1ce932268752ffb6c727240ca013"
+            + "e5431adf691432272d30b360dc8877b4319d201168df98e33b223e1a82cfd79f3c75b514"
+            + "bf79ff43c587295a1a415c57f57fefa1c50cae59641c571d327d6a1caae11b99e16df8fe"
+            + "ebbed3c02d7cbddd3e109a0ec6b139963a7050c6e5e0ada8e0c5ef4ade68446dfc669a78"
+            + "7e48796be7a6cf9410d87899bedae7da22f57680d8e2d74fbe7be6867ded1bd291d26a94"
+            + "0dd9333977d5381ed2e7821600c53c4dc408601a93c57308a1595a7f4ef7bfd1d6f4aea2"
+            + "3d43e69e51c870efacbee2566b1772ac9df9f446a0796a0c36ebf7f6b7871fe2c25a5efe"
+            + "878119a6f507931b13b9a878a72a708ebb2ca1cdac93314ce5bee6053d986fa2f3c47fde"
+            + "fb81229e1fb433b18af563fb223369e036f6317d4b0803ae384575cbef4af81b2ed6430c"
+            + "cd672f6c76e1a8f31b3283e53a44100123737eb064b15241891d05584ae1a0e70c2ffe05"
+            + "0616e42beac9442b643682ddb3ca31c290be1e5e9ea716130df9e06ebb0e40eacb1d9adc"
+            + "26b957baf60d228028a884516963e133126b87599254a63d50ffd77f269774a210173a8a"
+            + "d1a661fe8eba3510e653d6a585f116938e5aa127a4af2b5fbc16b910e2071148d4509796"
+            + "9f06f0db0de5c5b8bc552e3f30a499de8816334fc1626f3bafa76162d952e13d9784dc24"
+            + "7a59d404d44e79c8c89edfdc189c5635d064ac48d981ebba002b2df5cd4ea3d35b5d3934"
+            + "7e13ebb7c442d509ca1661c471558951cf798ba69b7b9d4bb2d560783d2951cbbd7c0ed9"
+            + "e3f21a08f87ecec47cfc8da0c6884722de3630ab64a7d3dc2ebf88f968bd67a615e53f6e"
+            + "d91b60b8755222a25633a62d338667c87a47f8f8639f4058e03b2e36ce773d9e98a65178"
+            + "8a7eb7a5e4dfb54f3e171d74b989356a279db93d6b47d00bd2bf9963692cadf24dc42a44"
+            + "252081419404aafa4572a163a760939c3f31af66c6a8908565badea9b0d3d0dd1335a004"
+            + "9c0c00d12b0b834792c762a595d5466c1d7488af7de961c31a9c7cc4eef0fd1519df1efc"
+            + "1123dada8ae5bd7fb9e46db529e27ef4e71bb4b1d8fb54c6518b067da95400191159c522"
+            + "35f0c1722f79691f87625d8fbfe91f6ad9e855d751ec54f6c52da0640a6fd690a75759a4"
+            + "a97cae98cc194129493545ac2eb059639f80689576884a192f4e0ab885fd6bcb05aa7fb1"
+            + "c0fb8b6335ada730b93498e38f533495a4a79040963f9a3619373d24a8e3bf5c3be7579e"
+            + "eb5e172fc6902fa8489e02f10ee887d35bba54b0ff9bfddf2c8bc4e507af43be4d76e979"
+            + "b8dbd8ae7118b9c9e4b3d4cc8348722ad994bd1a6e3cde2dde3e58ae84b9b4120db838a0"
+            + "9ce71502609596ec75b4037ccf9f824987fa6e598d4804d82f458e63a17ea7efe65840bf"
+            + "4ba96dff3bd0d7911fa3acff7a6189818979d3e1f5bf04fff188342fee0516611188f848"
+            + "ea15cbc8cc8f5dc241e24ce0a6320c5a0081594f063edd535cddec4b0d86759e5a278b36"
+            + "c16332b381f73bfeeafbf1df81fe39c3cd535fb200c05974160483fb9e77f95ef10e3591"
+            + "28fc41fbc2242f164d285c0c9f468816d232be049ca81c75a3c6280768867f57096017ec"
+            + "1dc09d1b5e8893ab1e117d4aad1a1d537226c40d3576960afe42b9f0512f10a81265bd91"
+            + "2619c9c93d21cf37db48ce7676ab8452b9bba286ca9c98153032dd31e485999771c59c2f"
+            + "2de66d79b2a585eee527bb56aed5d46f5c4a93bcb4ece9b160b0a077ae4156413f654b1b"
+            + "855b66e55fb844cef804e382d875f1ad545e16ff971a2e2eb5351bc039fb8518e21e76b9"
+            + "fe5b7444b20e3f85a1376b52f3ca9ef183e5485c9aee969d159b4e1495acf04abfa33983"
+            + "3289f5d888c8d424854484e04039593eec7789dd357c55ab287cba87fc45128d761e2d8b"
+            + "6435289f443099392a762beef20d7fba97cf78f80d5b964d84457e3bc04ee9b5ad4cb6d1"
+            + "32c5bbd48529495cf2171b9572237558f04bb813ca067ecb12126e8f4d31a2d1f2582a5a"
+            + "36ee9d9052759956709c5f6e9a45b526ce28a2e563dbfe068698bc28b05af99f48e9b3a2"
+            + "7abc43a692d44c8d1221e4b3d83cb2ca95871a577d8d266e518ab0a64ba9965e7582cec8"
+            + "2d4852f7895038332f9e077935862866fec64e94a929e00dd3187919c84f63634f903aa5"
+            + "f209a53e3b96ebcff5a60711b70ac2f5ec179bbb5a0e1c010937d61dc03965e55e71947e"
+            + "627079151ba0c7e0ed668b2432ca24ff7786b0bcefb4448b7d5e962702927f500ce4ef9c"
+            + "dd630347d547de52af2e78ab30fd7d4f1c897035025086354aff0d31b51cce3f194435ad"
+            + "2108a4b49603bf2409334e02b4c4bd28f9b8ba0fda3f43da5bbf82bac2264f31a5877feb"
+            + "979399c9495f6d7fb407d661a05bd56c2f3cb7b7bdc4953d00d84eff7fbe63fa8f85bb71"
+            + "1ecea6ce4dbbe5e51fdb154fecd28fd77657a1eafe56dc718010d37cfd14ecb6b830f5c6"
+            + "26e59b4a1c14cef908d49a1cbbeccb68b55624357c8b29fe0753aec4eb39dbd388be43f6"
+            + "a9a159e79dbf1d54f4a76c2b6cefbde271fbb292a4fbbf1d7845f5e6275b9471775340fa"
+            + "c6102edbb4cecbbc86506d304d785d0f56e0372b7b49785926243347ada241619a966abd"
+            + "9fed990a1216e5ec6827cb723d0485dbd71c56f173a3814f28a658aeac33869da1dd5ec7"
+            + "b43ecc2df9b132ea802f12994e04239fd2135aba78c3c02e919e9f10c90c6d21a00e27a9"
+            + "44dbe164c96e35fe7f52e75c1759f1e1bfada5d50ac9c06219834f2222aa2b66446c6f18"
+            + "6bf80f7a2af1b5d1f1730b39e42334969a1464ecaef9b2b4e368604e7c472358c843df0e"
+            + "ecfceacead2d46a986f930696aceec459a95694f2ca6745ef340beddce1276eba5f2e78c"
+            + "d4cc4fb89bce9fffb9f03f374c779a69349d2a03b4a497f03227d8aa9194ba18c5aa730c"
+            + "f1e70e1ab5440566dc095c0d084d1af1337fffe3f35ca5e6e7bfdba8c66e0a3e4790128d"
+            + "1e10ba6bfbc16dfa4fc9de9cd1c4d5f89f07fbdf6f2075965d4ab03a61d3fcce0f6cb134"
+            + "45b8121ebaa22774dc2f3cb3d1b3dfa00a2463172b5678dfa47ba9d58b0ef9b88791c4a3"
+            + "76bda6b0e9062dab8074ebd760476bc7ff3620c4dc7090650059c6f5eeff68133755bc2e"
+            + "afd1fbd419ff407b624d4a0ecc886c5069726624570c595a506fa41a2c2d9ff3fc3c76cd"
+            + "58c0949c458b032a2698d346f18a2b22b71b36a17abf6ab78646be0a68585ab876645c43"
+            + "797fb542e059ad109c251877ebc5fea65ddf1e3eea7a708b4ee535bfe5643e9c0038204d"
+            + "cc4d8ba369a44c24d13b9c76d41f333dd3133f3cb27653ec925ffd2a3dd29fa4505e5973"
+            + "272e5fc4f733bf4c743a3a45f750ed8849bc880aacc5cc9d4fb31f22dcfd566cef92f3d2"
+            + "e3ebf4e6f24f8acca855f03642b131c6d97f76b49ada593753cb0e2dc66ffaedf9e0de40"
+            + "da97423de101824651faeda3335b7c39bb84ba7f948976ed559071a406989f6c527c9d38"
+            + "a10c7aaeb1e326b05b14c328e18446666cce1815915bf76dae46da43a616e883664b4947"
+            + "2ccfbdfb249061ad8e4ed23fd2cafd21c1e510b2ef816171db531ee9dd6059fd042b0dff"
+            + "3a1d18aa7522ea6d01b802535e4221959cf82c06f694598fd392929897d17b04a171558e"
+            + "9aa0ba97a9db9e8d137a19587b02bece5d6652908e2c0abfac68d45a9d08d11a3aef91ea"
+            + "a357fd876ea9147c0aa5630693cbb1a2669cf957060e5793b146f536f1985defcdaee12e"
+            + "136e62e79ec77aae6fce97097355bcdc6a8cb21813b6bb2110e4c61a538aa11a36f976e2"
+            + "0a2cd8270deec1c18b7f6d91f384b0c8d21d676aa336736e2b0cec288a68769effc584fc"
+            + "3255b04229a4d67a69cbf9a9358e4b749c861b19470afc707a903a58f1d0bb19ec9e87db"
+            + "857c36adbf7305409653e52d0b27c0e997bd2412cc8343aa849eabc5856bab036db2c27d"
+            + "599a953242ad259cc2eca8f421fb6d4557d7b486c159184b246f1b1165b7f6a0a7e1d4ff"
+            + "56267b3b862c8109244c1f3ed4869b2d9b55ec906c49d5b35485eacc48a9454df757eaab"
+            + "fa34d0c0194e423dea18b473bbf8daed12e25347ff8e96b7501f176126e17157bd515023"
+            + "a5cf43b012ec3f961a3f23d7912fbc557af413b4399aa3f56adbf8f95a8122cdd1165781"
+            + "08d456835219049b23e6197033a40f92edba4b7cc63f2ee43c80172377c48c8cec88e8b2"
+            + "7eebb0f145d9ee0dff64eaa83422ffd6eec479b4dcc8f463fac228beab22ef403569dfa9"
+            + "b713647ab62e86fa6eeabe30af2e65d237b8bc9d5bb26cd3b18b25561f51cd091f91f4ad"
+            + "4d66432ebd444ce73924f7cb261df6f569ec5f354bde9001af6dab0e027ac21e85ff5db6"
+            + "28a50e5e8364551e62771c8aed96ef1c268d55cd5e9df8ed294fc80ab8d6910471a30b83"
+            + "688aa8dbbaa00a48a6289f18f0777dc55fc278ab7589e8f301002da980b315f98436ec07"
+            + "c7110ccb098913c5129bb86d9dce2f87c6f8511acd5919fc5cab78685f84cc416e2823ea"
+            + "9bb515c5ab1ad8402b18a946c6a68345a4144fb55b8fac05ebe6ab344085bf16f2e35589"
+            + "7f8a940b06fa5eeb22daa6644d24de552c8de956ad9a940e0c1df5bfa7622da1c316e264"
+            + "b0675c606562034c544a9f5a7c87081b49ed707917d949809f70d2871fad5e0cbebdf914"
+            + "5d7e59bc8d4a2122b67c589b69d5639db422320c11c48361c61217cc9cc65a3f68e7fb57"
+            + "da9c56960056d51b368514873cea56ad9119ef0ddb651e27a7383a87b9d82f62fa2141cd"
+            + "9cfade1180a0c701f95898e559b13235b9bbb7e307623e3239278477e219350382c1ff34"
+            + "68d5d0660716803089e2c82d643cdf4ffcd05933a197585a1db24facc791243aad9cf812"
+            + "4324c445522583d9876bf2fc490743e878419991dbcf6bd0d942912f5acfd6eace6333fc"
+            + "2ad97daf41b86ca8e080b045def649d9a80111b9182b0224e10012397d7cb0b266e6201f"
+            + "d9d4746f5525a55f0167776ea43013e1222d2cc22ff3bc5c2018416e694f1ee8984c4db6"
+            + "d8fa1857e6ed19d55bfd54f958fd115f3b1b8678b6d45f142a587c073cdaf312ac5824ba"
+            + "35f0aaaff84b17ac7d02a425f42f63e259693b07cd6830ca859f1e350c4af4f77d8f0c06"
+            + "d99f81e9efbc881d6964f08ab817be076cfc297f41d44e775c43571e4805bc162ac7f84d"
+            + "8a965f97a083df143afc49dadeb51a697b4af2597b18bdddd831d8a091d1c5128b29f228"
+            + "1af3dc5fe7310f2d4b35db2b6384b1b3c706252e1387e73d06844a80cbfe2b48898be26e"
+            + "54ab22733c8d14fe5a7c118795f1a6749493e02f2f66ee274fb0d6ad05404fe62d7a03aa"
+            + "880a6c837bdde15a395900648624add8b7198d8ac622812c132853b919c0e36474da74d4"
+            + "32820cb1a06fa191e26871041327b7df3b56e3ef1dc1e6b4b4dd2f32226277f945bce242"
+            + "023aa821f8e80cf8c5003fe98e4459168a28e17143828e8bd156eae591f29044bc864bc7"
+            + "fb58af4158b9b1d66dfcb3e07f645ae8036dde82722d379e5ed0e0b5f2794b40e9b6417d"
+            + "cb6e97786177740a3d46a16bdfef921277f52bb06843730ca446cc0e243f862d77b0249b"
+            + "12a619b47131d1ef7a7537dd2d77703a19d87a4388c89bdeaf3663c509d00c4816a55b61"
+            + "2a0f286c1c9c1fa9619d4dcc1d09f71441a1df4164a63c916b8ff8bd9809051c319bd122"
+            + "df02820ba2bd77d03a14726c3aac1cddd5f005084a83f5c0626ce4dc43fe1408eb859bcb"
+            + "f0345c21c539e3006318b242cd230771be2396b9b7ad9fb8eeb2004f2225c2ec2399b88d"
+            + "474da1efe75c07a55b201cfab4cfb8e77c0bc56aebab0ac5a45c7d063b06f437a67dbf82"
+            + "f9730748227717318b1d6341482a78da8a86130e31613c05b83dde8824605d122e0b0801"
+            + "99167de5da04e3f5505b07634051e3f1918c697cbceb48298d6bbf3cf9260939d7b71ebe"
+            + "bac6534d1e7cea242bb11ba601250b223b1bd42418d8ceca6dd4f01f31d51810a91139df"
+            + "04885babaf4467a2b6ef2e82b711e5c4323627d37f062380cbc001d52524235aceccb2ad"
+            + "a17444aab32bd883017e01a17b75262dfab5039684dbc60954431333440ebd9759b289e7"
+            + "af303a86a4a88344770da443364f59066e5b659ee2b2dd9772b21d230518b93f61ace293"
+            + "26792e93f54d61ba59e1f02809a67cefe56e6277909d6157b0af330b9a792c33f9a2fcc8"
+            + "284a13d3db514e6c5b064db33f290b99ffa57ee61a474b4f18724e2592c09d917be68225"
+            + "67ca7e603e9d05f1121bf8aeff4b16d1a36f3da0453329b80ab6280c2451595303b8b50a"
+            + "fda467344f6f87bbbf9fb761c055ab6fd4b8c91f80e35a4d42a54ccd364895890556d868"
+            + "6bac4d85cbd86cc5b7a3ec98d5019d52da251f0e7d348b3635f0a9f73e78e1428b0e7d96"
+            + "9147bf1f24d385f89e75e283217a1c60a40cc89a678facc5611dea8775354b8d6f0319c2"
+            + "c9d3614e49a09390930c7b93304e0b6b0a2c3b0a4dd78f39a7ccba8c69ae0abc03b96860"
+            + "f0197261a82b1892783f9aa94a195ddcbb86c9ca7fcd104af64694ad0d67d11611d2653f"
+            + "6c09433ce59d32bf523e30ada9813caa872b1a19fdc152ded2e954dc42a80237ebd12a86"
+            + "69d80e88a1fd9a653183e4bfaddaeeac54ea0f6488930e40e01c1da9ff8127dcd6468e70"
+            + "4f9a8b3f45cc05de1f8f28782720260d9d041f70a4bebe18a325368916c7230f54207ce1"
+            + "74b6cc16f9ad3fec29c339e7b40c6a12940f5ac05a30548db2c6a19d1be98640a01e734e"
+            + "d4186da54d7374b69d090eb8fa39a207209562fd0c11d78fca13bfe6ab85808e2ee558c3"
+            + "1d4a5ee295fb8ccfda5c6842914b7420b1ceb372d312d1bc29bdc50a7b32729a9b1f3662"
+            + "adf915a089eee2887de1140519d0ea8ca394cefc2b7a658a0fb3182275f0a78721bb643e"
+            + "22d4b8c77ecf5bb8327309ddf1169a77b0a0111b9793d3a3da376d843b6f218638e5f690"
+            + "a79c42618c5f1f47225400798cab6bf1877723e330f06faca18fa2eb089f1d2118770b06"
+            + "83921a4dc72e71b766f240beaa70282ccae49142bfc6137109f7e6f770df4e0c99e662a3"
+            + "ee0ab897bc1182a082e7a344cea94a12f7591c681b84612ff4de370544c1f4341735d354"
+            + "05cec6a9b863f666b528cfa9f6b7dcfdec7777220f43a763b68c45e186d856cef1829a52"
+            + "d7735463ff890f8b23434a062c2096906874acac493a7ba5cc658a06066e9641fe35b6f3"
+            + "04f047b2384b8ca05167d6a6fc5fa71e52a611ef7fa783e07f4a6b9eeadf345996d39096"
+            + "f3d1801a40f61456a1fbf9c6137702383354206bfd6dbade4a6cad7cd57f99bf1522fc25"
+            + "19ce0af6b1ecee27ab8ce4cd25e3d519d1da0da71393b046b61af7c9d1881e820b639e72"
+            + "59a1da72ac3951a067575e6f6cbf4c23453933adfddc5f64e838e67eb2d16137c72ac91f"
+            + "ed7a7ead716233ff0fdd1e3a0de867a61bd2c77d237d89dbcae503632f6aa018e4ecd2ce"
+            + "1f2c9bfc562b49d2585c151f6463e5fe82690660ef5f6c5de963a0a45a26231bee4b60ca"
+            + "722cf59c5f81cb986681893900e5dac26892e7f33c93802770cf8302c0679fbfb915850e"
+            + "f76cf2941f60e0561b8d9224fb72b3cd78af28ab02fef100ca0b6fc2809248990f9efcf7"
+            + "ec1743a512658ab36532d2cd19e441a59b464ac5c630a334531246954f970a6fdd8e16c8"
+            + "85dff3638e8bb61f9dd286f5babde8bdd624de1bab23c61738d7fd68f4ec36e3f269cf4a"
+            + "32abbf13ccbfa2f45b63386681276a51b8f2c94157b65582c633190ebd6b5d79761ca5bb"
+            + "862989f257cac1cbdb02398707f759b1e1d05068ce12220b9dc74fce62fdd74e0468e8f2"
+            + "2b52667bdbdcaa7556ec962b1988e7928c637623287cb7a3150988678044e3633a62d7c3"
+            + "63f72dce96c5967923a02656c9cd01e60ec494bf545ad29d875c1e953ea121a465deb5cb"
+            + "7cc2b11480779d6003ae529724e463b84a3d131cc9d9b083cc3f28202578913e8571a2ad"
+            + "691a1d5e376695ec61bfbf8d23d8f9c33d4592dc1d3db9a9809073cd7bcf389f73da5c16"
+            + "9776346b0a4bde33cb18bff16dd867028219797e5cc1ec3ad32fcb61f4404de0c520b78c"
+            + "1e6532be83cd16cd079f919facd9af2078099c3c3cee372052a4e6d6194695529b3f006f"
+            + "d4bc9098614cf60b450b13c677dca567c2a3aaf2a9a958613a2e03801d8ce3d52e660bea"
+            + "f946e4510f8c1d69c6a169290bf0450d9acaccfd5f9eb9c96cf920c5e06ab4b4ec4644a5"
+            + "694e3a6a6c6c464eb03f1a73aa213fd335b71b50aed725681e03a7f0bdc5ec15048cf673"
+            + "826cc29c2473148a30c37e288569cf821d5b396ebc3a2cf53d43e020ebd03c8409e19f97"
+            + "609ce2569df47bfe74a83e6121ee26e12448ba0ab1f9e89a93a1d31ae520289ddcba87db"
+            + "cc3714c6217521c506a1ae6ca72c6ddd5730dbe1547daf4e9c35f264eb587af33da59b03"
+            + "fa0f4896604ce5c2c84d8c6ef8e9beed22d12374591725b01041035e4b568525972dc371"
+            + "62b1b18ddfab02539dc23bcbee241823135589d43c3d5b7bceb5fd93bbd3c0d48d0a2fda"
+            + "9869c5aabaeab426524390287a3eaa085dd99bd17610a51d42ec345ec21478dd66a334d3"
+            + "dd27fe005a687a303cc453d0545942110d18b259f651e44726b39fc297fcef9ddaf8342b"
+            + "855035593f64500b52b0b98b1d7afb87540034b1a2c84452ec149d17c6a4fad14e49a20a"
+            + "8cafd7e9d07d4b73fa4314abd6b5f338ef4b0b093230af54b51bdaf6af97500698d55da0"
+            + "ac182d937f07e680567bc2ad1e6e1c955d6244f563c9ee7e8910a3d9ba860cbef5db2018"
+            + "86e0ee6aa33d6d1419f1b48e92b7c85d977df3a0b6b0e14b6e31eec03cc40af606d26ea8"
+            + "5a5f0f52b4ad67d430577dbcf4d1935837a3e38e28a11748f657b47e3f68457d585b76ab"
+            + "5a90960b723f2155731dbf5e38f535a7f60eaa532277aad8c76484937f9d11a4e1e137ae"
+            + "f78e6a0adc5d5412cf59818634163f908135c16d987730075c67402cacdac94cf648c3cc"
+            + "fff06ebc46e3b2dc36b3d823174ec5d9d376e015ce96a2fb08e91096e44937d400c674bd"
+            + "f14ee6b779bb392914f0a80a7c149f4814f558d3fb3a65dcd260b11dde6694edf3438385"
+            + "ddb9732ad881b0100e0eb29fe4731092486212e10c035f7eaec3b995d738d9e2160fb86e"
+            + "960ac872c68cc9e2bc0425149e504ba1b169fa1e7bba8c6ae38a1f071e39e3009677dedf"
+            + "f10074c200ca2edba43943f04c8a13c92555b37a58273c512dd63dda564d0a076c96fce8"
+            + "7b0dd062905c5822fd40049e3cd2744e2ae39ee8b9a0aa8e7982de9be9444c82ef976764"
+            + "86866fdf74099e2ba87e466e54931a7856fc91936552d4b5f049dc0f1f0305a7345afb15"
+            + "a376e2505278ffc89e95615cc0dc5e88608f607d7bfde381956db0a3be7d5f738c8598a2"
+            + "46a86e316ec6d6b6a09390cfb15814518fd83f1d05b8571d6f90b09cbd7c6709d9f77af2"
+            + "a6be1e8559267591ff9b07c4ea4f9000b73972d9d70760297ec6293e9e3533827eb4675a"
+            + "9eed5009729c77d9be1505df463b65f2960ee2710e17704d4217fe4b436040ec6d506062"
+            + "60186a1863f3b3859c67832c31d7ebc7b4787ac37cc678508247b65d0bd921a8f01d6880"
+            + "113977ff46b88ddafd527254d1781280622efba0c9278b23ce3364eeb18150ed927718b8"
+            + "21ae8ba9d0f10d8f09433663cbec591aaeaa66605317c151dcbc78fa590e9fac9e903b1d"
+            + "4c3aa3b6dfb9abff8d74c40157dc818327aed5d90014f4513ba314a3e5700376b7c1f9ec"
+            + "f33d0fd75a0b994cdc9b0e90068446ca4272cd90e49b22e99d66e54cc2b9d801e028fc7c"
+            + "05acdc96e85bed2f0474cdfb15d14ecf8f467673ff2bf754f8105f6e6c981595be96743b"
+            + "2a212c2ed0d33a5349e227a2183afb4b8ca09723c454dbb00900d0144fc23d93da01e422"
+            + "e0fa7991e3c441f73747356bb54eb135ef6a64272de477e42016083eb2372b89e74bcc05"
+            + "d4866ff408f7831b149fd2e6916a02e696c0e277a4a34cf3021ac7238da08232fc40eb8c"
+            + "0f8c13e02c474b518195fea20ae74eb9ef9d2e7bc706cea89209b2b5d3422e94a94b1ede"
+            + "0baac06b6e8da0263e30c422b227a0d62fbb4098ba4fb68c5c154688dc7429c735d8c1f6"
+            + "31391521c659201aff0735a50d102b5269ce0e1b6c30dd3ff19722aade2370eb45aa6f4a"
+            + "6c8f0359c5054bd9b66343201d6fa9695a3d87499ca48a642ca7ddc7d5fc47c0dd0f2f9d"
+            + "d48298e02337e1adc5ef8c04e967708097943cb4ca5d10102d6088472e9a219e03aa77f7"
+            + "0a282137708c1b3d6298e79a676e160d6d2610b0d15a42728d41ee503352a5b66b174c6e"
+            + "bcf2558309651045bf79eba7b54c4690058458e2cab6c279f87c76f2b02984a07447e0b2"
+            + "acb3a2aaa160bf4596e791d6f00bf130be24ca0aaa6f613479ed8c0a8971164ec25c1d5b"
+            + "67bce4439db5c395208017a68d3e8eaa2b58169a26ad265f86355e5cb25c5422ca85ed32"
+            + "8437a23b5a79dbebebc56c2a118cdc527caceeb533e7b0fa7cf9a38f87e0459332995fc1"
+            + "b202a24fbcd8b34088759c362ac6f7321f223fdfd6f8951a0f68ed067bb00a2fed2f9b52"
+            + "243942eb2495e37d4c38a824367c9c0b56dc112eb9f5b9dd498aebd9be6b6cdb02e3a216"
+            + "2bf107155e3e6e9a09907afb1511352bbdc4f0e7df526828a38b444e9b7c830ff33edd01"
+            + "027de5ddbe9a204263ef0a22a5cb7787a77c43bfdd348a15d4d195429bd762b1860732af"
+            + "6a16d98bdd56e8f67bb911852b8e8892960cc8e7586951fcb02395ab62fd249d497f088c"
+            + "9b827cafaaf035029ba4b13fec4b0f5251e9ef69f62714fbf7bb2607e9a42af0a8b76353"
+            + "75888b8c8ccac0a3edc6a7b8dd51d63741b7738e30d147e07bf0c935fadb81703d6d76d4"
+            + "5e769baecedec0d7d28149e70cd7047ef26383f5a31be9139e4ca9c93a96a57a38178981"
+            + "94f393ce83c3b6828ade4a642e28b81e6c1716e3ef67a539699945e8ecfd926e3bee4f63"
+            + "41fc9377b64eeceecba1caa1da25e127faaf7edd12f0e611f94e12ae507ce3b3ff637143"
+            + "3bd453b11bd7f17ba0ff63db22366aa22f6a3d2b523f815b10f5b970e85ab1d9341268a4"
+            + "5e3b90768367413d4db5e8ce9cb38bea65bd43a32921873f0fb463088a5f32496c9283a4"
+            + "d9b0aa95605d92c924355989b4d6666964f31b63e297b793563376cc87dfc46cfc32872f"
+            + "91474d20ef94660660d58e2577fe944280fa91d9f89cad7085e4abe56e6f526837138a33"
+            + "af4a992003f42c6c1da63e4cba4ecf75f92b4865edf67ca5b25ec58608bce536c19fc492"
+            + "983137c5a0db6b29671b44f4ac96084148c8a1c65963d149d7a9cc900783806fe7818fba"
+            + "51d65fcd8e5bf364561e4b17c973907b295421f5b2660100426f3d19df9ebd4b2c8cc0e6"
+            + "c5bbbe9318c4811076a0f7f061b762c93296a168b5fa99ea5ba54b3a9211823f9af4561b"
+            + "ead0749414126cfb2a247346e2b0e8adf7ccf76cff290cc797080e6509fe5e49f33d7f1a"
+            + "7c4e034ba8deb10a978f0f3d76036fc437c5750a97d53c59ff69f3d7159f13f0499b67ff"
+            + "b552d0ae756d6f03d3b5a8770e38ca59c74addaeebe17b7c75a13700d12605a56c636a87"
+            + "a87b2f84a13b456d1e2093b6dc8effb248480c20b7f7c008b71cb92fea8f047dd8b460d7"
+            + "bb9c05599addd45d901a7d7c57058745c04a1f1ce32068cf95367951ff88e3eb6917704d"
+            + "2e7f166db9a16e28b26d49d37af5f6916037f516344b62b0db171d0241331fcb5fbc2213"
+            + "d63ba19025d83f8564755c3ce0126018da7b7946d7641eb368b6dc8dbb611f93355a6087"
+            + "ee30a30bce131895405470e89bad437a9bb1fc050c800eb6165cd42e9100e60f17e9eea2"
+            + "1da315a194fb5f84977df7d8fa7b885f51a2784ea34fe3cb1401f2385dc95480304764d7"
+            + "c6b5b086a7b75b519865d7c11e5be0fa7ea3fca056eec5a82b40426ffc6e199d4db9bb37"
+            + "638f5992cd7cc1280bd57a69c290ed26f764a923b82c831ebf995f80b4d6d1fe51f3f6ce"
+            + "ed5c1934e1d713b04d0313ed6f7d8d3c239184ecd182b1820ba176ff362ef6dfbcf6f0ee"
+            + "56287fff6ca780ef0d6ca1c14d17895da81037bf1001368ba72f8af39ef76a7a66261ede"
+            + "f43de21e77e247dac0ac1bb2029894ed8eb953b7a5e0d0febc6db6a92fa84b7e278aa2a6"
+            + "f7952ec7cf9877c610a611107e8bf2cd481f612262c61c03ceb36227f4b6114e799e676b"
+            + "e20ad9a1dc4554250dc125ddebb912af2799d83842c0f1c4c3865e084ac9cea1c8c0c2df"
+            + "a21d3bc3431635a4c38107c47043c195f3bcca58cb65d2775a6a797991ccdcb856a636fc"
+            + "57848f571ab36c3f615b3f4c772770476f196bc3e94101423aa170732b41bd6d91e42b2e"
+            + "4cb47f7044e14b269c1ddf1b7b91be767e0b7c787e816b368b9e7ac2f9b807e44d9ec224"
+            + "4b303e0fd95b6f21811885544910518ac68e04644c7fdcd422b88ef20b3f21166fd9fa54"
+            + "383d17f2e20ffd68272f445b3e699fb65dd9a0df9a32c7e4962541451f5e800b0b7b2fef"
+            + "df7fd15378e66b0b5e948652b59f6ca775b7ee0d558bd084650567931d9bbd79f4b60efb"
+            + "3ad6b2161f9ab876dc97de95aa3f648157d9cbda81fe73338b834f6b1724354600c1aab4"
+            + "aa0bf6eb85b3223786a743cc35e28d81c05d4efa5e132973ce42d9830ff22a8e1d1fb1b4"
+            + "5134bbf9193f0570b43ad0bcb533f59d3ec8b77998509e766bd0261e5b913e11a2e3b219"
+            + "0ff660f71eeef5549fde3009ebfbb30afe2184b73d23d84e47fad48934b2193941c47453"
+            + "b44832ec9690ecf664a5b88897fdf8595909a93809a394ef4d08682f610135e2aa6d93b9"
+            + "2c295748493e0d71880a6454c89a4aae706f9e712415ee37f0dfe4414bba183a5cc25c1a"
+            + "688605d280abeb3afbd7dc1bf696f1f4d109c694a2b4b7c4dd67e0f9f6c556be8bcff103"
+            + "afb40928d25d91da4222f71fbc092cabe214bc12f4df702e54cd5ae9489a7b14e4f60162"
+            + "04080449ddb6c0cb4cf030a36a46c457e6a8e410dfad630299d7bd2c158d27da09679509"
+            + "2edbda28b71f9705201b0746f3375513bcb5aafaad0e1edc8ffa76faed6e5defadaeecbb"
+            + "9d4eeecf5944528ce1a1be1dac0477f8763eab422757378e922d0231496c791d0527dff1"
+            + "849526b125411c2fe471356879b993bcf1375d6339df647087d1860410a91d89f7baf07b"
+            + "5815fc7e2517dbddcb49d57e6f2e678473c416ddc2c878019d138d50ed95c3f3a0af8b3f"
+            + "c9aaefaa9cab78251ca6f899043f7efd66e262be492b655ae1f546953253736272d7ad7d"
+            + "c2ef505768a1be8755ca2ad5c959efb89068718955400fe9d1bdfcf631a2ce7127ecd865"
+            + "9b4aff6ba4e0e49a80485225de88e389bb25e4ac4636edf8203e285d8b9bc65e096256aa"
+            + "085ca7977d19afd3f7df0d7c72ffe2f5fc9eefbefe2e96be0e904f1a877792f9e4ea3e20"
+            + "f953892ab92743b3737fed3885b924bf1ebfeb3860490a741c62b6c1595b06d89018669c"
+            + "ac8b044fb833b6d39c3f660b66611df9b04f35958e486a5265d6103638f6777e5101101a"
+            + "aa1cd56575a294b0f4fc4fd830264ed086e1d4072f60c1a64dda3a0fa1a0ee18a7ff2355"
+            + "ac5704fb10d2eb59f8a0267b5555f7bf25e2a77c46e48eae1607fad7b8cc456e409617aa"
+            + "d7ae6ae443f362dd4cfdf6792c728d19d290b4e1b09c8521c0b8ebde286e44041cf50169"
+            + "75c509caee50af40de65ed809c62cd260320fb9b49c6be4fc939d9ddd06f4713a4b2a79f"
+            + "b125960930e9e059f44d302ee71e271325c5c411c82b90ca5eabe1fb2fa920a14297b69d"
+            + "12d25e9208f9a01c31a4b4abfce2469475671c169cdd4f5ab67eb3aec9fe8ad15dceb1e2"
+            + "91ea893a221245c4a9ac9dbc7f4b15d54af2b38b27bc0015fa32c61e5a9e1136dc9ff81b"
+            + "9bb0bed60f26d056337fdf548e9a8c033539551629d94f988a0c250b519ed92530056c24"
+            + "47d327f2d65be5b7b4cd11fc5ebba3ce0c143c7e83ab7fd51db5c6e2c09155e153013381"
+            + "e53bbf5691526832524a191157af1d6f6f2b8f15e86cab014c374609be46b5908bef3c59"
+            + "e829b2389daf473d472e937010dbf9909a3770b495d48c5a4f9e464f2e7ed39b964c594b"
+            + "63586de4e8fb3f10cea3a5d0411d09e9154b637cb0d3bafc219cc93cd960b11131492b38"
+            + "1606cd93faaf2b2a0a52fc0d5492610d527b515a696e1e5cf511fe8b25a0ba6559951f0e"
+            + "e7d3dbc25bdad5f4ac334e6a3b4bd66f9e924514a273dc30abff8a5a14c7ff4fac5693b8"
+            + "55152470538c6c6ff51597a7123681e787706d2deecb71ae525ac9a050efa9188bf58da6"
+            + "440c9ec2dc4d3fb993a8803803b0b36a3b0708ea265799b414be7dbc9d120ff6852bf5ee"
+            + "38cb740322b6d4b036653f2b922a1d86d054f2544110dbcbfd79d9fefeb13870c78fc7da"
+            + "83772b1758685e68e75175ff068b938beded3861caf5055e7f4655e6608804f02e4d1853"
+            + "f70310ea037788246549279da6f3e75ad07f995996dd9c8e36700a89f9240acf0939241b"
+            + "765e3d850aca760abdacd5e8a9714a2a713e1d9866448053e6b5f801c01b854d2c083743"
+            + "f20ed6fcd35514dfc28585ea61fd247889ef54a7c17a4183e924756e1c7d4a5b4ab2c681"
+            + "ff755c954629ece9dc13fbd46719ccacd119898a32f8a9f01d7ec397c7f633c52ecd6f50"
+            + "11f17ccc96298b92e57ddb086734035a6bcc31d8e831166a31ed6df350e48e4c69e5cad7"
+            + "cde3d9ee545fcea6dbdb640560429bcac05407f50f5cfa1800803c66a4d8b6a9a596489e"
+            + "43a3c28f3af7d032614677bec94dec582ed76b7861f3dd4131e8faccad2870da1cc49deb"
+            + "ef37db61f3e8e4174cc2ec2f53f5ccdeff83374efc70f7fd003a85c52b099df1cbc560ee"
+            + "94cf695db01ddd36cde96500c39bb2bb865c1c6da705b09c48fd024290ceea0b35037a1b"
+            + "9833b4c5ff13be2904c8c61873bb8d1abf3ee86bb23b634b773844070d9d8561c7c00266"
+            + "290dfac68a136cca2fd1f06e21274de5ba339a26d35a1056c42649d145dc8e5545a404d1"
+            + "553c2930baef962d53dc642566ffe59509dd858a30e2a9a9b3ba027c0dc48c2db7c54d0e"
+            + "7813765eb728419c4789081a64e3cba0cff0e070338f7d9d4eb855da450f48413108cc37"
+            + "19d70f0f498553d50858ccafc951196fdc086b9e1a734c8f58aced167dc71c999474694e"
+            + "92b3e374fbf692c41c9e463cd85953cf72fdded2275dfd61f3be638e837229d0b53c36b9"
+            + "5b90847d74abba6a88ab4f3eb6a25c291729323b0345506ea49a88ef4879410337f37c43"
+            + "67f2ec6bd05cb8d363bfebdf721eb6b87f32a6c4ad137f15d4663b5e716cc131c2535972"
+            + "4bdb0a6b5403e6839f9b62ff76730630af976b96e251c2b091f5393618b060f16d488463"
+            + "fe23d140b0c9c4b88de9e71ca1ba7699f36037338a9ba504dd9260e259f15af7305a7c3b"
+            + "79cb834295627c44d59c79480430d61f839ac6d3ebe50852eedbcf14fd62fd0df62eccb7"
+            + "edc7597f3292cdfab3e9027b3d3f39b24f46a81b17082c4e2f979e8567569bcc91bebcfb"
+            + "d72ecd28e404a6fbbe3a8cc1de56a0965a05ef2c0ea9dadcfa6850e8a6abe4487ca89a13"
+            + "c5d1b1363d2eb5cbb34076d2629e0c138c0790a207fe43d862e7186bf83ee0617e759770"
+            + "4084404a3dc5eb7875b966c5533f1c229ce7e00390c8391784f0f62609e022260fe7b49c"
+            + "93c7fe503ad6ee8deb0536bfec5918459f80dd8a2b4c214898ff06d370f43d99f8690cf9"
+            + "a387e4bdf294f3652e28c80f07798c75a7f7b5cea2129633c53a38e703c193bb80f7b901"
+            + "d189da0419487fe472689fb4dd6720dbc5d9d6da40aa8e718bbbe3a56995adaec5613ba9"
+            + "b3c92e66e6930643ce439010a5ba96f2a3997d0b83e43d24bfffadc343cce9df9450e933"
+            + "b46edacd4b55d96bca18c0c3dd07f5daded34cdc173f5a9ec947e913000cecf49380138f"
+            + "be029154dc8cb9935170c32cd58ee4a7a0aab4ba252bd1934a8247fbb2037f09f88f9dc3"
+            + "fb2fabbb39d4717d93b8a81576689697a2b4376308201402354ec1edefde7af6ed87ce65"
+            + "3e88ca92de0d0567657add49ca3587bc9649a2845c223b1603061363498536f0f316a3c1"
+            + "0ac4f0fe6f0319c809733d564478f6de141772a702595352fec747bc92b245ab0c774a7c"
+            + "9b36d01499db586e3d693369c0c8bb51d74075ec6fd81200405951fb808e41bffef60a4e"
+            + "dc7099cc4ecf1272a62011c4122b45a81c9591dd2fe3ed3ed976ec3c61409c88b6ac4621"
+            + "7944be5d0536cfd27336a688ec652c3870ef1ad5a6ac7c663898b74c34081101453aacf0"
+            + "5fcd990be99c3a8d7377caf3b154207967ebb4a9c7ca285c184679009cd440696488ec81"
+            + "1b617b9b4c48896ebba298d24d65d6e3b890a2a540ecbb300c433d5899ea9019bdf4f572"
+            + "b861d0c6b27a7916ad25a7b85b5eea8ffd7b560d5b30d8531485a4270d0e690b71c7ac6c"
+            + "388bd6ceee0d801924a93c46187182c365835655721e64ab139500f03a60e8194aa378c0"
+            + "7924254f36107d98c266e50019609273b9b3464f0d3bf12eec0a4f15cde5cddec00de241"
+            + "1e877eb78f579fdfad439202ed0dff21bb9c68abec115a009d5d4eb7c09199724a736e70"
+            + "b9861c25abe42de0d7fe28ae51380e5f273bf0592ba4e7541f7d50d3ff4b8facf5a20859"
+            + "0eabd4c53c4671fbcbf54b9f3e91bd86b031203752de9f1b09271ec703f03508785de019"
+            + "99fa581f26e1b2a862e77e620c781a3dbe5a9ee45bf5a9143d30b204270ea3c8b3b45c72"
+            + "6ecf3124b81f04c9e0f891fc5b2e8e946ce40749391944e7a0dcedca744d4dc886ae7333"
+            + "e7b02b1d3c312fedea2434abf64bf43ffb5afbf33eb68071f7525c885d31229a6a733345"
+            + "3f64610590e5ef453feef802e8edb7df5fe64bb909c26c7e415e208867d892e988293ffc"
+            + "8ddbe3e1f3dad628220ed192d12a1f4fbfbdfad4703dedbbb359042048dc4fb3e160adb0"
+            + "11eabee0608e18d2664ac60fd32e948c66358ad6a1346b83ac47cf3d53f4a649c56fe14a"
+            + "96ebfb7e6bf9c0b2496c779655359bad854ce693286a8c0ea0ac182db048da80aead6430"
+            + "c69fed482e3771227f2ce1070161470a7f762b6a9d4c6a22903fb45868110bf92f887fc1"
+            + "e74169b1e3baad6e3d723cbec45376251e37f9c9faf998cf6948ea93d9ed27736a928f9e"
+            + "b4b5cb91a0e3c5c2dce192299f4fc5e8876d860ecaf873287acb5dc8a13e267d7202b050"
+            + "168b1d819a5610bd46c06fbe8497edaccd534036f3cc4704f18b9b92a5431642f169cbf6"
+            + "6d586bbc6be6c2835dcef2ed7e35d51a00531b037a404b651fc65bc9fbada10870bb08c7"
+            + "274d0f8d89302a06e1b62006e71248823bb41e016a6d5cd14cdeb602cf8cb38692b9fce5"
+            + "60260fd5abfcb6acf5951c69caf5648dbfe85f27ae9665c737e91438f668f3ef0e9e9826"
+            + "67311ac5850486c91516929640dc33ad8f51fb80b41a6b373e9ebe639595d231b1fa6768"
+            + "3191868d856605940a290498546e9d2c6d15b5a0c00672200073b7c967b2df0ab8a62dc7"
+            + "13109a04d13b0b4b802ca8e1a3544775c2696c82acb35588fc69f2a8d9ccf11ce35cb0ae"
+            + "f43ba968f3b52e6e04f2de16b11a4f26acc55cc4d0bafe52ecb1d5bdb8db1723123cdd80"
+            + "e495dc95a803ff9ff68ba5cd610ec198eb9a785375b0f71019c861c75b1bd301e8ac9f34"
+            + "5532e88a14e217a938fc9a827912e01baec31a16c5c8a34ede046d5d665a24d11e243e19"
+            + "dc58ab1bf614cbe435852b8edb265eee838518337b881e1fb11d6e47aaec850613cc0b5f"
+            + "4042787f53f786cc5f504318dfe32bbb8e5a0886408bb9b52a593e60b0e561cea833da2b"
+            + "ed34485145dd2ddb54ebe84d6f18097ccbe70b451029e2678ff7672f2b31dd9ea0d03db6"
+            + "1e292b024319fe5e4f427cdea7482631d55338cb554129d55a7c3954c36cade3bae049a6"
+            + "b84654b116bbb9918655d1642973818f21f15f5853e9b1b59240f4a10604cb1ca90e8d0d"
+            + "5643bf1d60fcbc6a09bf4a695690c7ad9048b0c4c5548844755d02e5c8de3ecc1c88562b"
+            + "e1be873739bc820a560f185edc4ac229558128a98e1a69b2fe1fbc133cf082cd7f9e6cfc"
+            + "f54d5d0dd74c03cbda2db381efeb443f996db2ba84e81718575e7107ba7185ef456cfd20"
+            + "c240b3f12978252050c528bfcca6e615e5e523299ba71e6bf12dd86c6e2fa99d4237d70c"
+            + "06ac0599251256f4cf41862cae91f545965c467572c7019c7411ccc38072fc8c3480a369"
+            + "bababf175228987de46149dee8978d759d8d5e81eaa3a1de9ae2f9b16b0d2643f0c153bf"
+            + "dc80256b2668b4736782d96971197bdf0abceb8cfed9bcefcc7eba3b5d6ca6b830c47667"
+            + "4d27663324201a6201056d8410f90b0a56d22c2d99946aaae7df267e043b9a30149c780b"
+            + "7b7e1b663b3c98390c2a4694a4aa9eb32c633133acc9dba5da41cd4d1b857c72acbc93c5"
+            + "5dd4b76b3df6a8b734b19c290babd085f1961126ac8008671de1de3e1fc54963cc4bc7e4"
+            + "8b40af8af19e23e102e2b48320917e6d49577b1548fd6fb0a4489b2e21fde8c2226b3151"
+            + "4ade72eba062f07cc961b2bf3956e8c0167675b5471b7766535774b35fc0b08f172f0a46"
+            + "2632557ee67eca9949beb4dc9bc4b0432a3630c8496a8a9d88a00d666717e9e3f3238b53"
+            + "5bb222ef78cd7d1f2e5456854e51ae35bcb9aad00c91fc08a303e276d3938f6b7706401a"
+            + "3fd1d3803a1d1a3045e30b5a96683bdf8347f5993edb904018ceeae816eb66ea444cabda"
+            + "1d62513b9d7d5fa7a36424404a99c89be848cbf0a969add4754b7492facc83e3cbf86117"
+            + "fbacb60b37bc16e1bd36ba86bba14f3bf5b5fa9c4fbf101e42e2fd9d31f511002c8a7ab9"
+            + "d32c87c062160403f932aa67320aac3af6b86bea88b549ee3d01ed3a2370c99e4401345d"
+            + "ef5475a2a62990d510f7ecc23cdb4ea13b8c81dc1a1d21c9a344054e2841de42565ed06d"
+            + "f734855ac51dd789d9edd3b321cd4a416aff2116a8160fe92800f34c0dc2d9c3cfafd1bb"
+            + "ecd737e463b9ab70aa16e1b979aef9b95a9483382f28a113b1e658662286b3514211865f"
+            + "d73a4002c27c164b6843597c43061870a04cea20906c170f2a66009c0d26f7051714544d"
+            + "de24bf891f412197a7fbff960c774894965bef663d82fe0792b56624e03437888f083cc0"
+            + "a8fd4a7e8e5903730f943007780eb34293b067f0c1ef5de9eb8a15318bdec5365f5272f3"
+            + "0e7aadcca097df76e001c6df0b961bd7c31b6246cca18bf8319bf4b2e65154dbd7b5b572"
+            + "733620e4d551d761c5734d09aa92c9bf3fc2dbbe4b19e002b92b52f0edb5393b67dd0a23"
+            + "2473a7d7f35618a5c9462c97825c0b8b6517cc25b4616eff3b58078ed024c11f08a9459d"
+            + "afe160e32dc3ffa93a044ae8c255d8ac5f5486c28d5967c0f48109ff4372be29cfd9823f"
+            + "128c427b2bb4fe09204c3c6067c9ae32eec726a48a151f0f11b1231f6c035dbefc0e9df0"
+            + "e12cb2a12b70d4b10869861613fb01e1306c3e0c24a9c6fd062c697144e93c3771969658"
+            + "e23560d9a2aa1de260109161fa6d95ab5a6cc3be7f172ac47c290758153858ec8c60c728"
+            + "4f8531913ec36ef5b0a4ad1c4f736a65c01fc7d0ba05c85aabbb2365da664aeea2974c95"
+            + "030f2814e306d2a65ad48a3351976f7f0d6ae5a76adba8a52764ceee9f5d75d1e0be3d6d"
+            + "ae4b190ba7d17ae9cd7fe51131cea2ab0c95a5d1fac27cf2c757f05b5d60436bdf1f9651"
+            + "b0332df834e76107647024545e773f835d0dd41bd10fcc70ff578cce77213bd605eea6fe"
+            + "414035c6c11cf8c359e89a4126b8a5b37ff53805bd0fac7194b3f1c589c158e8c4d2e342"
+            + "abc4a767789ea263616690e2e6413fea8e980734dde9b0b237e7962d620fa7659d46fc87"
+            + "260466be275ac41bd1f22322df094a6509fa26ec1ac04f437a1f9d791d938d94556ea14a"
+            + "5e2cb17e72468a47e98a5ffb4ab6ab03c79c5399a5caa32f185ef02b2ae9b53f3eedeb83"
+            + "3f7b973d442c8d3056fb4ef033d38172f75383cf514bc9df40f6fe12164a2b98cf9fee80"
+            + "f188c64c50d9072efb14f2a517cfbdf0dfe4f2b53288cf035c755d2950b55f07f9499dcc"
+            + "77f7beb1da6758a483f0be38e8799f37169ddb2f9a5128927f469ef90171b85592686d6d"
+            + "ac9e484140af2e14f6c72bcd9f23772a1fb474b8dd8e55b4147d449854a2ae26a4f1daa6"
+            + "b32b5cafbb123b7a6a11fe4670723012f816a490c2b2ff9946015c74bb3cb9e10c53a314"
+            + "a1b61f144a569cc99f86effbabae912b99ab6338d7342534d45919060e4f09a27ca847e7"
+            + "8ec2d21f093536eb93a4040a800e099d1d083d3b006aa5dba975d895dc7760a599a9d220"
+            + "31248837e6ea6d8f6426c2156c516d90233b2164ba2301a965c9e8ae7cdc6890b07a6338"
+            + "f2049fb6af223400e628f7cfde8d21cad5d78ea0459e6fb93ab76475afce487dc8219efe"
+            + "9797da8e4f6f61d8f6f3aa8a88445c226571c41087c9cc719926a2215544e36233adabeb"
+            + "fb6e426ebfd9742d3156d77d32639861632f63c408ffc62abc085fbe563401d61b885390"
+            + "950deb8f9c8f31a69ecf83d16daa4e1f3b22705969aec7d3402f3b8e00ded36c2b83cd34"
+            + "999f01ff7b257eb947f68059d856f6943ffd45294947394e4019e81070f68cad769d59ea"
+            + "ae70592d127195275a25c24886f3a72d7c12d4e89a12d0645996d33a163a42a3b08bcdf4"
+            + "3bb1ae77859311630736efdfaf5000821d5e792af1bc76069c541358bdf2dbe04316d9b7"
+            + "071d4f01f8b5cf584e09ba1219353c09997087cc5da02eccc9ab838b2260079f4ec5810b"
+            + "ce0adc7cd66de8539f89c96753ea782bcb2bdd765f84547f085273799a0a9f35fd6e0076"
+            + "9878fad01be6bef1114be94b5ffc4fe087b9fe82c0558cd5f622dd18f4319f5ea54ef42f"
+            + "14c70be5acb5dabbd09aee864d902185116e5bb5be2717fcdb5e9b7ed79bbce616babd83"
+            + "78d8ac445c";
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig1), sig1));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig2), sig2));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig3), sig3));
+    }
+
+    public void testVerifySignatureSHA256()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        SecureRandom rand = new SecureRandom();
+
+        byte[] msg1 = new byte[1024];
+
+        rand.nextBytes(msg1);
+
+        for (int i = 0; i < 3; i++)
+        {
+            byte[] publicKey = xmssMT.exportPublicKey();
+            xmssMT.sign(msg1);
+            byte[] signature = xmssMT.sign(msg1);
+            try
+            {
+                assertEquals(true, xmssMT.verifySignature(msg1, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+
+            msg1[0] ^= 0xff;
+            try
+            {
+                assertEquals(false, xmssMT.verifySignature(msg1, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+        }
+    }
+
+    public void testVerifySignerSHA256()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMTKeyPairGenerator xmssMT = new XMSSMTKeyPairGenerator();
+
+        xmssMT.init(new XMSSMTKeyGenerationParameters(params, new NullPRNG()));
+
+        AsymmetricCipherKeyPair kp = xmssMT.generateKeyPair();
+        SecureRandom rand = new SecureRandom();
+
+        byte[] msg1 = new byte[1024];
+
+        rand.nextBytes(msg1);
+
+        XMSSMTSigner signer = new XMSSMTSigner();
+
+        AsymmetricKeyParameter privateKey = kp.getPrivate();
+
+        for (int i = 0; i < 3; i++)
+        {
+            signer.init(true, privateKey);
+
+            byte[] signature = signer.generateSignature(msg1);
+
+            privateKey = signer.getUpdatedPrivateKey();
+
+            signer.init(false, kp.getPublic());
+
+            assertEquals(true, signer.verifySignature(msg1, signature));
+
+            msg1[0] ^= 0xff;
+
+            assertEquals(false, signer.verifySignature(msg1, signature));
+        }
+    }
+
+    public void testVerifySignatureSHA512()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMT xmssMT = new XMSSMT(params, new NullPRNG());
+        xmssMT.generateKeys();
+        byte[] msg1 = new byte[1024];
+
+        for (int i = 0; i < 3; i++)
+        {
+            byte[] publicKey = xmssMT.exportPublicKey();
+            xmssMT.sign(msg1);
+            byte[] signature = xmssMT.sign(msg1);
+            try
+            {
+                assertEquals(true, xmssMT.verifySignature(msg1, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+            byte[] msg2 = new byte[1024];
+            msg2[0] = 0x01;
+            try
+            {
+                assertEquals(false, xmssMT.verifySignature(msg2, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+        }
+    }
+
+    public void testVerifySignerSHA512()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMTKeyPairGenerator xmssMT = new XMSSMTKeyPairGenerator();
+
+        xmssMT.init(new XMSSMTKeyGenerationParameters(params, new NullPRNG()));
+
+        AsymmetricCipherKeyPair kp = xmssMT.generateKeyPair();
+        SecureRandom rand = new SecureRandom();
+
+        byte[] msg1 = new byte[1024];
+
+        rand.nextBytes(msg1);
+
+        XMSSMTSigner signer = new XMSSMTSigner();
+
+        AsymmetricKeyParameter privateKey = kp.getPrivate();
+
+        for (int i = 0; i < 3; i++)
+        {
+            signer.init(true, privateKey);
+
+            byte[] signature = signer.generateSignature(msg1);
+
+            privateKey = signer.getUpdatedPrivateKey();
+
+            signer.init(false, kp.getPublic());
+
+            assertEquals(true, signer.verifySignature(msg1, signature));
+
+            msg1[0] ^= 0xff;
+
+            assertEquals(false, signer.verifySignature(msg1, signature));
+        }
+    }
+
+    public void testImportKeysSHA256()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT xmssMT1 = new XMSSMT(params, new NullPRNG());
+        xmssMT1.generateKeys();
+        byte[] msg1 = new byte[1024];
+        byte[] msg2 = new byte[2048];
+        byte[] msg3 = new byte[3096];
+        Arrays.fill(msg1, (byte)0xaa);
+        Arrays.fill(msg2, (byte)0xbb);
+        Arrays.fill(msg3, (byte)0xcc);
+        byte[] signature1 = xmssMT1.sign(msg1);
+        byte[] signature2 = xmssMT1.sign(msg2);
+        byte[] exportedPrivateKey = xmssMT1.exportPrivateKey();
+        byte[] exportedPublicKey = xmssMT1.exportPublicKey();
+        byte[] publicKey = xmssMT1.exportPublicKey();
+        byte[] signature3 = xmssMT1.sign(msg3);
+
+        XMSSMT xmssMT2 = new XMSSMT(params, new NullPRNG());
+
+        xmssMT2.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] signature4 = xmssMT2.sign(msg3);
+        assertEquals(true, Arrays.areEqual(signature3, signature4));
+        xmssMT2.generateKeys();
+        try
+        {
+            assertEquals(true, xmssMT2.verifySignature(msg1, signature1, publicKey));
+            assertEquals(true, xmssMT2.verifySignature(msg2, signature2, publicKey));
+            assertEquals(true, xmssMT2.verifySignature(msg3, signature3, publicKey));
+            assertEquals(false, xmssMT2.verifySignature(msg1, signature3, publicKey));
+            assertEquals(false, xmssMT2.verifySignature(msg2, signature3, publicKey));
+        }
+        catch (ParseException ex)
+        {
+            ex.printStackTrace();
+            fail();
+        }
+    }
+
+    public void testImportKeysSHA512()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMT xmssMT1 = new XMSSMT(params, new NullPRNG());
+        xmssMT1.generateKeys();
+        byte[] msg1 = new byte[1024];
+        byte[] msg2 = new byte[2048];
+        byte[] msg3 = new byte[3096];
+        Arrays.fill(msg1, (byte)0xaa);
+        Arrays.fill(msg2, (byte)0xbb);
+        Arrays.fill(msg3, (byte)0xcc);
+        byte[] signature1 = xmssMT1.sign(msg1);
+        byte[] signature2 = xmssMT1.sign(msg2);
+        byte[] exportedPrivateKey = xmssMT1.exportPrivateKey();
+        byte[] exportedPublicKey = xmssMT1.exportPublicKey();
+        byte[] publicKey = xmssMT1.exportPublicKey();
+        byte[] signature3 = xmssMT1.sign(msg3);
+
+        XMSSMT xmssMT2 = new XMSSMT(params, new NullPRNG());
+
+        xmssMT2.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] signature4 = xmssMT2.sign(msg3);
+        assertEquals(true, Arrays.areEqual(signature3, signature4));
+        xmssMT2.generateKeys();
+        try
+        {
+            assertEquals(true, xmssMT2.verifySignature(msg1, signature1, publicKey));
+            assertEquals(true, xmssMT2.verifySignature(msg2, signature2, publicKey));
+            assertEquals(true, xmssMT2.verifySignature(msg3, signature3, publicKey));
+            assertEquals(false, xmssMT2.verifySignature(msg1, signature3, publicKey));
+            assertEquals(false, xmssMT2.verifySignature(msg2, signature3, publicKey));
+        }
+        catch (ParseException ex)
+        {
+            ex.printStackTrace();
+            fail();
+        }
+    }
+
+    public void testRandom()
+        throws ClassNotFoundException, IOException
+    {
+        XMSSMTParameters params = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMT xmss1 = new XMSSMT(params, new NullPRNG());
+        xmss1.generateKeys();
+        byte[] publicKey = xmss1.exportPublicKey();
+        byte[] message = new byte[1024];
+
+        for (int i = 0; i < 5; i++)
+        {
+            xmss1.sign(message);
+        }
+        byte[] signature = xmss1.sign(message);
+        assertTrue(Arrays.areEqual(publicKey, xmss1.exportPublicKey()));
+        try
+        {
+            xmss1.verifySignature(message, signature, publicKey);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+        assertTrue(Arrays.areEqual(publicKey, xmss1.exportPublicKey()));
+        xmss1.sign(message);
+        byte[] privateKey7 = xmss1.exportPrivateKey();
+        try
+        {
+            xmss1.verifySignature(message, signature, publicKey);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+        assertTrue(Arrays.areEqual(privateKey7, xmss1.exportPrivateKey()));
+        byte[] signature7 = xmss1.sign(message);
+
+        xmss1.importState(privateKey7, publicKey);
+
+        byte[] signature7AfterImport = xmss1.sign(message);
+        assertTrue(Arrays.areEqual(signature7AfterImport, signature7));
+
+        XMSSMTParameters params2 = new XMSSMTParameters(20, 10, new SHA512Digest());
+        XMSSMT xmss2 = new XMSSMT(params2, new NullPRNG());
+        try
+        {
+            boolean valid = xmss2.verifySignature(message, signature7, publicKey);
+            assertTrue(valid);
+            valid = xmss2.verifySignature(message, signature, publicKey);
+            assertTrue(valid);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+
+        XMSSMT xmss3 = new XMSSMT(params, new NullPRNG());
+
+        xmss3.importState(privateKey7, publicKey);
+
+        byte[] signatureAgain = xmss3.sign(message);
+        assertTrue(Arrays.areEqual(signatureAgain, signature7));
+    }
+
+    public void testPublicSeed()
+        throws IOException, ClassNotFoundException
+    {
+        byte[] message = new byte[1024];
+        XMSSMTParameters params1 = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT mt1 = new XMSSMT(params1, new SecureRandom());
+        mt1.generateKeys();
+        byte[] publicKey1 = mt1.exportPublicKey();
+        byte[] signature1 = mt1.sign(message);
+
+        XMSSMTParameters params2 = new XMSSMTParameters(20, 10, new SHA256Digest());
+        XMSSMT mt2 = new XMSSMT(params2, new NullPRNG());
+        mt2.generateKeys();
+        byte[] publicKey2 = mt2.exportPublicKey();
+        byte[] privateKey2 = mt2.exportPrivateKey();
+        byte[] signature2 = mt2.sign(message);
+
+        mt2.importState(privateKey2, publicKey2);
+
+        try
+        {
+            boolean isValid = mt2.verifySignature(message, signature1, publicKey1);
+            assertTrue(isValid);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+        byte[] signature3 = mt2.sign(message);
+        assertTrue(Arrays.areEqual(signature2, signature3));
+    }
+
+    public void testBDSImport()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSMTParameters params = new XMSSMTParameters(10, 5, new SHA256Digest());
+        XMSSMT xmss = new XMSSMT(params, new SecureRandom());
+        xmss.generateKeys();
+        xmss.sign(new byte[1024]);
+        byte[] exportedPrivateKey = xmss.exportPrivateKey();
+        byte[] exportedPublicKey = xmss.exportPublicKey();
+
+        xmss.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] sig1 = xmss.sign(new byte[1024]);
+
+        xmss.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] sig2 = xmss.sign(new byte[1024]);
+        assertEquals(true, Arrays.areEqual(sig1, sig2));
+        try
+        {
+            xmss.importState(exportedPrivateKey, exportedPublicKey);
+        }
+        catch (Exception ex)
+        {
+        }
+        xmss.sign(new byte[1024]);
+        byte[] sig3 = xmss.sign(new byte[1024]);
+        assertEquals(false, Arrays.areEqual(sig1, sig3));
+        try
+        {
+            xmss.importState(null, exportedPublicKey);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+        try
+        {
+            xmss.importState(exportedPrivateKey, null);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java
index 991ebea..a121f5d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java
@@ -1,50 +1,61 @@
 package org.bouncycastle.pqc.crypto.test;
 
 import java.io.IOException;
-import java.text.ParseException;
-
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
-import org.bouncycastle.pqc.crypto.xmss.XMSS;
-import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
 
 import junit.framework.TestCase;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSSPrivateKey class.
- * 
  */
-public class XMSSPrivateKeyTest extends TestCase {
+public class XMSSPrivateKeyTest
+    extends TestCase
+{
+    public void testPrivateKeyParsing()
+        throws ClassNotFoundException, IOException
+    {
+        parsingTest(new SHA256Digest());
+        parsingTest(new SHA512Digest());
+        parsingTest(new SHAKEDigest(128));
+        parsingTest(new SHAKEDigest(256));
+    }
 
-	public void testPrivateKeyParsing() throws ClassNotFoundException, IOException {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		byte[] root = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
-				(byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e,
-				(byte) 0x0f, (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x03, (byte) 0x40, (byte) 0x50, (byte) 0x60,
-				(byte) 0x70, (byte) 0x80, (byte) 0x90, (byte) 0xa0, (byte) 0xb0, (byte) 0xc0, (byte) 0xd0, (byte) 0xe0,
-				(byte) 0xf0 };
-		XMSSPrivateKeyParameters privateKey = null;
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(0xaa).withRoot(root).build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		}
-		byte[] export = privateKey.toByteArray();
+    private void parsingTest(Digest digest)
+            throws ClassNotFoundException, IOException
+        {
+            XMSSParameters params = new XMSSParameters(10, digest);
+            byte[] root = generateRoot(digest);
+            XMSSPrivateKeyParameters privateKey = new XMSSPrivateKeyParameters.Builder(params).withRoot(root).build();
 
-		XMSSPrivateKeyParameters privateKey2 = null;
-		try {
-			privateKey2 = new XMSSPrivateKeyParameters.Builder(params).withPrivateKey(export, new XMSS(params)).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		assertEquals(privateKey.getIndex(), privateKey2.getIndex());
-		assertEquals(true, XMSSUtil.compareByteArray(privateKey.getSecretKeySeed(), privateKey2.getSecretKeySeed()));
-		assertEquals(true, XMSSUtil.compareByteArray(privateKey.getSecretKeyPRF(), privateKey2.getSecretKeyPRF()));
-		assertEquals(true, XMSSUtil.compareByteArray(privateKey.getPublicSeed(), privateKey2.getPublicSeed()));
-		assertEquals(true, XMSSUtil.compareByteArray(privateKey.getRoot(), privateKey2.getRoot()));
-	}
+            byte[] export = privateKey.toByteArray();
+
+            XMSSPrivateKeyParameters privateKey2 = new XMSSPrivateKeyParameters.Builder(params).withPrivateKey(export, params).build();
+
+            assertEquals(privateKey.getIndex(), privateKey2.getIndex());
+            assertEquals(true, Arrays.areEqual(privateKey.getSecretKeySeed(), privateKey2.getSecretKeySeed()));
+            assertEquals(true, Arrays.areEqual(privateKey.getSecretKeyPRF(), privateKey2.getSecretKeyPRF()));
+            assertEquals(true, Arrays.areEqual(privateKey.getPublicSeed(), privateKey2.getPublicSeed()));
+            assertEquals(true, Arrays.areEqual(privateKey.getRoot(), privateKey2.getRoot()));
+        }
+
+    private byte[] generateRoot(Digest digest)
+    {
+        int digestSize = (digest instanceof Xof) ? digest.getDigestSize() * 2 : digest.getDigestSize();
+        byte[] rv = new byte[digestSize];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = (byte)i;
+        }
+        
+        return rv;
+    }
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPublicKeyTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPublicKeyTest.java
index f56ad21..9f835c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPublicKeyTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSPublicKeyTest.java
@@ -1,15 +1,11 @@
 package org.bouncycastle.pqc.crypto.test;
 
-import java.text.ParseException;
-
+import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
-
-import junit.framework.TestCase;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSSPublicKey class.
@@ -18,34 +14,24 @@
 public class XMSSPublicKeyTest extends TestCase {
 
 	public void testPublicKeyParsingSHA256() {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
+		XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
 		byte[] root = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
 				(byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e,
 				(byte) 0x0f, (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x03, (byte) 0x40, (byte) 0x50, (byte) 0x60,
 				(byte) 0x70, (byte) 0x80, (byte) 0x90, (byte) 0xa0, (byte) 0xb0, (byte) 0xc0, (byte) 0xd0, (byte) 0xe0,
 				(byte) 0xf0 };
-		XMSSPublicKeyParameters publicKey = null;
-		try {
-			publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		}
+		XMSSPublicKeyParameters publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).build();
+
 		byte[] export = publicKey.toByteArray();
 
-		XMSSPublicKeyParameters publicKey2 = null;
-		try {
-			publicKey2 = new XMSSPublicKeyParameters.Builder(params).withPublicKey(export).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		assertEquals(true, XMSSUtil.compareByteArray(publicKey.getRoot(), publicKey2.getRoot()));
-		assertEquals(true, XMSSUtil.compareByteArray(publicKey.getPublicSeed(), publicKey2.getPublicSeed()));
+		XMSSPublicKeyParameters publicKey2 = new XMSSPublicKeyParameters.Builder(params).withPublicKey(export).build();
+
+		assertEquals(true, Arrays.areEqual(publicKey.getRoot(), publicKey2.getRoot()));
+		assertEquals(true, Arrays.areEqual(publicKey.getPublicSeed(), publicKey2.getPublicSeed()));
 	}
 
 	public void testPublicKeyParsingSHA512() {
-		XMSSParameters params = new XMSSParameters(10, new SHA512Digest(), new NullPRNG());
+		XMSSParameters params = new XMSSParameters(10, new SHA512Digest());
 		byte[] root = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
 				(byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e,
 				(byte) 0x0f, (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x03, (byte) 0x40, (byte) 0x50, (byte) 0x60,
@@ -55,35 +41,20 @@
 				(byte) 0x0f, (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x03, (byte) 0x40, (byte) 0x50, (byte) 0x60,
 				(byte) 0x70, (byte) 0x80, (byte) 0x90, (byte) 0xa0, (byte) 0xb0, (byte) 0xc0, (byte) 0xd0, (byte) 0xe0,
 				(byte) 0xf0 };
-		XMSSPublicKeyParameters publicKey = null;
-		try {
-			publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		}
+		XMSSPublicKeyParameters publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).build();
+
 		byte[] export = publicKey.toByteArray();
 
-		XMSSPublicKeyParameters publicKey2 = null;
-		try {
-			publicKey2 = new XMSSPublicKeyParameters.Builder(params).withPublicKey(export).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		assertEquals(true, XMSSUtil.compareByteArray(publicKey.getRoot(), publicKey2.getRoot()));
-		assertEquals(true, XMSSUtil.compareByteArray(publicKey.getPublicSeed(), publicKey2.getPublicSeed()));
+		XMSSPublicKeyParameters publicKey2 = new XMSSPublicKeyParameters.Builder(params).withPublicKey(export).build();
+
+		assertEquals(true, Arrays.areEqual(publicKey.getRoot(), publicKey2.getRoot()));
+		assertEquals(true, Arrays.areEqual(publicKey.getPublicSeed(), publicKey2.getPublicSeed()));
 	}
 
 	public void testConstructor() {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSSPublicKeyParameters pk = null;
-		try {
-			pk = new XMSSPublicKeyParameters.Builder(params).build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		}
+		XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
+		XMSSPublicKeyParameters pk = new XMSSPublicKeyParameters.Builder(params).build();
+
 		byte[] pkByte = pk.toByteArray();
 		/* check everything is 0 */
 		for (int i = 0; i < pkByte.length; i++) {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSReducedSignatureTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSReducedSignatureTest.java
index c5a73ab..eac9342 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSReducedSignatureTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSReducedSignatureTest.java
@@ -1,81 +1,63 @@
 package org.bouncycastle.pqc.crypto.test;
 
-import java.text.ParseException;
-
 import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMT;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSMTSignature;
 import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSReducedSignature;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSSReducedSignature class.
- * 
  */
-public class XMSSReducedSignatureTest extends TestCase {
+public class XMSSReducedSignatureTest
+    extends TestCase
+{
 
-	public void testSignatureParsingSHA256() {
-		XMSSMTParameters params = new XMSSMTParameters(8, 2, new SHA256Digest(), new NullPRNG());
-		XMSSMT mt = new XMSSMT(params);
-		mt.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = mt.sign(message);
-		XMSSMTSignature sig2 = null;
-		try {
-			sig2 = new XMSSMTSignature.Builder(params).withSignature(sig1).build();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		XMSSReducedSignature reducedSignature1 = sig2.getReducedSignatures().get(0);
-		byte[] reducedSignatureBinary = reducedSignature1.toByteArray();
-		XMSSReducedSignature reducedSignature2 = null;
-		try {
-			reducedSignature2 = new XMSSReducedSignature.Builder(new XMSSParameters(4, new SHA256Digest(), new NullPRNG())).withReducedSignature(reducedSignatureBinary).build();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		assertTrue(XMSSUtil.compareByteArray(reducedSignatureBinary, reducedSignature2.toByteArray()));
-	}
+    public void testSignatureParsingSHA256()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(8, 2, new SHA256Digest());
+        XMSSMT mt = new XMSSMT(params, new NullPRNG());
+        mt.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = mt.sign(message);
+        XMSSMTSignature sig2 = new XMSSMTSignature.Builder(params).withSignature(sig1).build();
 
-	public void testSignatureParsingSHA512() {
-		XMSSMTParameters params = new XMSSMTParameters(4, 2, new SHA512Digest(), new NullPRNG());
-		XMSSMT mt = new XMSSMT(params);
-		mt.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = mt.sign(message);
-		XMSSMTSignature sig2 = null;
-		try {
-			sig2 = new XMSSMTSignature.Builder(params).withSignature(sig1).build();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		XMSSReducedSignature reducedSignature1 = sig2.getReducedSignatures().get(0);
-		byte[] reducedSignatureBinary = reducedSignature1.toByteArray();
-		XMSSReducedSignature reducedSignature2 = null;
-		try {
-			reducedSignature2 = new XMSSReducedSignature.Builder(new XMSSParameters(2, new SHA512Digest(), new NullPRNG())).withReducedSignature(reducedSignatureBinary).build();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		assertTrue(XMSSUtil.compareByteArray(reducedSignatureBinary, reducedSignature2.toByteArray()));
-	}
+        XMSSReducedSignature reducedSignature1 = sig2.getReducedSignatures().get(0);
+        byte[] reducedSignatureBinary = reducedSignature1.toByteArray();
+        XMSSReducedSignature reducedSignature2 = new XMSSReducedSignature.Builder(new XMSSParameters(4, new SHA256Digest())).withReducedSignature(reducedSignatureBinary).build();
 
-	public void testConstructor() {
-		XMSSReducedSignature sig = null;
-		try {
-			sig = new XMSSReducedSignature.Builder(new XMSSParameters(4, new SHA512Digest(), new NullPRNG())).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-		}
-		byte[] sigByte = sig.toByteArray();
-		/* check everything is 0 */
-		for (int i = 0; i < sigByte.length; i++) {
-			assertEquals(0x00, sigByte[i]);
-		}
-	}
+        assertTrue(Arrays.areEqual(reducedSignatureBinary, reducedSignature2.toByteArray()));
+    }
+
+    public void testSignatureParsingSHA512()
+    {
+        XMSSMTParameters params = new XMSSMTParameters(4, 2, new SHA512Digest());
+        XMSSMT mt = new XMSSMT(params, new NullPRNG());
+        mt.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = mt.sign(message);
+        XMSSMTSignature sig2 = new XMSSMTSignature.Builder(params).withSignature(sig1).build();
+
+        XMSSReducedSignature reducedSignature1 = sig2.getReducedSignatures().get(0);
+        byte[] reducedSignatureBinary = reducedSignature1.toByteArray();
+        XMSSReducedSignature reducedSignature2 = new XMSSReducedSignature.Builder(new XMSSParameters(2, new SHA512Digest())).withReducedSignature(reducedSignatureBinary).build();
+
+        assertTrue(Arrays.areEqual(reducedSignatureBinary, reducedSignature2.toByteArray()));
+    }
+
+    public void testConstructor()
+    {
+        XMSSReducedSignature sig = new XMSSReducedSignature.Builder(new XMSSParameters(4, new SHA512Digest())).build();
+
+        byte[] sigByte = sig.toByteArray();
+        /* check everything is 0 */
+        for (int i = 0; i < sigByte.length; i++)
+        {
+            assertEquals(0x00, sigByte[i]);
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSSignatureTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSSignatureTest.java
index 8c8153f..d43e1bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSSignatureTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSSignatureTest.java
@@ -1,68 +1,56 @@
 package org.bouncycastle.pqc.crypto.test;
 
-import java.text.ParseException;
-
 import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSS;
 import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSSignature;
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Test cases for XMSSSignature class.
- * 
  */
-public class XMSSSignatureTest extends TestCase {
+public class XMSSSignatureTest
+    extends TestCase
+{
 
-	public void testSignatureParsingSHA256() {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmss.sign(message);
-		XMSSSignature sig2 = null;
-		try {
-			sig2 = new XMSSSignature.Builder(params).withSignature(sig1).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] sig3 = sig2.toByteArray();
-		assertEquals(true, XMSSUtil.compareByteArray(sig1, sig3));
-	}
+    public void testSignatureParsingSHA256()
+    {
+        XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmss.sign(message);
+        XMSSSignature sig2 = new XMSSSignature.Builder(params).withSignature(sig1).build();
 
-	public void testSignatureParsingSHA512() {
-		XMSSParameters params = new XMSSParameters(10, new SHA512Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmss.sign(message);
-		XMSSSignature sig2 = null;
-		try {
-			sig2 = new XMSSSignature.Builder(params).withSignature(sig1).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] sig3 = sig2.toByteArray();
-		assertEquals(true, XMSSUtil.compareByteArray(sig1, sig3));
-	}
+        byte[] sig3 = sig2.toByteArray();
+        assertEquals(true, Arrays.areEqual(sig1, sig3));
+    }
 
-	public void testConstructor() {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSSSignature sig = null;
-		try {
-			sig = new XMSSSignature.Builder(params).build();
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-		}
-		byte[] sigByte = sig.toByteArray();
-		/* check everything is 0 */
-		for (int i = 0; i < sigByte.length; i++) {
-			assertEquals(0x00, sigByte[i]);
-		}
-	}
+    public void testSignatureParsingSHA512()
+    {
+        XMSSParameters params = new XMSSParameters(10, new SHA512Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmss.sign(message);
+        XMSSSignature sig2 = new XMSSSignature.Builder(params).withSignature(sig1).build();
+
+        byte[] sig3 = sig2.toByteArray();
+        assertEquals(true, Arrays.areEqual(sig1, sig3));
+    }
+
+    public void testConstructor()
+    {
+        XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
+        XMSSSignature sig = new XMSSSignature.Builder(params).build();
+
+        byte[] sigByte = sig.toByteArray();
+        /* check everything is 0 */
+        for (int i = 0; i < sigByte.length; i++)
+        {
+            assertEquals(0x00, sigByte[i]);
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSTest.java
index b152331..81afe81 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSTest.java
@@ -3,429 +3,476 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.text.ParseException;
-import java.util.Arrays;
 import java.util.List;
 
+import junit.framework.TestCase;
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.pqc.crypto.xmss.NullPRNG;
 import org.bouncycastle.pqc.crypto.xmss.XMSS;
 import org.bouncycastle.pqc.crypto.xmss.XMSSNode;
 import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
 import org.bouncycastle.pqc.crypto.xmss.XMSSSignature;
 import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 
-import junit.framework.TestCase;
-
 /**
  * Test cases for XMSS class.
- * 
  */
-public class XMSSTest extends TestCase {
+public class XMSSTest
+    extends TestCase
+{
 
-	public void testAuthPath() {
-		XMSSParameters xmssParams = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(xmssParams);
-		xmss.generateKeys();
-		String[] expectedAuthPath = {
-				"e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4",
-				"251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce38",
-				"53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d6270",
-				"5683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"16b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee9",
-				"9d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7",
-				"233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb3",
-				"9317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd5",
-				"14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca",
-				"6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5"
-		};
-		byte[] signature = xmss.sign(new byte[1024]);
-		XMSSSignature sig = null;
-		try {
-			sig = new XMSSSignature.Builder(xmssParams).withSignature(signature).build();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		List<XMSSNode> authPath = sig.getAuthPath();
-		for (int i = 0; i < authPath.size(); i++) {
-			assertEquals(expectedAuthPath[i], Hex.toHexString(authPath.get(i).getValue()));
-		}
-	}
-	
-	public void testGenKeyPairSHA256() {
-		XMSSParameters xmssParams = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(xmssParams);
-		xmss.generateKeys();
-		byte[] privateKey = xmss.exportPrivateKey();
-		byte[] publicKey = xmss.exportPublicKey();
-		String expectedPrivateKey = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073c3fc6de1195aa5d69f9dafc9db8504aa8059115e8319ca15cf58a1c83c0de3";
-		String expectedPublicKey = "73c3fc6de1195aa5d69f9dafc9db8504aa8059115e8319ca15cf58a1c83c0de30000000000000000000000000000000000000000000000000000000000000000";
-		byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPrivateKey), strippedPrivateKey));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPublicKey), publicKey));
-	}
-	
-	public void testGenKeyPairSHA512() {
-		XMSSParameters xmssParams = new XMSSParameters(10, new SHA512Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(xmssParams);
-		xmss.generateKeys();
-		byte[] privateKey = xmss.exportPrivateKey();
-		byte[] publicKey = xmss.exportPublicKey();
-		String expectedPrivateKey = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f48df90f8e217076d8af6327691321bdcf63668c4bd28d021d49f2334eca845fa3073991049286c0eef5dc7f23ec0b31f5c1bd1e5b8edb2403ae02f292f6f30e";
-		String expectedPublicKey = "f48df90f8e217076d8af6327691321bdcf63668c4bd28d021d49f2334eca845fa3073991049286c0eef5dc7f23ec0b31f5c1bd1e5b8edb2403ae02f292f6f30e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
-		byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPrivateKey), strippedPrivateKey));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedPublicKey), publicKey));
-	}
-	
-	public void testSignSHA256() {
-		XMSSParameters params = new XMSSParameters(10, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmss.sign(message);
-		byte[] sig2 = xmss.sign(message);
-		byte[] sig3 = xmss.sign(message);
-		String expectedSig1 = "000000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf80c20270a1e779fd234d57b893ca841994e128a6df3f1832eb5998b16e3273084c84e049da83c891856b5fb20f17e1ecb6873c3a87e1a630e327b36981a5622aad6044c3f4d8296c4e744bca233a84b42f0524ee3ccf68eca45b66de7dfc123cac6951a57a802da0b6aa1a1e1ed698934dd2200331d1aeebbdfa0fe13f08ff8d3d2f5c9d542531e6021e34400eaf9d84911ebc0a2e84dff10dad8d078ebb356f522878fe61adbc7218d48af31516cc40689d06771b558c26a700da094d2bbd856c512c77c392b368f817e13fc10050b181aa5ef9294e6d90f1ade14f0774e70593fef221958f95f743c0b5db6a2b782b3b9ab10289fdd2b77d333d5476a0a3a48826e8992cc5a18cfff739deb931f5444b564c86d199916a947222c5d728922d912a30ec58a2422e0ddfe9cb018d99cb3b5df37f40911d2d86478280ec18f4f61edf22cd1eb6b785fab62ccad315afe6e1945c71716cfe4e9a549f591d258d2d128bd471de0054d1dae6523932f036d974dc80cd5eb123f5c4cf97b8747dcbf0ff2daaae1a2b928ad74a1441eac07053c9665650bd197afecee8271e54919695d83286d635afe6977b30a653b2792aa8d8ce6597c87e046c603f44355201981a03095230dd060c88b76fc48fe6714ba50cf8fd4fdfc537e66c2717d96e9f20a56c4d0858d1ca68bbebf405457ef5bb3cbf7af08063174e9867aaa3d7d65dffffecb2bf24ea82e05fc453e4c078395ad9996e3c2b0aae3311ea4159c1e59ebe3dca21a0adb6ebe7d495d96ade93ac1403804fc5d4b5c6d632fcbab7fe5b16394c60b076d2d02c85d0f7dcdc38a18c6599367e1711c6bacdee11ee16ee8caf9330809fc08961ea8f8bf7f1a77bc51f9a4b8b2bad1c55c947fd70986cfd6bbdcf6cf77241ba949b06f63ea54334b3bfeda451aedf7cbb15a2829cd1cdac3d20042c24faa5e3022634f43448d3fa88344b2f0d653940e5e0c01412d6a4508bbdb55aa08ce709d11d6b9e88fff643babbb4791e2dfd8854d66844ed3a2b58461bdaa5112781b92c03a8efaf344316b404c08795a144ee4a38914c88055bbdbeb3509b8cc7505eaca973d36dcb17ba354e9e0ec7511f88170c04a237610d4715179c6a316ef3500819237fe91135e71f90023e5e221b30e79f48e1ea84ccb1df84e52cc84ffae809a3b71176615fbb2c9e28be9f9b0833ba669710add8c972a0b7d2d9b7736d4aa35e2db99d70177015c4e6771f574d8432d25030bad4dc44ea6d4abac7be799361c3ae29ada40c0bbef4e1ecfd083bb52d7e91e17185febcf1e0ec0d566ed27c248427e50994b7590377762ae1bd7413fa4d73495682c45770c2bc01f56ad7a5522df28c46c1e683ddde8698353ff2a8ec29760350343e2fbb4477341113479d04be6346e783800516ced69e657338407125140ffe9c7179c3ea074e0df7d30f101e2f09bf2e6958542dd0d3644263aec86aa12beee467a8ec3aaae41fef986566a0991e867426bfdad50e5c8c8229fcd2bc85ef6f4a585a320663c673451b64f55aaba51bd160f857a64e428943e03e723873753da48b300a1987585ab1630729580a50502225c49897f208534e10e9def653f79eeeedcd599e4a3b3d5d9fbe3e172adb8643853add546c9b9eea6c49fce761242e858c2dced35311bf740c5bfe733ac2e536e531b4e9fd1767307d99fd4f1bc5b8675f598251f2a4c56ca580aa9cf90ad284097cc3dc150a7e821b81d55b5621ec952577e83482ef94ae393ec2b4d92575e9d7b9cec342cda8345a5d649d2e47884d7946860e066beec1a48936c98730ab6823cb2c7374281d735b7da5abc7dbfe6cfa459ab27cbc808bd464ad3fbce37d26698a68095d97506923991f9257ff2e56ed8cb90c0e15c15e9b817c8808a7c84753678b442f44668eac7ae1d8321eca933c5fd470b7587efce06146e34106b0994e1ee02c3541842cafcca19ae9a9ccda89d371fafe4f50047a8b9406ecea2990b90eb6da266724d8e6bccd6dd7ea88109adaaeae790790f0fdfd9b64364c37f9f1293fa60c8f5111bc21118932b0a7223fdb680aeabfedf717995a5f6a2986e647dd52efc6a0b733b57f4008f8acfbe7737b65a525ba04fe91a9fd654a36a8f963561db16e5715408c45ecb32f0e3f92cf22652625ad46edfd19e137fbdbd299cc53f8eaf88b58a13b736650a03afcddd920a34d42c745d690c61e7cefbc8ef496e639d61067c36a0c01caff42e606d9d2915b27e307efb59736934f1bd6e14f19767cca6da00b15a5cea1a6bca237050895a377338a10bb47df8dce188e510fcc1418dff371643d4441332280f81b7e42629e4d2b93c344b6a49df059adf75aff24adbf7efae31abc51ffa71cd9ad06f522c3609c797535782d9bb12fe2c15eadc64a99def3707b05705805b0c6ee9b5c1df4daf1fd42b70d4b9d2f740984d7b81ea164d0033838f3ae5395c17b56d55af6e599f720f0f61e9e5aba451744c6ae136091f5d2998759a0d609937b3e6639f82bbe9ad43e0db5700cbfe2e5697ddf34112d0b710ac4b5d2b90f4a4f48dad8fbbf8a983afc6b3ff638fb62e3d166de11aad553dad08d3c7a2bbc4a1bcddf96b3f319f7437710364d60a6ae3bd1d8a32faa0349093c5101da8e37a33222ecd1603e677bdcd28b8e4eb824673c198ec05434b339b865291e0eacb87ce7f78e3fa8debcf346cf16f347764e8591f21ba624c3f978b1d2a21d02e06b84853a1c94388600d208abc5ccc1728b14d4a18da3fcce1e0d4656105fea7ab7673f92d9094aea3aaa601969c4f64d9b5e3eb132094631864903fe80d6365960923b4e96212f85b78e0586cab0f1663dcf17beaf051071d6545190452d78252d8968b0d8cab0257bcf0ca1ffd3c3ce9895271c12e80d05c3345cf0c8064bcd8a760ef3e534c06c3b02b992f4bccc3fc7ff364a07183b43a60cb6666ee69547ed53895f6be2083b11d5017219c006469a8818787a38e813a3d45609c3d7ab0eef9180b127740e9bad05f93de0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
-		String expectedSig2 = "00000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c21a7f15d081b126ab18592db4cabc163790011d2fadfa7483bdf88059bb8eaa6ba998998f7d69207bd41bfe452ed03ee118aeab3430c65105ef59c113ee601c9ab166a28a181c18530915e6de51a3f63a35a2ae39ce610875bc32e6b06c7851a4556bf2d375c8a0e7498a436cea199563818b4a42cf9f2bd630354c52d6f7a2d70e4afa512104168ab91798fd7f2479bdc6d946a59c7c4d512f4c221f79c8aee49bb0ffca4ed14934fbdf189888c9d6f9b53fe29727019cfd06da3ee308870f2e57f09b68e253ac19e14e3b24edcff13dd55aecdb127d0fd5003ec234680ac58f904409991187399d2e80d57543d13ac34d3ee1a453fc7417378e7b8014facdb9ef989e6b0917cd56012ed6602e63643a2918f998a65f8e17a1fffc5d433ad879362bf28c47e72e059faa5d4713a73e399344cebd059a203f9b5b317cc02fdaa81f2ed2e41db1bfa29a73e21c80068ea8ab8b35842c85b7a41b95512551da8768cd0248378eaf715bac2b82f5540111150ef1cae360832ddac01ea6ad1d9019d36ffec46e516d2e170c7d33d8caa395adce28b9138ef3983be4eba366142858b230d46bcc2108f8005db044c66516d01876a1f194d5fde163351965877ca63e54a7163c431519be7717f43f0b4d85a277afd75b91fdd075c4d6d303110535305bc499fc6eab11041be81759e76473a286a85de85e195c95b6537ccdeb852b388337148aa51f62e3909f9b6b1f690e5464f51ad4bc8e2fa38e85fecba6db7921d38f2d98742315ac4631e6e5cf544ee71143b4176e93fcd9a6286869102650c40cc543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae46b66882396ec84552fe83ca6acb3da83b0a2d13a4dc105b79110fe70d81c70eed6cd4596fda7baf3e6dc68d47bffe5addd554d49e3999cba7c96c2656529e8a5dfc67cc93473a519165a2b58cf441caa42c195d6105b08e89647788b2113d39c6a1f764cb4c908c712ed62a334f70c18341e93a5565c070c89db35e609ebb2d795abfc937579d621d015ea60fd05d246e545f8770f0ee02b051e7a87aa44c2ed5543dc4852e5a8e25ad132041be35c5ebd1df079f537d2b975b740c16fdbd94528bcb24567d6aa104e1370c728184a63aaecca933b50f995acae35cc927b0bfca7983ed923d61b54553a79105cf4e3595c5bc6878367ef52f8c13018d86f380d49dbf5b54cba7e104af3630f9d389d64665c7984296e574aea643958485840fba9b79df765ac64fc2a9442ec286422e0af685e1ab72394934ee2dff1801a58592f6a001fe2293f96033bf5625ed5501bfed210ab35812fe153809ec9f76ef0f08a084b4239aab5c3daa0892523b31c9e27e7e33df18b2e29ad7caa33ae94a2e7c84cb5fa73f51c8ca771dac3b0879c42181abdb1f1cd29c52f1d7ed89778ab32c01b941caaf976f3bfbe31e0bd3dfbb7da45534bdfa773d1a96c7e367c8ce3af5dac8d82347e0b30d3f3e13db8f59169afbea85c2f1a519b4aea6097f2b6d15dae174902cf0294497ca836e782b570b951c705fb485c2ea24f771135f3f21785ebc08f425353bde0fafc19593dee71882f84a4f8ae42a13a0c1ef444ad9ec98e5670a8e48877d285b9e6eef58bd52497b978fb7b33c435976370bc62f86e7746b71114660ab681a5d159d5c62561d72812bbd8ddd6ceb955d1e91c8b284429891fd7f0bcf20570a5a401a51d368c775f81b58b4c55f8c7375ed9c644454a097409c01b2be3a3578f159e3241a9078e45685b60c82f21a05f86b7bcc3ab69e42bf77b341e9e55b9c42e9c4c792f391b686aa29b89d9b575bb13e5f9811770d864a20e28b3d48b8fa47b8b8a4013ee14ba01bc3721ac8186f46d00b61ec0d9c68b3868afb93d23f8e2512cccd2aa493b2953374ddea144e8d202164438e808d73ddfda97bc60198fc63d621d7cf54e39492b83c9b8eaa855b95eb91f7d25add1bdbe462f72d877515deacbefa570c3b1b316b63da7f313f65d0f63f2199c78f9c35c4b997fcb2c3da8c4e5a8208297138ee08d1c48dfad9ab4d7c8fcb66280d349df86038625df9e70ae98c606e3ff095f94b1eb3263cdafc9e401d29e7b801e287de025ac7be7c9faaccd040da519bd479a4caf9b1c14bd9c64972773863a85773045ac068462b82ea43cee9628ae2272c1a8d17fda179a22c64e93a83f040afafbcb9c44cdc0a9d5f12cbbac991708b0cb9b689141ca9fbf2421feb2fadc27ce6948379cbd631c654eb21f3bbc6ff60bab018a9a90e9660eb540f0d16981f16af76a1ad8bb91e67cb810b5d0a3a0e49324e0a75a47a756c3aefab4931c230909b440647984a271546646d2fc67a35c14064a0d1e89a703d5f89cb865c501b3ba55f128fed7c9b0c31beccdbc90c6f0a9e754ce090abdadf5c03edd5ea3991a84fdd1d7bed7f9d8e3872f331081aee35ecd1e81fae67643b38d97cb3a81b97a9d09c73c140dbda2efaf1a8775330e1e56a861a8e517f238b871ced58091933110ab033518ca18edbf8ab6cf80838cb85a06f660ad2ae664e870b4d26bfba90510facaff2ad4ba19715ea6ddfd656f9e414e660f532d4fc43c166f01f150b3400cd85ad1cb9599fd89285a4eec574f1b2ef54af021b990fbf1a01098c2d79d80834769f32cc7e94dfc3237a6f66dfeda8a1ae97d9612e462b787cab6883964709d69ea5a649c9af376799558733c5e88cb91e6544693961bdd03ec1b8f46f8ef725eb5060ebbe41987ab5208992f1a366809675f8265524e6210e636c81dc70a24cd34acd9ab8a3d6b296368d56ee117de991e822f4d897cdad4f10fe8356901e29395afa12d5b2acac38a78afbe938f54161fb7934ef4a0cd1b661434da377befece43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1373be54db1c3a125ab4032a6dddb6d38396377d98eb30d1e81edce3aa1683360b02309d8bf40356e685274991ba72e28f045dc0ac670e1bab70b6019ea659a83a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
-		String expectedSig3 = "000000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875ccaa913998ed0c0085cae2d9c2e563315d4df92d857520f22935aca637e8755fe9f62d6389cbc9738ce8cff101e0707d6850ce003b9b884fd3c669647d697bfd3a24b675a6955113297e9ba806b790a776362c47d3081dc09e430172d5d6212893a751c05c70ab1481395e204d1fb675e0dc47e226ca450d132163dcd9dbec52d4df457482e99dcc06c7746eb4ed000eeefd95ee8fdb6d154db279a7f7eeb2aec2f43d566cbbf92bd7c45cd3a56f52223439bfe06a8b136aa35c76381ed5576e8ecc0a25657200ac81e914ce227bc2dc859545d315fce5cae6f88816fde127a76d9cc67953590a63ff7ec230b27658e97a2d5f9a9edcd87b9323381f72d121dc2c56f89312eb58294290415619247ceff45fdf49284ce795592f519288a5c7a86436c190883edab0e58b31eb277ed32cc572e53241394e526496b94f338abdbe84251ae0183edc154fba47af8c0a9e9ad9a2d7ff45aba5987cdb127b344a22b4583cd1926c5791139f4c9852013b0ae8acfd8ce7114ae6d45b321d245a3db19a082d3a3205a6ad3b7838c824bb81952250b09858c543254a42ecfaf23affd642fff84929a4bcb40d65fcea84e461c4b3fc2f8d4a1ba3e561be9805fdfd74741ab4c407252d40188ad932d4b53ac3b806d674a01140f2e97bfb25a3baf30c29b534db198680822a0da9c1a103278e059a8d0dcf5e444178f1a55e3d75ea9bd3147739364960c959667a61645ede0d5fbc8cbb853ab42002a2205fe271b17e9fe095cadad0d168bafa8a392eb8e3e8d2afeacdccbc5a6f4addad61c0b4368f6a92b182c39c2eea41520419bfea541ba2815ba1f539275ce944fb66f94f72c47383350966125927196399bcb39c287889daad0f1dfc41af86bd70429ee6ffda828d4ded50080c47b7315150768eedd9bfe586fdf37d7a0c74807838fafc5a1e5e64feaeba16a1b87cb1f6241af614997ebdfb83681b74ec33b23e4ab6732417ca3b6fb5336b3e31ebe82c90d60aa218d26dd09e65e6542aac5c3f295e3e050e43d68792d10b4c2c5e6c7d0129d85e3ea79e6b13c2af471a2192e8ab8a6f4c96185468e8931b4dac3036e87467d2cf75aec5d908a526a507740e333575ff3902d4142f88bdcb5b06583523197ca8b9ab390fa61e9929ad6a4c73d1ade927f945d45f77175a813623caaf0ff586330ddbc6dba9ee39e62da280ecc13ffc22f83a8cbfe7f3495231a7612b9628e17fc7758f020fa42df2dcedb74b4844481aed37c108bbdd8576f189b662633fa1e91fc481b381f042dd4dbf5e2f6d545b88f1b8c2f04126f62d6f2147aa9001781f930984298edb117598e711702e0788e0e9304c760d9fa1321254290463c5ce096daedae47109657353704ea9809a7e10f2a0cfbee0604098b7f8431a7ec26ad04698b74097b9ec544fe510a29dda677dc2e12e73e57f639e880b0aad0deb5fa29771abfdbb582a76061f3b42c3939c12dd426a03fe9d5287f1bb9f8fcf4f5b2598210ac126c52bcdbe27695432625fbce06960d3460012e52971b5d19fd1b1ee55e52971ce27377dcc4a907bbc7bc2ace94265f22c2089a4c3dda2fd42b3e388e7db905eb043a48ba49104dbbd47c5baae0f1af80a7f414e22799c4e5312a98a72a464ce11850772c4b6bff995be2567dba1364c98b1fa6326b4bfcd4bdf30c48a25f36158875dbd77597987f2bbed2bf0d0ea457726cf108af5628c32fd0f36cf214275aa8961b22fb1e042959d81bff3c1bf2128d4355d0f964dfa6469b5579d63485bf3ba3eb2cea80e095b32ab8665b48c0b1c04895eaea3c67edb71f30bd308971b130eb85a0970b570f47f4e613dfa5e3ee6ea68c6f71cdf369c79146bddb693a1a92b8c80acd061815b34737b5e4adfd68d28e293a3dd85620d6aecf5714e3ea7cd148b9976738b367291ab32de5144a3de66306a248109a43b32d239e80b34ef2994dcb2af1c28f7739f6f7e502a2cba8629cdb74dbab64162782da7b72c0f3d5dc4fbb351eaaa423026842a7fa340fcabb7188487d805abd8f0bbc8dac9d277d3976aef2d300eef62fad9ce676dd243beb43344577510f974a39fe48d922c2a22c6fafebfa549473d9432df34836163cf71dd0a3f337027d2aceafbe27d8bf99657c137567de22bfc060c60c15539b058a6c9eb703be266cde32ffae7eb881a35e4cb453fb5f1a5615ff99107de5b9577bf85d2f00937731a0d96fa7c994a2a69c8ce789bfcd448d2bdcda4191b6ea3ed3b020f78e779f70a1b526026c5d9c0bc15861800e0e71a1b481f0e74069354e08abc6d7d1944bd46596456a4768ac26b555785ebc68f48cff8af0e52b847b7e4d40089a48c741c9adca24754abb1e201020a5d39e7d7b9060a762957060f12d26667bf59cd39b7327e873a7ac774018a0fefef3d22d920ec00cc0d8dc53192306d5306aac11092c750985a226fd6cbe125f68133c12882ee31e48f5c2781e27abefe44cd4a1b4384c42be03218b26722fdf83d6c4490e9bed4a430151206eee84694e5787c94670ef60d5ae8158e78af8797962082fec9b492769231a39f2dfd2dd5e08babc3ecd90d648e8b3acce525680684c37be1ccc9f2abf75b9dc198f33c9dc13bd328ba4cfe1a375a6ddd2fb8b2e44a16ccf87f5eed8247aa49efa0368718ed7de0e3b925ef0d2e362f466763d61baaf81abd468a04c0a164938b77c9293b70ad9ea6fd500d406eafd4e64a716d4480b3861a3f4c1d56df1494877eeff2934690c2e6b03b3d4c618cbca4cfed4e3a5bc0204aa307b023e24f17cf41cb26a52797cf55a94a015a79d1fa5a49df99501f4590522645f54bfd8738b8395b3b7da5f5bdd2b1a2cdbaa21d692e6c8894c9896d42ee55088f4829f07e226a2d06196b15d909a709df1ad319de092e30f3d373595c704192d1b9f6330dc1631bdef5fb01d9ba7c0790cd840f3de0d0cc48b5c6640b3b7729ff9c1dc23e3b44edd9da3615e4e46080fec3123382f7ec23eb2b9c2e37befc6d246af039b5765d039be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig1), sig1));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig2), sig2));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig3), sig3));
-	}
+    public void testAuthPath()
+    {
+        XMSSParameters xmssParams = new XMSSParameters(10, new SHA256Digest());
+        XMSS xmss = new XMSS(xmssParams, new NullPRNG());
+        xmss.generateKeys();
+        String[] expectedAuthPath = {
+            "e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4",
+            "251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce38",
+            "53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d6270",
+            "5683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "16b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee9",
+            "9d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7",
+            "233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb3",
+            "9317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd5",
+            "14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca",
+            "6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5"
+        };
+        byte[] signature = xmss.sign(new byte[1024]);
+        XMSSSignature sig = new XMSSSignature.Builder(xmssParams).withSignature(signature).build();
 
-	public void testSignSHA256CompleteEvenHeight1() {
-		final String[] signatures = {
-				"000000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf801817cca72fabe805373baf049b80c4309ebda5e5e2c9e01851260b7fc238d55f8c2552b917795d4ece5c30f1351308a91de47a7e6eb57ee1aa80fb3400cda35df1415eb392083ad68676a23b1b60f9fd9dc273f0b211d41545f09808d0beade6b889ca476e6d78339a57f7eba226ac5d31d0c7d6571e4dd7bef0e19844bcd9a174b668afb0688c081c38d0db3bda3b42cf1025f2111e3b2c05a9552d6f2f39ac7487bcf0e23f9bc83149295b5e1d42cb6c795b4be7cbbd4059eae5269824034aee1fc8c2e4ec4705957094fdd5a5d62ab4cd1149303522c06ac7d9c461cee06dfef221958f95f743c0b5db6a2b782b3b9ab10289fdd2b77d333d5476a0a3a48815e9a339f993f3ef7ed9b494c51f9f728e859e50cc461ebbc5fa81dd368e065de35accfbf7fe18ee2ead0d5b5ff56b99723eb428701ca1bc52881c551eca6163fbe698471f69f63082c4a1942de3af59070f960e5bb3bbe81ebe4bb75d478a65a2300eb347ec9ef0e8a8a6fba514a7299a623fb03e47f3f405f2d2e8e708bb44a596984dd6a92df6c0f0c27093445c42e03837bf9326aa293d2c3174a72bc214f0d0e0164db2367516a396688027e5b162d05ae72218d112379759b081895ccfcb6e2b9d18e1866e5726247cefe4a9eb25d5455d9849df5ee77a579fbd7d80cfa8be96bab83e100a406a27735bf033cefaa81737b88466fa1d34eec15f38966a13239d2defa006c04a9084f27487f81ef85b7cd75e4a0df87c57343caa9805addc396b4c232ce3658fec6790a69c81b5cb4d9e6e8afffa97761095fd5903d8a28a6965cbe360769f32f4935ef8dc84a8aae703954f473eeb99971396baf35bc481fe07fe056fd8db3e41dbf48a161b55c5d47278c6f9c6f333fba22886632acbd42edc89219320fe4faa0080214820eb60d3ed5a03ae97332a633061ed68c78d6573c7f2cbaebc35e1687b9219dec67f5e6dcba20bb6b40a07e0e0defc64d3c70163200166592c5898918221ed942c4b277640edee15625caf2a5208c9612e587a494c5a473de79ad1ff0092a41dda84fab4acd5435cc37e4c8a9e0895b60e638e2fe973cabc7ba40783e422270de34919fabac4126a6dc035531efc2ca612712d95be53453ccf8ba2096ba0fae14abf3a95727b66f1e5742de804ad0ff11febc3e8a6d80e2417baa67b46e8380949a3ffd22a8f3ba9dca07e61ed0606382f16e64ddbba8e390c38ee018b43220c68ddbaa0133dd05bad7befd31380b31df1a1e3056323085ff2bf96d4bee060d32d0e4c8dab17cf98a29bc6a0cfd2b050f7ea0c3cf4556a4a8aa8e73b24d7041a53d6ca1ebc12613e0d616e3d95ee725c1da119ccee0659148f2c412282400821cc1cb87a5d79cb64e308bb3d1708fe1b91b28d0da61e09ac0260e5a1224b11ac6d68393b885ce94d4d06c8c8a167ff4b040950308a1e6c0d1308ffc227c80bd2acfcd1e6f9f7cf91dd1efb662e4edab677aaf1f27d311f203d1d0f1f818a228cb6021eb99a95ad22d3ac120ca7bc593bb8e9d2996d1b22ec594a87dd2684b55074502a8a32ffae9c9c1369d8d5b2b06c8190f96cafd52ac09e13fc989c08516a252354b0a38120715629805a215564df9445eeda05efdf580767316c55037f3f6f925fa5b46ce78e7a27a85c8ac1a9b521201970d6c6e4931632450100217319c22462acc98ed21b06cbfc531a7cefa7f1896c8de4d642a8e9e11119d5e669a018eb6a58a6e55dc68823f94c42564b075036d42a8ccaf5c087377ad9ce9b1b9a870dfd045c21bd1040433bd5d6a880d9932fc455b41f576efb23be40652fa3750075aa88bfb63978347741d77efdee423563d20cb7521cf5f005944b15f57f3c41a9ad84ce85e5f0b7ccfcdda3b2043346ca63ea3566beb46e165b972adbaf606a6cc242114b235be167445863dadb50db3376fe4bca4c7c82bae61ae0074add5bb3debe92dc0303dc569006efe0f0a9cfd2cfc7572d248b914dcbfea4e2f632a1bfff98879525910c38737b9b10554335a19852c8d1e16f8fd933164778b9a411b70b1ad050d69d1581fe1edfdf603e617fa8ba53301d080e8fb7fca806ffd7064c82adbaaa0e90ccbd808c08d077588669a02a14c53cf9d56f6c369b602b1326e08126c932b379c960c6483f4d5e9cb48990792def9afeb54d2c472015df185514f500daa101e2878272694de5b6a14a671547c86efd0336ac9e0d931b7469093fb937fd66872c7fc817e941bc32106396effd47685d54f8028f300c822c9b578bf922a80945954c3988cf85cbf0fd304f2cfaaa121a1f3d01ae3fee1e16496760d1edecb00e0a2da0ed66b0996b45f5066fd5a5e8d0082e89987f7ec7102f385047be38707080ee068cb55702ede0330f35782d9bb12fe2c15eadc64a99def3707b05705805b0c6ee9b5c1df4daf1fd420433af01841855e9cd056bf2c037e6b8becb69680b1967e3f33e7f97426af4d4c0700cc5e33c8b122af763e6865b88264f46b98283ea5150d6898d13250f8f4a8ec12505cf124681a133bf722281399f0aa600f30f93c3dbfbfaa27b500c6b549a95d58549eba4f88e777301aaca7d3ad00e653b4d0afb201ac6e26a5ef2c4d110364d60a6ae3bd1d8a32faa0349093c5101da8e37a33222ecd1603e677bdcd2180f0fe7f2e7c98ddc0ae9654f609e80acfc27a087e54fb9753a12f12e6ff06465d6c01fa87cf628875e2b6a355d5d7346665890419d04c6469d091e460a0a972182c015b21caf03e0a06b3582a7ed7c557bd9da3fd2a78e7ffc5977a77701af9bc2af3f79bf6a753da9f79a5294947c07a2caddf3d6a0d4a8a3289ac1e6fc5bec4f66596f54be641f5064e87b7a3c754e26e4f66cb84bde870f55a79a7577abffd3c3ce9895271c12e80d05c3345cf0c8064bcd8a760ef3e534c06c3b02b992f4bccc3fc7ff364a07183b43a60cb6666ee69547ed53895f6be2083b11d5017246a103478034bf600f5f9f1d9130b6c3195f6165f3d050f794e7499f76814718e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"00000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c255121e197631cb280ebf573fb6155eddb1d968756805f3edc4c4e3ea0008c8e5a998998f7d69207bd41bfe452ed03ee118aeab3430c65105ef59c113ee601c9ac28b498cfa0e628dd0b8f92d5547c68f4f3ed66cba1b8623ad63ddd7976b15538c55b94c63e6ffe9296a28232bd9a784f68e026437ddc807746fcc09c9d68076282cb4988c8bc501a461225fc8de39336d798d9032dda887bad268a57ba0196c6befff554b7a27de52da2dee7fe0b77850a050f87f0287b1c0c055d6610df95a64507b9724e9c3d89b4c41c8f3f3fbc25d20b268ddb8afb41b4132afd20c5a37c1c5d50f6ba64525ad06a238a3959a84c2a1e95e95a59582089114ac497f13b279830417cb255cd7ffb386bd24afcaf6cda74a3d5d7c75e9b04066b2d379eec1f9d5e7d74aa71807f200608aca913f933238c71fb3acef64cb76499517be047af9a8cef1afe16c7a0a282999f4d672603deab8a59dcfff2ce13398b7deccd2e4f1bc1ce02403c5b0f7930033dd6f813ebac9335865d03893d99caae239c17ff91c961301c52576890f6937194ad4c7e74d19f71d3b8d7aa967ff45b9e3a1324e63496d719d1c7c6b4eb56e52cdb3a64f44b87da76e1f1e85a1d891ceb88a3e64addf0d84b8f2d3d9010a3be169f41296afe814f2aed28076479140d2cbe60c232ca8a272b4197b155e6502e6a1861b7597f240bd2479c7544d6a85992d2d1d44ae26cea1ffb29952ca2ce8312626a00d00f4c84dab58d087f55c822ab3444170fdd75d9bb247d6588dd21d7a156f3423bbfbca4a92faa82c1d0e24ced7c6e5b6c543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae467dc0f1aba9f42d372a98d3cd993d2a1a5ba6f2c7bd385467084c049489648d34b0d2703e5b220b62862da2af2cd7eecd7d4483246ab87612bbe1401ed07decb7bbffd2d774b6a40598e9f3f86826861bb9e9693bc5ba9809cfca4695af4dd372c4ff56957ae29cbf197dd7a218d753541c2f632772219c3d25c2e900ff7d685d6750e01c6f4b88ab346d955db2de4e4bbadbfa46c40443c9402543656d2dd0670484b6872518d85b3f556d01aa0c01405a89cbc15aec2d11731007393d930e0cd10afbeb9ada0162b37519733523bfd5adb677fa1c316966fda4daf7a42d0d857983ed923d61b54553a79105cf4e3595c5bc6878367ef52f8c13018d86f380d42100740f8f0b55c90ec1573e660079653bfb0a45d17f5396aa2220e55c1d23dd1cb78ec51365e26884a08171f10acf2c503ccd1bb7ece8ed5333d466fe3db702ac207a9f15f32db4802e781beb41be0b97653f1c36daffde0345e6b74fadce3a189e65597d049a60ba58913d02285d8cc011ccddf9a5cbb905074ce1986a3776f7fa26dcf4ca02462e800279d363e25949f2b1b8a7f1f9373a3d487121c6e74abd8642ed9721ea7c5b32ee78cdf663f8ece0b99aaefe3e607a58deb53e2de6e6db3394886f8d1cef3ef6762bdc6dea1f93446273d2f6926a920d641f58c742bbd61c056e741543da9a3d4e8bc6237c5c6bf1888cf24d83f3e0ee9009faf1effb8884b6f5aa6e55caef5bedd5a51e4e467dc5229919cb843f09b206edf896e0f77fb6c698b36ca0f146f37c07903cee49ed4c498ad4199d3ef05ecfa3d2575b8871114660ab681a5d159d5c62561d72812bbd8ddd6ceb955d1e91c8b284429891da0040ba5ae2e6593147f41b44a0f2804b1805585f93d2be23666a7a28212982660bdd9e88771397b26e46dccfccad87ac926a444f79070bc8da116c167fb39aecc6e02a1b3bdfee70a61ca2df9a234e7d094aa84f51005df5a18f3df16b64348cbf7eb706a19d7d302bbbd32f1faf8e69300649c46e2e32fabb41e57eb8816f10dc65054943c8db35f6c9960f8ed853bd38c131abe9481bc1afd3f181a04f2c4382f3ad45a1062c53109a2a494f1b9e719bc384842bc9370ab81e8cd6a0955f13babc2361be35127742dd0551a5bc342b9721d7f1bd629c59d67b01fed65350b3c5da71fe6713b51a6fa17a6f0b2caed679a15ff38ef120df61704119d0cb2134b608d6c004a7aee902381550b94361366137e2ff0c7a937b85ce096e45fb245d9e8646e68e85d4b9d87f1c2bdc4193c5e691542894ba42197cf1bbacea1290491572fdfb5052f4b1c461b66e324b6da6264a8c25e1d194e7fd94697159629563aa61fa3bb9a5996432e76562b86a0317ba91771f0c1eaaa4ec418fc0690d7fd0a326319563a6bf2c29dc36318e9318f4caa3714309286bdbbff016c8bcc855352789d1a1159098d7bced399e731b80f83ccac1ad619d9bfcaedfdba9c6600b7a05393b5342182ea2b2cc2a9fdbb502e2c2ec2dd4ff4e84fbcb28e3d8c07feb1be042620cff4710f4d650507232f292f380d993ca55ae66abb6bf0e09c14393fcfd7989483cc9bfa24c2033b555bf0f01811be2b6af12c138b02d02cfd85bbea2ac405fe0420217ba9bff2d6ef4c7cb0ba4faf36344b96846c699b5a9873d81a93b79aab433be2bb1642e3ef44908d7e561ee74e7207d0a405d18544d0c579a8112550894deff455015938377f767f2d1bfb0f10386559afee4f20d678bb550b1bd0083941306a6c0b736fd08e5a36d8aa589d12b170d587a78728d97f5c308e2162f3bf61329b847641e95d83ccbd42a482764d6a9a784245f5333027f53bebb6fa3af629c78db6f8a174816b04d72e02ed956f565b53f3ae7d2e5286eac691dc23bb5d3bc5c4c211fdfb8e455357740e382e63af71856e485cf44221cf3a9c5625d11c850f63c8992cdbaf49ebf5d28701d338ecb57471a2fa50e008b2e6ecdd62d3ac2e5bb30ed330c71af066701d7e2437eacb2fde321751e1a4ffddf0bce43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1572235bd8650548854c13049e201eaae30c46ee46ee8fc06ba8c9e4ef1e3f309bcb2076d6293d19fcea9b90c00398e7740910f425703a0da099490e3b0472db2a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"000000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc1a024d58a92556c86b25cd3eb57e4c90806efe4324e6240d3794284d4c682f3af62d6389cbc9738ce8cff101e0707d6850ce003b9b884fd3c669647d697bfd3a992e0c3ff8b2dc7d51f3b3b8ebb1d42e933c89c44655a602fee6909fe9e972f503cc9a155d185e6638188f2bb8385a519563384778cc15284d03647265b64de909da1ca2871d66495f944abaaec13730a7cc63ae95e0edd4c8887950fae41e02d930b84c3a0f6cfbc49d8c9900dda13b6a7e9273cba461e52af92372709d8d35d29057792ec2ea5e7ce48c03e42cd71595f6bc21878b2d0755af9f0c6e203761059df223016065153d1ea2458fb1c7e4bd881ab3b3c777765adbcff7327270c203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c26ccc79944785df6ee955296fd29a555df7d83feca277dba523187c83c480c83833346cc9e9b75ac25afe60dd3a8f07a74fee7db3519c0d176d66c47ba8945f843cd1926c5791139f4c9852013b0ae8acfd8ce7114ae6d45b321d245a3db19a087128109b19fd393ca52327af307ba656132e5837cd861783668a8eb0a1039a30e8ca5c22f9b77a008758cf9e92d49ba7d5ac0afb6dcc71572c3b9e6c58fd5b91f34129133d4a4fe1a84debb1072dad543ce99a66ab0a8518d68650ff20ce95cd0d99725065625fcb8a8731de017b1ddcb6df613177280cebb62d004d73b56ed3b2550983f215f5ad25149e3c65aa92d531b9435aba210a1909575d5aa580cd9bf6b2f648620b17ec5b90da73e4d8978fa5cb361a46e4390c8713a3fd2c92026155cbeaa3e9ffa03249f9819b9c114d900ae376b48040cab1ee669d61751f82c1ff9fd61578d4ecb15963d5c62d6c9b64e649b33843c87f4b0bf92c192cb3c3d1c9a867f30afbba4feb549710a1060a6784262baf647fe22e2d16c8f58ddd8bf80aa6c2e7e08a9aabd6171a6a9b49697fdd8c896a7cf857789670d79306c3550d44f535f1e83b7e669ec1a7c7bb49f655bfe7d152e7a05a3c0a9659e2fa5ef951ab3d7d2d30712c561764c765c592e047722c756ff186c3094b041376c484489a0d4049b6f1532e52f97a852f5b810fe43160616be53f3d781d8a4b2d4c039bf5028c2f7057b0523a6617f98e25fc99229c8961488867c11899998cea957c2d135c46ec6de295d9017a5c1436abef766777da7876a1d0adf411863ba4e576d1d613eeca592882601723a1d9dfe5c6b04646c7945a2624cfdfd8417cfdae769ebdd5cb9d2a4016fa8d186910bfc806aed6bb0fb1debb259fc1e9c7362eb36f0913c8a35d1b8800f9b39d5a0844dbd281696b54900d4226939ffe49b07a48952ea9fddf5f0b8ebb0e79a19f83bec3c51b6e4ee8853ddd80becc8ac7a0e214b3f6f6e2a75c09e8f144428df8e2a9aaa930a7fe38ac1a5847933cc5b4c2804eb3833566da3568eccb46cf6a439ddb3304487370dfdd20d237c09613eb22bdf2bb36651ed4b564890c478b720d60c59e07aeff8101ff1b0ffbb9c894cac3c1f78ff49602f3b7c55e664011dc235d2c6b833bb790e2cb06f419a98f43e419c925d30e4984d868fd55b955bc7a73c3d3b8ba7b1befe54d67236e4e42f99e54409cd5cda875df13c0c787285c03d89d6241cc7a992732292e2ce4a6698a64527aba1d7011a09a2f21dfd24b86e16a30ec471bfef6675e72ed2d450309144da61655c6c3706bfd3ab7025d37daf17d0e852ad884a98394ff7a08bcb63f6c3a2b1c9a06c494167207a672aecd7748b34e68fb3719635308f90e01bf61a51df786544f2dbfadfbd6e8509bf6744c906b6a96e535865165ddb175b46c3b7258c32802c10166f2bbed48a8080933030a581ed12ae57483c3e08b48b48d817ea84f71a980b4028de31e01b543be905e9a998991fb44e82b8e6bcd88d09ba15ee58b380a44ebc1821c0dc6eba1d5aa00b9dee768ebdca34b5e757e43047688bd6a3aa03b047b171bad8ef66a6f739c1a11777381c66beef7be94260948a3b774656c75bce0b08515391488b850b3430530613f277c1ee7bed2a7bc272f3b8c6ee177c727e69bea31eb3edad0afb76151d30fe31d6649fa90a36f5903d6bee31b2cf7410896977d7aa4ca5f9749402f7bda44a33aa1211e392493640e2f0df751e769ce000aff1a60f9ba91d17c7cf6bda9f3186dcb1b87e7157b8cd264319404a909222036a836dd3af06c03fb52cf3c9a8afbe25ff9dec5c5e7c542b6bf8f5c535ebc653bbf4597860d1fc57c25cce1531b7ee5e86a990067a5db54188addba92b4e82fdbc9c5c781ab83f33d267abc0ffa02a8e3a7bd91876e7cbbaea246f5a8d3c1372e802ec929353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17ae460d640eee02be69de6e4d18606b2bf8c604de3028b42812f9cc4385924240c8f3426d5306aac11092c750985a226fd6cbe125f68133c12882ee31e48f5c2781e27a7aeec7b419c655c8e85830d403fce77a59e5fcb7701d2f24716b63d313a6f2e2e8ad76bf14ca509717319387b69a937d39d2edde293bb663b72b93aa58285c3669cc0d6835e8ea7e1a66f24236b2cb1879a0b391d7cf7b2e65d6c4849d9c365a21bec8d77091ca5c510f8dfd50f46cbe18ff05728f27d4a1977be024ce364f708b81a07f4d60853f01cefd48faf34e4b7ef2cfb5a098e5ddc8e758aa2108890bfb0f6392c1bd0b2a78164a490e2cdb88abac7c1d4829ecab6e639d6a44935d29593c1558cf5d36e9a59d275f88fef060f3aa60ac06e0c15f4ca46901553d27ebc5820896fd6aad46294e40ab0d2c6c6458267cd017ef82fd5fb8cfef2be9535952d6bcdb029abaf13be1e868e90265baa6f044eb9c95ea709f375272ffa245668c944f61f837d00baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d8295eb37955f4d3c29f81426636afbb4a1551cfb6c8bfb521941c4f167a30fa2aa5bfeceab0fed80c9cf813e3b75aee9162054ed94243cc2998739b63bb3ba08e79be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"0000000386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647486e381abd8cbdf2430a1be34c7dbb65422fb1f261079d086b4212eea4fc20e39638c6098e1fe17f4df9c51ff96f6db6c6f7488f167cdd09f52b191a85782ca73e42d7800af123d2ac2d705844b093404f2072dd430f48bdb09438a21119e1b3730ad685a39c4e8c67750bf56795a43d93be6d5d6181d05922a13aa08d95fba85b6653716610269ce617146ff16b1b781fe1d512b1bac6705ba24cab5e5389ffbc038a751e77edc1a927d33fb3449cc2a639ae6052b59f5a868b370fa8778e939a30149264898b8c050a093e1d345f3076feb8c50c080a2e0604c45995c7482c23a12400a3318a18d8fc93668acfa68a45c8797e3749bb221a3584f97445a6c6ad215be5a12e582d3fdfd2eb5f74032a6e7aa35718389304989170bcbe1c85d36d714bb2fff05f88b85ced919c9bc94dd004804e460818411821e51a6f69731d54acf5e29c8f2090b360fa4afde8008c0e7085d2989badb92df58f45044aa257ecd9857d4f20b3658c49c0a09b2a53ec1451a5e3a8e4d60432504475992c2099d3866bdafaa6cc626f31e95f20a00d7870a74b957c2db3b93544094c2db82f89cddc95724b541840a32d6ca4465e335c4bc8fdf5aa5ebdc3b3ad9874954771a7340530ab22c6120b65c45bc8c28b3e159bd8511a7e1898c6bd83340ced8c1dae51030a0159a596f08cb761e777e19bf31d9492acbfd5c442ac9df91ae90040a93ad5e935270563f453b35c0ddea505343215f0cafafcc125e56fc9ae09f238440a32ef78b942d9b35d06a9c12257925c3aae2761661df4f3df78db246d1526f6f2f9d007614e239e91a8c449601ddaa9516658c78ee08aba6cf01903dcc72e5f9c9699515a1fb1bbe267a8b9fda3000e6ebcee589a4a868695b965f13086c780ee72169894e3da655014eb0a5a3282d37b6b45b7dab67a62a648e29005075555b29b4de95eeb3656e71807feb351eeb20ddf3757ed8de48784c75f24b772fc8440b1d0fa18f8a747c87b60cf5ae0de49edc1dbcca5fed608f93a8b45cad33e444aaa0069586bf94e5a4546696dd37ad6b1a3835a250f6495424a1adb50a9e436c0bf29623e5b09bbe1d67e87ba0c6f3a2f92a4545cc9b6668c0609dd5149c52f088ede4f9ffcd8624bc6a46996d9a5b78c5533249637996f1ea03c949b27e5434b3b4df286a35c2a427c4f37fb48218cd4f75673e6c902071a289ba3d69de281833193b83cf2bcbff3746bdeb748d2a89c2c5d4bdbd932346dee04b6881c5abd78994c6ddf1850bd389b84892d0d59c311f67777d6f11f3c9191943db01eaf124556705cea4f2c14457ca2f8f25258ada527901e4daabae095388bcece387f1e363c606e131951d709e0312b01094b52374cf9dbfb1f08e2d7566e0f68a5df93e72bf2fc56c04ea1904166d97e957adcea5efad409cd58dcc9a2fffe2efb3ed1ee5a41fb93138765ab5d80c50bdd8649ae2ae4937153bca2c2271c74dc2cf72b26bdb5d7438bc729d535316ba82c1e7ae7b0041a935d91760f7d80c57517c3bbd6165838a478a9f7a17161f54eb729d8055e9db80be9b4af3f172a54a812e9ed513e8bc6ee2fbf814f8f3ee28bc0570e9aa7d134efd571baeb0e384938487f13722486bacce923136ccd3e3c5cc7c1426ac672fc0b0d53bdee953e5314013e566472a6479776fcd6fa892c6851a9baf4531998851a79f1f839b02953ed6d7de23dc8ed442b031394320e0b97e6693142bd8d3cae99af9848b31c1f290fed2de7ff1d1841c30d3c2d44d71c893f150056c454900c122309a9f588f25889833fac1118122f7c102eb466ac4ec724dbc60d431c470226099956f0a51cf5d78229d91af8e4acbce56d1643650c64ecba2243431c06d4065cf738e5cb0980da72a35425ee889fc27c9d514bc581b7e740ec12b4e75927754a0ae3d61fc3c6a8e5b3c93a748fd4cd93f8bf788879da5b82fc4eea69256ee44b5e8cc1c1bf0bcf577a165b84c2ac3dc885413ab490a4f85eceb5e364b6331e7f11b7a832f974f7facc9a377a69a3c9a4ac69684cc8515557402a5d6f70fcc82c20051c7db5cd3d6d7f3bbaa38ef89f41337ec131ab3e3dc3959b78848f2dc51c9db0fd0fcc3d2bd670d1c2fdf3f681280be64a80508f623cca14fdaebd5cf4374182612c2996f79d4a547afeaf0dcb34a8eba293646d6b76725666dfce8fca65a186532ee1b441a24609c80bb045ffefff2e7a1f330bde5ac665eb7296fdec860b6c885143056d739969107fbe50283076a9e6ee68e92a303cdf6ccb2fd9a7e9a25100e15de3c3beb5562e27e88bb3e02064762052fa3b0e69f70e44dcb1410ac1fc604f27e86796c6d381ebc896a463de474e2949d242d1eac60f4f8d36685392adf7bff0c5d579956ff4e71bc8269233834d56ffe830079a082eba25eadc5ec0252994942119eebfa0817627b1731a791c4fb4586c43dc2190cc25ca783104aeb057cddc69f0b1692254ecf21d1638c84831b536dad113c1f6bca25d3ee8562eb572aaf23614b59ed0e9f8ab077f9c0a022e1f65fbaa56c8c1c64457e584416009587f8f57592f96951ad0620c1bac4350d7260e973f6635062dad70f3135515f5144814ae13356918954a53f90b3980c08da3cba1d8f112ef4992b38d1183638125dd62fa05c68cc3478064fb87406a78335c85866fd427079f647bbe439e7f8235ff966d591a5cee90cfbaac4873a30519db48aa1111b27e930b644c4f83da0f0b6116d9dbce9eb962268dd839441cbfa116f502d46d66008bde12ea1e33689b7b870708a05252d44ebd390380cbf245d6434fd972f0c7d09e6612ef7867b2f39feea4bfb39f255e3abd31677071ed4d8a7f394bb35f29f7465b5efa0d8b0ce9f57619bfe1e7e9beb2e49d1b372fd3c38453e9bd59d04ff5409825280d38e100f29eccc045ecbb69ccf8a0977c2d7a74eb22fab6c20c0f614f725777f9d1c2491436f87b65c032302bc24ee25f321e47b2ef1e0b9901037ca8c061281e1ff6080de1fbd2f41f91f32f0e09710e1c93982cf716daa1c350b4dbbaa17a95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"000000044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d73073204ca1e248cbd771ed1d69e737a113c705a4790409f43e5b7d70b628b067ce08f4b665ce7bbb9f1949220a19d53947b7b2f5c63592ee0740874634b3b11fc8f440901e5160e2398d80b465926d043481b1378fbc29a6e10576b9dddea2cd568ba15d7bd4cedd5a015e4838a3e37175cd0bd0334e055c4a7b3f6194fa8879fa63aac2da5c29ee03f0f3d9f9f22e3cab561226455b8d36436efabbdd3d17b2accab018f0d6bb46dec2518fa167bad997c0c1bca1153b2ea732243fad0eb56ebe357d7c6ffb654a6536afc80e83f276ede8609cb17298097f8d54b3213df6aaffcf3bbadfe7018eedb8cf284860bea973c298e6ce6c61360cdb1c5c5690f4d5f3ef8741c783be4a2cad2ee0aa2c1a58a17f1944ff11c2fb1e6eaec1190707977f862dc71c722e94d0156eb44ae69efbb7db7ec216f34adc979f7eef301d9561772f5d5c5d7b7820236cfc636216c6e4b223ef338f0d2de04c1474beffb8e8aa172c19bd6ae9bf103b92e5aaed352a3554dcb234edd5b965989453cbe994da7597bc5bccb4f5c07a7d62e56fdfedddabe7d2ed4c41e09a9acf9289547a8233cd3e76f36f613a205b4faf2e17388a8ab5e19559bd370945e138cd18565e4e12f5a6c8b429cbec8b9ed34492f321c1885570ec9022fce4ce6e1f3a657baa8bc62b94db4b9f9ca780adcdc0285ff33e976621ff78d26da6d2b66cebe392290fb0ddd76ef973ea3ad00ce3614b744f30d43a37fae5d9b85fd2a76052f3f4d88c056a9151f4c2a7e9b5749cc9e6155287ee53fe83251048bd8f926e6f21a34fd0f760186572e8c7a307cac394c3968c6e2b0f2a8bc822c9ec2c6ffcca5167b53a5923b6b5919d8344e23379b10b1422776724f37c58b1c7b435277aa340bb574495e87e364e2af1a9cb8d9d451d7781f983f03a63019eca75e0adf3be74ab44e76daac957b546141a368c3eb2c0eb2aee3906bbdae61b612f5b6dbcf78fa7758e2236cdd5d1ec1e4dd18cf36af15f1050ed235ca26663ae0fbf628a759e7545a5d4cbe0ddd4c77d2b5ae38b6e4fdfcbf7c45fac0e76023b02194597369710a479b7d7f4b7d6d28a0227b5eb75a4da7f2f8b8aa985a8bc07997344e1bd926e6b8f1dad15027a6c465d0d1d2b6bc02ca9455ae5cbb095f2e9a48f2d435ca0a70fc8841335d10ed4edb6e6d5fe6c63d7d0fb1cf7685385209d02924a3a42d3024d0b698735f8ea2dce35634a2b5420f8a02ce4dd665f4c83fee3cfdcb48b53b5742c73be3bd04cc0feb243315d48e1eacffc8e39395482b66324ee8a7db13bf0bc42ae9f5c105b393640837210218bc83cc8667718327df81a37e1ebe8921dc9fc4202829297aa47f9c4a4ae2ab44c8187f055513252437cf2181434a1d726390c1ea3eac34cc5f3fe03283be36fe9b043313a9e41b7791ee80a27bb2c81034b3b6b60a2034e778dcac848e9904868ccabd3375f1b193687ecf3309cbd3493fa89ce0cca82065faa5434ae5526a7e7bc037b636cb141484febc0c7ed2e8d53dbbe6e7373c70cc3a71705e7e8ae5c2476d254fcd25f1c17cd27d90da52969b4da165b83394e6bf7513b08c3107d25916db11539095a55d023f271cf1976095dbddb3006f57a7bdb5b42a82682ca78ecbb5e8d2bc99441ed5e9e4325e23470f676acf85b1ca3cb21d07c243e4cab727d11d9652ef8af29e6083a5e5b6b2bc7106a022e4b59e93a71c6fe073af7e1078468ceaebb0d69fe3d53e578f58b3fb5dc70d60eaf567e0020d561412626b9cf91d64934a951758b62737baac608bc6b46b7ccefcb181ab038bc10d39ffb17784158081461ea1ea363a030047713b7d28ae4aa2d0d54b08a1916828d00ab7cbe2843ead819c07718855c955ad6a4e7de489d5f0a345d401a855bf0afea122e8c9bbbae5d2e78607a28ccc1a88716f12e96506f39e4ae5b3e849f96e464239176638874f24fe0845df3f6df3d3141943f6eaf0434742253219f7b1c78bd539a869693fcdfe79606aa7e8d2eb28376ca3541fbe3d2ac509862f6e2929de405555bdd80ca3286868f3ab4ed84904ad6920a8e0be3e5527092fd1b57b9ab35bd66610d61fd8b96095c8903383f6a0f14f5b833a7cc7a8089cc7e17764323018fc5393db5e59ff088d5460e3144f22c5acdbb68e4155fd8f488be729e363d4b828a533ace1f3f39b8010f1667f44a2954cc71fd5dddc051a177fc87dc7025e571795ce8becee88c7da067d605d101234f814c060345edba8d862124620207aaa65ef54b961c449115daa1cbc9b6c7191dafe318b8d464c8b05916bb01de9622bd29bfa332c538ab31fda687239905972d740880df01d9389c5109417914a42c53fe6777cdec131276c357ae8d47cbde2f2ae2ffe621e536091677737a4769835cfac5e085bc6a8fe8f8437d222f4d616f3cb8078c0f00821731533f82096fdccf12381b514b47555e0a25a52b8dedbcdcfdf196d9d108ac98b2cda7f83306459e72856f93d8726a10a9fa69feaa274f811b90d36e643c9244a0ca02d9d54e2bdd9a16c1ab98b8a36664a3dcf48227d73993ff151f9b65d6c438ed52b0dcd2e4f060576b5a3d65a5152504292b97be3f0ba13999a8f21c0241aa99f62a7e841f31423980b359165e4fc47fad9c96450a75c500debc5d2bd2c92c1f961d977c4f460fe4fdecec712a4933a6c5f6c11a259e61528991c11e30216504d40b4de90d75528559a5c0953dfd31ea83b006c15e865b77f3a92e45f3fb6446164eb788bfe7aeead0f35e4b550b682ff160c8c113a5aaf91f38222135110f547abf4f5ff597c5c539bb03e67f457ae18406479d885982794b3377a731549422f5d4e8bc118e785173c580258a8d3ee629ab4362ab83f7a7b788261f6b6d50614f4c4082adde4db31635bf7ea4d5b96721f268e2e87c7677a823f4e20edd4f491f0ff63f660c8a64d7f9d192b1f29b175154498bed32b69479b2381c131585fd25be09b3d47fee2f5fbdfae2db9778ef2362217ca9e177f4ba40c34b37974b8ed57c99c84626730b1c2841fcc0a53b3b8fd5ac637c7ad4897c5b711488c6f93b0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"00000005b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b520aab69aba86a4676d6cb0bccda97e23a4feebd480f62f50c298b5f8b4dc8407b337095f7e04735c2b92df7753d6a2f8941cd8357fd4adf17c5a73999a159a2fd0a44787fd817ffae71cbddb5b05cbe70e7f4c3ab21210eb4f4254703cae0f8839220ac1aab57c77ce87c9e23ab080127302d6fde978bc12cd17f6f817a184ea6938293d8f389aaf8f87e233b4677f24b6752838a669b4b9da1f850c02495c30f38ee493aa83b79a44f6de5e7c7d92fe79c782df534566b3e5e8c04c0f449f1902e438395e2d782a64f156498d959518f68591fd86955097b11fc28655a3fc3c0799b5627380067cebf8b80410ff391e6ba1379bcd1762aad2e9976d3a62259cab4093109d5494c7608c58e0beedf22fb15c68ba8de2e0f2a687a5a70e29a59c24c5ea9c3546fd2f63687dfe0f98685acb108447187f0cda6e1b603ee5b0356408ac4ae63b5031f84932d514d15dc9a7b392a523c519f81047731d8df19329fb83cf19ad2e18c4f26d7c6873e5dfa197828fc40089aee779f8105f1349d5d91a0df6a352790f4d01f30a6e30d5a6f54b0db4ec6a2586e245caab461843b6168d58cc27b633c9d38a2d5f5738a8dbbfd9b8899227917c1260a22d0febebf0a30f3854d900aa72bcb95dc5fb5b2e977379969a9c0cdf5b9cc76adb6e7088cdfb4255d56fdd6662d2e66141ebd98c828cf3deba85928cd0df384c3b3fa1c734ba9add6180db65c9f9f4f24a6f2b4b96915645d9fffca2917bda8fe6a0674722181fd05cc8b1f0fd2f4dee22bdef9af5059ccde3b70c73ac34752e2da71fa38122d68a0d133c4ba0ad99e85d8226651585857c8c87d5d4780f7ebc62b58204bf25bee42b859261d88e7612b08edcddbddd79bab723078cafc4e9ce269ea7a69f0fedc5ef319ac44b2405b28bbd90eccd3b8ff749aec2632565aec783394fc7361fa991e6b912ab7e09200fc259c34b40e2aa18a887c297b58ded238dccb0191631b0b6ff47578a81c9d553c25609b5b89cc807d75a880a3ffadfd652dc40cb2364bcc34337a271cb975bfd33f2785467f9aa950f66d9bf9376d27603e2cdaf33afbdca5c17132248f9d9b1fe09348bc21f31ed35dadd3f7d38125b266bd5a83af107abd2d8b71f10ba303ce15206b507ac47fc22ae98a95a0db6d3181d20c77a5a62ff6395d57bea5bc6f77cc23b0daf828c3817ae9c0036f89665f42137b81349c36b78b61d811c8aecfdbe5828a3d551d327c35a0c7fe4876a17eddb09cd8c512a730c682f9b34b7ea8d90977723e016609f3e90dfd933264fcf5c607861d1a73082654b379d8e3bede877119a0c83df65559f7b58e37360e7c114702d54c3d656d413d0be889c0373f096d61b5ff54c3f7173d786d5694206680910c1f39b96101e6b4628c9a8d79a3fd778b44e95a758c88e4f843613b64ee44338061f9c7bae205b8bf1ca1be1c4ffa106f3563ac2f1f35c9a3ea8e29d217fab3264a8367f955bd1c6893534398a5af6addb02719f43fee53c46ff1940f7d9740260869171a3d922fbb753110b7f32b472fadc4855b0ecb22f56771cf04a17e7130c4b1091443761adeb5ea23131f6ad80f9571d2b22886e6185d7a11392950513a98dd34a0388f40027a3b27f0507b2c7dcd0a501fcab1cb5253e2fb8c2f3e2423e38d309e91088db61cf27f8e017e1b38b36869ff3d2cc3204392ac83c127218f79dc2d49fb9a5d222fcd67354a640c9ff0705f54fe9321a99e0874a25a565a86ab67f6a16b7aea94470e73a1a6d1e5776146c5d9990566d835eb91d1fad9c3670dd544f1f10921407dd5b4ffc2c41e8e5e8763e3baa7a67680ff5a86bdf46d3b7cf2e95aa8f706f120ab6a16633bd0ea4d51d06adafb8e48872dd2ebede21b746ce905fac09f7b239afd52c58a78e7065dae7772facc096ce4e7d1eb923028504ca79802e45ad542dbcfd83db22af81591ee48e135c731d609682eae2aac047fc898fbf3f2a8dacf8e97084e468eef53474d9b592abbd2f395af0604af3ef66d4388ff8d75b0382a4be2c255d202ea4ea23dfc2ca72adc87e689b527f45a40003c7e23151f4b14dd223caf446b083d013228cec20197ebdf5928b6bb5841be7181e528e0240306dac032b52c7608ce8fcd92beb24420cd5905407086d8dd6540f8c7f157238c9b8c24a9b4168f25c8d54dd40a5e509c2533a164d6fda26517b953ca659a7ccc7f4cc89d43293674ac2ef2f881943bb63fc8b233f0472dfd50ffdd0123e4296ce2e1e29212159d683d1e2fbc301c86123751a5991ce02fb0dbe2ea4d71241e18de4f6867a8b7bf5fded4beff76a56881e256706e4c0246b09f22ddc1db238c86e028c567c8a56e4832f2d33fe60a933fcebe83a2d6f3ac8a076b8ca4474f1dae5ff14edde0591535116d13ae80d982022d44f6adccd24d374050ab273a64420f3a073e4e18bc11d9437f32107c52ecc861532cb89dc8713d89547b779925b29fce17d419c49dae265a19d154391a70a3fb902a0085869e666a67c754f902ae8ee5242b372b3c1ea8a3410df28447ea8bdb682a179ab8db453d203796f5d891d76f44dd3181201632a5831e0e1c839173c0c61211dfcb7c6c88bcfb8514a42157fd67249b5276fa247798a1b2e364e6432df239e6431106ee416388b8f4c3cba3e9d34147b52b3b433d58d0c1266472b2f02b76a26277c6a900d7c3fb065c04a38261ca02d976a2f6e29caf7d74688d03c899a0a53393e1f3f3df3c206aece0a4edb1375698aba794631332f9df088eb80e53d1575141f4ebdb8238d4672be089e2f35f3b9eefcfaba4366ca03d7e1c5c352fc40579b277ec863384cb90da345cefb163e6d835809aec0e2d6acb03bac1683baf022f123c95d41fbbe59d681b647355c0cdace3b61de1cd55506602015d2d065345371c621634a2dcbe12a342837ea0d9ef11b8fd3b67bb387ebf5b8798c7483636d3620e3e4b57faa37ca95829c761d082562da5465c7b3658e91d54ad9c76c868de603db9ab6244432b32291e5a9cc210f552cb304dd14ec5d92e8c1d7200b2e0a7063dfa3c7b36fd9b0acf0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"00000006cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e84ff91613b1c9cadb930b5c719b144e6f3a47110899858c3ea7948a2afa40ac0ef9b68c30f7ea65fe5996d967960d02335cc3caa2d534aa2fc5c86bb516890449243fb8f48f84fd27b7ed92421fa520a91de4fab60e8da754af4b5a8f69befc28b690871cf1b0f68e8dc2df0627e6b95a2bbbca7a2fc851548b112439a36103d360fd042a90f064c36641b5d0b2b634aa423950d1227d61eabc7dc05f5450b5ebcfb89c03f3ffa35b0292ab9e1aa4d701c760aa44b0bd92924c007c74984f3e5e3eaf0b204c3a2c312c8eed0913cfdbe85628d5ffcba18e57f99b99f727430c7a3ac10fb2c822f31b88d36a8f0becad48a1fd07ace6ba08b6792d0c9741385bdfce6ef4e98f6d7b24ace8705aa81b534225a0f2bbbad16c79a07d0cdc9b4fb86dffedb7b0733e29f1c125e31a237586a2e640e6b015145c44a04942f74b404be00f256587eb820d92c36b56eaaaa80f72ff0336986d206a62b7151c74eeedf3fea594dfaa3b6ebee3c8a3058c8e3a1f489ed95acffe0fa9b712137261586c112e5f01c1dfb75e9dad61f956ba521187116036cf6322d323f19b46cae1112147618c2607cdcb34eccdec9dccd3f2ac2688e7261b2d4130325851eec469b378ef1f07545f243b194b954eddd7bb725f35b788bf311ab30c09061055a72d426d7124f0d707c140978152917eb84269b2dd7c7d4d5329adf8aee541abaebc494efb28ac23e82a1d248795151b0c40b500bcce665424ba5d01991b38304bbc0b0d3daaeb73a84a770e9ea8acec2b682145d07778737fe391b2a393580c4989e636dc37937ac660ff1ac740e4fc81f0c82bdfed52633315a2e5bec9618a69d416557df212d6e40f2e6d87b67982fa453b2f067c639afda3d7df632d52ea429fc9ab020efbc24b5845eeceda9dafde9dc22dbc9086b7cf1794bc41de6075f31b1ce7491ee42e2db52d041fe7941df16bee80dc582bdeff303fed9e877094bc2587fb4b653b802cd68372beebdadbb7c8870cba18a778326d01d242eb0b31f48ba17f74b7e42164b3421c8245960083374cc23f0095fd3463ebaa23e42ced60f0e9b083158212333677c9f4b2de3fa9d8908b9a0d0e496985752f8f9de26cb0dd57aefd160ff2fd1418666292dd1ade312b2cd518f9edff5ac18011cc625bd50a7ab746977f085c3f80d588819d625f4691937dc2668e151dd7f5914662fef371d731d20ebe7632fba9e71e79f7d760c8d71c80013743ccffd78664d7bbc32683f6761948b184534ebf0c9b45c96d93f5d955ef7342dd998ce2a466defa300db76fba5496307bad2a6e42cf16a4f273048070800bae4a07d5cd12d06fc0eb617f454fb7911c02fd3165e669d3af8e3ed3caaee5515cab9898b16804d36c9693043892658ce7a326a7edbca9f0c71e43b8852e0ee2e2161fa22392be9ce42515e77161a0a18ada62eff6142734cb5a1edc54ecc751ef632289a989aa34e75e83ac72c6999beb0c2aaf62788aa2eed98f9b2baed00bd27d71f7db088ee2b2918f0425aef16ae8c388afce27b9091d4b6cfea2726146ee2525eb6965222ff7b2ba75665546f4ef60c5eec6734b5a47b1b9fbc2570f817ab8aa19fe9604abc5028efb997cf5243c0ff2e84c1cb944f4cc2ffb801fa2cb3737c21b076cbd1779da83549f164367d0a4674bf152955732380d6bc68ae60db2af948691074296777389011643e75e66045cf30aebcc22e5b10c33f2ac9675b321337e4c895d33ab968a58533bd982c71a051349ca7e39fe79c9efcc81899f61760cd9a11e39de5d58ce48cfca98fcadd1cfa253c6c77ae85e0b2601f9d5865886fa51fa168ba556a878d013bcbd81369c0070d21a284dba6233e824d08d6b9d6281e064bdeb688cded8f967a6b61ce36969477adeccc4b21c42c48c7ab3754b2157867e75d15f4d41e218cadbe29a5375ac4dc65480b8c4ebdfd9f81aa828de1b3938ecdb9477452a41fa772f99eb86442b21288d31df55eedcdcef46e410859ba3ea7945fcc82b63f41061f456c580d76820d6f11c2aa7b6874b002d76ca42cecc7af1a88869d1c756690b6dc37df92c76f31bc923e99485a5dc06bd1c7b877d4ff80b6feeaf6323c548428d39e4e2d9f90fe0b2f971faea87257a2cc8ae33e071b16589969468f3225d52b3841a379546bf9b62474a2e60921629a019bf521a602729393e5460fe82234091cbf77ca483622020ce45cfc02ba5de1d613cacc1ba7c65a0036d0381d2108d10c3ad73dafb2f0ea6605ddce86ee92c970ddbd59c02d2f1991efd7b83f6f43202e36bace3155660223c1a075213d8bb8ffe67e2a0003bc9d4bfda432f5b9eae3dfa8b15a8a55205183b96ccf460610a07d137852693041b45fdb126960c53a798d164f8c832f749b66392214eb8e0cb0a3470f7d2c0b29bf7fe486e58f7f6e8a3e3f5c33f029310c5e1010bd940777f89632520ecc96e2b926ae92bcbd4e4f7037ed795e86e5d80aacea6bab0973607b7d15535052e1cb9989920cbc17e814caab4b93eb87bcba61ffc052b2162d35963990aabccf293f2ce359106eb728a6492ca7db1784f1347d5f806c8136254cda29251f579c1e99a5146f45f1dcd7eaa556dc7f3b6adad506b7cefde4bf438813ca0d3219196417a58650c2c50648401c253f25c9d63d33677ddf2d440ef3761bfa040b02d6e1ae3b0389d8d255b29519f22dbc6114edb0fc5e2b58bfe579a4b8201416bbe9f537e047cf6e83bd67e150b23aecd47e73e2a1912021f5a7577171ce83b7bbf0b967ee367c5016071a6f0a5943a090397d8ed65d3924aaf3cec9b85f6793a4716b24ae2b9d4b67ba390ec404ff1b564273d98f44b6e5de722e33c2ba6f16f30dc78f668287628ac00bb3d092037dffab0af4203762e5353445587cda22c6674d896cf813767dbcffb7c4e877274ae1922de5fc37ed04117448ec2a6c19b0c8cea3bd32603da50df8a25aa0246dc41041258ce47b4ef33b571af7e1d61e90df8863dd5a90bdf41bb2e4e9637d4685c07d21a1dcd9db42f4e6ae0e503a5cf94b2a2983f55a7d3713cc9ed56ce326a229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"000000071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e63f16eca604d463ae28afd19b416dfa56ede6eff684efc59f5ec2930581729d7fbb54a4b5134832ea60918d40e5a201c91bdce9e79a519a10fff50e715f9d7c6bcf4db6e9ea9ffe35aec463e842613a2d4d334a4b44105b03116ff9efbbde7214f7220fa96e6b876241ded96598be27cfbdeb867edf9eea1722953d2e035da6b9a4dec2a6e55958b482c03c0c069f6ce036849f9196b2f079263ce04bbde0ad7565b7a5ad8af21d00bdb12cd3f3f0522510563c53d4b283955ce8c6d415dab7d24a7c00dd71571566fb6580b413bd18e7495e30521493aece215df3ba96c06b4d21bb81d9a30e35e90a230448e9d3a9f3afe650d3d6fcdd284ebbba9b17738920c6c09934f5a4401fc5a8fc1734ca702d1928579bc2cd8934bff459cffa847a00d4a90641412ee8463e5801b23f640f3aa022af6e76b5b9df97ebdd533e6f4f859a54a76ba4c6dcc001742837f533472d3065c0f54415e20b7e9920f8d06eb28cb597dee5ff15685054d2f56ee6029f10e3d9b6a7ee799f295ce961539420352edfa241a174a24d1ffc38128a7328416a79622f32a33eba60e8d8bb09f74ac8b6f100b2e4e260e87622d14b6320404c2279bfeadc24ebb91edd79cfc72dc0a9384b2c1a9eed66c13a7bc9e718be80c7dd0c3104d5cc4f18902d0480e2204942021d01d94bb335d3b92b42a61e969d9148f091ad0f3b7d608180d05c100f05f35b8efc6192e6f05fe47ad5c2bbe318b5ab2d5dc1dec7aab66298af88a6e067ca22998d2692b1d31ba8c20bba980c72514e85147d6e5aa498dbd7ce4bb3e90468cb6d84ab5aab5b6a42db9be5dd15bcc505c569e41bf5b15bbcbd43896d61e70fd757fdb4844fd4b7319484a684fa8953d9f4ea4076e6013a4cff73433703dcaefed49f053be6f6ffc5bcc7b723064812d2a7fad25c1dfa3fc71704c672a2195461aa2d80534f5a6b78affc43568c641d6de3a1dff411da291399699c990286e4342f97c86984498190738522f3e68991dd8088c4ac752263c0652c0aa0954f80a1abb15cd1168f239c44710ab5e672302acd950ab8b8a6fa9ea3b4b9eceaed4ca0ec65758e70aa262daf1774f065688e3aa3c3ad137feb62e962059c06c68b7b0effc8f659614f4009fac8820449bc8bc75d5fa30a3f4f86a2dff0e750820fb5308c546525e3f6887c1cd0f4e44a639931fc9e2982fdcd8ca545cfc2742d3f6d6cbd48949bb4251864f334f5c41e43109b57e88646b356c36bb1bb8941ed9b208fa5ea126a5855c91c4ca28ee6771381002218bee79ac8a63c4f831b62429edaa33bddf3cd6facd728c574daf9a26f78747adcccc94c63e2eb02b21bb029e10daad8a08c2a3d3e3feff98929f0e90e607fe21458289c184f5ed3a2dafe15747504f3166516510543c3d1b457d5ced8431a7706de659b13e748f865768f6bbb403bf72dfb844931fb6abcbb14376a22b1539e37f81ffe65025e46bef48eb7e5cc8ff3390abe263c4cd01f8c19f36f96965f09a2b4dcdc4a10a634c9026609ad8fc331773c2b551cee9d44b306154fe6f7c905907a9e2598ee1801554c703705d015d19e7c5183ff472626e5b09d3f54c2235b0522dc7a961b17a5d1cfa676a0c84ad8db129dafb3369d9724b1da831d90f05572fbf89a701dd87c846cc52ebb4ba54887328050dce3c4c085561d62e0ca4c3d7ef6442c2b3a6675e7886dbea818bc2c802e7ce6a676f0f98632f07103b68740575073944d3d970afddbb73ee7555cfe5a499f1e01784c1bfa38b0be0548bb3d059522da078599a4239b829cf7b79fdc595a9300f9ff07444257d88197ac967b83a0232ed36d9db4235e8b799be35b42a84fd07f17fa8939ed51ad5169852d9aa90598bbab4351be9eb4c4d39badead8f7d680d187324e84fbc690348b7064a8a4a1d07509d4612175d89804ca8ac7c41c041c3ddaad6256462cd86a34cbe0a4c2ddc5352ac243fd59cf3c209fcf23c626a2687bd385bb11097dc5c6bd30dd9bbe0c37d31f74dcc84d53dd346bdb5edf896c4ff7be5375eec2a8c1422c73c04549f38ddbf8ee830d9a2fe46c0470c884ddff28379ccb3427f96113171b1087841d32f6f2a611c8d43cdfa36fb2c9b91b9da136687f5bf646002e37ac79f9838c91b7c86799af40717a1b2a0e07e5f5d0c84f9500c0f741c9806cfdb8d239d88a0e02734b257c9560197d96130be79c030d5de0e4849ecae44ab93c52ac403740dfabcc03ca7e8f94b04c7f1de55fa0e2c9e2dfaa8bca70944a9ead79cd664fa783809ac68a3eff5baab674b268be1e21955bc2507cdda847f424fdb03107cf951bc6877ea61079ae0c7e8c3a0e56eb29c2cb183b13d165e4322c0c379ab0663ec86e669522a0f6cf7283cd4d177619b7ce4fb15c5de902e5f0e9da6458227366002c07eea861376944b301ecb81b98e8f233d97a086414c0227f6944631258fc71769eac4bed57a2601f958696ede6699fb8152fb22d01f9520275ee5529bb867e3fcd59732fb204fb715df81a0e272f58d027e1eab4f4233e6e9d785870d34d274014630ab58916f761189b65676e84129b05b2582102edf979735c2b5cdf9a06247d668980229aa506bd32263400a07591947966ef478609c582226f2d409bbdd42e7a426410c658115dc4cd43cb84979cdd7934e0a6609b40a9129a9df73ba6bc36a78eb3cacd808461fb41da93edd42dee2fb6211422b6ab9fc005ade80c5d846f1a1b83bf92f5884d82f6e8ebeab4ad0bb0d8c27f4c9b290496b073f8dfeff060ce60cc48c499a286d731fea677a00941b7d5c371012d1959f791f08675136220a15fcc9f3aa57698ae034c39c4d390794048a24f667cb74f9279f83aac70ebc364a7ce5e2a356c1bf73f7b8b598a99b6deed765f8867451d62723ddfc04797ff7deef3aa27782813c182c2946ee0106f95f2ad4d7f670ed12208f9d138754a0eba619a6f4fd45d299cd7f0815be4a7938ca8e9040ab2ccb419840e21884447e12839f044fff84a63acee74c17f75800c820b8cef25c2edb7e82dae38e1e5e643eb7045ac256e9e93e471c051aa229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
-				"000000081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f786b05ed2fad39483479924cd4de178c562fb58819d592853ed98d556f0177761fd0ffefed384c434580a26f10311ba05d555f778ca10ef875ead1baef9a6f8e1bd4549d488388332151f9ab07517875b5c4118b7531b9167827ec9c084c2ae0c56eadbee4c74197bead837f5f50512df4eed350b2282c6e84b1382768677abf5317feb7deae2df6e697d565a625cb7015c2e9e6ebd65ec984dd6d4a15e9d2494c58e9d2b4bb938066dfb81824fd71b9b497a60ed30d8b67607b6f419572538159727a5c0f9b1309ff96a603bd966951716568d66ece21e6fc5097bb88132aa79b429dde6c8e8200febada61a995d1ec9c7e98ca54b45fc211ac617a0b251502b37f1c33fddb06ea636fe4b9f2c953b1063c17c73edcead49f12f746ac4619cd9864bf68ab1badde9b0e3ca25bc7e1277f50fc68a75abe0f1679c71700b67039cb7bb7200b068f619de809c5539236befbbc54b0a5f8d237700e94f0d2748273ccf425e1d1bf542d4da575c4b6beca17d33082b5becd934ee9d7734cda44ccf18b4097777a9817d7e9723407ad6e91050b79b43ea7787d0a9b7b1d228db9811bd9e51f1431cb1ee856980bad69cc2100c575a8cd48a784bb5ac214a6c3da39a12bb80cae219bc2d341a642f3b5f971f02939d451fced664bb2c29303d6d80565c0ec8a0898eeefa8bd7c7c69eb434d13e22e37f40a1a8bcd2c250128344d6b054d6fe106f6522fb907649041a31d16bcf65834812ec987d98617ecf66622600585699c28cf4ae8d7a9ddfd771a1926bbc0df264abb7ad6c7eab8d9853a9ddc23ca41bc1ebc7f83ac5462e5580cf178bfe7bf435ecf0b519d9605d9947b2b59ab9bfb4510e62b89b4d38895ac45e2a1842b8a13ee509a5ced874d34481c06499faa85918dd58b2910488f8288e9723473bdde13dc388e96e16d8041844d4ec9684ace0b3823f4387082d5d38bdc7a25f8575bbd1e7ffc84e47a1fb0f31951b5f7a7e7144a0e645a5f04b08c6ecf14502c1a5976671b2a0bfb188c300413d1e937a3f0141922fa885dd0f7f873e72c8e6605818a92b1f9fd4b9489493e184f1013c2c0fcd11eb60deee7873aa13554530c62cdc6929bc8b967248a13851853bf23baaec0c08ddbbf42142667095615a2818a0e4af39e6a4714806947470cc1dd6c263aee6a254ae603a22c2f68b38cc87d1806b20b0b96cec0c201916c5b0a64329a2e8bfbb2a664060d598461880a1e8e0ecef665fced7016c1177b5aaa69b265d58db6cc353b61f2c52ce784b0c81a68014a362c9ccd1098ea09ef816cc77a8d6d79275df684491a5ea30c98b86d42c0127928980426cf4e17122f69f228611626c1ec23b7dc4559892413feb131bb56925a501b386e638161a2bfc9a70ed096573356f5be73a56c2a90f56155bbd1e0278f3116f9ae78d05b2fc0d9aa0030ae67a781c20e0bca4cffcf91f16527e2d8b8f02475698d54514b39b8101f2a746a6ec223cbbee1c7c6d373d698bd3730e34e894d7d6a50f53662700003e872cfc7b2c21638b61a58cdd27a200f99c4a7fabfa67e96d2a5a5cffbf174dab74d96a70569e02ff93c7a9a4d7b4298289f02e6112b988789bde742098c626ed7ccc512fdff1307dba1df6675e1152fa089165b814288d35aea48ae919f53699ac082bf94f40d1748167e1ac806b13d47be01ec6b0439ba64cf33539a7ffa98aaeee6ed3c0ec110471c1d2e7a2c948db01cf97004b908b9bb98aa20120a868c43a94448063274a6d5a8897346fb2feaba12b8f028e48672bdead35893713dce5b5256b1a17e948dd33ceb2db1992bc152c6799e0d90b37d9f7907848ec91e52f6db9686512c63a2c53d7dc2f12321617a9daa218e934f3951eb91246599f07185af8e521eafb297adda701968711fc82559c7b9ca05b8b7d2309d5acb2fc37897faa43c8358b2ede6a90aa8321f4b152e6a4c50fee02a20930e50d7317dbb0d561c721406bfc57691c79217ab5f5ac6c8ddf40be23bbbfb997ceb9ed82e13b3f356a80ebff7d96b3db77e3f919d8942fb895bf1bc799ca084a6690f5627169aba23c1b7dbd27023af629bbfbb36b64a4d019894f6ba3bfd1a2fb4cfab4928ee56a01c8ae18a622e8520f73fd37fcf959d3caa653c013138475c903f2dbc034648ed0e484b0d84bf6860889d6a31f77592820c5a6d0b8d5fee0398aecc434f7502c4e3c8ae7d9d61b6e4c0a5b017415100ecb2942cbcc8e7c937329751a5d00283f16730dc351884b4a286ab8da699afd9226af80e24843c0018d8f9660cbe0532743a640fca5cc81f355c0b0f2ab264527f7e250ad5114b91a262c831c7993430cc94672db036902c42c69c844a191f4df828d91be1e2373076b09b47f384e364bf8050092a502f73886e9ad3617041807f1b48ff7a588ac581bb136ff01ee67122fb827849832821fba46eec6727480370ae23d9e0a29d89a761e9e79f78f26072363b5b7e0946a3833738d7f9a186d999ed09218637a08b4c5c518f3a117b1878f9f7b7a4a7e04e8f9827d3d210146e8acf450d2cae77b6dd6e9b70c14180ed5770b8836a05fce222c5edece192c6eaf1654765c2a5b5ef87236dc3e088d9505d578777e4bafcaa235edcaf61fa6e0ea15d4fb6aafa10acbbd8b922bfabfab0349f8ee07689d33b9fdb8f1e308f18fc101f3a203a8d8bec51852cf501724ec52ce90e3fb5f0f16a6c78fbcacd92cbbb8ec1bdb6f7b568905142523ec159f4869b4665f8070b81aa664a9c0a4069a16cff6622821d1d6c25504cee1aca831dae6e65994ae20556838ae50279da1a9aa9554d81d32a52744fbadcdfb85f92dffb154368b2d4977dd56ce44374e9127a4dcd5c59a1b3320380d4147d939a6b701f109c36e950091f24a26ffe79f911e5ca15c6d428a1cc1851364140915c768f4373acf166756893997b9500a76e3ee7700ba531fec859eaca35bb3251cc8f2ffecaa8c8537215f4d9e99186c92f635d77e350902de6d23faa517318cc6d759dc78b140492f1a787ea5d0d4d35bae14fe3017bb39d68d372df9122d6ee8252f6bb6133c72704c53b7d03ab01ee7e81eca889180d5453a68d7d7def571e8238457bfdf47102f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"00000009cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c101ba5ad887ab3529ecf7e18637d0bdaf1e55fa0e3e033b711228a50fe44b64c2f068db2dd2510cd97428dddd3e25a6ad9b387c7b714c344c705e2d7f26f9a19d3491399e76069951b8d7b7715b5bb84d82306f56a456a0859f90f815198dcc25565d8cc89f46ba1ed69311337eaf466ec06767e538dbdb5c93ff5321f051c7d4a021d3859201f85887f8ca43d0925ad4aba682833a517d352068cca0959beedc7fef29beb2642a1647351b8026a0f6a768654f67a52e910ff9b7e9a140c1411eb5eb4d828808c48c23e67e9c11a5bac13039142805ec894ecc2809123948664cab5a2b0269ef886a675f6d69b8c43ba6a32a6346e10aca33d3bbe41953263d192b80102ba68edd0fa7d23cad153d1525145b791efedcef04c5525b3a2a86a5addb9819e2d2eaabbcaaf5370e5fc2952ae8a07ecd307fd7d54458e9ca48c6d61614e790151ec277065ad8e438d28efbc93d1de8b616435ba7edfc939e79ffa33662277581c8d0c11358dd9e5f23ee7d9e2a1d08c6f3a500d3da3d822760291266ca6d807fb9ff3b1a852201c8e4604253d3ac1636258cf94e4576deae54db1e23b25801ea631e8754174c03afa4fa28208e9c570a2caf4eef5b67cee3876a83262ad656a7596b184376744f6b5f267fa61c7f020f43913c9e45e8bf558ea4351ed542aa3a1d648d20be0a3d1481990232c0521bf3c0f10d47f7b939ea97663377a9033b888e5cdd1a05fe1fd763906e9849f8980d91eb812b4c805bcda86067f0fb1bb447180374f677fb56b1322dca0a13c67664f3ad57ae32daec3adf4e6a38e905772ffc88a0af567e9e058ba909aa8ef4ae9609707483026dceb42c52efeaab8a23cba5d3999587f7b10b51eb422653271ba4212a8f92e2f9058d4164833cc47810259444a5ff4d58470bb9dceb12e34a63d735b0608eba962c22ad04adba48ee8f55856c9294b0c975bb11a8516aed5bb42318bcfb587d5cf4e228382963282b09b154b1c1fb0da5e3396a2f9b3091b2fceb164fb3b539a1720231bcc4ffafe6ef826c9119fbdf4abc79ed5258da3ae37da042b97927948b0e05c075be20a246b9ffaa256910697124d37e47abd10d2bf6197cfe481895532313e54b30248db61c36062692eb3ab5f3e495db34c82d07633d0ed6b2254e60e35c0ebec9bfa0c398fb9ce899c54d3209525b72e88aae0d2d1d90fcd49718f48bb7cf910ee59fcbb499ad84b969457a6be9baa452577409d9b05591c4c5c4d95730e920ebddb3caee6574ffc0600ba821efc895788ce2a0a0f9351db9578662c381ad6502a9d24a6e88433b0ce7385ac9540744781de943af30655e04ab719509180744339725fa61c110d828fc7c804caa18437c6f7080a66688342ae57400150bbb500537c34b644e5a1457447286dd94f9402af217d5c5ed0813fd645b06190cd85a593831d23c0e1d22739f0a063894d00666eb7917ff161b2618af8184ca3a3d5d242955478fb58708dcd9f3b6e6a231585224a8d2d1c2084487a8cf6f12e780e73a995a449a02f4615b2cba6a3915a7db947cc60022a26fb84c8171d15bd5b7a82200ad0719d13836654e721ea69e51eb157cd5ea579dfd80cd5833842f58bc1423257bdbade6a11e515254d1237ccad9ec61c62ab672ada6a5e26b97f8e4a7a901bb2dc326d1a06386821fc7c803d11bbaa7940b5da5dd9deb7756356bb73cf5828a2ed7b846c1cd4a55f419c4b024c0856b4cd908789a7e8a907f9a8bafa023efbf6dd919b150428d84f3b862357ea68a0305632a4034e0845447b6e066c52a7422cee90e3a5c06e7abecb5b37b002556bb1092d646d836c90e7eceb7c12ac2f2d51080ffe6e28f4c1b4616e7f39883ffda141a1d44f8c113e8f8b6ae4ee3cb9bf29bf060be37e7b4a01b35f2b5b4ab9c0fde267fad655f95befff5845b9e01502b98e5d24963ffdf8ed95f7c73110d1b9d5811045d2fa44d01aacfa8c7df38eed95ff6c8ca21f901979a2e7ef294ebc9744b2a67cc754ee006ec61fc7158392d3b7ba8db9d988bb944c78adb68db99b2bb34b617da65c8038b082730b77352cdc8111973d8ce3fe48857e47e05289006aa6aa263a415532e1dba6eba1ff78fa1fb14f41cab2f1a9c5859d6bf907272733a24f198c1c8b6c68ec47ea37b974d4729b8d199ec1e52e8c3c604774169b9ac2c211f47d6aa3e63081c46b65ca7df9fe50f5ce02481f3188e10aad46bf98d1093acc168131f3286101e1c4e9dc782d05d04a5e769dcac092229efd6e88dfe109189a08912ecba615b1142c41d2ff749fb2abc22b929107f4c6c417576947bc4ed1bc874e3cb6f1df223d4d851ab2ef964e802e52bdb98d7385244c3829cecc0cfd798d5140dfd1cfd64e819558e41a46899b3715f7b5cf9b88f6c2864309a95fcffd87be23e597c7f9e2a92b60d794e8a931200e14e02776dfc5989b7ba0c42b44d9699194ffce90489cb32d69cc75cd2644912aa6d65f901d3003c4ed0589fb5a4753355a690d2218740ba218f829507acd2d38f3095f9a9285d58833622af9066fb7e174226722c7b1f44e29994030388e53726a115670a69f6e7437d9c6fbd3546c76f270fd18a09056e37bce8a73a6d36e446985ef2f3a492877d70e01e6a6fbc1a30252ab624ab20807f6421be4869332ec197b53a9fdea553a6b1b66f34f3c1d6112fb2121826911061c402f9b613e1d5b1b856ede41a3496ac476e4b1690a1c59c3f84460bfb50851995196aec4a7df64ced8074d9a87b05a856e0f677dfdddd5bf536556d2042d30453c3b0b284a0814090763a28f4050de1206253c793eca74fa7c6dbf46515e2467dcc9d44b97ca3fe6f0209d613e33bf754ece4000acdd7fde0a5a3037a2ff62efa36dd771dbdd480c7a1d496fe490a1381ec2646e55271a0bdf82c38071ff8fcd923d4b3d916e007c6ccc7ba1bcca7a4a57122a3721946d20ce0e98040e3c359e03bc707ef0b2b39687d0fd7abca13dd3712a1513843f31f64918b9db4317d37e1220440a6ecc7fe79585bcdad93f44a71512a4f153de1ed6b4a1d79d7d69dc87116150cf32704c53b7d03ab01ee7e81eca889180d5453a68d7d7def571e8238457bfdf47102f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebded79c7175889b51032779e434a720a7e952eeff8555b41aa132f717a8d155be80e99c4cd16b4a1f4389d1cf0e2b68d628415b50ba50e2906e291a81eecca7764d6e8108f5a3ab82b309f591d50eb0b62c25869394ea7c229b705620ff23bfe6fa14b6cbf65106ad0ab3998cac6840ef0f9179e5f5134e6fa44223394fd480bf04e3456ec70c7ac9073ae356b590184fc1c0f31d8d262cc3df8534f81dc10ce33d470ef7d03487780bd965e813bdb8095c338e79038903460c75ee612e4d4a6b181893a981e3510d46d16cb5c005d727baaa8b945714dcf25069ea73747f5cfc9c0c545970e97111cc6c18cb5cf3df4bb8980cabed0f3fc3c883d459ad0b65d04ac4436fbf3bb7cc7d0de101fa1cb1d6cf81a5bd70d54730323d3bf7c00ea9fc6a9386969912e520b7730467f2bfbb836bff526d37c972e335a4b885e8586d26feb0e2922375e7f4a2a698957e8acf719acf2cfc08f35371b70637cc8bfaf53b9fda6372bd3ec0ce2b812863fa6d32bdb66ff240608e75c9e8165ad5a242b79159338272dfde63ea781747fff4fd19aaba9d542cb1de83b2acc79d704389928654ad09a3ad53f3c4b3739b8d29a4dd29294c24c41b2104cb6bf7fa8644ad3fe419179e0c62ca9c388e02ec0745e6997855a35bb7cda602b59d0685c24e94b2aa1cdeb16f3b5b02a5f0cefd4e3d6bba9223337cccd4880e346b430957f6f0cb3734ecd538accc583faa2b988d2c8d859e65755c03d6456855ff6238e4147b476ac17f5bfeedc6c241a8defeaa45019793185692ed28952ef275638dfc574a868ea0d1bf93579c19c5a1fbd99c78ae33d401e9c5777e1a505a8262219c969a032471ccc4dd26f9be409fa4a39dc40f9dfe00c55c2c1c3beffb5c77f272c35f128b874d69fbd72aaba25d0dd481032354fdca7bf6ea9ddb8aef1467838a40edd9e4561a41a5c2c4e3bc205c20078995399eb8f619247bca0959ecb79a90cc40aeba36283106e391525a56bba306467f40fe99549587525d957b995fdd53b8010d7573b626f37a5586d0f0baec43fa0457da68090b53b2a8fecbf9906f56c1937ee40d21fdb4a753ac3daa77bfdabc133a9467a022f5067e3a0a75bf8ae530f32bebc38defe85a11b03b07dcaca38032fdf95e5bea7509deb2fdbff4ca2f511944c54be6fb0e02a19c3e510e1dd11e034d1c70ae54ddada061e8130ce24f03d53cc759bd21d6e9572eeeaf441e42615519c7aa68478adaef38cbb671fc872cb5ca8e9d265911ebd999c2d10838fa4a471fb4d1bc750102c6b9d8bd812fa28f17a3e7c8c8f64be81329c5c302dcf10f07806bf94b2aaf462b236041b7ee9d00112021757fc7de1470992b16ebb54a2774917447d477367a9c151bb6177a7528e3ac55ec4b4049601a4a6a477f0bfced763d928efbfa9c0c817c68c7198e7de88a792c1acbba7a18edbcc88e57668ea3579377a7f1b3139a72555c0ecb89b4e7c488acb0645312aa93437b6c331313e9088529b1f311c73f699052ca735456dd54e9c8b2119462fdbbcdb1a2001ec9a3fe99cb35188a2ea4c8f06581d940a9d6b1756d38b48cb92d2a506c23c7426b7f8c57c275f3f0587e7db311abbc9a9677bac9c00811c538dfd389982b2aa22a70e45167914d1d00179df5e35a58155dee4b20d90d6bd839d78de1c7c9cc5c667915481672d583de8fcdb20581d2c5fe6acc8f71415ce01fcd556f28cdb84b9fb96d15c8692387b87c380ad64cc746f5f57d1546f833ae914118670fc63269ca3c82adee51fd918b97c8aab54fd27c86b799686718755e0162c51087573eed4fbe0ad97e407b7b504108124c9c7b0fdbe938f4d9c03ad1bc5c539cd5505fe1b58887f0bcc89cdd7772f1fa63e543f78c051de854b318e6cf5b77a05b61c2960eb686b4129647770c7dd387346362fccdae82acaaf3c7bb184c5500dc110079cb1a3027dd48ae34fbb3d29f880935054dd8173158e48f0b176583377b0f91043dc863f67001713ad078b4062295bbfbaf488239cf31a93b4fdfa4bf3050d892efaef369fcba2c6087dcd157f7cfe0c4141dafab2113ad6a066e6ccc6d60ffe0916ec2dbdad2ed8673b950f89ce7ba20459b8af9ad0e7a5be0d95d4fbadcaa2606cf0bc674db6a75f334bba25f125a0d47f5250130675725e8a7a87c2b875b2ebd01803980c1915de016b6829f92de422b172c0db88dffe4263cf026c7f404212c8dbc6bcfd87af1f250264379beaab70dd96a22eb1fe2ac1126cb5200aea1859818fdc9286cecaf12bc88d44d8e47dc0cc3726a7a9c7ef605ba9920368f4dbe9b93e516e2c17ae0987a94ed76c2006e4a5ae9669738db39604ccfe275c0aa6751878f095dce2a1d0cadccfca2bf40f6245ba8f0b8180abe37f9741910a2eafb8700f4e16bc3fba263ee4bf021402305dde577184a30f9fc63945b89c32097b158bbf8b8306606baa4bac3a2f7e0957f0b37d09aff97428f8d54412ecf5fa3623cd02b8e9f52296281a17e6ed327c7f76b4a4097ce05ace430ea14c2c10b7fb229e7457bc7702e19ae7e63d12008e038d0498c0ba2d74b85733ab7b9ad31308896c42f827599af08c890c3dee15c7978aa7485d99e1294b0c7988822a6cf040c07b01b820b703653805d851ad04ad763fbd5c988d636dda4925f631337963248e78c8b9bdb4122f7a11f9ed152d0cac44d1a39b293a0ac0c196d90ee6fbaea65b42951ef9d2e2d03ef6a688324679da533b96188484cd581fe5d756ecc7fe84cea8767229ab28da5c46d0639f4eec63066a52103319259feef759f4ebad0bdc57c5b510885f0f6a9c837acf5e1703623c4c83ac5558c323c042297c91254b54897662dea67450043c8e3bd5e6cc596f875ee8a7050f49bcdad83528e541c80b1d59a1e54e5475173decaf6ecbd16e7dacdfdc4875b4a6e09ec4cc9c739f6173525e156f11253d3cee122290870d90b571b3d9de4b7e28e59f3456fe728233ea8a336a11c35e39b69238317452be31d2ea13aa2613cd02ee51244fc803ee5258388f665eca59da8c4c80809fd787b51e173f4922822006a7705fcf01a6507b04158d503559eb527a597b5c220ec46ebd092e289f08fb02f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0001035fc7109dd60dfc60801fe09bf7878399e17d9b3c2db3c2b8bd98dbacb3aeb3b690cc15703124e7aab1d1fa2a9f60ae80e91fcc4d8309655eae632b7e6e7b73d6f3ee37d501aab764a53ad672b1dace6a4c3ff1c7f8268d8b0b4326cbb529df30299ea46e0e395e4d59a19d1f179f5062fa788a09538d74503ade7cfef60a82789e35e0c9eee947840b8be6ae62666240618754a656fadeec1a76ab1ff8b4bfa20ad709dd00fb311e4fa7e9eb47b69e7a0b1cd26ed16351ece9d347e9c4a549f1d93e0dd72d1d415e08a315da248d02268386bb761e053f3a9f4f7c3f6f9e02efefe04fd241c3679e39d92f921bc04b88512ad0055e6e3da706600b1685956ccc1dcccc9cd64cca828fb303742dffc8b17675cb9ee0e4f713c7588f3aab1d020a666389e77f3481b6ead8e47f44f8b76b3862cbae898fb3afe1eed220f18997cab8d971b9c28752ea65bebfa639e3d41445250eb761dd053fa7b43486a838e07de8b1fa2f5d74c1421cd37b51dd239141bb23eb5aa19a7ca5e2263d3fe0b391196b67171290b6df01072801856e2f07fef7931d2286ede595df731c302a35b62e833c8ea1fc569529ef10cea049f97e921edbb9a350dd70e6416852e480444731687bb6e87eec68c2ace0ae8dc8d5a802a020f15f38cdd7dd35c8f46801713ed7b2cdda0b32b3e1bb79ae6b9ac9d6ecec745789f0f1815b2251e9bf115f0b5b455750c7c1823a8eee68305d324f14f8fd0543c2bed95709f72e32a1c242d706f9389f54b094228ad23a934ba3b41d0793b2dd2a75817fbb3dd78db3b1d5ca8f35d713e6bfeb788a32fb7609664e9c7ee0fcc1aac9d82a44d1df0ac9d7ae3c98235164f2925fbd0b579db48c02fea17def08dc855b7da3d88cdab7a8040e697c015f1dc66b02148aad109fa08fcc3411df6e1bf26215323ef7fe091f13d2e05d21110bac56545a5ce3acc30e9db4175693bff9fccbf9d5766f6ff10834efdc3003d4e65361456d2daec0a24c0e48aae289d382fd307e4792eb149c34000d4c7cba7c9ac73540a651fd09d2658a6dfd91d5d9ba74671728f9be9f3bc316dbecac7f06588490f0e66d781a0a8942bb5057228a741d4154e9e01cdad9de60bd1b33d0564a2f27a12f494c911f51081dba961cc92363e725d1908f338deb91a5b41352f2a807244f0fa08f7edb644194454b7fd4f7cf838528a85f22afff5583c2e4e3fc8eafad4b5e99e11a0da4e7ca14a838b386492a87b14f084c8a50e9f866923717c2aea1b3c9f2d938c3c19036229c12a3679e16aaca780ae435242c3ef8cc65a354cfee7fd69b26f4d4b42ddb198643ee69447ce0cbccbc539090fe3684ef012c9131cb019851381a4def73e82ec78234008775701f0b4e0da70a42a6d558b8d6719de1a6ff92da978eb3a758a57a77e00ffcc5b68ae8ed72c8569a5e9ff2fa248a82a6472ca49ff01282ccb9f7f79ffa8a334f4466bed5ce1beb69b31472101e41eb8a80a95d81ab98074ed1215e8cfbeb60a7d8cc896b72fa79e0e2872b6a6bfc154cdda9a1696bc4babdc957f51daed08b81f49b6498f9558d1317f013a6c62f9b60b51be28d205b68dab2002b2212ea8e0fdaa1fb00f68943f07326402308fec2e7520eb0434f22624d4bdcfd0403f1d3bacdbda7bbe82c677aa4cc247fb7e72f157ddf24e0b5e3c6ef47aed61006edd262fd39711e6fb94bcdcdf2c71f04ed9889e63a8725c5d49bd2f4a875bea9e8e78235f9fae2d99e5e0773d8cb0f8ee2c846d1d240acba0ec0e71f40c03ea2ea874b244934da8c2576676de415c0203cfd461a7b71428de28467797f8204bb568d89cf592770641e5011a16b749ff454fec7492a52c3e5de85c1a5db8c826c463e1dc8730aa21cd1e65af9d1e1aec12ff64851dd4615bdc7da08e49d52fe31e18695358e02bb207467450d743382085b8737fbc95ed1104ea733c20db626b3f8f46c02a03c5462d55df395b9d32326353b4d9f18fd9cd3d5a3ffd633249b3dbc9c86922ad37f8b4517b85be5dfb7c5d2fb4b15c2327e023781280c253c3437a793567b0186c3e3574753d5da8dfdb9b6d01a08d1c1475df807f3a5e6987f0a0e2a66aa2a03bb413a8dafb08f00a704767260c22c654f654a34eb7960e880b629a2e60fd1debe2dc2c2a646baaff36621de529e1de56a3ec9b29fa6df09e2eb99c79af87e7df649b3d9c1392cbbb10efb1155add61a96b16157a4f3a3c54c612edf5efaecd6f9e0268c684d273fb4d346ecbbaf570090ae0bcf31c894d0ebdedcaf6d70d1f7ef954be56ff7d657621519dbbe478e43781c2fcefdd036fd2fd77b606187b76892feea2a4811adfa29bbcefdd19a54899f720261a205123fee7e8a2c36e62f194a1fae7f6f859678534ade46684ce1790ae83e004292e1e5c112fd18fa789b0e2a194a683608be914229636eb8eef01862b47e246d187b21103dcb7762f228b1b2cc5faaedc3f6c9773b218d6100a84d93dff7c86dbbfddd460c7720e8f0ea97bcf115d05d02b5906874801e36ad1ee39b0056a1311087345d63b054548418f0390944ca70110250605f6de0e5a25eb50d2f5978b36f7f5ea9d1bbb79e0f69341eb718a4c4207799552f7c1ed96bfa8b7f427c695933c48e46d4139b8523956fedd31cc41591bcaf61e96eabbf23849992825081334662b720218b1e173b9f045171e897a189eabf56d36efafcdaab10d94ae84b93ed64d826877d8d2fde6e28dbf5bbde94996e56e435ab84a66d4c7244b90d1356c81776006ba59056dc122d4863282eb40b2a4a03104621c38ad3b595b7ecf25666f54e4cfc46dc75fcb0f49864019bf6f2f59de7a2c615d4bc05493e165a253ea7ee2b0a4d38e474d0043c184d2aae4391d19af9a1543573a03c4d046af05e40cf632c7fb1c0e1cdd3b7f1141794620398f5231d78db1bb66b96a2f85e014702e257c1be3337ca27fe5752dee26d79d6cf7c3e372cdb306d6461eddb6289cefe0f504692b8b40c69685579b4aaf5c8ca910ce70a7470559846402e184adf21f8ddc477d1be6b210c237190893dd763a199c06a7705fcf01a6507b04158d503559eb527a597b5c220ec46ebd092e289f08fb02f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce7037f7a90c4ff1c6b80e186095b9d4c0a9a1d3fe3c11beb80a75f0ce783ce7d5e13c262063bff669617194ab5f3d9f731f1b668bb21fe7c4675f0ea7e60fd807f8a67ad75e07fb97e5c493eeb0f011feb1e37a1dd67acc1bd04502c9c74b4964fa68248fd36cdaee5512a92165249478695b189d8e317e4f454e82f1458e30ca2e3c5c935258123597d4ef0cecbf5e76d2bfe9ae57e76ef4ee1be11fa087af53956a88281942ed2843005e4b3c497c0cbcc23f2285887fe2b33c5578274e124b5bf279c9fc03d3aef7e71edb0fd0d80e0a9cce5337618f60c01f3480970abe2ec18b00facb520fcb4b1fc1f275135b8866d3a2c8dc078dd1fc2e6905eeb1aded87b2d9ac29f8fe7713d3f029a5dedc405915be2bc8faf1334cd7feaf2decd281ba36786176506ff259cc568059cf9c2bafde6b649bc84b5ddfa03aa9a4c4db58dcfe044b46a554a75f1e4fc70366678f3de50b0769db796272d66c302fbe1e41a760d7d3a080f0040b9de2403bb6708792790b67951cf9cb3784e636852d3f31077655a439552a58b59eccbe2d8fcc97aac41a787dc30040b2d5dc696297b1a3778db8d32e5517e7e4c6f7cab83302af12b79911df99ab5de94e1aacffcb46a15d19374f34a871e333b8d1b6ccb734c757991ce613cfd6c5eff0652c9221497839fe499fb0b6a4b749fddbbf72ab4099dd9a6f4a3bfed2966140c778bb5de5fd84d8fb5216111477443d047e0d02cb392acf655e459e927e9b2ca137d62d67453f2c9d0e7afce761dc36ee6d253650427648284ddeaf1b75837cce84335501b2a5881c83267538093090366b53d04c0fffc3df131e6399528427e5da7c3e03f0c759a4c7f42f0e394cb9cc3b2d689c7185bfdf300e0612fb350272360dbe8b8f346ad73711bdacf95a1aae06172a171ab9555337cde944926c1c06e5cceef8815912414bf72d31d49d7090e99545e04380c6f525919c0ca68980a3943a98a6ec9834ba0479c5ff353f4336f36b470634825cc815c5892ff1cdc45f48a55dc2088394ac44d653d3355a5bee646aebe535262aaa8e8c015f35129d3bf9a0410b3fc46652e5cd354b09759cd17c8f0d945e6874a119702c46dbeebda32f493720efe5d39d5f13f987c4d5776065cd6b01747dd791cb9410abbbed5d419de9016efff221030ddcd7a9d1df2a5e564024469981580786a4b58a5f35f8ef34ec2833ff5180c6870b46f582ee6a82c840a4dfef47b2fae0962652006aed0473fd67cbd4662af23fcf0a46b15b56921df0cc22e4a265a29e6f6d7f9b3c4ace4b4ea9d76c2b9069e7a164cebbb4fe2b78049e887b1c24e053634eb69468fd55809b61e2fd03d035155b74704fae62a7af068962af1fc3e82eb2ff18eea1b4f2dc10e86f4ad425cc28c4ac3d95caeae42ec9fe0e406ee241c8a3e2291eaa7caf0c21adebf4e20a9be73f9468ae2e334809a0fc1d4218495774054be1b07a1994574c7afb30709e96c6bfca3ac35f78602c785ccb47185b0a68bbf6c0b53f40e6a892a88a90daf828bcf18a7333e9c074f93cbf3b19c6837adae9696b75cb2621cecddd1b97c1c4977a90533a5a22073411ca30e4717fdd321c0284b9001ca25de47c66a5d853c9b0dfb762a682c89757ea8ab52062613d5820ebffff9fcccccb916eab16a339ffd987c5fc97840f60de475c96f2e25acfcd6f8402cca754991c8eacb48a546c6ed200f5eb9a39060bf18e05c0a796f55950893864569156d97dd3d905bc914ac8c9ab8697d98def2f8f9c8245855bc856bdb0c29d047f02a5d97b458de2578e68b56f82600f4723a1a5d627c4154e4cdc21b09ce9faf1ab7ef701afc4852b4031112bd462f2ce6ed353c2a0348a08955debb6ceb25bd73c83a006261ca9edbf7c9ec656577022e5b449e0b7a940571e85b941104ff11283027f0168278de2ca8580bd5a52dc65a7897c318760080a7f1df88cda94588fe9cedc3c49a94480c0a7eb61459828249641c09ae6fed24e63e9d9049e394f5c6752f1a529ecc95bc980aab793bf150fe2a577dc47b1cf3222c701326d50be7b79eda0899f3a9e3af6ea076013502faade815fc23f733c513a29587f7354bc98ab6ccae80d07718a281af2df22f3805a790550e4a76a205568ae42237db0d9ed133cb7e154ead15783775ba336e4853ee06e0e925d8ec332fe198d01b678f012a2f25f0fc950d04987e2ec16ae9af0055ac3865acf52086a48af4be746b5b373b62455fd107cd73b1b3941b6179ddd3bb6d86994076dc846208569289c13b63010a1ee8288cba5dba261db6136c3095a1f3f07d7540389794c011893028f95f61f44f86e6fd4aaa6d72ef21222f2749687bd68e94c84f6202241adafda41d60fc9aeae146259fff46343936a59b1834b78197fcfe245f35c21f7bd00d981a1fd9018c598d4af966c6850a06216dd11db3909cf81cf5200303d5ee8f437b533778c2519a1e4b9d2682958a215f6b78b720f460d533e98f2bb634dd7da93e5123d302971b7753b9c4c1e7237f15741cba88ddb2385659a69510facecde6c03aa4b9ba743e1607ca74eca4a80a823d8d57287f59ad4c3efa388b31d9078c7cd036657f1513b1a455606a0a4a15be4dc2981181c3c9eac8921c1c7bcc3ed1e704b8ea97d0aadeeb7a3ce252e6c02f3d23bbc89b481e4a003ea9fa069399529663fbc6f1a15c0a304b8721c916f35ad368edf51780338b684ed6219d4ea7e5a54b21e6562f32ae40ad20d5870f9def3989e18af7e82b9b56d9278af70fc73f6868aecdcb00b9ed367423442f71df2b65942054a233999b2e8d6654f6a946ebcab047e7c1dddcb9a38e5f9c0d660403ea05715488923aaa768600ba575ee01eeb5f4349917e734958bdb7884cc22bd9c82e2d2d2405addc84184e812c889d5ee364bf77ed5964cbc53ca931511aaed59bc2abe7d88cf23786bbb70eb0ff68130e143d35acac03edda59b046e9b566ef2d1d204fad058339b1e3b6d750aabfd556a34b6e692fda2429e4ab41fa90f7c6ac9a1b5efea1be6b62de767020eb4432e725a1c47a68c1b4d91df5e83a9383fba252835f1ddda1b9ead2594a80d91977c594c0ac4f1035a407439803b4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f475daa7030d66e06aa3af46ca036f84b6e3b64ed6fd3192a9ce620f5646dc3e27cd949e534744afe3763f3766ad1f3040c8765b58938cef23bb6959863450b322a4d6abaacf215216be0ffff2d0032ea3d2c5080b75bd836bd89286193dce3c869c62dcce82a28a7383c7aa44c05bd97e018c56686042d78076739f8a335f3db927f9f02d385370c05d72859ec69a2a361fbe07cb6e2dc6cef48cc253d76fe80c65c2afb2df7a16e2f380d96cc054408fad0e97ba8ef4ab07a40720ec846c43c00b5bfd6359b187ccd9a39c44da45d9858a2e98d65c432199b44b09ba66d4db8908cc8d642bbc41e2353efd83c7b32898791ce4ab95977f813bc8af3eb03598f2773667277aad09e296675de39f5256c2652da25810bfeb575b36dace7ce0da4a188c30ab6cf32dc02eda4518b4811dce8df899556d50382a0df047b9d60cc2fb746da68e2635044bb99aa9b55b0b91c709c273a6b2e4639ec33cb0d89aae6667d2a7548d89930021cf49f417702f0affff0a3643bcd0d01755db385f32c3da9e8a09e1ba753aa8b2604f5e54186877aac6e3bf110ff66219441b84d9c21835bb9c6fcd0d4eb2cf222167d3d0d6cbf5c701c0372c8cece7e54d719cc87d5fdf454fc01be4255d00880c2c43abb6290098207fb9afa3066ad64c6298eea1db4f4d0955d7f5badddaea10a7594fd2cd97c36433489e2cf4c68edfd2b2de08144bcb43a72786e244f235812aeec01da3bf2f0b8b8f5a0594bd206990858361de2e5cdd88d58bf4fbbe4a360fdbb992315ba586bed1aa31993264969f3054b9d182c027aca53b6e44b1866e0f76ce781e42f65f330048ecdb9a0fb41ce5b6303871fe893c4b824f639dfa3fc44761caeacddc61fd5495e9e62697ccca4a25cca3264dba4c2e84c3c9ba0600fbc9418499865654fbfa601114d2bdd2ed7ccb8a98cfe1b01a3b6432a9d5ac508f118be58be1b1511f7da077dadc35efabbad2e8edc1b715f80aded69412833985e0d043cf454d0d74c5f0cd439fcdb14ffb3dd42155c00c6ab40cb975e4fce7e24d27be4922227d42f66860fcf7b2fcab0970c78a5b8354886cd1e0be2473a230f52ec9e9b0d0eba098a498b1f15f53c02bd8eb8b9f977c76d930e886bc1e8d2f60df02de4fa101c9be366c4cf6f12c1826d9ea8a4a68312124db136e3bfb3fddf46fec16739dd5f791d7a3b215cfcf245edb8173ef65faf7674fd461d0b61d9f72ccfa28092b1f879297f7eca38e91624a525d013025190371798a93e7271ca2687d9d8b9d19b152aadbdb3b1086df9d27bbe9700726be574823625561b65d5a4cf7e865228766bbdde67291eba7a33bebd428d52767a8fb9a96f7f7dc977defed8076a4acbc0d1bf78ef933e4d58b1edde90584b0c7a440db4f8a9309170ed54b2c673137a565d6ee62deace26f1ee568939ff5f921cc1e4a8639178d1231d21954ddf2326cd397e456904655e4c00183cdadd8ac2ac33b1e1113de190bfd61df44383358f1ad95fae88b43031bc9be52ba88d65d60f7ed19a49ba8d39ce4e7bf863ffb45bdd4430b76face2669e7e0cd579286edbc414558c56fdc44d79bc0e1d9dcf3c8a8f849a4ea5e429adea28e3be1408dca1d380ba0e06828bc5f33cd9a5bd62675ea79827b00c11a8d4ce2b095fd158f8a1b58907b016065a78a70910f450634e08bd132f2f6c510eb1bd3dfd3304c59676a8d44f87ef19623f2d4ace042c8d12f63f2dc7ef60d89ac00fb5a193cb4e6c40a2d2fbb1a314e78148fecb96437af58dbe7973c63b375788cb8f7a8af9a77b4c4d2010359b36abdbbadce79ff7353d4662fb8c28231f0c0631cccece50f217170093e2fe7063f2d13ef13bcd2cdbaa4a575bda1ba8b73b990ee52cfe27d325a6927de33b3a42b4059d3bafe60936fcf26eb7cf409dc7bb70b95a82a2ca81ae8afa7120d9f9c4c66ed8ad7ed4b02633f11913f23bdbd75d55287010fd027c7c4ce5c5c1f561236dde2ab0ab9156d8c75174d2ca2f5870977ec2cd9b2aa1eb3bfa7f7c828237cbe6217b8c317f27504aed5dfc6045aad43c8d9d20adebf022643500f7bfce324fee49118787b544db1fb511bf1f47200e6f2df8b779c7d9eb629b77912c3665d2003b3951f4e3bc5425a8183c1094de2b4bf417b3d1db7dd35549db5194ba3c34889ce99a13bcc5860c0fdc5853217e10796bf5af771593c47dfa432d83a9544da80102d74f3f920d134244d74646aac0e8bfd3842719dc8a79f15ff8876eb556191d20f66947db08bd93e7f3ad130a10defbe215b5961d15e0b373d3e99f2cb23e9e9873f1df4484d779daf8cdc6f2e0da7855e1f6c67c19c60844abd2cea13a102287a950712768c2e321782ae8eb7434ff239b4210ac0941789464de35420b2518822792f9490a6955c66ce5b9e6b90438adc255851152350aaa57a1c96391bd08c5d98b679db4c04478fef77648b0c41105c86f31cf5e0554ea3f79fa24e8ba6a6e14ae6c029376f651abc1e6e74341338919f9607a119d493acd94168a4b6be73287a81dd1106f3ce877eb7e4f2426355f2a3cf72b97c54de2de4689f9bce7b90f72a7c27cd215661ee2ff2de308848161f7eca05ff28967e1c5676e33d13fb0c1d05cbee97b0f5a2e9a3146db6401ee85a25ddadcb2f3c98a43cfe47391754e0a75fce59f77b4022c07530d86ac217b3d5c864661d650ed9520bf8b5daa2dd5a37a8f0741229c43c866fd5af070c5f41d668a651182c7ba43fab5246eb1c47747aea958946952d61171f2f9758eaa4319f4a84cb5c020034e818835d06a41dbc9d019d6b9971bff8ccb159e5cdbd1fe43e81b0c06246d163c531f0bf3c5e2369e8c5b2bf21925c3c261a500de90b94fda0c85fa8d781486d3085ad7f3745b2f056a4fd641dfb3270c3f1f03b91deb6ce1125cae8aa4afb42db0cfec6228450839f18812edb4cb0edeff8456de65b604b4fa7f6e880139b1a09d4fd94c41ffd2d07496c7f0f30545677b6da3127699a73fbe84a0e0b1bb7e23cbfb473a07adc74cb81454d7cf337c97eaddedf4d2ffe8de3ad645e77fab66ce3a9383fba252835f1ddda1b9ead2594a80d91977c594c0ac4f1035a407439803b4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b315e8c52f1e7fad26e3aac48f95c68fe6e9eaa92ba7a5b9afce1963c4caaa82cc295b4c86e987242c5fb2864fa0c94f9ab41f9650a8aa8a9ffcc182366e6bf33946eb58d4b64cb8e1453a6027638141946601792d523f663b79a1bcd062980ed486b2aab4848f50440c58c9effbaad77b427048d6f8549d31b6ee09ccac94e2b7da0d162759e8b431ba3ed061e0a057c40aeaf868f7646b221da25e3f369a1945b36e52dc77c16665ecfb615f83939685ce209c5559875310961d63085a422bb2847542513316b02dcb814290d28b52005d5c98a4d8e88e461efd45ca24bdded2bab455b60559c06dfed5acee085665741482d45e4d0f983e8af423895a504f05dad8a949357bb742f426e6af11384ef308bbad6d43176cd4857b51d64c1a97898508e1f9ec4357f18f6e8d426d1a56a265546994d030926dd19c6645225d41ef0efcfdbb871e7b108c3c4645dbd705b8fa45e8c0573e5216353208ecd28605ab3e6f5aadb8f4c0c1d9b519a45b62491350ae8d75ec6d50d2bed4504896b2178b95846b878097f90cf472ec998c2e540955adbcbcfac0932a5f7c8a81cd4771a823ab154a881066f4129142abda56f87ca3107d2a0510b8b00fb3dd96c0858e975ab36a566af9303e42485b51ecd4cf549c5ffac67cf238faab30667e3ba9aeda74deefbdccb85b037f9b91b82c12f7b5554767c1e302dd53d7630dd283b3d0d96117acfbb20da7bd4b1464eb57c31daec6acb4476b311b1295ac70735a184ee56482694dc6819a1b249ad865a6b270510f4f719f57627a325106d304abd4e0ef84289d311ecc13e00fbe675a27d5fa28ae3899548b987aae7eec3b74655cd6dc1970fb1a77abc1815777b6b4c97b02f32841ef6f191129bbe7369c24889b108e5dd12522639310c0659f41ecfdeda2e9d55967be9798b2ce9f490288b2ecab9b2bea33c09d89cc500005d05e6cb41abda760b0a1868fb9e89f75d74c40514ee21d87e8f5dbd1e3ce8aee21629b101716d1b4ecf5cea2a8d97effdfb204161b9bef0934e1777610c4f05d2f87c314f011a6dedf6a8ce3d934316f23d49f3c36f43bd55e31dcb695c2de70ede2d4c323807984194d9bcc47bad6917f290b0086f6cd0452bdb62d642ed5a97f9e4c1f6a4d4d15785536a437fa11ee766e39cf4d4ef92727620d33a477135f65cfb138df24d828f943fbf76d5911006130471dcaa81c771c31ac39b4cc668c9da0a7fc5e65c494d83d658ceb72a969b9ce261ac53ca1d385ec87112e963c85f7765af477b07c20e51187686435a5fddff3cc2e7902b408b7fc7b9901950ab9f1994858229bbb2f45e7944c71086d6f56f4bc2ae15d1304ae57b499898a414bbbc7f0644269d4f4ed1f85596985b31e468991b8d8ffd349c94b2418e9166d5ea4389611540cae6239ae91801ea4302bece753989108f6e0efda4a0211e5a43f1fef8a503188a2aa15ae58c2a81359f58b211e7ab646f31652cb7f3ad57e3ec0d3b4dfec1fe8405c32ea502c1a86c864cffd121c1c7cec942c41993b7f2a7d000ca971609ffbb8f59b1431202afe1417397f16eab04d886e76373d6349404b49d89b0890e5f3e45250bc79ecab9ecc6587de39635bfdb3add8c32824172df1e141f572d64267d08d0b63a9d212937d19738aafafc8c0ebdbd98798ba793abd4c8728415f5962abd3bf1f9b2ab225a225fcd0a176a0cbe887a7f3e0511e06a0a7661711ff65582ac1ca59e6afcdcdc21285d2ffd6dac50d7078da618e6f0f9bb3cee3a03c56a81118d03c9dbaedf20d76c2897a12225c3e953c9ef1a1f75f47e029de3b0c1ee2b93c21f2552fc5579da3ce345bb19b07ce351ea1db17ef1bf73f226cee86e7c5b4e9e42d980da500aabd6df7f864e1789e7cd10b185449bb5da587038882522e091436a4794f521068789b6c2410002ce62fabb190a484028a2d31b0a97c6629e12f6d65bfc7be6d245beedff8495bd62dbd1d69f9766df4ecd4d1e23f81d8e6c1cead0cc5e8af21b4d765f7d1af4a5ff1fbfd100e07a5c02a149c59c202d280630f07032998b7aa286755673ee01a210f24d93d7974e8c825dfcf751b416e32d424d2d9a53ce35c68cace06902d97b2cca12f0a5ccd59c7231e8a0455765633a63ad068b1335f62e0ea0a3dc2c9d0f8ce614d4df17b711133e5bbaf45a2956f8bc717ff0f79dba290598917259c0aa76ae867e5f924e182265ad7d5c82f122379fce815e41a785a343faf003410f708aa6fc989884bf8fad6517a340f7a36b8690710f0ba3c583b2ace425fc9af5f9286c662a703bd213b77fb9e0d953a511199c477f810f2f79274fb30dacc6330b61435fe067f44a4a6c7527e751a3598f8dacdc4b0d4ae0c24f5372d808e6cfb1f36efdb7bf8d676c231dc84c70123cad4e69f2d3cb66162c0e604fa43cf23984519965b23b8becfc9970fa946ae7f1d4e9bf237d4c9fe02760bece1f80edbf117e4b06a13a03dd3cdff1a6fa19e05cbb11408fa9a7098c284e60479eaa9d076a5881e87029887f2525cc29c167338a88322189b433965b713239802055ef76ddab6194d27d945810e97881e861050f13812170f832a701501e715edef71548de53ebeecee4f23bbf1468b77e4e5b293acb6aa7d93495994c43c292c004722cdbfaed3f1662609a370f8c41020a6c7ee8dc993b9201875aec1c1c778eb6033b7fbb96133107a1e7d91a19ab613f2b5f8312fea4b40eb6a89e2ca9d8f5e92b35c0e9765aa9bba5567e95ba2db688fe8ee590f4184244614d3dfce26d79485c736ed8fa5dd068c0d7822e75e9dc06ef7e98a461c44cf735ee16cdafdec154cf075f10afc698cb4347cffb4f98ce045a8a24d4a565085cd685dd7de9b5181e29c0fc963d577607be5689840d1408fc894444917505af68fe6edc6c843e38c7347c3560f94e91b91539e99d36e9075a3bf425b9d511ab03beb1603d6639ec212628cf9195d13d2788680509b3f8fc3a3fde61bfae24748a496c06ebdee026de45f8ac53f311401ab9b00db9178f91397b8ffdb170db6b76e700e16e3b2faee734e0516487b09ee81116a7763b0bc919ea062ab18aa9ba1329af73c4597fd612d79e5cb4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
-				"0000000fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a0983810337156d5d733a91a9d511129654d5a2c04e8f3b2a7995e23d075dd0a63a1673bbb3148560f41a7bcdfa0bb63a780e41916dc3bd8939d0b72b608b36789f1c785e3e74ce53916966dabf193cae2afba31ed7324eb1c69e20ef3cfee4e54593b8065ed26ba1b6bccc20db8ff5e5f3be8e2e5709dab064ef370d79b2973065d3e87e9547cbc99a5f6503e60becdfebe889f822b80af071b5ac4199c627861881ba14b6127aff5bc9254cd0db3d63e09041e64a3d26e34922d4c42bb00d99c25fa4e3a0485b3f4b28a9f287090bf1c25517334a9b56764e898a1c5f627b486cdeef3131fd959ce625de4f1500a4ab9eee693fead0730cff0eaf872f03bfb319ef51a1e00346d4158eefb68edb0270fe603021e60d28ac6967882ccf65d75f0cf17865deed8715c9e9412ed10ea7d551c2de1652410905e564573122912721636b1f048cf33b1dd72aec11503c06726c14397a801b607da4b266e4a135917d9a002dd29654e9603d883753273aac7007df21464fdc1c3bcee0028e31fb8033c23277b19e970ba7070a0dd3d1ccfd32b8ceaf8efd3c5442256279b7dbde34ceb1444887f4e9ae64787464aea9ea00597bb2c8a67e4ebc565db7fd90ac502bf54b34eb464c50988d895b3b530bfbd7d91b20a1b1484791eb62c64b9dd64ac121865368a6db1b264c4be7c650e8bd0ed6f51935c6feee99c666b1eb95b4d35bcd2d9470a9d9c6380c367c073fec2e270d1fe9a9ac73885b32bccfaedb37b76cf2690b3e5f44611748d297cf37d929b33b2828c4a4f360a60eac4ad4414cb97822c5673b08f5faac36dede224007fcb6d23045bcca880b505f10aa1210dccf4330bcc616bc70cfeab4e23eb8f69c55e184067ceb6790b708ca2e6ae0da65c5c4b22d8e20dd4ab8322179b92347fbc1d944ef51e98ee4783e49c133770a28e77f99bb485a1fa9dbf0ad4a76c8f29745e6527d73dfb9f0d31b733e88931b2d30fbb2bcd18ec462d86b1d6ab4912ad4ef8cff7e0cfdd1fda1b9e80603b2f6afc8fa94dc9613d11af205ac0018d2286639cbc2d9a1285e9017accf5997b5ba94df03f11df3353f96441398198d1ba09a9746547c78a57628ba820fa1b6b8b40a310454cd57d4d4505cb23a937fd3d1a8e4705132b6b69ba446dcac02e3d47ee27aeb2c9739e513137db01f843b44fb62c760a5ed949f78ce4ced311eef5d058ea29ec89f4d19adb98c79e8f5de1b84ad422ac26e3fd9fa2ab96dcd9c57807720b0eabe4fba74f262028eeb0cc97ee200cc9587ecba4c6dee3036268561210063cf8975f04dcc229fb7245bd57ffc76bf2d07eedd956ff2c52f22b0f40d3ae7ba6f4525f85120ac7e582ad921f44d3d76dfd7ba98bcebd2c891fac705bc7909688838a31b2d922b37de1c81289562a9c9dc0b470fc4be6a82c7065e0f998755badd4467f6fba1c57cf849d69aa585d9ae0f04b478c9e3d7b19e1f66f85c672d618784b0b0ea288c8947b4c36bb4867afb71e2ab384d542fbb6a88f73c3751c8a804b65fa524268d7349b402d3a3afd5d272ad742723c61a26bf0e50b801240f4babe293e42d5043ed660a079258752e38aaadab4938854aa5a18fa30807b703726c4137587ecb0579b63f7e6a2ea21858cc977c0e8dfd1ddabe69244faae38d184beca54cd691f175ffc6e8b621a4628e104dc8a9629a1169947d17061680f818eda5f7c5430bd87da506e304fefb9d687e2ff764c0885cd8b9385b5633daa7d4a5d50850fba6498261f76dfdf9e4bf1c431999db1d731e68c1b74a11f4fb5fae6d0745fd3f823e621dc553d1c42b0e0ab488bc4e32d21a2018c292df801aa7976ba9f0c55f14910fa9d9abc13375e93fb8b9135ea4c25ccf2de87ee7388b6cebd99950469a815f510a444f388e1ff5ff57984179e93c2c20de13a69e9c676272f5b64d2736a75be057a470d4f9c825525b9a0935b01834ee82383bcb620ac6096445db5297a7be006252325c0752272ab9904d7c7a00f6661e726268528cbfc3fc13180ee32397ba91a04c81912603ba4e98bab97e415cbb2a7660e88d52d468b2f6ed9b98d4223aa15c661d187282e55bbe0b8d589e528ff9df1cce6302f48ba9253d2be75d0ce398318a97717d14a6e085588265dc0cecf320eab11725d3756019a3d8bf054c8e3d4da6e4108ff4f5e548576f79eeb11b53eecacafaddd2a648bc6d7edbc8c251a43d64a5d738570594873ad3342afb00d8cc9470a71bff591cb6420b4cd36c52ad5612bff07966a0ebb1361a9dbfb09d72230dd48aadf0a9132757591abb55f11da7978454aa414e8cd55d895d74d246a0c8ec98ed3954695f7b36b79e65a2c7af3a755e647887955932861d2ad6cdb14f135958e0858e0be9fd563d692a0a0809f69b49198c5de2318e6af59e7901629e9a8d364051cf5960145646ff0d7436bd05a18db69af2ee8b078938af8ac1c7bc1cd0fbd742cc21697c5e23c3511de7e75c0fcddc3b4c0de91209aa252e7c7853759bfb25a2ed7c71b45c972c6722630f0330fcbad42ab8fbaca41fae5fd37cac2bcc0eb947801b6a545828e3c6ee58c8160f3a8ac65e146783bfe7a42d800f48b6b8e4c10ec5cf996a7f1c9cd88aaede2a44607ecaf714ca38976ec0032c5f070f82b101e84784b1a47124f9509f6e61770c68347d71593ce6619802041e1c168a75e6035b8fa41aab6ee46f1ebd72b68e59e3fc2cc512fa60456f33ff35764be41deea78801443edd9486c45affae9ee6c5b479b6f1a543b11dfa3a88c1169e26add505ac6531ae26e7cdab1a97305d74a78018d09789e058b698cf8d3cf27072e8b74134eaf0322fdda88a2e3c9f905103021ec013cfffbfc0afcad6def43c04b1780fc73cb7c11a61b69a539433e961cdc8bf8146288384383979f0059d587b44eb4591e36b5acf05798c91f8177e3b30a82a837d199e63a9e7c403c2070588afdaab2c766b0a39b7e0cf8d7681d346dbd739d37ea35740b84c80984331eb5093fd0b2fe49d18cc0f7268df4925d3a1884ad7b6eee2f7ba0afbc9e5342024b614f4fbbab5561c31203c4f1fdb6487b09ee81116a7763b0bc919ea062ab18aa9ba1329af73c4597fd612d79e5cb4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603" };
-		int height = 4;
-		XMSSParameters params = new XMSSParameters(height, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		for (int i = 0; i < (1 << height); i++) {
-			byte[] signature = xmss.sign(new byte[1024]);
-			assertEquals(signatures[i], Hex.toHexString(signature));
-		}
-		try {
-			xmss.sign(new byte[1024]);
-			fail();
-		} catch (Exception ex) {
-		}
-	}
+        List<XMSSNode> authPath = sig.getAuthPath();
+        for (int i = 0; i < authPath.size(); i++)
+        {
+            assertEquals(expectedAuthPath[i], Hex.toHexString(authPath.get(i).getValue()));
+        }
+    }
 
-	public void testSignSHA256CompleteEvenHeight2() {
-		final String[] signatures = {
-				"0000005b5211d3a3a61178505fb4f4abe1c58d7ca7c17d9ae56ceb93762c262e501c4b096ea46a1cf5a874a2822d932a1404caf54ca8279136f09cb89f33a09ff6045c6b073f2b43cec2f1538a2613d6186abaadd4a636b406e89433428a36f93f02eda785eb33fad4393779aa0830407975dd55bdfad0bb84ff3d804f0df2410491a9fbe51ec2b093fbeea5b577e08cf46e05d4a27f7159bf590d19ef3a9aa2e97e1f3de574f30ac6085cc8fe82c22cd4ed7e79f2a0ff91d4bad8ccc1067f10fe5f1fc6c2ef1ce5080039c82974d5512afc55f33f125b7ef7543570d75d4428f038ee1519a22872a5cdb50f0def83eef3ded03a55285c107dbb4ff3bd9c71f1833989821ed98c35854289583b737f12b08a750f4296d49d90016e852d01265ee6fbcd67485a5a1582b5edeae237e155192e8e0a99ccca6f2e0d5a11be271c350c0568f11efb1f8e23dae3039c62dc7a8fb8d75ba9989067d8805eec35a78c51dba7a46c7d22a18220a1e131a157beb4eabf9fe83fd722fc3c8e24293da0fbe91a44f10ae7344934995da41d6e9389c170789d06f79b686a09455b4f335053fe62946db7bf7a91ee8bf6659876939858941372c1ec2f470e8c880a83e20f9750d6a7d4de06488297fb82e72b4831b1e2a7e02369e695d948dbbc6f1f77e031d3a3ddc9f93a8f36a6b1eef632d9c7136cae6658715373a22f9d56b62bb9a4765422423baf1c6628fd5a1508690d22ef143392eaf2a451df58e67d459185daa4a14a8b8aa288cc070a28007845f6621bf4655c92e6a555ad47cb94b86452d6bb4d30d91dfdabf099046bdbbe7f376b4953a566a8593364dd44cc804f9c7ac38dd1f1962f05a7c728253abf52092106d8e95a07c9981aeb49fcd64f3bdb1188c5c7daf7671cad736853865cffc46f90f75e512931221766a03ca980c4c886e8163927b8ab0acdad4ef16ffbf43bc68d4a868e28375bfd5a75be2afca8d4968482159e823810521a20b721ba6ff11df8bcb8fda6563cecc65331d3b2f83e698d9ffddbce7cd39cf955f22bebe1bde95d4629b52c93c3ed1a241a53315009a5c0ce636b0780c0c8b9ac838e3a2e89d19cfe7f682a17d649a4c7293feb38a42faa2c7c949185cca804a73f123699c4c79d8bf4ec4fe69f7192d2ade210f2bdcf660af503425651b85c22abe5e5612b59622a1dfd32d0d301abdba78b79954925f702f10af7c881f2af2d80a5e127c27c73dd098ea60ca1ea7be24a94a57ae10e809a24e1c807b4482ca2d9029a627bfe0d9dcf6643800475306a3eff0c33c6c6cf80935d3e4023d2fb93f8936e0eeb852cae7c2bbd3c87b9bc9bd94325032a755382306d0239d1d07b79c0e50c9fb4fd8184e9cf7b33f196a6a676654e01721a42bc7b75b5bbfe0561ee699b817256edf20234f4e1e00e147c40e4bea38c037e24ae2e3df7a11fcc08e7e3998aa7ed236a8cbc04086b8fc622488df47ef3c51f02443975f5927e9f867ec6d068059893ddf112b27e4048467225dbdda0997180a3725149fb55db004aff67310ba94c25b3003d2a50b39cb44e1744c336db6c7508cf0ebdddc382bef767c8a4feb2ee8c2c84b3195a8d42bb0b045df1e54c5d82108f87ba40e1bc5abbd0e51af7cbc8f605e1afff90eeb753c104984231eb44e2f308424ef8e991efd0a2dd7d66ac7aa9988996bf9c682b41b3cec98872b94bbdbe7e9eaf16f608851a94d65f711855adafb77746a518514d9536a26095048ec8fda2b051be163b5c35e22dc03285b1affd95a8d9d1b44c67dafd04686e1d7ffd742e1937120a72c5fca42df6c5db00fec5d08004ab2dc3351348f525ebd4acaa3f8ad6cec7659dc25afc2e5c84965856698a4714539f63a6e605bffc77e45d8cae8a5281b181dbe9242aa23c216c9866e25105689f7a6dec6687a11e92986edd714c0e0badc86e33533b809a3848888f12b2a38776f69fdf46abfe170049ce1473b3fb5f6391c19aa71f852b6f80ac50a465015ad2989ded9f636b004324133d16783ab55f3beaae8e52e0c194a37f50a3a423019e10a18354a4860bcba06d6906a45c71062ed7533dc584930bf5ab03ec32340b0846cb3879d539e6bc57dc57aa1f4bdf4ccf4d4ae7c61892556cd9385ab4331f853bfa5aa928b982d4f9418262d32a162e1dfebee44fea85d4af241ad66e21ff831789aafe11602d521449f5795de91a77b0661904848f5514c3639a64002eb45f4da11fe35dcd0d29f8db611817d77d4116a3564616648010e92fa33689ee35dd0f2a19dfe0c54b7f0590d2490c5f848b1cf292803d35a271ba4ab7fe42449f3b0712938d8dc4374f76ed125a7fcf738310862d1e5c988f7e9d9f394bb92d97204bb724ec94161d55e1f8c9f040207113fb873eb0f2d50df86fa3e35dc8da193227b78410612554ea5c547a64b4a1d9ebe0247cae5aec850f9e6a44936d1b0bb183228c7372c3ec09e5472b70d2bb6128ec89c36c21777c91d64355bb9bcd4f4f8cd691a0562e37dce60aab74ce96c4146fea0cc249d22c5a0ae399f31c5f0764fe9478b2286b91cc124eeb5d4d00e071162b6fcfe1e78773f3465771b5de35f32ac951561c9a16dae856dd78b8bb02495eb55932d85ed9bf1038e2241ef44edf467b7e38bd9aa41ee6fb3fdda95ed97fac045ecda99ea2b1f82cf2f78b1ca9a93130a6b325ebcdff155ed9070138449abad864074b3ff250c9354e7fd25483f1eb215d24174f40ad297b36950ad90f4e39b5277fc7ae711f6ffc03e0b04f8b3df5a7f9d9dd612cbf366d1d55ad39a59732fad55dc06d87516ae0403caa64accf02d954b507f971fb03baed4f9080bacec1b6959e10b5c33715704ae6bb38593d668f0619805e737eefa2557a5e1c114ac77d228ea7d6bd8ebaaef4b84438b61e6d548cd4e7bd65c1e948b00ff7b408f05522ae7baadd46cbbf9a8c672783b4348123adaabb4b5f4b4f4d30a4a452bf08b2bae6c2daf6c3d3c77e3dfcd1789adabcfb5ef56a795ef1064bce634af82f9b14d93cf4790d231f830fcbb54809beb863eddcb445e316238e1d18fe1beb096b5aa49115f74e3d28f09802cb237e59f0f72abe9407dd1899187d858a9a17e63ea71e3eb69b5f1cf7b30fb55ad5a6bd0fc3596fecb52c183f216b45be8aaa048bff6f56f7c933d29e99b3f25d18cf23f965d9fbbad25d4593fec13eab3582bb080722a88fde63e5e4df8702bdb68dd1b5551acc4ee207b5b657e6553c31d6be48e69a7255bbedbb3dada702f8a7ed9dfecce203d9f522cd201cd13b854991f37e3ec64fb2d0bd12707299bd8aa4c1f0d7b24489363f343e7e53f773e0e49032e80d2e3c2c7bfaf37319537bd7f02203aa761d9cf96fec320c8a17969994ee3b3da9159317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"00000082299a59307212641af3782122ec7aefaa2665be6734f4864d948458aeb50a4f7acc620c905f075b16770526a0ff4fc657eb76df19756af8e3cdec9e766ff1dbe0f6bd129b3b1ddaf30919f4e4399730f2de322348c69e443e0f7b8995d494186cc3c375d13e836bf1b75b5dd40d4c94359e4a3567984a9c1cf5cd50d8b3e095be22f5f9bce65f5b75aa75996cb5e19715a8852373e342143ff8631464a3a8d4792ce675c6aeeca8aeff13f4efd886f2332464463a88b4953010a0986016dda1544f42393ad991e12822571094e89c5d7e9b9cac61ece754fbda2b765ffa211fcb102d5800f6919fc4b6e208d5f1fc47bb41b2dd0de41a42db8846b978f4bcc49ca3a1db5d4ba8781dc3019d3cb1cb0737ca3ba5a8a7f4d2668239fe8c1e8023e3302f3b09245e8d7892160bb09b2acbe6d4a3ad9f2026d1020e799655fe3a6dfecfffeb8c403e08b8e53037e6f93049c68a11acdc3b268e0dff6ca7fb624fce3f04e7dd1258e7c1bf3db9b73de7751fa2b06be2cd6996da0caaa7cc38589eaa376863f07e5402ebf67cdd46dbdf78a3b7a7b0199b058e48a22f7c761f808e05e3fec93041a26ee2d39c875030a89cec39b34b3ede5d2894cbfdd47966dd57a7c0b237d632c3d68a199b4ddfb0c3a73a9d6907c60f11272cce0abf18d90be62eb410dfa806cb753a9fc54dc9181546cb921268a2321ce4dc358a2e6f6cb7ff214886eae3b8b052e5a527bd4a979eb04d79bb8793c0d6f59417d0ca6e2d9d6ec7a7e8171f7ffc4dd598bc04083c9a71e3c8989c834b1246324c3ebce759072466ca4ba44014d30877fd093320b3b4d48f64d107d7e5ff9d203667b42b909b0fdfcd76ace55fb5236e2b2fe91241a5367688791ec5ef6ac32a66420191c3a448d1a216eef1dd4697f2597308f30a540d65b144420a22e1e75d2e8a3939aad78cb2e868f5c074942344e0597194bc56c118715509e85c0370f526ff2fb6c366745e1db333d3fcc754d1f67e1cb5194c1e636a64891329164fc453fdd3abd1362b4824de266d890dcf8d0af7e05ee6055befff07c68c359dbd0615e408c8e14ecdb51dd15f6ee78a8d45b0c3a3478be08b3656b941cbe355c43eb86abb6b92dd034ecdbc5cce187c88988263fec50be9c20e0ae53e1c47077805de8496d649a906b59bb930bf537756218b5ec6e1b1cea9a2204cad1faf02f07a585ef3270512af36dbe7ec4f7e63a071db8a5e753ecd1bae7cae28796759e749c8ca4df97a8de783a73bf0a4c621acf4accc2258ffb23faccdadf74cd0b532ff594b09609c5f68545e96f0ff1be666a8bd1a767b1fa938038afe31259b45c10586f604d77c7944bf4998a24325cab1e2d531a503401a676f440590ed42c7d98e2e2704af3a7ca6a9dcdecae0485566dd83941992d21c8298df3fcc8090149224250efb3827c102abcc06e66cd91f330d5653c8f50cdea1e56dab1e1b10bc68e062fbc7d94d5c8be08c6303112c4b41d12e2ee9aa705526b2217742c8f07d9c7b82174d47da5b7e3a3587dca8737fd223a035b5327f025d6a147181497eda09dc58ac92b2501ee3a071e4e088e8a9fc4e48e3e672a1583a1e90ff76cbcf81425a91c4b4965b2f4e1540f78b299f0b19ec137b767f66e953712c36db40fd09036d3c5402191ee64c17c86fe08b69b30e47af60da9514b3534b9e7baee97fa3d239c3a35de48034c1b859c423835c2689431b1a38f0d01c98237396258abfc451718ab66c84f031fcc7f720a22a952ffc2d527f750ab8adcfd5345a2979897097db3ae6f15d0240738fb0795aee45ee6a4c07a0a9f9b7b98ab107656977ec8ff18f6658265cb01b684903c3cb2cc0233c0fffb21eb56fc0a1fe6f6ec38514c7ce0651515bb975e3e16aa7b0ddb03b8760b57fc7371a4bb232037e1e94aa413a430c95ef60116f632a937947fbc0904b318ce220b71e9cae8452d9bf36dc6c9a840ab6ee7077daddf9aa38929176d117b0f0ec373321e72448e3c53f347c4bf32b2d98950ee8536568d58da9bceaa7b927b5fa048f5978d117668c2a47db89b1f7fd80476121df5e5d6ae1e41a430400b81edb5617ed4ed1ab594da576eb24557a8bd558993ae75d48821b8864a356e703c6b0b87acb7224fe3e59cbe97d61701a81df0590635e05681250a1458a2c93618c82bd91df2746af6c33e4ea348e27eef4e8af5f85212f16d63dde7b5877929aafe71eff5a5edf476f18eda9bf3dc70b9a47c58b8d1b593f502aa67452482efb84e8bb5486699adc2aa8202dd0255904f934770919f92a4433ec801c529752f7e3128d34324c5e04ac08a4975836ba69481d81b651e33c6993f12777659e5475c9d83e6cd08d381d69dd0d13b1cb7ed552d0f3c5c3dc64ec01f893333050cbd9fd2dc9b297e9234d3761bf3af63d6c4c5c2dfc45ef573f07adfaa1f8d869d7e4940c8dcbcbd1f9b262dec4034a5e4f0ae61964b2c1cda30ea85a1e5cafe5aafb162c2dcd601f599227719fb9c2fc4edf744669a7c39e8d19b908686951d4fc91983ad12451ff9822d5937b324de12e0d29976850b9c1b6e88894d42e1bf53d72122d4e6078e16e9ccdeaeb55bae1489b20cf1895e1e7c2a7c22061744c232630a0a6c22dc136d5859a01acafc2b056829a191c53f9e71db7b2e5899d902842f7b8547a72b3ffbf357052ab55acfc63f8a95bf37facdeb46c53c97fc09831a363a26cae6f4dfb84aaee10e61b4972711725a7eff18519af7bf2fe067c6140b9b99f4fec0d4d085fb080fe882556e1bbce61b9418feaf475570f15887feefd839e0528efe433cd180614ba97c15f063df08ef185fe16f61aba686e78681dcc921d693a08ab1887b6773e9d6d31f6221c4e450e528ba5e04e8fc1ea2002f4293b984ab5e4be7b2936c37006db42f1e78c0e6e2195f284357118b2777bf54e2a75940897bf41e6c6732fc9383e56f5cbc7dff2eabb6009e609dc63c41385c76430b98764826b94cda6dca08fef1e94a0b7c06cd769cd63f8534bde1a509ae7addacc7938d8284d333d5df116dd8f23bd5f17ec3bd2148d1391c07699abea5026726e2f24b2fd88b88399690819835b1b7d957a3ef8961142aa03390ee8c2787a3e4a9c976608e055797abed36321593b21c3bbaabd908ba6498d992717fca20824cf66300a5614f9cb1bcdda41ca1fa0cfb05a9ff9dc27278387c7ff082b4e2f11b616bda9017fe666b50d25dec6fd7b59f1da88db41ab4a10f675f10ffc4b539f24bae8a8c3e17f0506c89897afc1bb9221d9da2644a542b5f9e639a2e29b7248b28c51f52a75d4c8ea635e7b066341f942caf368037e9b13476608c1565c80311c8e31436b541591517b401921598033fad05e9b12b913378f4ba6ab6ca59a42ed7ee99bb3a754a8ba4095c260e8f5e9a6a1705fd92e324044d34d14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"000000b0608636b65bd2e93b0790935fe0110644ca7ec7189eb50ec64bffedf64c8a718f30cf1e2297513da5145a6ae6e0dcfcdd4fc08991e0d9edcca95f0f13dc43220211964daec0c73e29fc3bda0a4253e68330a513cc3f86d3c1907c9d6ec271ac038260b375c936f868a312524a9970cbec77667cb6b43fba7deb8fa8844f5530e644cb7faa36222d6d2c8ef45150d214044c36cd5b98beac3a2c29557702f4f38ee1b63851ec492efb1999beaed988c100d94fa42ee3e119c046fede9d3dc7915507c24cc99e7f271a13c861a7eb6b593826b39e99349233071e2ed1fa92caa6960d119729fb266ff297e17865ca3efaa95e5745debd51043be9b4a1ad96462ff404792724c201e1aa965afe71dabe13dc184f9723d69ee51c5f6951ac37cb5310b1860a698670c40f61f0c22b35452db35760fbe65a928493fb219300da1d9cc0ebaf3632fe449443033c309f397998adefd2088abd5043e80371ec45809e71f5de18a85521120c7846665e97e581e2cf160e36cb5d12c87e64ba3fff00cd4693b0e7610746c98c7048b725bfe352203ae581e4c026c4e3bfe8ec10a20794b9e4dac00da44bbab2af4c47611979892605d1ee4c43bc9165d8c45cbc52e38eb1705306e6e3183a73d82d2be435a9426eca1b4b5bc433d881e991bc8c73a3e4232e48a6deef5fbfba9f0c748526bb457d1a4249913a0a549a64ef1687cabb820ea831720daa68efb11ac41d8259df5789b41d8f5af7edda5bd0f65f8ffba79812e56f6d00ee9d57fa629f0649758865f1ffffdc56725eccdf9dd7a2cb1bc1976a799a471ddf1c6d779fbb9f28d1b80ce4f1e34118e876e6eec6674491f545ff648e5bd3b45bc4343fda88ecb7e508a155eec53ed55cc4c3594616eb68dc5bc0cbec815e6de7b2ac9c3062240143c259e1b851dae99825864d7bdd67d94187ce6630db9c5cfe4a93df53e46e22a50e16429c72a2e8449c424695a2e21b6aff8615514392b0291d36eab45ff07005e9faaee761b28e95bdda8a76faa9e9a6b783b926e595781ea36ab04c0404a78141bb9c9a163f5e54b8758107414710936597e1a7a79547ab41ea3d1d578e11044965c2b63de361acb948ba1792740da8960408a773ba9b110a622344c8f4745d17590b3117ab0c5f22dc8f650f9cfecd3baa7777da37fca90edbc9ae07cd8e103dcc315b68842c4f1d752335deabc20708c6ab09b8b5d7c649da79ede708391bbf38483007d8087e15d5bc7e03df2e5031c0673755cf68f7a473987637f189ef08c38e9c434022dff19fb96b5ef1fa3ed564aa7364cda56bdbdeea82605676efc98a0c28451582d27ab1942b05cbcdeb106a62298d39c8e1a44f9ca3c61bd23bd9c5af9bbb1aac00cf177a40e8bd7f6fc4f80278b48cd8938e40be86d09f4d527e5987a4a3494c0d45a6836e11328bee88382b0175b77c0add806cca6ca7296d3d289dd3145adb352b58b67c690e346a5e626dbd0d5ee953b037d0265ecbf3aa43eb737d857b6d3e29591c926f1647ebaabfa75a8fe5658fb913e9aa6b0388fef3e449967a106ea66f7be97f746ea24b165d12f32e22d11aebc5991da331a6c7ac5de98adee512df6f33ea9ac41d7ee67330a91980bf114c2383f3b9f727d582d0cbdb2d09bb7a89a5f0c7c89b8d17ec35ff8adc9ffd3436881e6cdc601fc7d0a7ebf85f82f1597fba4201eb63b778dadcb4f16c78ef29fd903147e6c1e058f2dd4db304e9cd387c8cbce7d3ca7826ed7bcb59c4ff44373904c71626262ffb293cfd74f787490b21da55bc060013741e281652556209ea464547836e1e0adf46d14b61ffb2d9939cee5c00eba8274e724fd3f785240f3f714132679bf501e182ed1d92d66586733b8c979c58d5619f91fa14b720875738539386b0df4d8fa3496ce4619c78444593fd52b513485f994421b8f9efaca61fd4ba457783d9950d050f64e265395c4393bde0f1365704fff7de9e9aebb600bcc795fe994e244fcc7186fb171dd918143f824282b6d9a3f5414db61200fd25f4fc172eae69e85aa71a8afb4f296510b95c41e27e33c573b705bccac633d162967bd0f24039fd33bbb4d051f99185c0083d0db4225881fdf201bf2a9d4d9a913658b175116d9e3ce57101e5d5f4d4eba99aa4e08a813bab8364edc94905cd79d021a2cfd656960edcf77b1a49f5d7e932e9b8c81c7cfee1d561c78d83e8a20257d3e0c33c9743e08fe9d3dd6c4e265619af8d1a9044b4f41fdb0a257ad7aa8688054486d0a43deafc566c4f74de2d91c56f01e2f58b3027b19f82bfd3f622d7355e933777ea81182d68218bf7e0a74cef167e1f5f2b18d4422ba3a34c724c798bd6d6eb0be5ee2dc0667eff91282d54f4acae482705139cb7349d7acfd8c95fdf6d4ced557d48bdc0a049a155420761ce3f4adab176546a0846258968eb1d6ab67b9fce931e4aa1cb9762c7eb7509a8a1b1cfe5d17b9b3c055acef1f2698ca063207f0a358ad034d1d4d55e38ba0d979c13aa17be6c555a3f959557f242c5ca3e2877013deb20d623ecd688418761d208e7808bc014486535f3192b50928f1ce861157fb8891a8da4c09d13f3c791f5d1a8b20ec25f8500075a6fce54afa972938ba838c4e1212f1b07f8aecac78f0c3b088e003f7087ee64c5ff22f12235ec3b9bd7d539408308e10f0b651fe37ea69ae5f860dbad25a4d4c55193ee59336d21e864ec2b4da0d3816b3328da6d22e9feaf51df9af33a5754a4bb67f65bf795dcc9e6edd7430882634d6b8888106b8f68b1b4308def8871decf9d242b89e66dbd669e3a737ce22259c9f5f197bc33abe2f61ff35bc0938b55c3bc1504366aae415fcde7031bf92abf989726ce72c14f200fd742509e4f344d6776796be55c22dce3d5595f1228843b8ae57cd093693872415fc91d8570870151665e11b85c75c9828a66b8114105653784272710917d044a558bd21d48efacc29b07b8847bed52929115b7c8f7a80690085b296092adc2504c6afb5984d78408f66ee23d58b8405a88c4a413bce09d73ba35467c55431a24acfe601ef3402e475c31f01cc9de7eb8ca5b7ac0912b85770411d8ad498b03b2331fdc10fb928d287cd4af01e28b98194a68b39aa8caec883c6d367bd63300910af32c98ea678c4c5ceb546c2c77e10e96bd0dd03bf5c3d8d38fbc5c8605826470a71137ee4365d8b384d8021a8f433c6060a90f879cc463108eea5d066246d3757a3aab6c4fb2e84598ed7a81f004889771d506ba5e22acf84abd03ac3cfa833f8530c68674ad72d9cba048c0650e313343434417257f951c14019cefcfd2643f6c52b8c8b431a59b0c0349c1565c80311c8e31436b541591517b401921598033fad05e9b12b913378f4ba6ab6ca59a42ed7ee99bb3a754a8ba4095c260e8f5e9a6a1705fd92e324044d34d14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"0000016dfe3b6ac0fbd2514e3449cea6c8ff65c128c1f144e64ba73e4bbae7322bf6f22158c1ed378a1f960ae78ab934ab5c47f25679fcf2a503ac113bf7698e00397adb0f65f04143f012bfb31a7cf50df8f404d7e744256bfaf8cc54f140b187fcb56ea081aaf8336ed6617218f47da2ec97714516f20876edb8795afcab4f1b7af4c75d29f35dade047c746800eb2c01f90cd7fca7a2cc3cedbba6fabc509a230f951b05883cb3afb37641c48f359e0b2361d3ab98c7ec62f1c7b04a5fd773db58c7dcd55b6748379c735ab7443f07594a28348fe3b74fdab37e9d58a4a69c5c324d6e6384a61192cf23c0c564ad988afd2ba14297b441322483c5a07fc07b65d7e1c16daf8f9ba1af043d6ca706c0f49cc2c78c551613e2f73aa84a085da3a0cb5db387e39f552063d31ea40ca36e76d9f70cccb4645f24a12409338ecfd2f4f2757d9327355b49286cbcce7cd8445fe059a0b4093f08c1a326f6ea17d3821535464de238a40f0f867626443652fb1155cbd49ef0d0baaa284a54853035c6181e16bbde611d048d7452255cb45d1c91c5618566a972eec3336d76cf9b1bc85c057f756c55ab442159cb5fab99fd9e53d69611a9785506b57a5eca14096f74a8204a880eb9bbb9223cb13a563bc3c55c4b9e9af481cab8364d36cb19e8727e5fc29784f122b399918efa6253284d91c468f2800417cbf25fc1a5e8986071fb980119b70e77d8c44df32c147ded0790fc74c829b3a98e5b1d7f33e56f1a915a0ccf855d281ecd867a1ec96d59ed96906369548525d97cdb060233a89d000117a18057ea3826d73c0462dec997598dbf41bfa6ce4cc4ab42600a1c4fbcaae8eaa535955c456917e1a9949783a48790e8387a12e813047c4df33647d5184b33ad85c1174e338db84a490776101eb8def21cd090020c7dc7b4b8fd311cdc5c6bb4790353b21cc12e5264b81e626820562f8f40952a92cd31c3c34b884fbf3fb3a82eac566de40c1593ed67519b69ccb9dd3c12793039c91b1fc273d3b6e5173a54a69c088f52814c1cbc9db34425788441825c29246da976592186df899ac681a7572c4d5b43ca86a70df98e0206c3099c1649e7af116460dcd7a23490252f93519f02373400436ca5d84137ddb9b79ab2d9ce2d68cd9fdfc9f9ded8d224236a992635e448e611987ee061368782639444b852f5579693aab334e00d4bea4aa0ab8410827a3f2c80819e6fd8e19016aefde55ea96d12d97e1432134c68343e6c6e241bba990a07182c8c898c7f58c4486b6b1f72f0205059c6f7dfb640d770598ca6c5128d0adc833aa5458c46d8417dfd27779f9bada125bc8b52a1f22b6540bc53215234aa9a5aac15a4f9ce82641b1f12461b28cba637524c27233a359f01816cc1f9ff1d9dd2112b291fa2b572705c73efce4fda27f90cb5ff8d847f720d2f1a5eff800331ba166a791113ac90cfad41fb9fe89bbc0c0e2a95a9438bf5751aedbc654e88666a2179aca638e5ab10f53cb195dab9e37720958413bf59909f91c98ccf87a36f3e42618c86e3e2b5cf270c211d5bcbb45683bd9676df3edc7ba6905b06d942d3398f9765d168544155937be426d9d4618f50f6869e808582abd14d3bf306a48b74da1104f53bff753f35fa061c66bebd7ff906697567f11b9065d93a8c03175ad8485d08c038f944b33f9543b5c3c57fe06f69ec1302fa5bcd475007c78f7e8f683db89b73a09413c0d15c96130aa02082916a8ec80930a378cc4eed87e184a7805e2385d09eaa9dcc6024e832040b264fadfdd817682e3767475add5e0c08bf6083b4046ca56f268ca4366c1e786b4735a288b8ad32f45b33beb2a9910b535819a052e6a80864ba177aaf0818a2af851baf6829096fca10d6011027e8d142375cc1922068414e58e8418d9855ba3b15ef9a76a97918f00a25ad0a69255af0e46be6572b5acab9474820bb7bd43a66401a14b63753e6c7881d87a967b816fdc814d6b4030c7673776c5c406b9baf2d42b0ee9976d6522613f703dcd09206f875a3c25709a552d11d6713824d4011c75a6d128cacfb388c2282bb7a086bab96b2111a882f12740d415ea3a45a3b8add4cace6852d085ab5f26c54f681bd23d9a17fc46a58ab95f9b41389baae09be46b4ef5b86ba716b6d1bf2e72bea129b6408e5453e5d355e46bea0c82631d25dde6ebd7b18b11083b8551afa8df60701af75c76a435f03c7fe191bc7d643e01dd4835e6eb7951896910f5ae310d1b04401e76afeaf55d0a9fbd4a4ff66fda49928a82cbbee04f617d00ba783bca6cb6832cfc73eab2cf5fd66c604aeaba0c65d193beb57af7674d31cefec824035b97ee24d9916f2a88ffaee98bda22ff5dd00035328b98db66ce3f9bfa296558a9e77949e67589a7b4377a91195f10a07086d7c1325e9c4f058350f6fce1cfacb73e53051a563d338e0ca9f8d3e66ea72d8a2f3a7f939bcfe3ae71a2a3c0579d05fb12d6bda6aab3ba13bf0d35833caea068a5a1c4de4cb652e23a61189a3dff10698ae89942f5a6624bb068f697996c6f1ee903ffcba03971d5d49b145eda824aac6a2027905d9bbf5912542db90b02af512a4a65df73ecb2a61bf8a129e12e14d802b063bd9f8123890604bf257c47526c83dc054662d347b83a68ce377f202e0300f222f9e1bbac42c37d1f64c6783e5263099965088948fb13d272c67062a93e1b1b6475eb2a633335c365f2df3beb89f5daedd95433a0d6472a33099006f36704f60df2f62bb74b3ac5fba744e2ab5c09ba59068ecc6facd0b34329ed4f5b620fc01e237d175eeb455243c523e0221fbb49fa5fae5bfe567a5e9f05b26239da30cf456aee0aa359b2660a71d8d4e14979c202af96ef4385d8ea3a22d1f0ccbab1d036d3e2f91fde24c0675bbd5022f3d9376a38f9f07beb62765635bd27e3356efe40d211deb5340b329c3f5a3e163d6456effc2fb68fbdc43bee3bc600912a8c6e5f585d7ebbea1b8ed43ad93bfa74559461d09b402dd0bc5360ed11275e1859e19b3dfb95eea79fd4377a53d1dd746d11a42ad3c98955a64fe8304f242ada82aaedcfad1906c6334145888312ff09d86da3ed40ffb719f4c40ca4f10c04629697906aec52ddbb4f629b4ce36e5f5a2ef5dc768a9191fb4a8ae3f1daa3e9008adf17e59c1b8e30299a11c0d764b74725ea60890a113155fa3938f9a757807402213bdaeef69aa67940f52e7f177e739eecbf49ba6f62faac6c560fcb85adc3ebde724f109223ea68387bd213727e1228962a5154cccb248c76a1e09a17fc1d828de1b2d5007321ae418c060d92537535afbdcb25921af73b487c32fdffb2be04518b8ddc7e34f6924464729d08d1e5052d75ab9717a9376262e6840351863523bff546f02a4e761f52a33ecbab0da332c4b250708f3cb43e29284d3cb1e45a1e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"00000189488bda2da8c08b95768a0349a05a6e374630b142aaae7f5be79b7e5b8f43d7b39db46560a8a1b29001f61a5f819613a71b97ec48c2c8d9b1ac893719e7ca967f1d8397571339facc1514d940b61547796e943f02ff22bdde4e209a3ccffe03a1eac9bffdaafcfdc126ff02df4f6cc9d74c062d0dfc7435f40b53afb4708a9f7f766618a77077c8c122a7b7dabea6ddfc8841f8ab312289d1ef163a66435522033643279179b72505a162d55903116d20a221d85d403aa1fb26338b05bcc10c74c6eeda9dda790f14e89855d0c00c15b3be886c92bba3618c325ab964a855abd962feb7739d47fd4c88694b7f5fb12c4ce7a8399c99d8a1b503a70380ff634add1e21dd0cb5653cae5d200b8520577db845186257705e95799f55a269e5bf4e9daf7653ee36980b056f1688a63cea18d27816ad5300af012efe22a1a992584fc4426cbf4fe99f06007d0402ff5a89dcc08fbe8740e62d21227aa96df01b25bb143e65d106ff136d52a38ac4a66f1962719a3938b36ff349d7bc417a892d452c450b5b3a3d7eb4fbc6380060ea2ae323e12b8c7c22bcdb1e48823ef92c919a0b477bdfb8b549318ee03f630738d1fedfc1c0aefe0d40958414f975dc679e5cc79cf7c9643aef94308950c864b79098f77bef7c6a8b3ff2a4b7754edd4034b522056203e4199a155d1bd0f5d22e8dce513451b40b854a2fa8d401e519b0c10c2b71053ca2d4afb71de065343ebac83aedc46b6f0d9c88f8b600df0c77a8c14a6147f6de550530a0cb3a7b1f3046ee2105b59689bf80c9230e4dceeee7029926245f735c20f13ffa8d6ed0828369839b63316c8a29a171f204cb1d905ca0e9cb7149142fffc534de366b96c238a76a85ea83b06fccda2310b87702832727159d024204e5058230abe558c76625f8d5280fd5069214061a0c1407ca8b3b6acc90ce9c1905397b38cfffd47ebc8ec7cf5b8aeb78f25e1409ec34841f3d47fefe7b23e6e8a739fe3e8e59f2285167a7d98539558813eb542f400dada96efb676e398820dae60c3952660217939a614183777510cd855bb9a6783dbdc259645338c5e691f13868a222ac674a3cc18b9b47dbaa667fc46db5e359107d63a8cd87d4a96dd377af8d869d90218a15d934e18af814b1795d06d084d0a0b47d9ff30b681fa8afa770d60a63127d7dce42c77849dbafe96f0b7964b15300d8cdc8f1a91a286bfa1c735a743d73da17a01e7c2e7d39a2802fd40f98cb8a2a54ea950dd364a32fa300a5c6307915d61730c0046ddb696278854fda1e42f44d6fd982f812fa120548d723edb768fa48e34ab7485df3575a8de13f4984514e2921a35d43615419eb2d7a72418de5857e9cd0dc92285d659427073b96b021e2dfb53858a6b920176c70338cbbd63b0c864b3daf7e339a87083a72858b3c23b17b8ad5a446c93960d92bc9fcc678ab19be30d7b539ca9e45f8d33608338d53e5244428e3487bc2e3d9079b7123972fde4add7338342e7046874d52311d3bc5695d07b0dd8caa667dee6f165f704ae1765009af49315bffef3fe3e0cfc0b5de84586f3959de739cd8a9a00bd7689133fb03daa07bccc69c64796b398392a1399caeae318f0ae538b4296787f5adfc70f29829aa04673189bcf4e06ac52935220ba55de3c91a28eeba8e87628a8cb79faf82d8bb103317d89e9bcb74266121539ed2be97b2c1d8b95a99cba47bcd3f31a28ca04b2c7007b23a819838884483ffa6ba6d25df1497f957b9650bc53e9599d5b7ddf1757ea3d1fcf6cfc15e46fb8234b098596877ef70e6743dbbadd753310c94648ad3abf1d6bf91871e5fb2ef7e8a0a99c0086ee5e7099bc89b008a1799851fc6225fddb8ec0ff4d8cd69f6532fa776a410a1f1ac6aeef561a80b4900086aeb763f71de59ab508d33b48ff63908736c0836bf0909bf9727b2bc3d39dd85b9fa00ab83c7858f3cddb87bb8ec062efaf168639c81b8f7ad88bc85e276d70472c009b4d3a42df77da085467fd13b220974d3ca9804ee8456b439bbe134973c212b56fedab37f1bfaeac9c6152cb49de2e6467318084f81c28cec904c531730d86fbbb1bf8a848b1575fee7367f12b9e46b9639c1db23e2cc0505c6766ac18ca2bf7a0fa09eb4a58929e04b70ccfa6cf63042d88fbae3ce259cc00e9b9893de0b5f40b0da0abc49bd1d5ad455cea276857f8ed1a9c88b9a4ff26762e67504ae169e149a8701268fee7afe30b00917b1797f1fc6b1244111955e7db27b584aa2a77525909711a6be80fedf29fbe6b2426217db854591924d645e524dc85676263f37cf761936ba9e4de9a468babb0726c80b8615e039a6f4df19377209f6f585b51fbbba017510ee9012d8eebf6970bc702ddb048d7b5d2bc0c294d0f3bb6dc6cf1086048841b0c03b5f352b27370cf6e3eae5713f4ab0663177ecd9e42fffeb662ba9fdecf10256f1d2d552a37843dc1e0f200bc71256e7cb4f1f36944b5c55082b4f1f040e8c9d5917d01b6e1e1609486ba6a1255a343020e31742b20273726648a46c634a78f62ffc88660409e43e77855de390b9eef1964a52bcc38ffe014a55d4664ff5e04a2cc2ad0c7fe48ac6846711a3bc015442adce5b819d12be2ce8a5de7cd7c0379b86b4fa5346ffbaa477a9d68170b44edb447c7ba0bbb9a799a97f2750eb3e6f2c0f05064e85239f9e5b9e5437579f2607d029e57d7f84d7b7670c33cb87ff92b041666089c7edb3909cd91e8289365e0a8a68ed57cd4c9890f1444f81303ac657b504a5a1abf049244fe06c586c3cec9016567811e9756f815d466cc375e70ed3ae80a9fa0a0179a1c5f77e58a69ce2806ff935cba360ca480b14a2d95cd1e9653265617ffb2629c5dbe6d90ef493a4d68fbda9b8fd84d04de71486c248acacfa2f698c239d4966fa3f073cc5d4135a0007637963b5efb0936100767585d3dc44e703c34bc370aeb90cc9b844db7e7590d2db191a7694178d06b79272f71ce42841f79242ef843196001639e095da7e57de75f2aa7ca05942ed371244db7bdefe455f2623981fc76c9aca7acfcdf23edb09778f9b71ae724efeec438c94e307bed9c1e6f9d275b121515e851b155506ddc853bd3bbb1692ca7ee89e14f3dfca44cca15cba1f50cc42d5545fcd947be7d76c15e969411219a477216e2f74ced2655ad95b9b8b5137ddc4aeb62b3dae5e4ba4b1f9e2162b64d517dceeb780108ec8ef04ea0b948660bb035f6ee1bdf89d247759ade8cdf1571aca25881bf922b0079b6487b7ac02714a8cea20c3d67a9993847b6b2017c695ca77ea73f4ec87298356506064b7969b46b7ec70baa9473df1f21298d5d04b1f1841e43b16c7cd8ed3acac4217451b2e0051cf310fd51899627ad041f2ef8007e0c4430d6bb8c968f25238aa2669e8fd09689007b97f258f3f4e4af01e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"000001f9471714edaf6ba829296c75394d1b4975480f47144472cfc36a08b3ef435033d62d7528ca09b07f99f990124e3c53fe8d575c3ee2c100a8005adf5165f33afe41a8e975c16d6c71cf3b117e539daf2d1a48e6c5bdae3c8ac70cc06d158e2dc0e872005aafec694ac4399fa16a83993b5cb6a21e88f2cc3f7bc426600a0d50d73cb4ce024649728a36b4ca6c9e11d59bf7ba4288a204c621974188e5620e9592f68f21eb4dc56e0af5ee78863607f004d442a87c45699e0562aa41e82a0bec06712f630260c74c01a3a8a3f6d29cdf8b2a08edd72ef7cbb46d339b52d2c2c0ad509d7c01bf639636f9cda1453186905d68cd94975c42203e2a0a8e24c2ac17a1f57b9a7de3cc0b7da5b81d7b6bac2be5f1b9cdcde64d78acefd7e49d17d68fdd4bfc4eb6bac15b0ad509e13d03cb66b852aa63a2529348d6d604f3628799a21b288ccc0470aef69043e476f1d821634949dfb1497e4b7efdb60ff6a4ba57c0bca3a3f607403b30270d624ba6df629e1013fb8f82d0e49e975aa5793407df465ae1cccfe9c2ce9f9657845071feddc7f6f0e592e9d59d7ef591b5fa9f96c58d3cfbd2975a4c19b35d19a5fef201ccbbb1eef15f08c26386aee0a1452329570f22064697fe56de3884f1b3f22a1aa39da0896fa6a046fa549bcaaf593109efede2dd521deea1de8fe6501c995d9e2e84443be1958d4383a5348bcae5809e7c44416e042d1ae1b7d1d3e1ccf99f733df6d218a0582898f3dcf1526c1a1d8c4e6b59663e3d73ec30bde655eac9751751596fb6f1dbfc85a2849a1e404f441f172b101daeab15b8ed06d0840ffd0f5704648be6b02e3cd6061ea081561300a1481f709992dbd5e8e617a9f66578abb2914ee8d7f796180f4d0758a8c3b216f8c5909c3e92469b7b0431d21e213370ba901a9e9916399be0659e6bdd841d050aabddcb0948fa22bdeb5318919e31ad2c39e5a29ec65f39294b0d08c9d88e71ed438a23b84b9fcf5d3d35788f3f50684d6d70b63decd3a87179b4690374f3e7d16db8e971e838c74f24c8d4d218a3dc4a437d03f3a543cea3f230cfd333b0d6855568dd0c361eeef818b783b6ccacfe4154246c99aba22c94787c028c73a6e35a84a0383c3ad02e278363d9d6fc1649c74e376be25f726862a1f5894fce3f7c496580c09dd92b10ab674cee9174f2ba6ee532a37ab95047d5f1bdb003d887478de60a4dd10de67fc0e644e367dccd475af7569b6fb78d7816565e1615a537cab3dda5258157af66bd4fbaeb198a4ee450f9863392a7bad50b7f4d9d9c3c94b8ba4e0c437ac3b73672e62e8ab2f0517629d7d95bc9e28a765c3d54b43081fc3e6eba895081b532de4310b83043dabf9c1d1eb1db6695e136f32f8c59f9dfd713ce5bc82e8195b5ad8b57ff18866beb470396fa87a7394455e5e98313cf370be6721b77ed679679445cc97620d6d497d6505f6dc1876d7e7e4519f793fcbfb2e65163220013a483041b84854c64ae9abc53fcf45742202bd3190cdcf868502a79f82513d89dc8a9046af0408291db828a77328f88ebee7d7fc042eb0450d369571644ca1a893d5486e2e63ae3f25d5c684b276fe2237744e12a93d63ada8960a4e96f18f9fc45110354f8286f9366791249c56cd4d2532d0e9d1968a6fdab6108b7180d7931fc930c6f2c5c58c3950145545894b64525f5a6b201176bbb5a69b7f1d99afd8ba5de64f1556b2c000388cd1b88c55c0cc3270f36d7fb2cafbe25dc66ff72835865feb56e8734b3eceb0d882fc53293c4b81154b1aff10ed287b5f47435e908b01f2af29c2d70e4945a5e38131577c1edfd417e15837834253b4d6758625f0a37087fbb4fb07f19fbbccbce534976fe8ba870159da334e2b289cede259b35110cad468bf393c5a7af387fbbf8efdec65e7755bb178eaa4fa9932e4ecf28a018c908e51d447bda1a88e9ec3b73f9d97c1522d3edc681b1b2e4c0ac55918972f088fa46f687f911064c8baa0fb92b690bd0f54705723c2997b1a41096d92ffe3e5925176d79b2870953942fe631e7358028893632b28bb6373a56c435451f29b5cdb08c83260156284ff3cc5c3beb01ea79d59462c070564a8c72fbf0e8d9c46d8c4fe54058a65cf0432f08495f65bb6c14ef5e927aa11385fb59d5713280c2df49b9bbd47c4c5a1a774a543d09dc59b4aa7ad592e34a2feaeef2cb221a71b4532ef1ceb072e1a5451e98ba52e18716912babc06cff8b3076984ccd72328d68438cba61c18f45ab74d90784dfc1b796ebb29dfb55d1ec7c30bfb2502ede2298b35ce1c7b3b58cd8d13dd306188ca353bc8d5077137948f805c91963aea36f541c63ba35d139aecb49fc65a839501c37432e147731bf42bf5226cf0181f1f8de771f2d6fb0d6fc5e367aa7e697111aff2c853198b458f258d9ed76649110d1f68f45eb6632b3960228f1d70a410b98c91d5b749a7ad528f9547e017646fa9a3292b1d7d009d30d2d0f10fabd4c4be92eaff3a494b243a7db2e8300e46acce816094cc74a99cc1e061260f10f781fc7f5fca8ece34cfcffb411a3fae20e29c3a710afdc30c9849873d7163368b241370d081c39522837e00315e1be6a9c44e776927759f28aed74551a4bd7860a106ffcaac56d8cccab104de169a44e6d3efe6f0d3f0e4779b9b3a548c0e305fbec8d58814c2a2723131dcafd34f8372670313946b62a3baf1303bf3749135cbf1398f5448eb3b5f1a50070841b61851528e960ac6b71a866dbed01613272490cc31db8c9510c828de8e9e2e731958daab1a2cd2a2e9accebd30fdc08c22a4bffa60ce7baf05978b615be01c369e73c45672bc72993f57cec918b0f055188b7652b74be2e1a68401c2def306c7a3ce15a04884dd789a5b6e43cd5133d62280d46e557d7eddaa0d32ad72891d7fa59ee9b9082bf4e052056b6934cc6f771aed4fb0e906ec0dfed6936b4bfa2b01393b2ecb93bd07ce6f1a187d60d36b24f54a2bd353c1308a2b705cbda7eff5102f238ad5deed3813e7d1a13efff09c9c7f23d87befa946915a53866cbbaf457e52a27183260fad640ce75c85d06793fcd6c7d2a9a0eaa83e56cf5a9bbb9c80764f15223751e352351bf6192c648af260a9a80c24e4a329106c0340e72075b5e4845a51e3b1df891d85ae14b80e7f87160bf5bfcfa92dbacb0a358552f7cf6fad82806fe0d699c4d643ebb7e99a79e0b2facbc69d8ad866e7f2432501c70e18d7ccaaa983c16730875a6f9f7eb927f557df00e832d7ff8a5e9d95634fd9fe75de430da29eae6d08c2a7a96f3102e3825e68154d52a4164f84a8baa39909699c996ac354c1edd4e31c398ec283713f0129690641330519c61ba6ec17ce29aca5b8667e1c8eb4100acabf2ef8007e0c4430d6bb8c968f25238aa2669e8fd09689007b97f258f3f4e4af01e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
-				"000002a10219b7e1173dffc0bb0a2706246e95d218846b5fca2d4aa52288409697f43c473817b6b2fe2c1ff766512730dd558ad3844aa5d52345da6348228acec9206d88ff1137ca95d63369ca51d04c41e3b611ac3d83df8c2da2e263e498c8916231c1132408f030c1bb326b372a3e9b30ab00c246cc16d3867b9f3dfcd90b22831fb505e0a839a84395f2747458cb445cd865cf41f74c74960d169336cb4cc3e91ccc68238609f4fda677bd11f0796edf4738a00bba32fb4cd5c8fb7b5694949f0dd219aaa2ff6e315b75e487f4b8ca4a0db48bcf9dbeef53a7904fbe8df0f59ad618bdd175578fcc29cfdcd8b45c0d9e492d72b89a94a60100f70d17b91b3209b074e48b6fc8299ffe6801fa8bcd68e1e9ca2c39fa511eb41e494601b70be2aed5748a9cfe67f048cf24cc6f545717f03b3909fd76e35cedf344ce1fd1cf0b698d76438e670df5a9b7bb30f6fa5b0971b9267617e1aa450f63e006c555a56ee2b964d6be465fc2e333e8647f18af31a3632654194bd9a8b92b4b4c4480e2539b60d611feaf99fcf24ffaaf328baea05fbfffe395a21a47dc6453b08ccb00aac1728af898f18822f28c18f502feb3c1c1f0f4ca7605b32ccad8e4ad0ebfc2e97b7b183671cdffb5b37dd94a39a62d9c4ea7eacd5e86569465712876a55fde7349403ebbabef0e768037d22e652c243785ce15aca6889972ef18a05c741c35a125e4499f6d4cdba492a10dc6d98e25bf77ebd86f762d7b1c1cab34c92ee239504268b0fe8d2f1ec4788816932441b5d13abcae0a2619771d42d79761415775baef162e7f5a7d8ef3f34ef298250b9738307e9adba17afca264c0502cbf73f2f5fad115cfbb46578a22517293614b66765792434cab5bad52578bd500a55bf9f81a1a6a75ac22cd7b1d3c5dcec50e50353b0dc5d3a55b1e4ade1146460afe41f000d3eac8af6a75873b6ad174acc50caf8a2aaa9f4ccfc3461fac4b3810c5a4ea7e4aa14da9d0eca36ed33630a6082dc2ebf2665a1d7f2decb7a6f623d9735555a748aa077005e0d19d174bb071ad511990e56ec704eccd5cfef38ed0b059deaeb1eed0ec0e0ef2ce26ffd7777110630662d1fb7b89a70a2bdc5b6b4efca37512d7570a9ea6bf157ada2914f682e458652693c48fa3058e5731124746af96e7716059962da68e37c8ccfa8e5f79428d520c99356255b037eb7f4d00ca1b591f947a0cc288792441ee35a367d865b2c352bedbed1a040fb97533925fbf38a47a2f3ccbef00788c7e53af1580a94240c8dee54f8687725c0000e84faaa0bd7330404533771038dc99bb2ca663f84b70e46a9f773dd42ac9f5e8e22de47b5d231eb784f7b13a9e97737e5f76aed92a5c0a6d497ca3b7d9040a3c30fedee0fe9c45e2396541f7763a78716b0e3b9d13bab0fefea7e790fae52bef6fba83cbf4fe1631c7adc77381538b43ea1e944b6d333c927909ad424fcd951cfe5824f1191b74efb38e1454a31d4b5ff04f87030220ce716392e515784cb7609287704ec08eaa2c8a008a5de8e6bb379190308dc5571b249f3f6763a00d6f121ca3381ede5e30ddc4d70f8c07cd00ded09def65834cb9516993b950ce989dd9bfa48cf00ecfea49f0486de49b548e574ae81b3889a58cf0834f5173c81b510d6a3f0d75166e8b500973531c38fad0242fe7e4fa915bc2d6b3de83c6558870944888b63b5486c4dec79eed70229c0fab41fd360fd3092bb7f8babe53e01a9ea990823c698cb8af114713c9c797b88a04ebf1b8245805dbb763602a59660b3435787a49e505052ad88cfdd0d1c5c9f4d08a7caa54588daca668b3e310a9ddbcf670a0890ca8a57070165852ca7593ac0d15be8b5e3edf31a8f950567074dba2e24d2bfa91f74922d0106b4753f6e004c751a0d9ec9fab6bf494eafcf2c92d53c7e3cd4e2188bdfd17b1e6f16c4afb8e9f5af8875d86b33b49a633619e4286136fd3f85ef1c2162898c78790ba5b12c233109ee810db5efea4eab6d0cf4b824f45c8c611523d88f5b1b672f7515f45df527e26b6407326a9d7b3309101a7f774fc0fe0f3c1a9531ebe749ff589945410bdc73fc2515ff0454588b7afa505bd78cc1c13a2efffb6d27ed13e8118bd4409d216f1a0608ccf7f7f232054fbd1ce07d3ebfb5be8564bce04a014e5a2575197ce4caf3ec460d887b0656085f86d112c2f601780c37bf1e5429be0a88b6cf1e4bc1d4260d9aa41a42de72312dd4773dfd6ab8675b0ea58977bde5484602c4463c2ba4e02203ec3a5986237076e017473cd71a7434b0999cb4ec72d554140ecacbf1c2dda1e5a4c8397a168d34284672209836c2dedd828455a526380ab2b61ffa78e7a0ce02c054e71818a73c5a8725527ebdd072d92fd65a4c84c2db9027c97d24c0ebb15e98e516fa6d5fa088af5e3561a4447e8561649e6faa059ec00b79121ca1de364924de4c8abdecbb5024a679bdd4581087529525d28b74ec7880c8c207fc940e6898d7b21562361bda0fce8a0917eb0c8bd4dd98864a8dc0c8c0f1de8f4a972719458726c3885676fc798a0134a218dfd6e08b405131a7388c694015826e1ad25e2620f4c22d211596e76f5561396cc91f2d0f134c5c6d5067649b4053603d9dea26babe6ec9dabc4e408f8fd6d2d4fbb9a569bdb2beeddf37e43ba287a6c8fa11130f5e6cc97defd7d202d2d6e44ccb0c2c3b00b8c285466ed15abe51516c0e1b3f9f156a88d272bcf61a48f7aef9abb1aadab1e4becf9cc48e0f4eacf2a9b08fb7d4beba7816f1dfce96809d3d084bd24d944e0b9166d5c95bba98e3e398aae0fff7047b3cb13832add0b6e3cc4025ea817122fc367fa961f4d47c117dfa8157e426123b07d7ab4714bc9264e57db3fb56be1585487bca25f24fad3ab84007b6b7cae5656045be786a2e6a1f35cb39f41499cd4c7d3c36fa09499eb9e37d3831f4058abfa3afc68348431c155e9aad69c58b4984d0c2aa6001896cf579529845fe58f2f473d865c1c6d58bebbc98a9a4c7d40fb9c600c85ae552556fa249ac1fbec93800c56920bd5116f604e8edfd2db8b4782488c401514890b5b088576bce8eb5042facec9cb6d1ae4a53fff7545e7b3f60382a29a13d61ee6e7da164d11c9b473d1dcb830437e9c5542958723e4548991670fc09af20277526cda0010c47889e9746759826b6caa59348d813bb6bce2d04e97f32f96821e41343b87219abbab4b60413e205a0b075a550c14a7de4f4d8442d0e8d643f863e394f43b481bfc7d1c257f00835b689002f87e25405a638f546c422bc411d1a96ab1df0027f28444d6336308931432cf7b720b394bb6e95143e1b287013e66596abef8126e253e3c82f9765bab31fe1de5590003939fedfd2830b367438e5393ccec4ed617a889ba511891c5110f209f416d35e545cb976c281c4040eccad072ed0707f298269d325ac79cd7c10f1d5cb945c36bb32374cc0d43e0112e6ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
-				"0000038bd3eac53d4b89d846bec2a1207f52d572c1e16d53962b55337903826035a8b07d0a962cb60cd7e095ac127e689bfd5bf2abb7b07a07a059631c529043a97562ab2d60e5623baaa9901b78fa38a61848af03bc0914f56c8ce935e60173e1b0f2f7b7c036b3e7ad29fa159bc3650295634342275977ad16e2ad0e1687e176afadd0e88b0927026a111e9d88ebf416b98ec5a28acb4870b38bdcabd8a9f1ac6c47cf10ec9e100fb5d488d24357bb1af2d1540057a437e0c7914df5f40659667340a88ece0e56a28382859874238d522bfea708b4492fe6a477e557049548c6d9eafbca8fedc598d0ea74dfc589749175066fedf49a1dfd55305febb49f59827034b4c22e918281d7164d4ca513e652459d8ee6469b9daee5b9abdc0cd2ba3b55d6f4f5b144c449cd36abf5510b352ea8f441cb4a47a6a87b0f007e496f3ffe74b2f7284de4a4f11f897bb0ca560dfc32f16ef7842e1a00868dbffd2b9b55a6f7ec0d46c7cb2a313f5fefff5d46c48a3eba0228b5fcf106faf1b7ffa4bd08c143205c1d22bd7b1570eeaa9853b13dd204ffd70787e6b366d5c2a3acec691fb43f367f5b49e9d728853029549fb5bb91787ce69e1d63f34de4b4a20ed250e0402177ff72f86e3014fc7b273740b49101e39142ea95a71b9e874a558b47ef6fd563c12485d809dbafa9392fcff2fdac9206d47cb379caf127e8d2d182b42c08637315d6d717f80d12f8739416e4dc75c1a5d670375cab29051d93dbecc6ab963dc41d31fe70c3491146c51d51240872d167c0a8352694c4f7cd4ee518f34c6721d3d02112162880173be028e7b88fb52e2329548aeb2415dc823342935b12e572e8cb10023298cf3cbb7420d1e5acd6c8546bd18ccc123391d46b267f6675609cf339bb2ffe806db156117aeb9157b39157038f6cb3fecdeb6e0c3974f9385148d41916a576215914e731a623d1cdab46fbb24bcdd839d2ded7d8bf9e01393870d3b8dfc3982ed7cf62ff1e4a4cd583a7f833a0224bb42a66822306122284bcae9f7b3399f7615eb8979bc3046f2ef5639bf27a2b9efb9e7b072487fd30793b65c334ff07ad9f9813bc8d8e7f33200e262677cf79081b9d09a9ba78883d45f128a215ab7b1b68c6f416a40a92146aab2a9104870c7191a105d60540b465caf080d65c910a95900e098f75157ff767e03c8be577bfbd4c3c9d5328aa1ddc8c009fbe2d111efdf7b11785c13f8958d63b45e6f74398a62e2d676340f75e020b1a66ed863488246c1a28d8877623f5485dc9728650c9dbcce430c0505a45b6b77523dc737e24339c42a1b2575e80c2ea0f7705fed08cdb21316a4475560458557572952b1c3d6fcfcb29e25d2b1db95f8c27c4226a2d4399526464c782ea15bb2c041278c438eb19b2c0a1f8c8583e5fca2c10e38c4abc6046cef2e46bb76d908101f9dd9536196b9c11a676c0ac679871fedd60a42fda2f2df55a4ef04c911e16740f4c41c3599f656f1184a5fb1af694d64a9f89a1203f7e0588a46a999a83646678395b0b9a753555d1793358e668ebfa4df12fd6aad633163e881936982197b9124d96efc0715693bfd082c8e251999fde10173e15c90d11ee01d3a2ddc18528fcb8275d4521c1e7366044dcb7991badc306515ae3cb5015c6546b3eca47704af8b4cb7aaadc67491b4ac83bfe51ac9bc45aa4f536833e07736862ac17f03b4dde13e1b880842fca6688aa34e65692c5f188f013ce92fed50414630c295e2c795441968e069c7ef1a8db2108aa549dbd6026eaa167f1cb80131a04b126de57ec8f129a9211bc6d490a04c8d2c93c6c3cbe01dbf735a9547ca97744523106330265b42d3a01cda4f00f0c7858a1a53de8e5cf5ca73bafdff17af82e83297fa9cb9e4a9f5b7924eeea188240cb7c7e6e6fdfe7e6f16221fc12ac6ee82c71218df5c4812d0ac7ae7057be06927bec1fbf6bd25dddbd35c7dcc697312958009e303342a27ab1c1dcc0556cab52acb92685dcbb1a00de29cee6e31a6a5ec853d5aa4611b172e432cc89f9097e105cb1cba2077bf5fa1a01cacbd54e1914cc44b1a9e842c254b68fdb756eabf14276853e44e934297a1e0f61b30777828afbd717d26419bf30b99d7776f0113f46f4e4b294f0dbc8e5d61f4b44374fa687ecf54025163cac93c5f1a5fd5357251d2c4ac1e8926ba89a9064153a10d4f1df87122d33e1f23bd4cd5783861a310818c8d6864f3a5a7e806e1bce2c0b07456f93a3abdeab5a9ac2d5df76835579e3d95f065bc991a9fa7bd99c112c5daf9bcc801970ce2db4c653e26dd1b464831ba2ad06c18920f520e7a0e83be89a2ecbf4d349361753aa5db31b71fa9d528261b0d42034a86dca89ebcde11762c71c60479e53de607b3ee903159c53fa65c42db41fddf67b9ac57d5d2904d5695c707a4532ca67f2a1c68c5b132a1d20cbc1f6ea7fe9a03e68d7c142684edf0b91f515ab4156db31374e12603a37f31c1027bb0e647c334130e4033e6adf3a2bfe52ae2dc848a3f8a744da0584ec94a54626c8daaecb40ad8dc2cee8f7f04f5eaa8205d1a76fcfe15d305aa538c3a42fe4c2ad4f6445a4a4293a6d11e8b4bacfee60440f435e0a81c53d9ade7704ede7ade996cc954e47c62253400138919f615e9a7a364e9cc479d89e4f26ad3d91400370d5333883f06a0ad5f92255f0ce43f42d07c448cdf90251549a2d3aa00e23d6da5c74a0e699130eda781ec1ebe87bf366c1118804205879db3b04da6cc9c3a89c07101e81ee7629f50a82b97af2217dd13bd9c69a4b5d77dc8d68a2e857da88c1b3996f0b363faa49c9340f711590c5da20c8fcd7b5d2e3b86fdb904de3287008d12c17d45ef45e5bbf4f2f6df48a881eda11c9c94db555778f565fb120df68b63009204366e0e973d9cd9a8d8eb4641d48c6c7340e5febf83af0d16754ef7287a4aeddeada65b398ecf948fc0042ef01ed8f86aa61b2925e79ba12db63186e7e7965a9503e965db2fb2b91c25b92da638c4484089576c9c05686153a40ca69b1bbd298d1a3f369a9c43b3c321a809df4128d7da40afca5d47d49dee4e7fe72d8e7d09116fc089ba37a8c8a669536e7954788fc8605c4b993b3109bedd1e6ba6089469bafb794703bbe181f49da226e91e43ac114a4ea6787ea60094f7c56fe0fec97b56fb5a22771c4c76b3c8e844bce4d5221a70dab8424b997b2b8c7e99a016f7dc7bd0e7643088b6384a6258f76844ab642a08c2baa62a9c1d184aa09bb547bdc66013b3f27bc4c73e608feed4d7788abff75df9619cc9d4c271b7b76772e2262dff41566fe0957cb5548077224b8237c5b7ff5582eb5cb07e66dd0d4fb8170b31b3f10a52278b964cccf0420698473c67c304f431009b0ab7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
-				"000003fe19e89df20c2fd4b7227948357829765b81d88c2212de4197d1700a69b3abdcb5033e93b5b82622c868902172e85b326aafca090456b499500bd7487748e0065c21d3100b95852ebb2cde9fff508085caee33d9265e5cba68715c660e930a49c77b66bed414d6d456a163e3eadcba78561246e1754bcfd44a0e61e6bfa90c6fa6344ff9c2befc0748d1d84814da4cf291bde892c7c3b4a1eb5c6464fcd3b8095d21a0422c0b9f25654f65f4f5453b1fb5b2136d312ab19b5e0bdeffca0ec190f0edc270d854e931cac8d984c19b048f279bfeb3fe318000553a3c6b1b201c86a3b803f3c291a2cab9bc351afaa64779d83f3b8c1fd5b083a7838c0f4d4417170d8e9fef533369383b751aa1940ae67c29156768d2daf5f2aeb97d39d9ddd034764de4d53826a75edca1b545c0448858511423351555300d251011ad4412afc3bc1aa9ff0aea6d0f7cebd4025e0003b5f25e19ca79195db64fa8ff3459e6861dfdb5e0941464e9d1a8c98e1df84ce1f54b532e328399f59bad26bb9019190524aed50c93fc1a29f243658bdaadc6b5c24a052aa0cc248d04e5a65a11b3657b7185194f416337c2b6275711a79873bd2867806ba690ca6292e2a3dff1258f5fc3d747b5c093df03857799dca7cf028b5abf5d1a165c41065ef2edee7d0ed5d8f573f19676e581570d209e42378c437b408dadbc59864c9e404434896e66d084174db91bb7f8c8f0b1359e9b8783ed33560b5a6ecb7a871e6acf99c65810439e463c71c5775fd0b6297569d17b35588136339c613415de29da583c5923bed18fdac04145aa9521c06d06d8e32d085016168776feb484dff2f44601aead607923aa957a7ca686071c27cc4a473cdd6ec3cab9cfd9f606838b3b7f3dc7f1ffb3ad5448535fb4883c222430015af5e02a98e40a96116cfd19f7a3ce1931ff9bbab10a1cce0728c7d9ed397d42af29d61d5812cf03c88d4b1ee5824b1286705d540bdce4c25b84003dc861efb576d52dd6701d9d99ba02288591d40357dbaf1bb741d8d50cfc642cab9720f3530e8a211c6cdec132a042d3a242abb93750802c9a4b0da29b0f7a3a09971ca2cc0676e87caa02704d2c14ec8371c1666d6c0ba8cafc2126dc753ea89a944e156d301c307bfb3f69cbb0d3efc87496b0124e7b7464106d58a6729b5a830e3a24521f241b389b494488ffd559e02822c8d212d1ef277674e14ebf639d682fdbe2bbbcc9bd9dd1ceb70e445e66cb1cbfefbf1045043e402caee3510f90d6e9752c4713b5cbfbe0349059a5d69835a85d4488dfcf55a76c8aba58cbfa8d6bb94993482799c154f67c019424404005c117aeb9dd9a5f48488edf3099ce07b1bfbb5b82244399df6822e8a30efed3daf1b48683d33260b5c0eab17902df15d48701ccab1c5a81a29ae1cad220be97b747ef54bf3b8bff33fb0aafbcd8c3f1897af8e4673e4b186aaf7714151311e8a610234275219f7e2c8f7ef5b11090b688b563f35b42e4b6b3bbd4e1a39f19bb397015338ebdb2ad4665474e04e53a90aaef44d9798060d1db7a2690226762f4aa23cd687f07a8bb3a23c72780890bd6f0c01be74167c9f39b83f9672474bc0cb4c295e089d943a81002f1e75a4454dafe871561415a7c2193b5e719f002fb9f6649173c19681d3acfdc9de88ae4345504735dec213104a0f809cfee1fc980341563c4987bcb0e6e9d825abd1f6dfabb3acf5ccfa04e7c928cdd2dbd8d012abace8ef343d4637bf7f68c1370036e74f406209126bb6339afc5987abeb8cf709d96c84ce1c859de654a6cd501bce2f4746ed6224a458be21bb66a71ca9a614e52262d7620ee430661ab52fa2bc97559f4c56155c3be5230cdc4f3985792b37428fd8db2c8d0535670fb198191ebbbaedb239228720dfa58535c7aedba80cbda98b93c99cabf9e215e1d093a5f49ef4dd3c83fcc3265d0807b50c170a2f2610d77fe7e4283e774d3e3da31dc6415c54be70cf9c7b155f59dcfb027423821f39f1fdbd49115d9fb369d85dcdfb3f94a79652e9f409288d48013ce20e39a8de41f8e3718397e2c18e1ba9809a6585ea7d5021ed2fbff47a51ad586678992997f4ba1e72b5a75d7e3d3703b66682a56a6b04e21f69c6b862edba1afb065fc410e85c0ddf8f2179ac9aa92405bdc3e00e08e9757f45c97cd2bfc1fa25f85f2ad57217f158208157d74ffc85520437fcb6e8bb8a9891c5a230555d12b273e76d29ed3d8d57a041e9188242b37b3f982d1edf82da410ef800b252db25575bb4f69862d38249049424938da3511084892b7d40c13268a3451528064845538e6e43a07ea529549858f3fb131cceac1bedae3a2ba5a1b3a445f3a513be725c7652d6c51405d6bbbd94325afb47a900991efb1c72b876c855dcce7787de4c488049051f67b99bf9ff9ed68b6cc46bc08436131a01c4412503df90eff7ecf25434a1816ffef1e7da02c587da4107ce5167eed39f9f078c09a8c301f5d532e9523c501030f5b5669a1ff911a798ca0019b923a1349ca1bf8354e2c72acfdbc7b48d4bccbc2b7b1a0d7528487edb86eeda97d10b1c7f2950c9d38aac5d221e7cd712c92ac0a49aef3cf4c9f8a60e6969d47a19f42530183a6ae7789cf16d793d91aeb45db516f69f006fc66f1f6bd215d95b07eadc8ac830d6df966cda8a6ddaa0bbf98e13d45e7c6d38e2524ac78fa383bc5814ff81d47a9b016f76d52ffb85f0e23cc9aaad36b686936f83e9d5671e0fbe52748ae93f2480abe482249a7f7c8e1d3dcbe527a0745a8d0115325eb58f63e57b53f9369e7944d1a737d44406d522644d197b58b36e1ba2dfc201fa0ef12831e96e18106e4e204cb560814e8551813b2ba3fedf343389dd0491021ff7e9cf70eef3fd5999105034b974211dbf5391215173a3296c3deb3df8af6f2089431ff6351dcea6e0168a42a4cd7473863fa13147ca7ca6bdb9b2dcf8f8eb5adb2773cddbfdfe3c167dc5a07eaef8b3061e45fb3147ca7a84f6fac7f70a45d3004ae13d022a6990c15ae415c62657f11746c5efd1f547e1b0023bd577c0ab4990ace6a8c11b093912b4ea719e2768ea337e8807efd7472a572dcc6f033d1f3629f540aa2e226316c94cae7bed61420d86e06ca011af74312071086db5c50f0605a443b877341f9ce54f761e7876cef992d1458f62753366f6989838823192add70fcc2c96aaa6ab6d10cd7235e7a1db837c378fe231f280cb9f0a95b6f27e2741d680da80836fb3561b3b155f750b86288eb9ab996de45b9c7d4d18ceae200def16034243602692246d7f4b1dab8a7d53242cd2467d758c7482a525b2aaba7f41e05eafb369735f476a27dce4e1adb09cf2c09a4670c5f0a134d239425682b040e828339bc7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
-				"000003ff3c180f191eb9214326a3df038f5b168f96539b2af6bac89088a2c521534b4ac109a6c0211ae758222942d48290816f48bd47213a677c3abbf5a0760d7ef064ce370d2616576ed3304672fac02edf44256d37bbbf79688b80b8b3a3282ca55984fc9464ebf4db7b5c2987e642f7b1a528f2a5aa8f69c1e03e3320964fd12f607653eb32b64cba17b9a187385dc5f524ad53207b3f212cd2b43c558899b37fdf69b54bbf4b58c7484c2976079f23649b1171ac0d60213a9d21d68512e655472f3e6bc03e23d9f4cc0fdd46d41f15c396f40459a70f69d2d52a5a8c58e85507bc6f84d73111169ec37dfd1e8a792b13a17ac64c32bd656e2fc17c3e7a278f2f13b8b1a28614545d69f5b20633f6a6a3e442793710ab21f77e90cb070bf40083c49cb769c5146873d1311b6d07e7b3ec37d6281e0c09de8a37bd428947d7f5f6de29c068ccb43129dcf5e66673be8b60c04156590ad40dce51baf706b23dbcda54cf562ab615f58d9d98ae2610e51182a843c7f2eee26b6ebdff235a8914b5519987ed39cffb0fc5ee4c8546515abbe827b076627f1c7e2c55aee9b52ec8e4c0c3ed31583f38d9b8f32bf82f479521397537f4ecf0a1fcb4b0508de3b123e0bc764fb29eeeb621eac55967abaa62e02df8ce6c6dcfc1400ad996bfcc7d3633e96789896c4f804050d66af5517e98666870043b24a1f69e4f40e49a1d2512226441dfe83b10b1bbcce79f066f694fb5e4e4138056ddc7eea42072437e0b137c6f1edec173cb8677d4d52b121c8b6a610b5909a5e1d8d4cd9749948b6d8e40dfdb800f2ea1b517c993858323e7b1c08284743112a7654fae111778096694669d8d020913d22a86cf97a036de0ccdfaef435069b784e3c8eb8e73fe53e0f542286dfb9e240c4a3e09132545f316dce3b284913ded7ed7ac0eff2325ca23a999fd85129c4c7a5ec5f6afd91bbff4a0e8d9d7c0ac09951545bb752ef297ebc03a542010bef3e06070f7d0ef14c73f0daac37347e788614fb25fe4019e6ce2d8780f4e3de935af59b2d8fda1013d5b7f1ee8a3dbcbbdf63281ba0b501478b53871d79508e768308535c5c5426e29cc2bfa1c2702d49deed76021f1783861b18be7067e5b6e28605243c3bd7ca22037678d0d8bee639e69f9a7b5fadeb8b5afff414a4fd89908a0b0705cd3315c278b29d241adf57e02ba384709eb3d50c50264c82739adcb9c70289c220fe87702cddd3150d6ad1fb4b7f734fabbd41518e4c795e9efc4a57c9d25222d140cb624708567b326fb560e83f155fb5a328d523e938e2f7d8636f135014f7c827ce744f0044f1ac00cacc225fbfa6a7198dc04e62e8d1e55384f6b9bd2fff09f5c63d7116384d8255fdc32c047d2b2042f1eafdd3b4f075ed643a6ef8fc4dc2aa806db4b0b4e00b19e089dfda52798555856174f1e13045a16d5196adf61b36be71b90a3cb8abc4836e1b6e1b92cb63d844e7eb4d128612f6b447ea1aaa9d86de95ef07196f5524b3a8a9e06728a2dd5591f41ed08375e159563d6828d4de32e46d874e6fc8e5141f9f4cb51d84860ba45c9a98d62c1c152a045e4024bcdf1fdb76b94edf3fd234ebb962c37c0514080158e78ad8c83edf58e0af1ce7bf0bff266291aea00478c8505e51adaac2729929816cbf6a1d26bf8bfbbbec523dc2cea4e1cbb93a5eb8eafd2baa1f2b473b6430c8b43795d7cdaa1c124597960bf2509eb19b6c7b83923ced37cfbcf0abfb87483363b9b236d52a19863e475a6e3fb8b9e535ebb092e76f91dabc5acae01041d8931be12d3340c9ad9e75f7ab82fd5d3582c2e1d070e4604ae2e16bbb70cfd7c33fca0452e5d46fff311331387c336897396b3fa26227e2287b2589e321e003d1fadf3fb3130cd363363d8bc16d3569662eb33927ca396a1efd55b82eb76102a908e505fbfd0456dbd3152f20d223322847a36dedd70518d9f61bacb123acb471a992f0371825a39056ee1a2bd5c6723c1dec830c38bdc5299c4bea07763a6f0a1e417670559878c67a324d91f6f33c66e3a3028837bf21a240959f9f069781ca83cb6f0484577dea4f88d9bac1f18624c16895fb097caf7869fcbdde5b2fc2d45f5981120c23f5f9488b03952aa436a993f0f79e40be170e79ab73e034ca4ebb30f45f4734d959954443503dd30a7cafb74ea8a695626e056b967df4ca0e2f3d9f60f8802b3457b5aff3abce70247e881649eb90f6c55c1a5959ff9fbbbbf0121f9c12824f2246c198ad8139aca5694a92afb467ee148032eaff73f85e10536312d94e1658f510ed9dcebf29c8b11f672adf856153e916ec68996a317c8ededeb1bbc274653f03be02e9dca745d39495b26093f1d98fa9565278284f110744f9be6e34dcc70cb77007a2c65853156800763f15e850b8a01e122b8ffce5d3fa4417b5361eeacb7a0b183a0b835781779e8ad63906dbf5dfecf0716524c60bafcbe49510ed015f1f1750a00d07dca5aa3634671104df1c79236a23410bdca01dce11bba3d9aeea7d06faf7b16b18a4da9c0622e4c17c4392262293a514d0bb0ff8458c5366bfcf3bbb6a04110e67e7e91dbe9185e60282a67204b8360b5fa99f26d1300cd511901cbb6f59fd7545efe47776e736fd1af2d845ba343f4296246a19e874d875f7aa4584da888526665f233c6ceaed05057063fa5d833278e3d07df84fb482142dca6540b679840a38f035cae800f32849da78eaedd04b463dde31bf61b752f6e69cc795f6c7515defc881662b1be2f74b1889e456d9be54b19e49c4dce025bc95ff63d5e9fa5a10d46ade9f36c78f5f22f27e955fb5331284185bf8961a57f1d6113fd868f645efebdc441cc8d671c5546ab2a701e35aaa4977604fbcce6fbddcc9f493be42f974c299ddf74321e0884ba2c3708ef62d00a0e28edf6dbe22c94e351a0501e81a645d761b8875f46d79df128f9df0d869775b18036bfebcb5a5e10f733dca63ed896936ade6c0d4bebc064752fc113823d8bce45d3b2648f01b1702b274e5674c76e8bd121ab67e05a77b5c6a68a4a7f63c1aee14e9cf33eee417e9f4df70045767a0b8a162e362efe51cce26eb2351cbcd239bc3be2ea75c7767474d2799b26316c94cae7bed61420d86e06ca011af74312071086db5c50f0605a443b877341f9ce54f761e7876cef992d1458f62753366f6989838823192add70fcc2c96aaa6ab6d10cd7235e7a1db837c378fe231f280cb9f0a95b6f27e2741d680da80836fb3561b3b155f750b86288eb9ab996de45b9c7d4d18ceae200def16034243602692246d7f4b1dab8a7d53242cd2467d758c7482a525b2aaba7f41e05eafb369735f476a27dce4e1adb09cf2c09a4670c5f0a134d239425682b040e828339bc7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67" };
-		int height = 10;
-		XMSSParameters params = new XMSSParameters(height, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		for (int i = 0; i < (1 << height); i++) {
-			byte[] signature = xmss.sign(new byte[1024]);
-			switch (i) {
-			case 0x005b:
-				assertEquals(signatures[0], Hex.toHexString(signature));
-				break;
-			case 0x0822:
-				assertEquals(signatures[1], Hex.toHexString(signature));
-				break;
-			case 0x00b0:
-				assertEquals(signatures[2], Hex.toHexString(signature));
-				break;
-			case 0x016d:
-				assertEquals(signatures[3], Hex.toHexString(signature));
-				break;
-			case 0x0189:
-				assertEquals(signatures[4], Hex.toHexString(signature));
-				break;
-			case 0x01f9:
-				assertEquals(signatures[5], Hex.toHexString(signature));
-				break;
-			case 0x02a1:
-				assertEquals(signatures[6], Hex.toHexString(signature));
-				break;
-			case 0x038b:
-				assertEquals(signatures[7], Hex.toHexString(signature));
-				break;
-			case 0x03fe:
-				assertEquals(signatures[8], Hex.toHexString(signature));
-				break;
-			case 0x03ff:
-				assertEquals(signatures[9], Hex.toHexString(signature));
-				break;
-			}
-		}
-		try {
-			xmss.sign(new byte[1024]);
-			fail();
-		} catch (Exception ex) {
-		}
-	}
+    public void testGenKeyPairSHA256()
+    {
+        XMSSParameters xmssParams = new XMSSParameters(10, new SHA256Digest());
+        XMSS xmss = new XMSS(xmssParams, new NullPRNG());
+        xmss.generateKeys();
+        byte[] privateKey = xmss.exportPrivateKey();
+        byte[] publicKey = xmss.exportPublicKey();
+        String expectedPrivateKey = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073c3fc6de1195aa5d69f9dafc9db8504aa8059115e8319ca15cf58a1c83c0de3";
+        String expectedPublicKey = "73c3fc6de1195aa5d69f9dafc9db8504aa8059115e8319ca15cf58a1c83c0de30000000000000000000000000000000000000000000000000000000000000000";
+        byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPrivateKey), strippedPrivateKey));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPublicKey), publicKey));
+    }
 
-	public void testSignSHA512() {
-		XMSSParameters params = new XMSSParameters(10, new SHA512Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] message = new byte[1024];
-		byte[] sig1 = xmss.sign(message);
-		byte[] sig2 = xmss.sign(message);
-		byte[] sig3 = xmss.sign(message);
-		String expectedSig1 = "0000000025fc9eb157c443b49dcaf5b76d21086c79dd06fa474fd2b1046bc975855484b9618a442b4f2377a549eaa657c4a2a0dc9b7ea329a93382ef777a2ed402c889733b98d7c21a6298d40c9e790fa75c064f12cf9e2634d74c86959085cb090b9dd912f6f72fcf8f4e796de983f473854c6134c4616df07df9923eb421fabc45cd07c78f027f46a88dcd851882205160c7a066335203e50f415f7d907b8eba64d5d436b05b43cac92afe459e7d7e8cc9c0b9f973c4b7b103cb4c042b595c2f6cf206ae6835d0a4adb9d85adc76974b3f8d097de7ecd7a806b93c943d66421a83e61b5588ac2697a2ddf0b22ba1dda387522e7142bc6dbf86b8f6d8de68bca2f1d03188a629a33e568818b022996aad87afcd59c5c8203e6d6b4ab83e12bf00bded2a705ff3701754a4a993af74b361760ceed8baec5ad4543282f9e0574d00bbb6e9c98b2da32d463e2bab0394bf60e34ac5edb9b5196445e1db3afa25c3dde5f2489c6c5136e4bbe4151a6692becff2250474277ac1c4626bd1bdb6310ecfa8dc8f9664d61991023ce7a04039b8bd46057f36a1a6d657b530f11fe14dccaf9eeecc7382af507ff64a464ff6e8437c7040a17409317181b2333bfe781945c47af565abf5bbeb06c0c6fd1810c872bf3ad6db9382b048a5765debc946b867c8e10705312c74b568991b4e1bd29cb05132304c9fa11e6104fe6f406a66fdbe3e2f031a68340c6b849cedd32d8aeff20ec063b862c4915c0bc4745029d2dfe23003d5dc9f8f7328b2e21ec494da726d70cb4400edb99ddf0f9f5aa8ed4069a99769e7e4b905e0cfba8255304dcd9eb4e7d4cf9ee92f0b1a4406e01d1c0754d56f6e14b74da7373eeafc687a4820623c240999ad143d9ceeec76cb600406a8f4eb2bb196e8b53d7655a889a4a25d65aaf6eee6f309ebb5ef096a563fe5c231889037b3f0d33a3a212ba4f1851fd44bd367717ed6909a032ce36f1d7eae489f462993c41ae4333528bd373217c98acdfe9522474f7255c8089e6422de79c65112c6d6ddd98d53d306436bca504f8603fa6beb9925d84c104d374cb4c681dc2b304ccff8f1b1b3b10f58fa701445d83810b853f17834017ba9a21206fbbb066b1563a606f1c844800712db2cb64b07f9fc834f2242d8145e6144c6096cdfd1cf5a2c467e961a3cc02ee2b7e103c8d54f38f0082823ce663b26c78f84678556cfc42104b533cb7587ef1f1567d50a4f4543de5e3f8235e91aca91f003d53b9e800eab741588e2c6498dc6fd4a7a67f16708ce7eb683a867e2448f72f770c189aa4740848f17776d093f24e633400da20fd8270e62073fedabf4b891a25b4cabc40fdc9207af2e41c99396cafe08a3d68443a643aa55ff7853330bdd80e1260953aadaa30d1e1a1342674a29813fba0bb5774fb3714a4fa8b170569cd735d36a053399f73d057c40cb86cd397cc173b3a76911df7123029a09513db529beb1f74439c364c388562138730e5f0e195def2c30727dbb43ed42b133f3e7637b4d6131d8d8fdf8dbf78fc1da3db2ecd003bc3e28e065b53917513083e354fe7251f509c87c29536eae80f2aaf2fc7e4fc045ccbe4973d2f7bbf627c19a8aac11017b1e00403075e81965d1b3bb5e723099f9a5f326a1cabd480ff12eb7c33c2aa5c1ec410ff6689a559c61e4409ca1867161c1c7fa5f87de53e164c0d9dc2b7941af54988c21ed676e005482ff38c83a6a9d66410a9252bb6dddc50cf8fed31df5b2580745bbe76fe024f0ce7888152cf6ccb75f359c7ca914ea93fadaeb7c43c25464eec9e23fcb51a6d17178f4949278783117c88c86d10a30e191e0dcbc1b0309e25927804ff449866ff5f9c4e42f29c06ea2921c4c7f6ebc862f71349e70b6a84fa9c035fdbc6529de608da3e64e23e9dd8cd93a05d9b3d1c0a816b1d67ac97b70a0ee3bf0872fa585e7c66dbd1884bedea39b19413e7c9bba6a9aaed65215407220697a8d986ee5bb76725c945433d84057cc83ab0526e85244b2a91c5eed1784a0eb5d8d5b287c5e0ef372e3ce8865d20881a5fb63d3bcf9c2272f950f81426a1fe3fee8a1190f760d0842e2a624a202e22ee6f1bf3d742b8c3c568af108be2bb627eaf154dfcb32d6f2950ac8ddeafaa4a6d03d38b6d06ca7c60c3ad5ca770da8f5bb6b2936780d75e25ebcc3266ddf4de5aba712dd9d30bead884a0b477b22af3c97edb94c131861700394d83bdad35b4f1feff2e612abbdbf0a0e09fc3d3af8f3a1c26225a9f39d0be38ae6a16b54b2c9f271588a353fa121bd690496d5af5d3b83dafabb292713beb23d076f6215281d2f0f4c851e738a39fbb075d883e3e2d96d436734311aa46ba605bb4f0ce1c42fc2648b83d7ada023a4775d4f98a4c9f2df015f50291eda9bd3a4b24a46280c90a1fc79a9555b51d15fca1904dd56257f189e2833a4c778ee26c4fd2c682c84d0621e3acecd392600ff68a4c15c9d903eda18bdb07ebdde86d31853dd06f1b65c5004647c0f9c22433f98a40b02d50294ad98f3660bf79610aa182aaa59d0f9a113bbc58ed58c02226516ecd977286f8d58fec91ae665ffeb7c0b449b024a1adb3d3ed33438aa60724bdedc8c7d847c704798c22153233e6e9e1f32142b710d57f7f99ba352c9923775e049417449a60d32fb4fe4a816a6b7c7f31491e2bef5b17f2c0d5f60d28a673219721bbce70e2c3c9f235e70aa1ddf27c9f499d31b9fbb1156742005f6509706b7ba176254e1889a427e07334136eafa1b39ca52cb8cb1c24cf49d1a2b2c07fd8907f4a6be01275cebdfe8c3f804bf7ffdff2a5bde0ebfa4f0add7b3d37db819de93e5eff1ea91abd27bb76b4201e9607d5cefa144fa48590ec3ab339fb206b275bc314f4b44854141c5bfb19fd00ed8dc4c928f585b07289fefa5dfd489e78f3602672d0a33beedc85bb873dd8fa43222173881bbef88775cb2d37d2353fa955f870553c0bab49c8d17bb1ae20f5f08959ffd4e77a848c820607ddf6df88ca91a057ea9eff0131652b8e99fa5e64d27068d3c79ff370410af3a7f9f53d3c75b0fb865388864297a8582ca79bdf47c56f4035d8bf99a0377aba30a0f1ae7f540e0b9bd97c63f75fb2ac8280af823f91c32428a0a5940dec0db8edf0495f3b1776e2ece83f0ce4c43b592ed70c5c8b14056df1050f38ef1d1975a57abf365f21d7db03413a820595010598028af4c46aa718e643df002e8d5fcca43056468e91758005d942c2f7eef5fb2c58c524ca0006dcdaa9051772af7ca50d4d3e4c194775a127ad8d974d7266c749af5b63f32a8f93e711a6990d06bbcbe1edbe42d7e180117687ed09af96957db605e32822f2ec6f7e8c45b6b1f22e2ba84725fef839d5ee08bd0bd3d393c17753c21e0f6952300a04a5df9e16c7d9410250d13a02237c212e0dae3cc4a4d04a67cfb3b02e7af63c65b01fdf73183761a72c5bbe42f02f407ec34fb451cfb785004de387d935fec886d2101d26e4be221072562a6af51db288321c8ed79f157aef63681e1b21f4c55c06bcd262ac382371f14c9ddd9ea87bccb5f015e591b7fcd3868972b6ad30091dc59bc6adf197f4da4f6f7ab7de7e928c8f7a3c38fb851b9598c350eede7c0e71dce689fe9fccdda4a00e3a67675ae2482b97d27deab6a1959e64612e244fbe8b3b5db8d8321f2c73355eace7a988fc75ee2d0b67acf9eb3de0630a661141285b5aedd0fcc92d82cfc70f91ee2a41301f726484b2c5259c347add1408b0f5325b5eb2cfe4866d2f5e245b542b290173efa7043115e60f3adcbda09776975fbca448f0de92ddb0ce444ac9079d6be43bff2a996d645bcb59e7bd467236d1476e7866308aef87fd7fd236d1967569290fc613e285cb33ae657c22fb8768701aaa3544206e77890f726d9639c49073885be27c23ad1ca54c3f5ceb47d2a7b11758873f3362ae9c40324f188ba2066ab61d32eda690bc2a747150f01e5fb7007c4711a84d47224bf28b90daeed7d57631c2146e9ed44b4157084fe02886c1a9e49dfde6be129b006f17a1887ecd1a15b36e5e211074ed7d6cae345467b954efa70fe2398df5a345b942e779bfe0f21e8e2d6c0a67d7dcdb7954335d4fdb3b4cbfaa6331bc33bc81f5f9fda1dc0c947ceeb7c56fad107bd925c78498e5872328b99792cc073f066c39c5e54c05e821ec8e8305bf5434f961f4c9bfbe2cc0a0727e4205013160e3a0e2ef98fdb8d1f96c76786b4229e7f2798dd53db36c31bd6839d62c0bdaad428e0fd3ebe9ea2567da61c0e5315eb3d6f2aa76f87cb6203f6bcb1d341d4d5c58c6a458f45d2ca4692f932f09a685069cac65762d98505cc36e3ad293fb9e11df2fca7c492a5d2567c6cdabacd5ecbb13e2ea0cbcb59b51385dbb3de30f9e619a62487ff6470b491f23a93c2d5407de37f4c39c0b936076c0dd7097b8c9e413b1f386ee52b4f33de5366554a292607f72a59d21364590a0f5516611a4ebe7c64358407b2c6b6fad66cce0399db60fab84abeb023e9bbdf7d1aee52a104a71ffaf75cc0ccf827b10a372eb44eecdfd795be15bee70edaa70df6430f2bcbbe437d1f7112c19eba74d887d2b61f5955c055e63a9efe45a0ea355bf5c18adc79031676151f907c08b7fb2d6f2f002d8d003d19afb8aeecc72d77a33cfc06f4239e3d7f0737b79981054c0c71625ab4c5e0aa2a8d72cbae38ab281fcdacf478a6e50b018ddc5d69d7a73bf8e576844e8e3d0d7299416143ef6067240b9f2cba3a5e20cc9a03d7f6567c75684a868172499609e76c07caca24704178aefb067809ec83c1a8abd500de894e77166faaf9baa7ead78105c03d4008527218fc58f1f7ddfe2461b90fa31bfc6f953beac2efad01e8a03dd6131c6d5b22bc1bef3ba1ab23acea334ee26b35515eecea74d31877da71a904dec0dbc30e3019a4deabb4d03ce5903c3068e668ff861e739ca51285222e7a79d0009b775e8de27153a59db2602a7c717260f793547d52edb5e0bae9080be48a3830762ecc0c7b89fd11ed1015205d193bee79808e5e115fc7eac4af8f841e66a1ba95863d8731be2309e3ed622fdbff61d96c3b5a8720dbb34304e9a899adef0c7be7766b413eb58878f0fac1c6518efc7ccd2dac7d6328487c36f78b79459f068aed29ea2a27d08e4d054a8457bbd9d6dbc396b7ff6fc69896910dc7ad319b01fadf46e776671d352578ee2a959718d960e24b8a565ddaa2f0ee9d201bbd55c4211d85744c5240c4fdfe98e9426e1175e68b975dece12e4b7d00ddc3a9aae64fd060c5faefc1a3d45571e80b687be83c9715970b960763fc1eaa9f2ec84d8bcda8bd771a2f1a1dc8b927d1efbc671a0829b8f0a5db3195322c634eb9b44ae8ff3d4b36d75b26bb22dd4bbeab131b815c2a9fc0034b0c9a2ad5a1847d4fd2a344a7dc3220f9546a80cace35a7bc48ac4422833e5762082e7aa331cc1d6698578a4a3ed8c2a33706029398590da4b399f09e88bf3024dae196983dc32b26bf24e0eb9b8e0b5581076636951cace43297f0aa968c104a25de43917f84bdf73b95110d0f99c95b05e7cea82b1cf8dbac2214d79f0bbfdab8db2984d7322bf6163bacf5b3c0abd3dab2c10894da62ff8c3ecb4268210071b0aa4e48978394ac053113465d4e3d14190408c44e95942d95fdcb22683e68c4b9d4ab36980c12df3405194c857c3da2653d9eb7866e6fd5a4f89ebdf7193141bb26df35bfc44962ea206fcf95e9db615761a8fbb203262959b7c731e20d4cccb6c30c4ea82799ac1fd3a64c442f85c12a70c2c62d543bd362bb6ec6d3f3c1a87f555c1f73b7d1f92c3fd9e1ebd2bc8942c887efeffe65e3098abacdcaf0c626a97931a2953e78554d850614a0673741410373b5b2a67086e20faf7bca17a7e3e74f2158876e1c9d0d8816be4a3b517aaf52600153995465daee1a2bd71ff572267c18cc9ab2e5e481be4d1646683cfc0167b8e7eb6781b09f593832b4a1e8a72c59aa805a30579c528f2e7665c8954c3e509d4705da201f99b3cc3d763800aad1f699339cb767776134b9618bf06e4142089b845adb526ad540444b035fa852777a993b741461b8ada57583a77ed45c08be19a1352515be5fce1d97f227e1c880cdecc45e43190b46e1778144204b77fc858cc7cbca258a3383adfd71080e64bc0d7cca7437599cabefff26de5238ed8744ea96ff5a29cf1a30dead85d073dd5ae13aeb140ffd0c4a41ee3332c2e2bea34467494bb8a6169bad610d20f62c2fee042b02bdfa47a7bc4fbc56756718e5c9acd0144b0892016f82b5d3a43b8e9db8c23ed42604b123f046d029263cc661f658aa0318caa6883daf6af3c1f4eca0aaed6c8c59434a31dbafa162417ae3855a1afd7f98a3235dbb0e83837133be6bc3212dbe7158967f89f4f56ce7dccc8a1d56b96a5747947ceea53a2a8422f8d073beebb94e3621ac546c77170dfd6f682af631876a1989e85a273358cd41bafefa48af4e8f8cfb21689a89c2b6c696859f726cfecd458ff1d0311df8b671bfd6dc5e849f386cc9f6d076bf0d42a982d80cf488d2b8b9c451f4cef63754c948250a34a95a86187d4b0d1c92473e9de35f760701267d761556dd5d9f99e8a5bebb84705800bf99bed16f132f77a10959ff72d357a9d276db60fa199c27bed7cd40fb4b12ef3578be414a5b92ece6eb830de5ea734087dc44abbc38e400584a980518a8ff98345de4a1d80e74b97a1d0a1a3872a8259dd4720e9bd2f113aec31c911bcfab1b0e79fb810491ce74b31f631561753826771e2fe137425beb2317a5a480b66ce163018a3d8eee1bf2d99c4d0c0d4560c2f7979f9bf438f11c9b695d490b8317ab529d5401a5af788c495fb81d2d4d12ae76bcace10ccaeef81b48bb09a408a2e76330a77aa3fef5e53760be73daeffe8d53966cd2b6644d7ae92f3fd13fb1b4adf01fe15931b136c8301908c92adcbdc42ee7bc844a2a2e191d5f05190d8d29416738882de66cb58a63310507501275280e7e4f3f290047c050db2f8047eca1a2a697ff386f9187a574fca5834f219fb0b79dfc1d6b55298f4d9900457705c749a4490b7aede2e69485767aeedb235063b3ae75b0a8a8721031ef6659df4fba5bf2478326544c708c7e25e5ca7ac99bd568d849196371de12ac4e7754bd35bddf7c1509d1a32002108eabde06055bde6bd4dd92173d765ec45cb57244019d3a5c537e4f8e3751ef208948b774e4ef17731b8c4257ef2efeaa4770cf8b95cf291291825b2d374af17fe0c16aa05edb318a9c201a63a30832f8df66637e6243f6e23c83782bb6ad6443a623a9125afdb70ab50bb5494faa5c8c2ffcacdcce931aeb2acc2a8e5d35af2331c9506e2f1b4ea9afaedd48644c0c642946b31330a140e59c59a7e53de994abfadda7b7e041d1ef27bc8ad33db485cb20726edacb43f9e92d8601fb8f1b65f00b75d5631c34564f8464b43c69312fd30612586fbc0f4a5bb9002ed13451963262ae44afffebe38d1f35768dd678a875ccdabd86561d57b05147bb519d9ef27326da505efd370d44c7eb023dfd36d1224e0e214bd14de142e4529e09c1b6caf11162233ffd48af3368cc3b989c4d6316b010a7c582276929daaa3f27c81ac7732e2008befd537c9ea2a95e862f0bc17295766c7415a9cbc6725ef08ebd0df08942224c4ed4c7372c82c28ca7a7e485287ce7807b3df47a3ba3cb6f18fcaf6bb216ec91d99e642fef4e59d9e150ed09fb906e2f4123060cc4d55a0f319bbae0151b68e02aa5c6f430fce57ec994781b90a8eb0064e046b8a6acbaace6a3ea9165e95ebe8116baa05098e49d3aaf76a45f9e27b8f92c621c15ae84c9b0ef9e1bc51d6d9d9dc9b3d3e67213763776f7f6384a6b30fdfb27260d0dbf26508b17cee5bcfc6e63de06ce8e9286552baeeace047aea95f9c0c267705a55b2dcdbfd63dac433d2f6ced9048ac5be3e712f49b272f54d02e37b6698eaba31eb9cf2b51bf8d8b47376802da992280c8cbd7d632949efed734a7b0b11ef9921204a2919a3fdad9cd03c1e08418167512aeea932e8f0016cd461f749bae6713eac495d995547527723cd54b0e1418519143772f72632a319ccd1e521c6f354661d0a894bb728e49323e84c3896032e987f3865d14a317b1a23e11487ccc7bb9e0c1d0092d84b849cb732df32f62480cada8e555341b127617da87f802c25926a82d982b094129d4532529a4bc3d3041a6adfa9aa2a86b147a0ae253a728f4870aabdbde73be2ebcf9d1d374c68f36177012ab5b25bf710f39ca6586a7820e69061b4edd2f37f3512df2fd0364290e07d284baf84423dee7bb7db241d127bf172c99560de1196f860026b9f622347f6f1780de232248bbea8a441f3a081b59f4a490072f19b8febc0d75069c9e60980a1d2a300fce1c1b6c78351cc09d9af4b57e9ed47737e9d51a403fc19475065ffe76a8755cf60fcd124d941fedc482bfc3536c58406b72d69513a9c993caf05f683f7975857cff2af0eb731597242f114d544bedf1871244a072f0b955077f102220aaec4ece8e0ac7ea5b7ad5452c054b9bbbf1256d5bb9709ce00f63a7df49a26ab355d689df1e6c06633a74522f4a6d5308bac8a87f4791bf663659d880c978db38b05040b56d9d6c5f810391f25ad157dbf7552cb542cc89c2da49ca2d735e91c06475f894f4619863eabd4fc75b285d8f03501b7a6c49148b72ff152670f14940ad01923b62899298ae44082db5559861e6a95fc159634c0688f68ae09df5afa2ee7ad6506ec9b0e428f0764798c2ee49a5eb6a1728b64e460827f3d648101d3c2e75d8fc02cf50918b85c71c564b59dd6a51749dfd317ff1ae8d27925df0828cf56ba3e2416a6a11b041ed78b031cb8af85cc2994e24cbbbe015549d4453faaaeaadba9a1f81641dcbb0cc90b602b1fba3ede41781ba00c30fc5759110ab400d022dc3e9c59072bed45649ca704fc25607eedfde1314fe3f3025f7e1a6e7bb9f87943749b1864592201ca26487572253cfc0d2840745bfdc54fd1705111e06fb29b3e63cea6f90f962335945cd217fe72a004c66e27852be515b6f5a417ee6880fbff188d1f4b5e4ec16d29727444fa884a0599bcb4e31821275a75b0080857f275fc0cda849b8819b3d3662974bd0248cb2f5dbb89a3328f9e9807cc836058d018827c380e9e1dba15a25edbe077cafc345ed6ade7fe215a7036b33e8f3c51d51d2b866204ab6bf7d79a586efea08f86aa0b5f7a584cf7ae3d8c2280d907c123aca43cd559e3bf6f5fe36754318b6aa7e33ab5ce90f43722118582a778474dec978e0f275605d2cf350bb48504368cb2c2094b7fb941f92a7497367f308937f15abb8213a9b43044d0088679880982b10448c651f270b84a0f58f8721d9268d2dc2079e569a48ec27390032287c003760eb30d18a4b9f6d0ce4172c201bb5559122608f9124211f2bd40542f5b8fbc70ef88ca3945558030b84cca8d99b81187abe7d1593ca45c11607eec2460031c0901f6156929f53c1b4391e24024be06ca0d85dfe60df55ccd3b2e4627f882fc3eb50de259710a48be91f83afdb1b21885833db92e2a8f151ddc2dea5af3ebcc8255dcac14e7c15e19d748641448aba6969cb218db6d10c1867bcf4f4faab66bcb26f5b6d0f99fff2dcb695d29d73e00e58c06f4bcec09a45bac2d2347f9822e08dfa36e5bcb0d5443b69cb3acf4b1cf4142989e75ab12703699510fb47173897c7c2cb00ca456a17478ab2d18104267ba01cfc4486a5e00e3f4fd513f649989b50b9fac2a50429aeedfd606b3d88a491cb263d55ad8755f449ff360f84a52780c3d9dbe2ab067a6212fa7b410d0ca38899a77fc51321d850605c8e06642f5b752f168d548cbee3252d9856ad342d5bf10215e9f3bb4539816c3dc5a546324e228fa535237a9c723b2affbb3dffbcc1f0f3f649036e1d3f95e0ef0f7b3827bb554dadf68fc6b1d3eac298dc139fc7da4aebadbb56fbbbf6c4e47ad36c36cdf1c78b673e40fe1e19bd631ef24e15379d5b1f5b66e2061761f806a0cc8982f79a1396eb3e06782384ebc852b647c7b71c15b7e6a156ac0032499c399b131ebf9fc4624bcfeb903176f64007291ebd408979016327a8529d1f7cea27e54c669f83775276bd003566b7fb170185a904bb0cf0929678a2111e5d18b824c9178fb83174fb35718e1a7f5c7463867bb9754b55f862cd3b9e41b550b062a199b5316c0268cfa3b066402db89906143c06b0fde74466e53ada95be048878d126969f9ef50976b35f55a27531a654b695bf00b969d73a923477f260eb7e31821452f31d42e7e246f65924c45e6558ca0aba67ddd18debeaee48f838f059aa6b9ba664c978bba5dfa130b13fb2ef5cf9d7a3e4117022e21f2686830583612d9b912d2efaa2ff29ac9acb45289d091c8a38952e78af4690511f2d807f571e3d79b0363942769926c840a9eaf6c1d78811dd0bb9fb973145cb36e3f827e4461a396136be978e0fd80ba4d9c8d5d8d3598b36f509c691d0b84cb7c3d1a0c998ab3e4f119143f52aa3b169f914957e173617a0b332e8625a781cd94438c4c47bb6e1a004ec0604a99459d9283d0b8b809ece8a07a1b3d5b5b12f53df055bce84d7828d2c2e498f842e65632ad4f354a5957333bf68bcea3af440609402c275ff7d993eb1254c9dacb7d07af9b211d695258cba2eeea7c76043d9e697e3de87cffec138a566f52fe779ff67796a2c3dd4230fbeb1ba300aee40c9eee8c0894c45051fed1c2f7c419b219bd845df48582733c4e818811b26baf6d133ed12bae7dea0f3bbd23c599d62efac4d175c774068586459dc625e5176b2ee74680ba07f1d7cd11d7cd231e19232c0f73a26a97153c2c2b76b24a4f4b33816511878bc4c4522fb65aa8b37b7e70ed564662e4687b20ce192926fae3d01365cf21f29589990d0adcc64566ff2928f021b170399b76e8cf384d44024d4a8d982d37f534e0bb15dc77f84fa22ce02aa40eca6c4e27c6edbfcfb39547c53905bda30c95774c287534846373fd396d0961489601360f393f37b2be3c837ad046e0c217aaffac41442d2a79f77111bd968ad937d834d6c59735b527a67d8cb700585bdb5ab1da6ed784aab0a1fdcc5dbdb0c351310502cb902006761f98fa1ebc139791dd40b3766a5650f18938b0c87e590c4b7b460acd0c966cef59cc63c39cc3adfa42b67e6abed944a6b63070058c1191859f9de4ee05d8722aa0b1bddd8418508eb7d62cf9964114b20272c1230e1595081af4b5337fe83977faa35f5c1e9b8c5eec369cc9c3502aaacda1ae6cf78f0440fcef9e2a9af1068e985d8d03669ccd78454c51d0da23e6882e7ed6b67a3205ea90240001f896599b17088821ea16fb904cb49f4b4096773713ae7943e8106d59cff23294207d7af9803e2792b5d9661cc4952a227a5f7b6d471275c7c44d7f288360f0f79c6ed3894b0c7171ad2769c8c09c72e64ddfec355cef10d45f64fcbd1f3b874aeb2f8e4372ec2f80ecbca38d46c8b78345c3472a2c6af14a89c65e9b8270c66332659491dcd4df2ef4255a417b2804c1659ace85d558a439a6d463ee9f73aa75f3ea97ace4e76af0fc9c0c309c2710007b06aa04cea65cb3792e6400821c7feea655c5061002c027fdc405acc396306627c97041abb1dbf46613ea28daa3898f466f02b2865b9f952d62f5c216250a0cbdd04950347187854a273b03f985e85709c95875f4d07c9c1b195285adc7b3342c9d45cbdec27e11c368909cc74dcca9eda8126d92245ea74ed8e3ac8837354e85779f8b01bb03f2f83b4a8cbe437a9164a95f8c9c4cb316e8790dc22536b818381f2087cd6e7052d8bb447b1c98d7cf100c67f9bca8c83d156116e7710296b72f531408e59816c22d6048409b1fa8c9d6227d24d31b7ed3fa792988f1dce1259d721a5926fafd81578b4376fcb7476ba565db4e685a2d6be116b07d5b44ea9981a5e580c55d3e07202619ceae9daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd50026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
-		String expectedSig2 = "00000001e40f5b447267c994bdc775ce6d98b0e559cba26c4261d70927adf1bb11b3336858fe064267d0514a6d971b69939256b8ce8b3d8ba0d796413f561c6825e720e6c0c15632b943606aad0b427ca04a6808f4e783807111ec1baaded34ef7ec2cf40ba763a4555ae307171ad202f421548e7361dd06b9145e6fbdd34eace57be4829117e9c99bf3bd00dd4e0591c74d4e488e90623c6a4cabd5bc903252eb81eefe8b52b2d9b3df37bcbeb5bcff64203211d1cf9e1bd3250cdcaa429c2ddf2eb1bc7e7d366be2dc0dd774f99937859c40fe04a98ba532744ead3c6199ffe6f740399602658f65bbf67010e99fc57f841b91bc29ccf4b6bed49747b459b9835e4fef8a95cfd67eef5110d71e386a557562cde703369a4380718d957ea7c1377c7230800918136d3e9755bf99941e5e857a4d66478d81a89683de426bb466f9b660a6dd9e4dc28bb25e0afefd311c68355753be9f3c12f85458eede1cf39442c8d51a4f9297a91864cd98ee8ce77584174e594072df3c776dfddaf36cb1c14161965b516230db575b7e2a7306c135a61d6a4e9c8d297bac90828cd6cb55389ebbb5610d52415e7c8cccc5510dfcf4cecfff5104b75c57c20f3d3b2503f1d78e2574003902d66683c906542a5b0243cc29ab6f62535db4478dfb2dd2f30cead0ea0a579d594242f8b1d612444f126c26d878f8c0081df90971be86d6a7f29c4e3de77f7130fd2126038c5816d5d90055f85375ad8874e02e485067a791d880dc0f41e05eae9309da935d9fccbabd6141c4f8c1e393c7fb7f7f94976515a1b9a0e3b141df3ffda079a8866843425c2929550dac4f59e1ec47a4a0b25b651f6613ba08e31aff45a365e5d77c8b7bd8b88bb10fc4ca0b2d236cf36ccfe9fc95fecfd62795091676bcdc39381ffa6b53c067b666264eb738b3848969b28c3c8384212d679d164b07af95f3863445ce4ec9408f1652182396b5dc3261aaae84f6eb6e552899b0c3d8906004683326b64397220dd556c91d30e43b5ef2045d23b99faa517bf8972d6eb4afa48da48543b37bc4e2dbfdb12f871492f202c206aff2a607cb9c7e0fc580123f540525a0acafd4962bf48e3705d6800c0732944a1e71d8ed2c884b512fa1e9ff0907397681dedaed90b71eedf9d8369f751dcfe49a1cd376461e8ec3f9589f0fd59ad202d8dcbc21018811993e41cd5c3b43054175b2e4bd6b09cf27197a26e381f7f8656537ce1113085dfc90e8b97629a3785fb6ed85f442b39640a0525c3094b55416fa73898a228816461db31fc370897f0070aa42e35b21e1984feed7177691236854c8e2d1bfad5055e0e02acf04a343a6fb1b2e93b9cf002b55ac1092c2ad8bc7d54ec001aaf4473f44c6f3cacb435612f3c6b683fdac02e32e11ce1e7d5587bb5abf2c8d3ff5400123eff17098460f563b6ad8b01df3593bc35bf12ead873db365676af907174eba5f174da46690cde0c16eba96edefc3a49be70cee7dfe019bfd48965ea9b914b9d4a1f36c10301b37193216687b86847b9d72a9dba2a119a18d707ef64b1116b9cce49db2c4b949efe4c641e9d3ab4da9b66d0e3728890247d68b68fa4fccaee33225857d7fe860d4df1e3772fb0e2856a35538bfcdeba4c1b12ac51a508bac14f583207f9add69051faa5a079419b555be96aeccceef73e0a8d8c12e1afd7b7735b63aecd119a3f20fdd13687f8d5dd20f32d0047497b8fefa71884eceac49d37085a04a662083d001855cb3180b8e156df84f3813af16ca0d09d455e7262f19c5ac02eca2042098fc686990fcab671d536be0084446658f45bf308f44de4b9e68f1a0b7d0e116e9dc0933b320e47b99072d422c3c2b5bff63c65ff62051221d06d43d44aab22b68f436efbd4eee342d0e96919f89f4a8931cf88bcd3d431ef59da0bf1ef68a3b63f675093c4c7c4c421d4ff54ea6b8bc31b3e69cc05df0d0fb378a54e1e90645db9e35590b59549463d3a338f81ab6a32f52ab7e97fa1c3b808198a388a19e624214462524274009d5d84d5beb6e827f9b04a5ae5d144128d6d84f0212ffad5099d97f16d5ea51907fbedd76daec03cf1182f517be0df13df722801b217a909f90e76596b142c5c7d35ee085f1eaba2fb8c0b1db480f6264141dd3fb5b18cc4033f23d9867304f2e988a064da34cfb92aff8884df44a15a9c5303bc2ed00f9ca55c9abe751a43d7719c1d1b1529ecb198754b3d4a003ec37f11d0c1c76d41efd36c64c13f215fd86e8fd02b1bc30e9d2eed8d6a88b859fdeb4274bdc60d99eb0f8f9a2e380032a83a9db84bf1befe38b4922076cc2cfc83d9690e3b5fcb27456f142b11093dd7bdb7f1d51219ae0527c3d7aa990755877a9071d65f69c0b04753a9f3b3b00aa381ff38adfb9b98cc573ed3984e7f6dc9e629c6f2ec57885f80d816cff0d8aa4027bf132448cda4a5d0e1cffae3e88d0900d1f2dc4fede24d77168c9218c80b87f1381f4b2d84c5c7f2a4729256a64d8baf4ac4de0bd395f47654b029563c6066b59ad42ea5bd80edee2a06a62b04c0930442063fd443ebed52662325e0d610870e9ecb571edcaa992afc6bba6b7059c8d3cc0ff4f22109cc36bd33a3a53efeb16c377bfb5c42b2d60218c67fa22b84ec7fb8d44dafb86fdd7da314d357a9772365aa1deb68c786d818ff73d342bff6e5d138819f4cf0563a90c698a56925b6da78c9d07cb4d4a3f4d6b78f1efac2659d387d49cbc69d803e1acdf9155218afdae393d50d6346383a34cd286b68f5e8bdc6d8b99a1d1d7d2702906e2aa9a9a04d7abd96d129c99be31656eea08e8c3347bef29e2b44dff01358f72c949ca56f680a90b73e3c5597f8390f87fa2858a037845ee7764f56f3310cc2796168ea43a7db267e4ee36bb8a11bd3c771337941deef7676ec15ff3314a29b4ad0a679ed4bf90264401a71fdfdeada3666604fb05958fd46be35c63f1729efbd18ee60a3173ef7a709f6345986b77dd9f9b719f512270399206834ada721bea6fa1ae82c44874680b18ff57000619c7f09213e7e52bf33be4150b09cde207aaa6b47a772e077ef104e684561dabf41f74fcc864cd4e2296aa08297cd8fb99918a7b2ae59d3798d5ef19a3a98f416ecd01f42a58f8ec4d17c794cbc635ac38043159871d2699bff14697512763e9c692f6f45f07067dda04d494b47cc0f7fc62351726f83a5ea99a37505a7947d5a3306279f99599dd66255a06dd6f49728584ca036cf1952a9b469ea4549e762c077b6ff0a25a3b6dc5fe4cc67b254a1552e80471a4d2a38e992cc09a9deb211907a1d68b118c6f1430c18982810b080560e2c6e76c17e010b2b6c7f3e329707c3bfb2cc83249923483e856bd880c6133d23cfd7e5c2beb1a15a2d88ecb39539b645572c1dc9db4e2579c8a9caeb96f03ff91ed41ae668d6c8c16bf6c9167f8cdd3c0e9a5215965f6cfdbddfe7cd00b4ca702155878f9aa9141b72ee3305a48b8af6b1da38e639906c35de849233fa171c021bd15c4aa0df67ed2a519792c9e420d3a543150747db0b19b2935df78a47aff348d3796b9261baa850a8aafb7a215bb578a17939f44eb89f1cbd9dbbfcdfbd0a519a471578f7582408768d878bcc83d31f7b596bff1cabf4fe2bca47cdba816a1f3ec155686487bebc37c93a3944a497214cf3caf4b910b1a7452cc6781972422b73abca8e0571e7c5ade4effb63daf60e5f44dc1837f24ab5e1af86d025b6ffb5f6236cb7fd02520ce834d7f31d39e4cbf7d7d17b583941bf6a0bb5099d7033bba1fa6f5cf0ef47b4a5ebc2e8f66922d76a9be0bf2faa2ac172707f8a19bfeeadbe4f4c811ae8ce86bc636028f55a38013c4e39315be4a9e260cedb043122a1a4da3c6f50676ded55216fabd0dbaa659a6ac47e48e784bc2e4f7b3148739d6384bd525da874ec4c4c2d8d4e62b60be4f47f20f40eadaf54accbb106acbca466701e84db4be82be84c8c5736dace8a190519d9bdd5b71604934371cea43eafc76d833e3ce10819d882b4ee33718e74bdf95a0fc040c519ae5f2f47df4dde3996bedf64e401f85a3a01d3f51bef6c076181069e40af6c06c3a16c444322537e400a1b0ec9abcf65a5e7b39938e61e05ebbf79b6e6c100d5a30f23d446fcef89a4037cf459fcbe81e1d4e73cec2c32ef08e173d98766d10dedbae881f6d4efa8ec3fb8920ca77082aebe94851fc518ad791ae27d547fc6709b6e659a0e15e48914dd7f5950533db954c90db131b0d5ce3811fb3b5b6d41670fe450b2bcf99cde54412ad3d77c1585ec32c85d72a47817c63dcbd1ecf2c0f6bee6ec67bf82bf01a63da499df995eef91703c55bc950bf34369c929da4a5a6f4d4b38484b2ed9600927570c9fb4fc19da7f3b72c0a6479685220dbc332dd0cb37d9c9e8d8ad3df586efe65b9cacaf0aee6f27b045e40c22a305964365f4096f4cb28014560aaf0228edc507a62f59c815e17a774b63d57836c1d4b55cadc2d267a3419805372e3ab917f1ee1e8176d0d730361cb62cd89d8d6024d8fb007dccd58c6b2a15747634c705bb1f0701155c6ba5ffaac18954f74abfd27bd157d60e2e403ecd7a9a92da9c536dfb06f7a722441b5f6feb97a770da168947a88c7aad3cfa9f4c18727e6090646d4f9b18fb0534e0d5aa626ab73c5f996a15704503a9c5c6a2d28b683d49f777957120b73a99ebf040a20c1ff04a3dac545595a7f2d14e3f3d5cbd432dc6c1a8495ac16f94bce458caa2aae08cc44118be7275ee894403eb074030aba8f4e886d7011bc74b03596196906c6e0a8c3ef73c2ed6d92e71598a99e2b4b2b7e60ca462933e3decdc9461535680f65071c17d329ae91c6bef8471b12af776d1d273c5f3d39c9b23855389a643a8a9f0127f6b581e15fb5aa29fe8d60b75a2a65e219c003ae60c614c8c488693d5e95f89a5884c6b881efceb8bf666f177b5df88c606fd45ab193e0b273850b7a52daab18a692c1d016877fece51b9d73abda8471672c6fd603de198d8f9e7185789272ef7225a196a025a21a3210ec775b2ea3068840a34eab6f7d35c8b0e25c8eec6da99957c0e25b171b8e518471ef63fb0e96f22d1ed66efee3ea7f1911f2cd4384d01cb57f538030b640486cfdce7f9bf0881c8cb11055ee05eb2e9fc2d7b167e7c2fda3c80533a3e0b353eec3cc5cd90ac744606f9b2009573d00b73e11f083ed00f3a6a4aeaa456abf6085da1f95ceb6b3dc84f2660038e728c9c4b94d1b57f5194d0bade8a33367619ae8bffe710b3d422b578320d5115192a922ddd0a1f48d63477014b5bebfc948b2a2b59ee893524e060a7e816ceb664f5cdf7759cbe95714072f357e0079af966e402eebfb1a8e1c090f9ad2ab0f883c3083205f597f72d723ca8b9c30833b373e27052eb2e08377cdcb49350aa13b16b35dbb7194eae6501c9b2dd71ad873a51292a55eeab15d9850dae731aafd19cbe7837be83633fa312d7bf0e7c212b774e6be6b664b0777f01d557cffa41c23ea7737b552994096dd6f06843aceeb3c3a17f637cd5865641d90c0c6d1d5b439689a19f5182885496f553abb9b4123a956ca0dcddf5257242246636c8ea73d0cfe5db1d9fc759e723d0071cbda5b6ac483ea5f156a365b5b82e0a3273ee4293e1c230a29b5d06f9ae83f4429492ac5b764be6f209ba17d0ae0d6de2b1cb9b72bfde885ada2a91818ebba779af1d4ee4a4edfa0399c5d6c418bd702f1ef0588d5bb3653b3d7eb8786cc90b8d6ef6ab9b7a4898dc6681d4f000708a558ef157149e3fcf5f197d6ccf2599a97dbc066103f491ed424bd11c684b4e9ff262e4096a252785490536b676da12617ce12107f64a8b988c3c8d65ece1ea921d5f8c655b91e78f8e2f9f19969f31f5b806cb9898ee53ee4e9976f2b2a6730f8a77fcc4f4eeeb4f780de1019b2ea5862f7d53e35a06b45466cb8b40b68cbee1087b35415f023f86956454b053213e368565befceb5f0745fc8178dba8933ff8862c35f286447c8147a3d63ca4bbecfc873a2b49788090579b66c2ee25cbc4208ac0c1313188b37ff9a3f5c53103a79d8b3b92008044f6769dd45b4f05909d096638e38794f3a1b54b4aefd381e1b9173366065731c581c44fb970cb16f42ce1d6c0612d1e88fb3b444c58e25559de7f632ef95a9baebcc5e7ea0d7ff013ab5ac55789749eb9f250845f9f258115fb3ffe9b22cd13fcca1bd837cab33081fb3f2866ed2b0cd3bf3777054d1f61f7d070894eda7f59abaf7f0a01cb69bd765eb4a153488bf3e6c5b0f4e47dcedfb40affbac56ee8291c5cf339d8bce560312bda813aa4b48add4c9235b42541a7a723643a0d7ad7a1b680ad589c89b77571d40ebb8c05f6250355bb169e0bf3ce4a64689401be7ff53796e415c1c3264c7c85fc13aa40f35077d95d05098d9c962061dd6a2afab39f5145fe69a0923b82da0e04615639d74a2235715a7e51cae966bf4ecc94d27293cdc1eb9b8b49488a436a24ce0d8523741b52bc71799b5264612dfc0150fad48a7699d95ea382a9b0a61855c9e0ea688000a325ceae92dc744f01c034fd347ce1087a7bc518926810302b0d186babae019d8e1693a2a097ed1a291646e1a85c03c94c932eceb06085a2930c71b9063637650bf7e09ad7fbb5f5e7e429a273fd4f1a7c3d288a8d53330f2d3f0f7c9100f36cf2aa72a68226cc758f05c5643643ba33df59077a63556861bf1f491ac58c481f430a0d7984db2de3726ead1e97476476321bae08c0d99ab6bbafe7e31203f675b779a6007fbd98453715c6b64317e2d8d01e23e25afa164bae43a732508780f38236ce25e8a31121e650a93e9205f832437343901926d5121c633ff078388f21a9e59df4ac67aff27a660cf2fd407bdffcaddbb90a78652e5419069e104632299cb04bc8b9fdc1277cde95e70a9fdcedab5f41042b79e21f8362de123a813f5891c48d7281e2f7808db16c05a47c9fc4cd170f094e6502b9405aebaed273ac1ad5a5936fbb9796c9f6901846d1962da6e1a4c83ac5773ee2e6974fb97149467c644a50c7253320a9fcc3b790ce135cf130d6a9577f7b8749eb7ed892018b3b899e4be66a56fef8062c79cd169738589c744de81550381b3d6744155a793552a8d66cdf364fbc76eb55f47228a81052b7a7762f5727053196440a93ffdce13dfaed06e4977fa17817758d527d9de3baac9023c7cd1d33177868445ad9c5c809bd1fe64a2cb7754e0fc83731f3dfdc2b86ea248ae5da1a89bead60f64147a7062fdf6b5decd6234d19fabb23915a4e6b473b1fee95b68e5f74a1d3830cd3875859b4fa755a4fd9119f16dcec0f381e676cfd5a26b35c82ddcd300acd05a169b703e6cce39548de36c9eb26e1c888944164957387d0fd5fd4205f20785663858b8072b1e41044bbf84838829d5867a00490b9e547e3b7562f2cb1b866e1f032a09110166e31c522ab21eab4357012e74938c4b5c21a1a4dfd32f8c8dba7c7259087b8f19d71f94fe0476176f364d8bfa135c737253454ff96e38b628776b8fc10e05badcdf731d23e948bad72df75c3cbf14c38d745a25cc81e635e3dd46da05226a263c3000beea6dc6430085b622e90a59fc19835c3b74ecc26273620756684ceb37c2204051170adc21d0485c3f5e2356ae7562f02611fa57ebdeb0b632b96d0ad2ec503fd1c06a227b3705fb906dbc059ae84814dd61c77d5a0e0b96e3f48a0e1ad7f1f2ed564d50f683dc89bbedf99ec0335d51cee3806b12ece600e13bdadd445a2bf7f28db90052a523c28feac941ee550bfc6246a5a85dc2c7bad637e0f729b37bc73471dbae1b643e5d87e259131020605ab502b9ef0c17cbb657c53f069823efc4217fffb3d691e8873299d8266721d51c758b4078598c9c1d11d2685370d155ec5ef84b6a5d201675861ceaf658d4109f92a9e72fd83253082e1a5380600c8c36ea35457d8785e1185083dac9dcf8db9597d3a08d8dbb2611ff62d6991a8bef98e666ffcb46266fbcb8ee19f69dc369cb0b03abf5dc43e395391d7e52041bc3bb0495b3121c3a4a76f03c35d48ddbd7305bfba80e75a765d43ef16da9cc8faf3ef743c794466c52fb90ac730b8b2ada23faa3a55eded6793231f7b2518c69a37387b84b19ffa982c705eac7d6ecc0e57b017291ef6990e3a2f05fe09a314ece9b501d4f222140a2faa25261ef1aed3f31bc51c3673af200c1b3c8c858f0855d9bdfe9ea95e4443525991bbd50dc52460d0c10f8562bc2cd5fe0308f745b0bceb6fcabbc1081a1d14e935c03dfc9817e5dc5107abe901bb975f07b3818d495a1b38ddb4c47aa0a9dff86ba18d34044e6594daf49d792fff17da9f721084f8dbfce2614317fc7834ae807ecab516e7da751489558b3a5c649380854db75b657d5196e430fa64d6259875ead90cebe3120ed3a98447a45556a73ebc2f508e3b3b56cb2e5eb5fceefc1dc84ad1867567698266b42de80f67fe1f0f82d40e2d48d4779b26594cc8048cca2a209c88718a33555c38f26892455a3c808b3e3a6ad49ccfa23a609460db1f7928fe6f330626a662cdeddb0a14b643600f0db1f67d88d604ba544dee6c9ec18f23c066a0ecd51e1a78d167d3ec360effa37c661b40d911e13ab1454541851d0047a4e6074d3f81b87d3449b413d86e323c01aa5ec67683733f05e421dee3f6ee0692115f74f21094860d2b3f6ee9eb7a74014e852bacad1ed98fde6834a1230b8f16ce28c0232ff1df788f91ca8f0bdc53307b8a46eb4022c038f410214d09c68888081a6f08431827e03a2a5456ab6361098df0ef106c9112f18c917f97836725d01388d06e0637cba404ebdcfb45320a2ab10499b4c8d512882fc5caa5ce0d15982edaa1d03fb872e8b61b833d3f02bbdb3c894bfbd7a9910ce11eb95da485a88dafe60076c15458aa25ef3fda187a1364860dd207c0709767b344c8895d06b66f88a37db7dff8d42d6bc2acf1ea5f1ce6f9e9a273f0c84e0e8dcc9cb0f4af2d1968ef88961b80865bda7db91ec44d7797e6e62be3c77f6f5945ca04eb9f0154d016d5e4bdeb2b0f9c2f814920e10c7588eab1c859117ca0718be186cdaf7b7c2e949fe4818300a188f59e45d77a51efb0a9d89a9f99a5bf7e40d591d85b4b65298e4f223f00e9e4d6045d0e3a10c620859bdb4d5500344be5d2e9fbde2f01bd899b2c5932a85093aebd734eb20ca16f6efcd0ef26e453a3684db3643eb1631061dcca69cf525f117300135f0a4820651322c6ab72465f4fb114a50e990e3743e71df915c9b4435cdc4bc1d63ffc942132cd1cf8cc80aae9099e609dbb18f9e1c60c178e864a5bea1040b4e95fee935285ac7930c52a71c427278eca43f2120bb0fe2ee7972aacdc6d3002c8aee37aae7b8379c290b2fe4fb1c3eb7b9494c4dc55cdfbf4b77d217ac6d72c00acb01822c22fd8ac972fff63fef88edc638f8de8185beb7edb10afec1f344c4a46ab59408fd1bf46da0cadf7fab69d70da53b08d883312027d867dc708a2d60a9dd210f2ac53ea5288bbdd33737a9d1f34bc8f48aedad9f5792bda6dbe2da86c2ad54b0c78e0e9d1a97b612f4067a623005089352501cc4e6237fb5352c418d5c36f5ea1299df8053610ff37c6de68262744323d77e31f1551b86c04178c96d4e662d6ec3421a09661033ad4462a23550cee84e70e3019278d81410ff0098ee5604bfc32afdb688da229baac6fda3ae620b27602af50f1b34d0b41f71c1f441a5b6ec067503b0a8ae2f0e33d0b45d647252552cedeb9888ddab3c4d45c1a4a021afe1ec1c4ab0ff0fb5dbba385a9415e8cb5fc9927bfdebd93de14c4971e5e88dd493736125cf657e859c0a3df6c138e91014ddf556dae5333797a9b189c6f9a447c93f7b2d99504a3fcffb84a5fa577e2b13e678f08f072f3eaace5499e246b4c823efea98ec52517072870dcd3f35512dfdd37981fbba8e7429bad74a918eb179a8c4f8757423919371e7cd978eaf1ca264c2b4cecf629bb6c9e5d7b498e3808029e002a1cc4cd8001d69ee2598643b25be14a898121927afa0d239e4321dae55b4d9b1ba53d2b90b44e1710db107b74cb314d34ef33789727a2026d04b14bb017a23cb7c8300fee87d5a967a5a00644c1d52fa17c4c3cd605cfe96f01ba40ffa7a52d4885a97929e2d7298fac9cde983af32322b6afc19755d04932a024a45622b671980109fe3034ae9b815aecc5e328d6d57c5c179553924329b21243b4196a241b7877ed86b388f7a8906f242f9bc8e9819ef75c712f48089701554031351ad55a5c2628b398fefe40c8003ad728c5bb8f6f64e667843bc3b928e4ee76bcfdffc31e94fe59b7cffd1e386b3001fe6471d8841278e912ba4e0d7c32aa4ef338977963f2c14459fa23715871c55f2ac2a22cc9e7099cbc29aca43d314104f7463aa006a8a27ed229ea90e213b32c95c0b78040a41dac51b66bfa9a4989ed77b0b2114ceb6df08c176aa04169a011a469fbcd5b6792c3d43aff49cb2e0606f6db136c09d87674e028fd37d56ff9ac55e0f31efa4ccfd93bc0d5341db8a2d1ce5ebd2628b2c676c991321b621ee28726c18bae3a2a5a73a09a5af38b680c37ad71b051d0cd867643e83975670c92676ffd6277fb4d0c33403d06f7f21de22d76a84fe3b607a0250b1f22127ded8756b265f788cbda44e554b002eeca5d3b0622e78f284ce86c76f47f108154b7457baa011f3a3b340a1d0ca277f853f32ad050a78c0ffeb39ecfbd26630c5ec0b039ef7ac5deb63183fea1341b6b6cc0c160bc0420a0dd65035515539c3ec787d324a66ef80a5a9b8c819b7a2bb9ea3a98d14be83dc87cbac5b41ec7d280140a13c89ba6099db301d1e0f584d8c4f29a1f59412421c1f82f44083807c209ea44a68f3e1358b1336857ef1bcb1fc09b1e0fcde3381ad1eda3a5ede702e4b7d51f14b25ad88de858a2e6449eb415a846b0cfb8d0ae2cc177700f81a83730e6e084df7352969e64b2fa410a32993f58903c0990c39b8df17d3785224d468ddf6bbc3d13aceb3d6554d72cae7bd4a5b02cff0201ccfac1b426a4644f1b146f5f8661ceb22713c3cad94725c8a7d02acd5f40e66213fa7563ddef29bee2a99e470b96b0e650ee8ee27b445694e1d1a05e53659b8bcfffdefa9909183cc92a36c9efb86bfcb277596fc50ada7f413c0ce5e7ee648ed923260492b19885bf4eb787d7ecd3510f15c10ffe6cd7d14257628dd90fe41931dd3a8a9f307409896e1b8d876e615602c6d023897706c90a4cfb9ed76c1ddff939fa196fca7ad8d82d9ae8ef71a4c67476435af3d06891c10aafa748b72f80a4ce84a549fbdf8b219517acfd851c62aee3268f03d796274c7c0d74c1d95d3ed880ea35d3a76e1dddce2c87348786649ad518f12f00a5db5263a70d324999be93f6caf941e37fbac08edd3d88aeaabf1508781aeff4cae31b23ced8065a4e86e5ee1252b007ff884e63c338111b5a1131ad69a7a0ce8211a6c31f4cdd744b14f1cf824f78ad4e978b4f43b757ed48310d9d2b88b8944f595b065355468aa0b3ccb058d43c26ee8162893810db55f7683d2ff841d20aab2f49e1640d939638299d7389df6ea2d5ad994861c83b7f82160e0683bfed45d8a9c8dffa5e9b55a418b0952c4b0c582f76a57c5c5674214782d1823ea0470f9a399d7dde6a14d66e44010e3538ef8ec7bb31b06ffd4226b278b710518f1fedab126e5521f30f6a2d0b62e89dadee66dee532d8d41fd905b91468d75cb51fda2b0199e799db357aaf10247f8949e62ca965f1d9600a9f8a76fb66a4ec1e45d4dfb93675f1a87f9d22fa14c642ed19ece41335b220f2ccc9c88deb21eaf05690491db36fb61cb5953ee8dd1dff07269208a98a230dda82e88733fab9509be6639ff9a8a3460bb9555157373a9eb313e007da88181a9745ab291b8a25f0aa2cb7ee40efb4e5eb802df9b4e24509c75be717a10b45fa4404c6fe7b31d23aaebbbda44dcc2cc3928b620aabcc4f771dc7947b59daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd50026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
-		String expectedSig3 = "00000002e562f6fc4309848d94edc37e66c4fa704a494576668583b3325e859eb7c67c9f74ad76223a4a61b8f9d6bf9f9d64b74b6a8f41f64ceb5c9166787bec71f1c55119aa1025f6d9136555740a82467b8618bec24f91cc75ecb4d6dd714908847debf0185505d51b9e64c8dc9276186e7dfc6038ea0c686404401d17b5670a50002722097181934ec58ec3d871bb206f4009857a2bfb810d4005b9dd5b32da5a0df79c8d895a97e163947fc96792b806041dbe480a83b0f6c015967961173ccff05eaa59284736ef2309792c04acf3eed3b9840c45cc03ebc337e9a3c7a853f23f9488c91f27fe7440fba480e6955fc32ed4002aed645a61713b0569bbb40e17b648d67b539fc4ae94498ed6e7a8773492c6cf1d76820fde249b3574b50d9ff1f52e36f87389ee9f821d0a41bd3d224a9d12f0bf9fa54692b477c1b4fe7c88ad0191084cf0bde10db91ed8d3e639503b5a919640f63ce9c7008030a10759ad2f029a54d37c8505af0eb90e8e712e7d0276ddc439873d2c73bd75dddb936c6ab4794858d649d5bb291fe26c43bcec71f3aa11a7eb40cb8d103978cc88b9ef2a26092adaf6568ca4716bae7b7c6225feb2bd53b54685e51fe4158f534b1018d99748a2bdd8ce75361633a8a4d3cbf4c46b0b9352d89fe97797cbbddf6fbb211d845d47e4bae5a9bd752af1a055acf9dc3599f0edb292412bbb327993abb33ccee77af9fad2430d03f9d23dbace7d0081b6646ca141b3c1048967874e9f954c1d5ca23474deaf7188469983a7f80ddbcfa107edac50f032d735f38688e890dfa8944d676147a0f17e069ee4e0ed6c51e5c2311bb72786d4040d0120ee7b303f404625e6161994a34ee9a407fee7011ca90e97b54d0d458c397b8fb2c4dd4f54232bde55ff6ae23e7bce71a7096292de6f0a52993d534d513deb7309227c43cf07804af2f3eccb63e0691ec4dc38c8ed53f58822bb26101bc90437aa1c61538f48a73c4f6d0732e9f8ccee475c3d1bc28ec89328d4628a67ef167e721e09d54fb1ac4cd61bb49c0628cd99b56f8b70283f33f6148b0d14e2cb95774b3663b67261539e2b592d248289ac56ab3536fd34a43b3990dedc0052d01c4213df5995b35b0527d895ecb9a7f36da2ee3ade2181debeff39923f86982e3dae1d67edfe27facfdb8ef36ae5e14d417276a549ce352bc22911147ec791cbddf2a3f4191bf79b36221da26d7114e3c167f6d872a80ee39e5820adc14e41e25cef9300df3b93624a183c57138dc1627dbcca3e250aeb9514288d7bea14bba07f53983967cbbac39046a2b00c85b2764a2c87d52c73c1d7b267055ccd2f382f8f39d3016fb9580009a780112e53f748eda91262684512799c8648ab083dedee56fab62c5dc48172af7098e774ac7e369a6149fdc5349eb62677255f243e2c022e321b19a051d1980a35fff2d0d9cfa43afb01455b4802431abba05b6c47a73358abbb0fe2da5a5d7b70866b83dddd42148d614c049fee0d390cdf742a4f76df41a990d2013eb1c6398f4193f1409eb1ca61d3156200c01a90090e27922dc23927d621aa9cd8aa3c7368fdde0d465fd070c3090e955011fbc010d610701b12fbb230296d5b2894e18e538f566071cd5bc6c85915415be70ad983bc57081434a6d470f8322ee2a357be94d405389289aa406dcc4d0fbf58d04b4347138bda515b309fcc16c0e0428dfc9a61e821690bb059465759240c86c6633e75107f7757d89deb7d6d1774f066e37adf136ef1bd4e243299d2806e9b0104b7dff8d2ec617620170c139264697c26f43370cddf3e7d70e255f70e99db56aba6021ee7cb23ca3dfe0eafc659ba6f3318241485ce190316ce163b1e44cfaedbb9dee53e69418b070b8a910e195aaa8a5c627a1a971843aa970e5d3daa41bf628a5acb932412de7a0f757fefdb4b380650a9336a7313aacd7de945883b3807c2f96e741ffc3d04b8377d9715386084028bb4f598518ce31c64455c58dc9f8b8694109e940b9b78bfdd45dbff61d5bb17713d6774078a853241b43dce88f4dd51c07d43037864b05761f045fb3d1f45d80784334f3895beaa40c43d4edb3bf14229d438f8944276916c7b99312ad58a5e295adef49c4ae1582f4077e5e42759788dfd1bb63e1425725991c8b6bbf22597353e65908bc76be7b4f737d716d294349b4d17aad1a4c523b67fdf7c54a627d0e9d459fb9de783c1012d72ca90f58e18c84c8472bac52ea378a44105018f501ec476dd53632928df294fc7eb83d06b381295cfb2e47071d9b6f1a9bc440999771b9f2a89d06b36647dc4f787696c3f09b216f2a2f37567e39fdaa88c44fd2f1ba92969798d6812f2d43cfc7b15efaa706cd8995368b1d73b86551cca835aa6808f56fe42e34df175d653a619a3825ac15f1bab5e4139f73cd555a7319c8221dd9488b0607e9c73320968872c1ef68117f083fc54d4dc4ad87f4d3e1016db6d70dfdc427df4ec80a04ef264e5273615ba83932cbf0805f6bb31fb0d8293912fac7644cb4d8d17d38809a6bcaf27e0c06ca90d5ae6a28d2bb8e9026ddbd8699f43bef324e1bbf252d1e1d476ad4bdc5ee8966149f93471dae07dd2f46e668afa94838c63c432340e0d34fbe77434c945b620a23fb7559758def29ba646afc7b47f9ac58446afe47125e4a1b21cb9cd0e2a296cdfcc23a2bb113605c414d072ffe32cc74b9a6adb1f1045799d657dcf300ce40dd040bdfca91188d31a847d4a684da65eb9bb4ff9cd60dd99581df275e2267af6b9ff913506e39e563f63cacb06fda15c072d4192966ea623305ce759a72fa2f479cc5e46826c297f531e79b8da89b10ef955043aef04976338a9719cb650ba8ca4410f9436e975fe88adc1e66d6c9d2b60d378df2df934cbea13a5ccff890006279477d25e10e11ef86cabf240535e79566e5251bbcd824aea9a39dcbf450c18cebb3f4bb5a1561b1a1ce792074e6b6fa80ac3662a5b921027ca08f8ac48f0607234b7bdb707e99075e1058e2e168f6e4504d12b9d8ab956335429efadcd2db1582c5de2ab1c9a245f87efd381715e7d290ea984c9502b6d25ad570849a831dfca6d891391da1ca4130f05c610dbfc676e55f3d76f9edaedcdfe6ac6d14b7999234af814b6693a15eaf12d4684c767abef5c41c632059b04225c8484c52f856bc2d92008a924004b4606b097d39ecdff3c15d2135f0fbe30d2e81ad2e1369c282752772760928ed83820b6dda6deff91bb6349784fe45393832ed7a63e9da261525e5d53f2912f94e5a113abd635d69f61085621840bc65e252c51e29b722e4ec9211b739ccaf3d1f529ce90ababb3ade832f25b938d246087234423c06d2b67931c6b3ca9046438a54343d6c2c27d8f1b50e00100373ab18fe012b977c210df35bf469f9a0d1d6479361b5d0c0bcc9b2adb4d1795e3aa8b9bee11221f3ab5b3b09e5e021d991e1eb3e783302852493a15b7c1253cb22e1c09fa6bc8519810a00f5305798b3c2f539d8bac5d268eb3527a8b0411f4c676740540fb0a24915c53f05b4a37a9f30024756d0288e144847a2651ff8bcb40d0c2c189ba914fb25d4f413d4d0ef0bcd2c261639037cdea16394e0c42163b61366e4bcf554c291c252b10b3570e32edbc17ba223b79f323da831c83e37961cbe73cc67072bbbe1ccb009bd0c2ae4547ba9587ad05abcf479f74acbb5c7f78150714e356ec88de5176084506126f5b21b861664cedede54eeef7ae78f8a3a61dbab5438163d0190655044c9a5fc848a0ed70b04c1ef62492aa1763e981b30fd96bc84db050aa3aae1c913a2588544e45fa284424835500b183a4e6e61649f0b5fc1ed255135bdc204b2fbdfd35d1bb7e7f243b113d59c165e10f385569d3727f410b5ee0a25f25d863ea28e04d10cd67fb6b2e4792c4ff3ac8442c0f4c48c8a012d5caed2f4291f6b3f8aa94f27f075ae7118170e61b6e5eece5c088278d06d37e2171e2809f64d1a4762cec78a42e427373f63ee54f93557ddd46d093e4af86223800cc640b872ab0d4f8dcd1d86fa5805db002a86f45c50089dc292081840fc5d7f3e5bd82754452849f777d0ff6baded3ac1e8198471b241aeb2b5477aac62f2f5aa39a30580c2d233324d464499d0666793ee80ad0a03356fe077b1d23a2185b9dd122a62860b5c639de77f5d7b502c6db66a1c110dd596095ebca95c2fa003585ff1b1424732f32b1e209eab65049b6909cf5ca81e2556d0b80f8d227ad734e6a9dc5fbeb5fbcbf77cba4139d9f09e4e939280807b22b947f8edec895f1dc7b770f1c341aebffd4e1e4866e51bdb4867b30f600c6af40b86ccb2368eb8a769a879ecdd3d3bd52fcbd8503ec0f30cd4fd0cb2b51b9af3f80023d5459ab26b485a486e3c26de41898c86525dddb8917e58c374d395e85b299b5313122e95a23ed734a2b3ba6575cd8de8d61e6313c3066ccaac5d38271e93a39583f1fa01bda4a0adc14e6030fdedb5b1ba2bf071a8351dc83e9e492d68804bc4ae8d66ed93f5f8f1ead4d13394c155006f3ebba5f184b3aee9f9a0f5da94c1633ba2eafd679b8466056e41fe844f7f86e37f5bac0b69abcb4e9fd9e04f35eca9bcb68e50f28db177b84df8abb52ed988fb06aea6b95120c9a8f3dabb86ef40c99b1de25058071d0624409d540bc557b2b494ddf39ca018af1898eab2bae7693cf5542e7340e355b974c9e6309d681247cca5f5865225a21368cf41be4b0910e0df5be4f2aeea980d3b46b30bc644e6ac3e2eaaf7f3176ed9fd54b3a9d8729204006e266b35c3db40a56f999ca3a03fd6447ccbedd0ae1096c8b6f9ce462c024d22afdcaf0a3f38f8433dfbc494e98316761579e164d730b3a4cc552270d95ee97c38e02bb072cc16b88058e45ed25aa21fcef5ef55dd44406d712981cc3c670f283f80922a787ac8e1c9237700a3c38daf7a9387b4b778b96147daf7296c9929d188a205b73b8891d1a0c58b2bca842eda3064da08abf2d4e6364c1e7bcf3e35fff0ec195fe729725de5db371b033a8d5fa9a707f722477defc729bfd8c324fb9fb9331b91ebbf1ccc99b509c5c554be57ae6ad24cb5e60eb96a40e9790b8065b33168ba10514c08374d5c7b199b6dd9f0615a7914d59f19e9252648d60ad3f423e963f57fa194d7d5568c94a6a99c2a26ea037ded7e89d98568751568d0feed980ffb6d502b139daf32172af2deeb5a25928d20b12ebf4dc041094e870a9b000a94d6d63c9ab93d77cc9f1cf36b5d5f180c4a7e76bb394b78db19fc3e1d3719bbbcd8a5e377394cd7fe10013f754d5ed862a79d1fe1ec5458d5bb10e9f5310547084b2abc4d5d4f4850f268eac72dc8e3e105af4f63550b9b4d2838f0458f6b9b19da852ecf3c4de6c7b6411f7494e447af1899ef0e414938b152951553935ae512c84af5910af4892e8acdd08fe7a77a58cb601a4156fb583bda1eb00fcb1a6a95cd60f13f6248dfd46232bfb6078949b1ea583e8bf5909f57b7f9b4cdf3bf5dd7ddf1e4dba7b7aa6130da4eca58b0f9a07eebcb4019055caded4e32616b13c6b041d459bfb4c7670abf8100c5e30dd001db88f017f7fde72ed9da6928f0865b49ae73d63f6adf88d4ca7258ce4096b8221be737cfb816302a292209d4d7dd885785f51f6bdacc3ed4f9ced9a760560ed341732cd0b78ca888962d637c5d1f01682898af9ac1e56cbd4ada7c3255b3af91e5ea6b0a22f314595d85d040d0b0cfaada746620367516d410ac8e798ac07a34de6a8b6aeb219e1ecfc01e5ddf1b54cf7d32e43ffbfc34da07a6e1abefdd3dcbe814b3b3b885a5378979176d9c0968f753201746764465389ffe8aafe3a338853f6b1da6545efa803d60ecd2ad310b043e62bcb3c58bc2ac7741cc02c68f4c84e1912a67806f82392fd4264f691aec7b324324ce088e6640a894a9e5ee6e8c0a01260eab1b42bd20e004230a1182da42d6ddd57c0c3fadf31d913509e360e1684de3629f39ba8f9eaf8cf4f4ae50a60cd7585c4f5386a46aae959e2161765a65b3d8faf2abe7fb647d25f5b00a1454663e17899a92f382817787879f4d2a219d2fef19100bfef25b735de8bf8d00ce5449551a91ad0bffb6345dccc0705f9fc549ed47a1955c4ddc9aa06e4e51a165ed0ce02f771fa872a6cd82c49b8084a21e6e3b1d0ac6ca46f79b24ce72522965da32ef96f3e7a17f87282abe14c993e097d9988f016db35bda1120c0cc5317206fb659ed47e4adb17afd03a7b0572bef4d10abb0d2c9c8eff110548fd87c6ce1e46506996c0267b2af1c008c20e85c1a4f59e459ace14ac428572b7d9af57900bc5145be26ebe89f17c737c54f0a353bea5c2b5f5175793f9a34dc7b22b84760f36476be33e5a3bd5db623444c97f0256a84d4524941bf5446fd6f946cec82e8d4e47e2c48032a215dc26a664d5b7b800b6da6d6322560f7fa3ab57058ceaadcfc50e91d1581d6a8aef99325967c3f32e58782dac661c6d1c4402d654007f5bae4f41aa1accc50e378a563ea03b8892187bce7ab606e493b0e2c526398cd290d04605f351d4b927dc7f34d3a5ca8d61946d753421c58aa145931d321ff225d26c5abac68755e08af31cc40476eb4d4de8f4e225de3c48093083916d04bc6890c1bcfd07b47fc0001adb1c62c382a96da673bd7e34aa885bda8c91c19cec4f517475f58b2545c8d34176b64b499cd1daa90d2c9e63327ad5343a79e0b1ce052d72bb6c9382cfde957417a90cecc650366cd2f18217cedcce338287c48d2bcfeed7500a618a1b493aad76a442aed3be226ec7db2ba75e384b6c07799699f7ab2f6e86aa5b0bd24bc87719e700aaf66c4962a7856638e5a15a321dbfbcb0c8c38f7058f0baffe0b99fefa97e5086c3389d166753c0be24429b13786aed835bdc227e3c03e7a7c00bd34c526435ff3d6b6b4804e3f83b888a23e364fdef47ae8e0b736a77d95b1f015666d84cb79bd8b42e4cd5b37efca008e883d9cc0650f8132deec4dba392d6966390510d2fd509a1674406b4d90c0787268840e74507478dea704e8b41b7f78b163d649a27c59c75acb515ef41a8ce0f615c7ab05ea6c583b8f6aa82e837a89872d487e21bdb73dbd242628b331810fbced954e44ca161b0082ec013b019cf90782676d9b8e5b5ec63f922f1fb489a57d593e4f93afc0fa725aaa06feec21b3d81e38f57a30fe473a90efd043957dafa3f121a9f981810689bb4ec24376ae897e8f82654043ce6fa2f908ba69aa663b80b192cfc7822f1f06b655246b764593f8ac8c0d8f8bf7083298ca600a32f6cdcac230db8adf388e241b5fa008a77121ac49561429c94f2bda4177755df4d2a480cb4d7cdaae17304219179c2efafa5183a32fe5c4f7835a59e1212177af2fd6d88b87892d7d8a529e68e530dec3739a185dc56c28f0e8f77b016588c4bd4106f2c445f340c9ec4d8f618f7dc1741be067c5689c79bc0b2f8a73eec36ab8fe4d69a9f12c179cb6554f40ecfb06efe62a37e058d9d7c7a79bfc61c708f71c976443ecd9f89df77f02c64eafb76af0bde669a06436abc66fda04cc6da9a966fe16cf7da7ce6c267c8d74e3b1a9b09db419a0d52ca26b0f3b00e212d2312c0696e3e448d8644441f072ee8bed53861df320792680ff0087d460d524db562e363b8efd818b60f1274c9a7afc6b670eaa94980cc0e8b6ea5bd239e7808f1c214dec43bfb4131e9754ff83c99b954acbfb90c5b1cb7b1a297e28f1beeba26e28d3504dfc6b57e68885e5840c4f4746f280ce6b909fde4ab2ad11077ba15433ab6cfa42de6079de9210ee69097895e12ff41777dc94db76e41b59b88c1864f10be6d8aa80abad8018140a94e1c7a47e2a133f6caa56291caccd21237b122c30da82de2274aacb9ec476113daaae7bcd677a970c3c62b9b8c91cb21c9ab0108cfc4dee362f2346cafc43fcb67a0a37f2af638e1996f1e8d591aeb31c79482102dd0fa0ced6c3421e58bffa219566b735a4baf652df736a74183da30f9863876ff3da528971d67648c01a739bad1e026825b84906e2653a12ac77c24c1cd14369f426135c2029a5d9749be5f47c0d87fb953c2782a475dc406700c2b61fb7ab4faa8a3f23fc23aeb964677a1f7d55a2817b122cc9d120f5c3d751fe7fe950deb989fc13ee7d53d1052d322816b844685f122f7fa321bfb3499802aca9c564d41b39fbfff5ff777873ad1e40c509ac2016eaf08736334650d4bdb4a7c895033a16e1c44acced720fbde04982310639ea4e92bfe065ab16dd25edc4554137eeda0a37bc0a312558a8fc6f2f5c8e1a5b3d81bc75eade5a84b32f81a638fce94181bfd540a4a0b7411bd66fa199da76e10dac8abc04a3738e40ac28c4b6b0935e828b3800bd7d21f5f7ef46ee1cf05f12b6cf335c45fd4093df9d87852653a3717843f3693c240fff8ef9b26b76166ffec4b798490a9015d3ec7740465a0f42ebc2192fe2c5c74a4f428e7c9c86697aa05fb719345dd94a4784dcb9165b0f56f650d8242f89aafab44558a964671660da5aef2debc16172924bdd77cc06981478ac1859cdf0e2a24d5d3911906c7eec01d3a2f7cc6c0f05db395d7518073d7d65fa82269ca90fbed3b207beadc8ffb0547d3e321887fdeb208a9d70d1f6756c8fb7fb86fa05ac33a13c208960951922ded8cacf138b6ceed37245f960a7efddaa5d2111eeebae7de3a8704eac7ede1ca90077cc78d0b1c61bab486db05c7f728cb1a3983eb605c82ca1a36d5d4a90ecb7920862594699b3384b33fbdf22cb89ea98bad56b234cc403aa57dbd140cf0a35900d8a6d7948ef2ee5418db27a7499e37db44303ec8c5dba233c7faf1c73005a96a082c3ea5af948b3fed72386f2a460cbce949cbf9732282667ba64dba04728333f0ad5b6fa63e04b5373b8d8abb993841439aa51ef4ecff6893be4f1540985c3804afcb1d079651c03acd305da29de2b8ca4bd28cb422f11f28f43bbad5174ec7bb6dc7cb5b81afdcbbd5e9a84bda0aba6d06a92323eb34a9774c9ea3c8db37280fe7e2a1b8e9afb7844b30c07024ee283d2032df6cb3e4fe6acd1bc7dc69b75bc1015fc71ea0747a076a16101bfa4da39e647f98f753fd87d79a3fbdea6fe8739f460ca9cd2fda2a1b4596441140d42059c5e3b41e6202c8557009ead726912d8da3b728f4d98aa2cd32cb76cbacde8978f0f374fa66ecef2bbba1509d2c37df52274793a586439560be95b4ec9db4f8488165330fc97851fb2b7bbdea5e2765b2223d80783d675d52c115948fa10652869d91cd947ad9cdb27aea8c78f5ebb9e3ae6864f6fec3721412fa875d991017e3e6f56b7d440ccd1ef04daa1b896d947daea3d194591965396d0d257fe4bfb71c4b5c06a76d23fda40b603aba0557cac78062abdd6fc47647b5dcbb6fd8409ab67645dd40e28db17af59bf65694e7bd71a648461d006021cc1b76c4075cb9bd05b5f61bfa22767c6c967081f2554e046a120cedb0da59136a5d316bebd3401a91041d6d3503209e43746b9a3e5ea2dd2e64a15183f5921710659dc615545bead57d72e6336d393dd9a94c8ebfee9de0e1cb30cbb116e770f0464a2972e7eb2a8fda7dbf9acdb4bf7cb6890bf4dd2afaade4cb50e7d51fe4d95235cabb34eb63ebaff19f242834943945a4c525b4f9d5c2af0536cf3628525ca528ddf0cebf6f751313ecb64113c061f59f5b992d830242f88b9f06d8e9d5c7eb73f6f9991d393beaf70a53a83ec0c31074df4e6c99ab531ff2c2177902ab58c726b6e0ef1c69c43134760cb1b158e8bb0bcc15dc54d2a98999cdb78b33a4fa21121a46915888046e2c9d7dbc3632c4aba535b1522d7b6255d2a03cf1a216987c073e217dd997a989369a4e227d1e2b9f2462ad54cfbf31d591f9a798456f92a3097acd4d20199a4ad3e66a00faa9ce0539ba5547f107a164b6d4cc92088f64cb343651b43e67f0aa6bc0a0256d4a314cdf1906cec1857786cfe3a097ec9f3c7cd46929f1396aa9ac631a7925fe1e81179c43670d720e00ff4d5088ef3bf495c01f7b7c649e8bfa87c69749e6e2a51a99b478ff8ab7007ba5b3ca135df8699c448da129dc6140ce34bb672b05fe307bb1633c96e0dd84ae4dc1caf75b2e2b3180ced90eb79e76c1752b77ac5e3aef2f4bdef3c667df53dcb95ca1e18d6405f4b6310108c23cb2c6bfc0c17d9a9c62baa870e3fe7eff300201178fe00462244bbaadf3607e60380db37e3836035904116697af246e5a7253eaa194902f61246a76bfd41e7531bf4db33ca0a3cf8761780edb67e308c93ea5baeae71b368599f7563658c60381c3523999699e86095989b8ff6275dd20402e5b35b831efe8c4755709284c4ea09c6702b209864e63ade180ab0c7ecdb376c510c55bee53d33577a1ae32d0e6e8d714e9c6c40193ef22e771b521ffb0e246176cb09c30d28e98a439d935fd4d2f1be11548c9147425395e99bb7ee0ac880a43c9ae63ee086728e9db1d57e0324dec05f3be6ed1c4d192cf18e8a6704375ab9632ec065773a206b88eb2d1e36e428e4341ad6e300b1c49f636caafdd28b0546d6748b9211df0273ab27921f3a767efa4e0d38cffbbe2f53e77b7c888c5cd81369602d4d2b9ab3a92f8180306b7e339a18c73cec1a253c319c46e1b71ac8910a11df903cf6c4ff5b392a96b282f849819b47a919ebe9966bff73e202267cffed1deb3c9c9df6e88c4b292b8823e2efc111af20b6c25719a3db8e1451e710717d7412cbc08566d55923784fc0f4a70f903377d73b29033fc61a445fd57e2db1eb9be74334edf13dc5b9bdd9256e158ab3642b1ee3bf8b7e07747abd2114c4d8e45443e14480cd3b6998e6a4dec41290d854fe978bda36f2102506dfc7cb75950409bf174bc268e5e6f9192c9531ba98c0e66ebbb963736866e0f2ad703ee08ef9a5746e6dc76d17bcc192bf380e94a493319621e8adcbeefbe80bd0388f8dc84824471f040dbf72e1d1f068c77a695fc1dc47f18c70ee7e697f0106420e8ec0145b8670d4a3642d776ab7b90be5181a1189457afaf701afa3d4cb7e05219f9c031987f6d4c6cc49d8c5c0e6b67dc5b066f86a74d7e7bfc851af05f23d4fb477d6d4e6242bcf58f1d58a5f0f363e15833488fe98a4306efd560550eb4ee755c229f41d69b9947039ceb254fb64a24792c75be2a29c5058ce0cb89eb86647d657c146360943a29047e3fc3e84936d1d35bbc16885640d3510035eef02a36c582c86fb280292880518873d6bd44b8846bd14573c28ff95c7731bde4d3750dff967d336526773dc809657e589c023a96fb23bf79fd41539195abd3e21f538aba3da6b8b276dc69cb9938e57bfd19ee4ca34141a86fd509644c32dd237bdf26538f52ab287570e35ac0ae86e62dcbdc8a3f1c8ad4ac0a756226439b80ed2f3b5664e9093ba4514ad52da6f1f863b67f4a21ca1863662a266920dd7750dd863f22ede785775b89002daca5c8f00e31c0f9c4b2716481d5830269172e3596907aac87f4e81ef66cfd976e523905534c13fd9f006342d4ee39584f7c196f8c6db1d59693229e4010037302f978b2d8ab5079ea1ef4f431bb9e11ecacdf9f58ebfbc24c50f542911a9b950491bd4a6593aed15177981a94e0723a2dba5dd63fb87cf6d475bfa42d0df7d0fc5b3b1bf9621066c1f32a45061363c3175074fed91377556c63e104186f7609b5a06e4c3902a8404e295929d4d786c15a48be408d8745d9674873c5a7ad4756e41e64188016191e8da12aa4b30238b29e4d784e8950e808931f12424fe4dad6d8cb0bb55e8c0440e0148f04d50b06acd17af3a47854344b9a32a471853b0ded34b056bdc956318cc494f920330d23606698bde3caf30fafe14287fd4b2351bdaacf2cd76b30c90ef07a13dfd40e63f8ad23050b3819d08e6aeca55193b23b0e748b9b358aadb3ed7d1e2affac941f7be7fa35005ccfeb02929320d8db088245cf16f5bf811a90b37f8e30c7ee4c8ff0aa018a109e544f5c51e844152315da766f4c19033796bc9e21e981264f9f118947f1775acf373800c77a063be4473994d511c1e4919047f00e988c8912a8647fdde6c3a8a632026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig1), sig1));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig2), sig2));
-		assertEquals(true, XMSSUtil.compareByteArray(Hex.decode(expectedSig3), sig3));
-	}
-	
-	public void testVerifySignatureSHA256() {
-		XMSSParameters params = new XMSSParameters(4, new SHA256Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] msg1 = new byte[1024];
-		
-		for (int i = 0; i < 3; i++) {
-			byte[] publicKey = xmss.exportPublicKey();
-			xmss.sign(msg1);
-			byte[] signature = xmss.sign(msg1);
-			try {
-				assertEquals(true, xmss.verifySignature(msg1, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-			byte[] msg2 = new byte[1024];
-			msg2[0] = 0x01;
-			try {
-				assertEquals(false, xmss.verifySignature(msg2, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-		}
-	}
-	
-	public void testVerifySignatureSHA512() {
-		XMSSParameters params = new XMSSParameters(4, new SHA512Digest(), new NullPRNG());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] msg1 = new byte[1024];
-		
-		for (int i = 0; i < 3; i++) {
-			byte[] publicKey = xmss.exportPublicKey();
-			xmss.sign(msg1);
-			byte[] signature = xmss.sign(msg1);
-			try {
-				assertEquals(true, xmss.verifySignature(msg1, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-			byte[] msg2 = new byte[1024];
-			msg2[0] = 0x01;
-			try {
-				assertEquals(false, xmss.verifySignature(msg2, signature, publicKey));
-			} catch (ParseException ex) {
-				ex.printStackTrace();
-				fail();
-			}
-		}
-	}
-	
-	public void testImportStateSHA256() throws IOException, ClassNotFoundException {
-		XMSSParameters params = new XMSSParameters(4, new SHA256Digest(), new NullPRNG());
-		XMSS xmss1 = new XMSS(params);
-		xmss1.generateKeys();
-		byte[] msg1 = new byte[1024];
-		byte[] msg2 = new byte[2048];
-		byte[] msg3 = new byte[3096];
-		Arrays.fill(msg1, (byte) 0xaa);
-		Arrays.fill(msg2, (byte) 0xbb);
-		Arrays.fill(msg3, (byte) 0xcc);
-		byte[] signature1 = xmss1.sign(msg1);
-		byte[] signature2 = xmss1.sign(msg2);
-		byte[] exportedPrivateKey = xmss1.exportPrivateKey();
-		byte[] exportedPublicKey = xmss1.exportPublicKey();
-		byte[] signature3 = xmss1.sign(msg3);
-		
-		XMSS xmss2 = new XMSS(params);
-		try {
-			xmss2.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] signature4 = xmss2.sign(msg3);
-		assertEquals(true, XMSSUtil.compareByteArray(signature3, signature4));
-		xmss2.generateKeys();
-		try {
-			assertEquals(true, xmss2.verifySignature(msg1, signature1, exportedPublicKey));
-			assertEquals(true, xmss2.verifySignature(msg2, signature2, exportedPublicKey));
-			assertEquals(true, xmss2.verifySignature(msg3, signature3, exportedPublicKey));
-			assertEquals(false, xmss2.verifySignature(msg1, signature3, exportedPublicKey));
-			assertEquals(false, xmss2.verifySignature(msg2, signature3, exportedPublicKey));
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-	}
-	
-	public void testImportKeysSHA512() throws IOException, ClassNotFoundException {
-		XMSSParameters params = new XMSSParameters(4, new SHA512Digest(), new NullPRNG());
-		XMSS xmss1 = new XMSS(params);
-		xmss1.generateKeys();
-		byte[] msg1 = new byte[1024];
-		byte[] msg2 = new byte[2048];
-		byte[] msg3 = new byte[3096];
-		Arrays.fill(msg1, (byte) 0xaa);
-		Arrays.fill(msg2, (byte) 0xbb);
-		Arrays.fill(msg3, (byte) 0xcc);
-		byte[] signature1 = xmss1.sign(msg1);
-		byte[] signature2 = xmss1.sign(msg2);
-		byte[] exportedPrivateKey = xmss1.exportPrivateKey();
-		byte[] exportedPublicKey = xmss1.exportPublicKey();
-		byte[] signature3 = xmss1.sign(msg3);
-		
-		XMSS xmss2 = new XMSS(params);
-		try {
-			xmss2.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-		byte[] signature4 = xmss2.sign(msg3);
-		assertEquals(true, XMSSUtil.compareByteArray(signature3, signature4));
-		xmss2.generateKeys();
-		try {
-			assertEquals(true, xmss2.verifySignature(msg1, signature1, exportedPublicKey));
-			assertEquals(true, xmss2.verifySignature(msg2, signature2, exportedPublicKey));
-			assertEquals(true, xmss2.verifySignature(msg3, signature3, exportedPublicKey));
-			assertEquals(false, xmss2.verifySignature(msg1, signature3, exportedPublicKey));
-			assertEquals(false, xmss2.verifySignature(msg2, signature3, exportedPublicKey));
-		} catch (ParseException ex) {
-			ex.printStackTrace();
-			fail();
-		}
-	}
-	
-	public void testRandom() throws IOException, ClassNotFoundException {
-		XMSSParameters params = new XMSSParameters(4, new SHA512Digest(), new SecureRandom());
-		XMSS xmss1 = new XMSS(params);
-		xmss1.generateKeys();
-		byte[] publicKey = xmss1.exportPublicKey();
-		byte[] message = new byte[1024];
-		
-		for (int i = 0; i < 5; i++) {
-			xmss1.sign(message);
-		}
-		byte[] signature = xmss1.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(publicKey, xmss1.exportPublicKey()));
-		try {
-			xmss1.verifySignature(message, signature, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		assertTrue(XMSSUtil.compareByteArray(publicKey, xmss1.exportPublicKey()));
-		xmss1.sign(message);
-		byte[] privateKey7 = xmss1.exportPrivateKey();
-		try {
-			xmss1.verifySignature(message, signature, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		assertTrue(XMSSUtil.compareByteArray(privateKey7, xmss1.exportPrivateKey()));
-		byte[] signature7 = xmss1.sign(message);
+    public void testGenKeyPairSHA512()
+    {
+        XMSSParameters xmssParams = new XMSSParameters(10, new SHA512Digest());
+        XMSS xmss = new XMSS(xmssParams, new NullPRNG());
+        xmss.generateKeys();
+        byte[] privateKey = xmss.exportPrivateKey();
+        byte[] publicKey = xmss.exportPublicKey();
+        String expectedPrivateKey = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f48df90f8e217076d8af6327691321bdcf63668c4bd28d021d49f2334eca845fa3073991049286c0eef5dc7f23ec0b31f5c1bd1e5b8edb2403ae02f292f6f30e";
+        String expectedPublicKey = "f48df90f8e217076d8af6327691321bdcf63668c4bd28d021d49f2334eca845fa3073991049286c0eef5dc7f23ec0b31f5c1bd1e5b8edb2403ae02f292f6f30e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
+        byte[] strippedPrivateKey = XMSSUtil.extractBytesAtOffset(privateKey, 0, (Hex.decode(expectedPrivateKey).length));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPrivateKey), strippedPrivateKey));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedPublicKey), publicKey));
+    }
 
-		try {
-			xmss1.importState(privateKey7, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		byte[] signature7AfterImport = xmss1.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(signature7AfterImport, signature7));
-		
-		XMSSParameters params2 = new XMSSParameters(4, new SHA512Digest(), new SecureRandom());
-		XMSS xmss2 = new XMSS(params2);
-		try {
-			boolean valid = xmss2.verifySignature(message, signature7, publicKey);
-			assertTrue(valid);
-			valid = xmss2.verifySignature(message, signature, publicKey);
-			assertTrue(valid);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		
-		XMSS xmss3 = new XMSS(params);
-		try {
-			xmss3.importState(privateKey7, publicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-			fail();
-		}
-		byte[] signatureAgain = xmss3.sign(message);
-		assertTrue(XMSSUtil.compareByteArray(signatureAgain, signature7));
-	}
-	
-	public void testBDSImport() throws IOException, ClassNotFoundException {
-		XMSSParameters params = new XMSSParameters(4, new SHA256Digest(), new SecureRandom());
-		XMSS xmss = new XMSS(params);
-		xmss.generateKeys();
-		byte[] exportedPrivateKey = xmss.exportPrivateKey();
-		byte[] exportedPublicKey = xmss.exportPublicKey();
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		byte[] sig1 = xmss.sign(new byte[1024]);
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-		byte[] sig2 = xmss.sign(new byte[1024]);
-		assertEquals(true, XMSSUtil.compareByteArray(sig1, sig2));
-		try {
-			xmss.importState(exportedPrivateKey, exportedPublicKey);
-		} catch (Exception ex) { }
-		xmss.sign(new byte[1024]);
-		byte[] sig3 = xmss.sign(new byte[1024]);
-		assertEquals(false, XMSSUtil.compareByteArray(sig1, sig3));
-		try {
-			xmss.importState(null, exportedPublicKey);
-			fail();
-		} catch (Exception ex) { }
-		try {
-			xmss.importState(exportedPrivateKey, null);
-			fail();
-		} catch (Exception ex) { }
-	}
+    public void testSignSHA256()
+    {
+        XMSSParameters params = new XMSSParameters(10, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmss.sign(message);
+        byte[] sig2 = xmss.sign(message);
+        byte[] sig3 = xmss.sign(message);
+        String expectedSig1 = "000000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf80c20270a1e779fd234d57b893ca841994e128a6df3f1832eb5998b16e3273084c84e049da83c891856b5fb20f17e1ecb6873c3a87e1a630e327b36981a5622aad6044c3f4d8296c4e744bca233a84b42f0524ee3ccf68eca45b66de7dfc123cac6951a57a802da0b6aa1a1e1ed698934dd2200331d1aeebbdfa0fe13f08ff8d3d2f5c9d542531e6021e34400eaf9d84911ebc0a2e84dff10dad8d078ebb356f522878fe61adbc7218d48af31516cc40689d06771b558c26a700da094d2bbd856c512c77c392b368f817e13fc10050b181aa5ef9294e6d90f1ade14f0774e70593fef221958f95f743c0b5db6a2b782b3b9ab10289fdd2b77d333d5476a0a3a48826e8992cc5a18cfff739deb931f5444b564c86d199916a947222c5d728922d912a30ec58a2422e0ddfe9cb018d99cb3b5df37f40911d2d86478280ec18f4f61edf22cd1eb6b785fab62ccad315afe6e1945c71716cfe4e9a549f591d258d2d128bd471de0054d1dae6523932f036d974dc80cd5eb123f5c4cf97b8747dcbf0ff2daaae1a2b928ad74a1441eac07053c9665650bd197afecee8271e54919695d83286d635afe6977b30a653b2792aa8d8ce6597c87e046c603f44355201981a03095230dd060c88b76fc48fe6714ba50cf8fd4fdfc537e66c2717d96e9f20a56c4d0858d1ca68bbebf405457ef5bb3cbf7af08063174e9867aaa3d7d65dffffecb2bf24ea82e05fc453e4c078395ad9996e3c2b0aae3311ea4159c1e59ebe3dca21a0adb6ebe7d495d96ade93ac1403804fc5d4b5c6d632fcbab7fe5b16394c60b076d2d02c85d0f7dcdc38a18c6599367e1711c6bacdee11ee16ee8caf9330809fc08961ea8f8bf7f1a77bc51f9a4b8b2bad1c55c947fd70986cfd6bbdcf6cf77241ba949b06f63ea54334b3bfeda451aedf7cbb15a2829cd1cdac3d20042c24faa5e3022634f43448d3fa88344b2f0d653940e5e0c01412d6a4508bbdb55aa08ce709d11d6b9e88fff643babbb4791e2dfd8854d66844ed3a2b58461bdaa5112781b92c03a8efaf344316b404c08795a144ee4a38914c88055bbdbeb3509b8cc7505eaca973d36dcb17ba354e9e0ec7511f88170c04a237610d4715179c6a316ef3500819237fe91135e71f90023e5e221b30e79f48e1ea84ccb1df84e52cc84ffae809a3b71176615fbb2c9e28be9f9b0833ba669710add8c972a0b7d2d9b7736d4aa35e2db99d70177015c4e6771f574d8432d25030bad4dc44ea6d4abac7be799361c3ae29ada40c0bbef4e1ecfd083bb52d7e91e17185febcf1e0ec0d566ed27c248427e50994b7590377762ae1bd7413fa4d73495682c45770c2bc01f56ad7a5522df28c46c1e683ddde8698353ff2a8ec29760350343e2fbb4477341113479d04be6346e783800516ced69e657338407125140ffe9c7179c3ea074e0df7d30f101e2f09bf2e6958542dd0d3644263aec86aa12beee467a8ec3aaae41fef986566a0991e867426bfdad50e5c8c8229fcd2bc85ef6f4a585a320663c673451b64f55aaba51bd160f857a64e428943e03e723873753da48b300a1987585ab1630729580a50502225c49897f208534e10e9def653f79eeeedcd599e4a3b3d5d9fbe3e172adb8643853add546c9b9eea6c49fce761242e858c2dced35311bf740c5bfe733ac2e536e531b4e9fd1767307d99fd4f1bc5b8675f598251f2a4c56ca580aa9cf90ad284097cc3dc150a7e821b81d55b5621ec952577e83482ef94ae393ec2b4d92575e9d7b9cec342cda8345a5d649d2e47884d7946860e066beec1a48936c98730ab6823cb2c7374281d735b7da5abc7dbfe6cfa459ab27cbc808bd464ad3fbce37d26698a68095d97506923991f9257ff2e56ed8cb90c0e15c15e9b817c8808a7c84753678b442f44668eac7ae1d8321eca933c5fd470b7587efce06146e34106b0994e1ee02c3541842cafcca19ae9a9ccda89d371fafe4f50047a8b9406ecea2990b90eb6da266724d8e6bccd6dd7ea88109adaaeae790790f0fdfd9b64364c37f9f1293fa60c8f5111bc21118932b0a7223fdb680aeabfedf717995a5f6a2986e647dd52efc6a0b733b57f4008f8acfbe7737b65a525ba04fe91a9fd654a36a8f963561db16e5715408c45ecb32f0e3f92cf22652625ad46edfd19e137fbdbd299cc53f8eaf88b58a13b736650a03afcddd920a34d42c745d690c61e7cefbc8ef496e639d61067c36a0c01caff42e606d9d2915b27e307efb59736934f1bd6e14f19767cca6da00b15a5cea1a6bca237050895a377338a10bb47df8dce188e510fcc1418dff371643d4441332280f81b7e42629e4d2b93c344b6a49df059adf75aff24adbf7efae31abc51ffa71cd9ad06f522c3609c797535782d9bb12fe2c15eadc64a99def3707b05705805b0c6ee9b5c1df4daf1fd42b70d4b9d2f740984d7b81ea164d0033838f3ae5395c17b56d55af6e599f720f0f61e9e5aba451744c6ae136091f5d2998759a0d609937b3e6639f82bbe9ad43e0db5700cbfe2e5697ddf34112d0b710ac4b5d2b90f4a4f48dad8fbbf8a983afc6b3ff638fb62e3d166de11aad553dad08d3c7a2bbc4a1bcddf96b3f319f7437710364d60a6ae3bd1d8a32faa0349093c5101da8e37a33222ecd1603e677bdcd28b8e4eb824673c198ec05434b339b865291e0eacb87ce7f78e3fa8debcf346cf16f347764e8591f21ba624c3f978b1d2a21d02e06b84853a1c94388600d208abc5ccc1728b14d4a18da3fcce1e0d4656105fea7ab7673f92d9094aea3aaa601969c4f64d9b5e3eb132094631864903fe80d6365960923b4e96212f85b78e0586cab0f1663dcf17beaf051071d6545190452d78252d8968b0d8cab0257bcf0ca1ffd3c3ce9895271c12e80d05c3345cf0c8064bcd8a760ef3e534c06c3b02b992f4bccc3fc7ff364a07183b43a60cb6666ee69547ed53895f6be2083b11d5017219c006469a8818787a38e813a3d45609c3d7ab0eef9180b127740e9bad05f93de0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
+        String expectedSig2 = "00000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c21a7f15d081b126ab18592db4cabc163790011d2fadfa7483bdf88059bb8eaa6ba998998f7d69207bd41bfe452ed03ee118aeab3430c65105ef59c113ee601c9ab166a28a181c18530915e6de51a3f63a35a2ae39ce610875bc32e6b06c7851a4556bf2d375c8a0e7498a436cea199563818b4a42cf9f2bd630354c52d6f7a2d70e4afa512104168ab91798fd7f2479bdc6d946a59c7c4d512f4c221f79c8aee49bb0ffca4ed14934fbdf189888c9d6f9b53fe29727019cfd06da3ee308870f2e57f09b68e253ac19e14e3b24edcff13dd55aecdb127d0fd5003ec234680ac58f904409991187399d2e80d57543d13ac34d3ee1a453fc7417378e7b8014facdb9ef989e6b0917cd56012ed6602e63643a2918f998a65f8e17a1fffc5d433ad879362bf28c47e72e059faa5d4713a73e399344cebd059a203f9b5b317cc02fdaa81f2ed2e41db1bfa29a73e21c80068ea8ab8b35842c85b7a41b95512551da8768cd0248378eaf715bac2b82f5540111150ef1cae360832ddac01ea6ad1d9019d36ffec46e516d2e170c7d33d8caa395adce28b9138ef3983be4eba366142858b230d46bcc2108f8005db044c66516d01876a1f194d5fde163351965877ca63e54a7163c431519be7717f43f0b4d85a277afd75b91fdd075c4d6d303110535305bc499fc6eab11041be81759e76473a286a85de85e195c95b6537ccdeb852b388337148aa51f62e3909f9b6b1f690e5464f51ad4bc8e2fa38e85fecba6db7921d38f2d98742315ac4631e6e5cf544ee71143b4176e93fcd9a6286869102650c40cc543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae46b66882396ec84552fe83ca6acb3da83b0a2d13a4dc105b79110fe70d81c70eed6cd4596fda7baf3e6dc68d47bffe5addd554d49e3999cba7c96c2656529e8a5dfc67cc93473a519165a2b58cf441caa42c195d6105b08e89647788b2113d39c6a1f764cb4c908c712ed62a334f70c18341e93a5565c070c89db35e609ebb2d795abfc937579d621d015ea60fd05d246e545f8770f0ee02b051e7a87aa44c2ed5543dc4852e5a8e25ad132041be35c5ebd1df079f537d2b975b740c16fdbd94528bcb24567d6aa104e1370c728184a63aaecca933b50f995acae35cc927b0bfca7983ed923d61b54553a79105cf4e3595c5bc6878367ef52f8c13018d86f380d49dbf5b54cba7e104af3630f9d389d64665c7984296e574aea643958485840fba9b79df765ac64fc2a9442ec286422e0af685e1ab72394934ee2dff1801a58592f6a001fe2293f96033bf5625ed5501bfed210ab35812fe153809ec9f76ef0f08a084b4239aab5c3daa0892523b31c9e27e7e33df18b2e29ad7caa33ae94a2e7c84cb5fa73f51c8ca771dac3b0879c42181abdb1f1cd29c52f1d7ed89778ab32c01b941caaf976f3bfbe31e0bd3dfbb7da45534bdfa773d1a96c7e367c8ce3af5dac8d82347e0b30d3f3e13db8f59169afbea85c2f1a519b4aea6097f2b6d15dae174902cf0294497ca836e782b570b951c705fb485c2ea24f771135f3f21785ebc08f425353bde0fafc19593dee71882f84a4f8ae42a13a0c1ef444ad9ec98e5670a8e48877d285b9e6eef58bd52497b978fb7b33c435976370bc62f86e7746b71114660ab681a5d159d5c62561d72812bbd8ddd6ceb955d1e91c8b284429891fd7f0bcf20570a5a401a51d368c775f81b58b4c55f8c7375ed9c644454a097409c01b2be3a3578f159e3241a9078e45685b60c82f21a05f86b7bcc3ab69e42bf77b341e9e55b9c42e9c4c792f391b686aa29b89d9b575bb13e5f9811770d864a20e28b3d48b8fa47b8b8a4013ee14ba01bc3721ac8186f46d00b61ec0d9c68b3868afb93d23f8e2512cccd2aa493b2953374ddea144e8d202164438e808d73ddfda97bc60198fc63d621d7cf54e39492b83c9b8eaa855b95eb91f7d25add1bdbe462f72d877515deacbefa570c3b1b316b63da7f313f65d0f63f2199c78f9c35c4b997fcb2c3da8c4e5a8208297138ee08d1c48dfad9ab4d7c8fcb66280d349df86038625df9e70ae98c606e3ff095f94b1eb3263cdafc9e401d29e7b801e287de025ac7be7c9faaccd040da519bd479a4caf9b1c14bd9c64972773863a85773045ac068462b82ea43cee9628ae2272c1a8d17fda179a22c64e93a83f040afafbcb9c44cdc0a9d5f12cbbac991708b0cb9b689141ca9fbf2421feb2fadc27ce6948379cbd631c654eb21f3bbc6ff60bab018a9a90e9660eb540f0d16981f16af76a1ad8bb91e67cb810b5d0a3a0e49324e0a75a47a756c3aefab4931c230909b440647984a271546646d2fc67a35c14064a0d1e89a703d5f89cb865c501b3ba55f128fed7c9b0c31beccdbc90c6f0a9e754ce090abdadf5c03edd5ea3991a84fdd1d7bed7f9d8e3872f331081aee35ecd1e81fae67643b38d97cb3a81b97a9d09c73c140dbda2efaf1a8775330e1e56a861a8e517f238b871ced58091933110ab033518ca18edbf8ab6cf80838cb85a06f660ad2ae664e870b4d26bfba90510facaff2ad4ba19715ea6ddfd656f9e414e660f532d4fc43c166f01f150b3400cd85ad1cb9599fd89285a4eec574f1b2ef54af021b990fbf1a01098c2d79d80834769f32cc7e94dfc3237a6f66dfeda8a1ae97d9612e462b787cab6883964709d69ea5a649c9af376799558733c5e88cb91e6544693961bdd03ec1b8f46f8ef725eb5060ebbe41987ab5208992f1a366809675f8265524e6210e636c81dc70a24cd34acd9ab8a3d6b296368d56ee117de991e822f4d897cdad4f10fe8356901e29395afa12d5b2acac38a78afbe938f54161fb7934ef4a0cd1b661434da377befece43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1373be54db1c3a125ab4032a6dddb6d38396377d98eb30d1e81edce3aa1683360b02309d8bf40356e685274991ba72e28f045dc0ac670e1bab70b6019ea659a83a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
+        String expectedSig3 = "000000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875ccaa913998ed0c0085cae2d9c2e563315d4df92d857520f22935aca637e8755fe9f62d6389cbc9738ce8cff101e0707d6850ce003b9b884fd3c669647d697bfd3a24b675a6955113297e9ba806b790a776362c47d3081dc09e430172d5d6212893a751c05c70ab1481395e204d1fb675e0dc47e226ca450d132163dcd9dbec52d4df457482e99dcc06c7746eb4ed000eeefd95ee8fdb6d154db279a7f7eeb2aec2f43d566cbbf92bd7c45cd3a56f52223439bfe06a8b136aa35c76381ed5576e8ecc0a25657200ac81e914ce227bc2dc859545d315fce5cae6f88816fde127a76d9cc67953590a63ff7ec230b27658e97a2d5f9a9edcd87b9323381f72d121dc2c56f89312eb58294290415619247ceff45fdf49284ce795592f519288a5c7a86436c190883edab0e58b31eb277ed32cc572e53241394e526496b94f338abdbe84251ae0183edc154fba47af8c0a9e9ad9a2d7ff45aba5987cdb127b344a22b4583cd1926c5791139f4c9852013b0ae8acfd8ce7114ae6d45b321d245a3db19a082d3a3205a6ad3b7838c824bb81952250b09858c543254a42ecfaf23affd642fff84929a4bcb40d65fcea84e461c4b3fc2f8d4a1ba3e561be9805fdfd74741ab4c407252d40188ad932d4b53ac3b806d674a01140f2e97bfb25a3baf30c29b534db198680822a0da9c1a103278e059a8d0dcf5e444178f1a55e3d75ea9bd3147739364960c959667a61645ede0d5fbc8cbb853ab42002a2205fe271b17e9fe095cadad0d168bafa8a392eb8e3e8d2afeacdccbc5a6f4addad61c0b4368f6a92b182c39c2eea41520419bfea541ba2815ba1f539275ce944fb66f94f72c47383350966125927196399bcb39c287889daad0f1dfc41af86bd70429ee6ffda828d4ded50080c47b7315150768eedd9bfe586fdf37d7a0c74807838fafc5a1e5e64feaeba16a1b87cb1f6241af614997ebdfb83681b74ec33b23e4ab6732417ca3b6fb5336b3e31ebe82c90d60aa218d26dd09e65e6542aac5c3f295e3e050e43d68792d10b4c2c5e6c7d0129d85e3ea79e6b13c2af471a2192e8ab8a6f4c96185468e8931b4dac3036e87467d2cf75aec5d908a526a507740e333575ff3902d4142f88bdcb5b06583523197ca8b9ab390fa61e9929ad6a4c73d1ade927f945d45f77175a813623caaf0ff586330ddbc6dba9ee39e62da280ecc13ffc22f83a8cbfe7f3495231a7612b9628e17fc7758f020fa42df2dcedb74b4844481aed37c108bbdd8576f189b662633fa1e91fc481b381f042dd4dbf5e2f6d545b88f1b8c2f04126f62d6f2147aa9001781f930984298edb117598e711702e0788e0e9304c760d9fa1321254290463c5ce096daedae47109657353704ea9809a7e10f2a0cfbee0604098b7f8431a7ec26ad04698b74097b9ec544fe510a29dda677dc2e12e73e57f639e880b0aad0deb5fa29771abfdbb582a76061f3b42c3939c12dd426a03fe9d5287f1bb9f8fcf4f5b2598210ac126c52bcdbe27695432625fbce06960d3460012e52971b5d19fd1b1ee55e52971ce27377dcc4a907bbc7bc2ace94265f22c2089a4c3dda2fd42b3e388e7db905eb043a48ba49104dbbd47c5baae0f1af80a7f414e22799c4e5312a98a72a464ce11850772c4b6bff995be2567dba1364c98b1fa6326b4bfcd4bdf30c48a25f36158875dbd77597987f2bbed2bf0d0ea457726cf108af5628c32fd0f36cf214275aa8961b22fb1e042959d81bff3c1bf2128d4355d0f964dfa6469b5579d63485bf3ba3eb2cea80e095b32ab8665b48c0b1c04895eaea3c67edb71f30bd308971b130eb85a0970b570f47f4e613dfa5e3ee6ea68c6f71cdf369c79146bddb693a1a92b8c80acd061815b34737b5e4adfd68d28e293a3dd85620d6aecf5714e3ea7cd148b9976738b367291ab32de5144a3de66306a248109a43b32d239e80b34ef2994dcb2af1c28f7739f6f7e502a2cba8629cdb74dbab64162782da7b72c0f3d5dc4fbb351eaaa423026842a7fa340fcabb7188487d805abd8f0bbc8dac9d277d3976aef2d300eef62fad9ce676dd243beb43344577510f974a39fe48d922c2a22c6fafebfa549473d9432df34836163cf71dd0a3f337027d2aceafbe27d8bf99657c137567de22bfc060c60c15539b058a6c9eb703be266cde32ffae7eb881a35e4cb453fb5f1a5615ff99107de5b9577bf85d2f00937731a0d96fa7c994a2a69c8ce789bfcd448d2bdcda4191b6ea3ed3b020f78e779f70a1b526026c5d9c0bc15861800e0e71a1b481f0e74069354e08abc6d7d1944bd46596456a4768ac26b555785ebc68f48cff8af0e52b847b7e4d40089a48c741c9adca24754abb1e201020a5d39e7d7b9060a762957060f12d26667bf59cd39b7327e873a7ac774018a0fefef3d22d920ec00cc0d8dc53192306d5306aac11092c750985a226fd6cbe125f68133c12882ee31e48f5c2781e27abefe44cd4a1b4384c42be03218b26722fdf83d6c4490e9bed4a430151206eee84694e5787c94670ef60d5ae8158e78af8797962082fec9b492769231a39f2dfd2dd5e08babc3ecd90d648e8b3acce525680684c37be1ccc9f2abf75b9dc198f33c9dc13bd328ba4cfe1a375a6ddd2fb8b2e44a16ccf87f5eed8247aa49efa0368718ed7de0e3b925ef0d2e362f466763d61baaf81abd468a04c0a164938b77c9293b70ad9ea6fd500d406eafd4e64a716d4480b3861a3f4c1d56df1494877eeff2934690c2e6b03b3d4c618cbca4cfed4e3a5bc0204aa307b023e24f17cf41cb26a52797cf55a94a015a79d1fa5a49df99501f4590522645f54bfd8738b8395b3b7da5f5bdd2b1a2cdbaa21d692e6c8894c9896d42ee55088f4829f07e226a2d06196b15d909a709df1ad319de092e30f3d373595c704192d1b9f6330dc1631bdef5fb01d9ba7c0790cd840f3de0d0cc48b5c6640b3b7729ff9c1dc23e3b44edd9da3615e4e46080fec3123382f7ec23eb2b9c2e37befc6d246af039b5765d039be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d016b265f881e4b7bd303b7c38b8adcf31a7c211bcddb5260080105c1b70c41ee99d421eefaf1e51d762f245853cc36250bfbb3f547770303dc8a38d03dec2d0a7233b634f83a2456f279126c7fcb47f9301cb2d5e8db69f4721f68000d78cffb39317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5";
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig1), sig1));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig2), sig2));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig3), sig3));
+    }
+
+    public void testSignSHA256CompleteEvenHeight1()
+    {
+        final String[] signatures = {
+            "000000006945a6f13aa83e598cb8d0abebb5cddbd87e576226517f9001c1d36bb320bf801817cca72fabe805373baf049b80c4309ebda5e5e2c9e01851260b7fc238d55f8c2552b917795d4ece5c30f1351308a91de47a7e6eb57ee1aa80fb3400cda35df1415eb392083ad68676a23b1b60f9fd9dc273f0b211d41545f09808d0beade6b889ca476e6d78339a57f7eba226ac5d31d0c7d6571e4dd7bef0e19844bcd9a174b668afb0688c081c38d0db3bda3b42cf1025f2111e3b2c05a9552d6f2f39ac7487bcf0e23f9bc83149295b5e1d42cb6c795b4be7cbbd4059eae5269824034aee1fc8c2e4ec4705957094fdd5a5d62ab4cd1149303522c06ac7d9c461cee06dfef221958f95f743c0b5db6a2b782b3b9ab10289fdd2b77d333d5476a0a3a48815e9a339f993f3ef7ed9b494c51f9f728e859e50cc461ebbc5fa81dd368e065de35accfbf7fe18ee2ead0d5b5ff56b99723eb428701ca1bc52881c551eca6163fbe698471f69f63082c4a1942de3af59070f960e5bb3bbe81ebe4bb75d478a65a2300eb347ec9ef0e8a8a6fba514a7299a623fb03e47f3f405f2d2e8e708bb44a596984dd6a92df6c0f0c27093445c42e03837bf9326aa293d2c3174a72bc214f0d0e0164db2367516a396688027e5b162d05ae72218d112379759b081895ccfcb6e2b9d18e1866e5726247cefe4a9eb25d5455d9849df5ee77a579fbd7d80cfa8be96bab83e100a406a27735bf033cefaa81737b88466fa1d34eec15f38966a13239d2defa006c04a9084f27487f81ef85b7cd75e4a0df87c57343caa9805addc396b4c232ce3658fec6790a69c81b5cb4d9e6e8afffa97761095fd5903d8a28a6965cbe360769f32f4935ef8dc84a8aae703954f473eeb99971396baf35bc481fe07fe056fd8db3e41dbf48a161b55c5d47278c6f9c6f333fba22886632acbd42edc89219320fe4faa0080214820eb60d3ed5a03ae97332a633061ed68c78d6573c7f2cbaebc35e1687b9219dec67f5e6dcba20bb6b40a07e0e0defc64d3c70163200166592c5898918221ed942c4b277640edee15625caf2a5208c9612e587a494c5a473de79ad1ff0092a41dda84fab4acd5435cc37e4c8a9e0895b60e638e2fe973cabc7ba40783e422270de34919fabac4126a6dc035531efc2ca612712d95be53453ccf8ba2096ba0fae14abf3a95727b66f1e5742de804ad0ff11febc3e8a6d80e2417baa67b46e8380949a3ffd22a8f3ba9dca07e61ed0606382f16e64ddbba8e390c38ee018b43220c68ddbaa0133dd05bad7befd31380b31df1a1e3056323085ff2bf96d4bee060d32d0e4c8dab17cf98a29bc6a0cfd2b050f7ea0c3cf4556a4a8aa8e73b24d7041a53d6ca1ebc12613e0d616e3d95ee725c1da119ccee0659148f2c412282400821cc1cb87a5d79cb64e308bb3d1708fe1b91b28d0da61e09ac0260e5a1224b11ac6d68393b885ce94d4d06c8c8a167ff4b040950308a1e6c0d1308ffc227c80bd2acfcd1e6f9f7cf91dd1efb662e4edab677aaf1f27d311f203d1d0f1f818a228cb6021eb99a95ad22d3ac120ca7bc593bb8e9d2996d1b22ec594a87dd2684b55074502a8a32ffae9c9c1369d8d5b2b06c8190f96cafd52ac09e13fc989c08516a252354b0a38120715629805a215564df9445eeda05efdf580767316c55037f3f6f925fa5b46ce78e7a27a85c8ac1a9b521201970d6c6e4931632450100217319c22462acc98ed21b06cbfc531a7cefa7f1896c8de4d642a8e9e11119d5e669a018eb6a58a6e55dc68823f94c42564b075036d42a8ccaf5c087377ad9ce9b1b9a870dfd045c21bd1040433bd5d6a880d9932fc455b41f576efb23be40652fa3750075aa88bfb63978347741d77efdee423563d20cb7521cf5f005944b15f57f3c41a9ad84ce85e5f0b7ccfcdda3b2043346ca63ea3566beb46e165b972adbaf606a6cc242114b235be167445863dadb50db3376fe4bca4c7c82bae61ae0074add5bb3debe92dc0303dc569006efe0f0a9cfd2cfc7572d248b914dcbfea4e2f632a1bfff98879525910c38737b9b10554335a19852c8d1e16f8fd933164778b9a411b70b1ad050d69d1581fe1edfdf603e617fa8ba53301d080e8fb7fca806ffd7064c82adbaaa0e90ccbd808c08d077588669a02a14c53cf9d56f6c369b602b1326e08126c932b379c960c6483f4d5e9cb48990792def9afeb54d2c472015df185514f500daa101e2878272694de5b6a14a671547c86efd0336ac9e0d931b7469093fb937fd66872c7fc817e941bc32106396effd47685d54f8028f300c822c9b578bf922a80945954c3988cf85cbf0fd304f2cfaaa121a1f3d01ae3fee1e16496760d1edecb00e0a2da0ed66b0996b45f5066fd5a5e8d0082e89987f7ec7102f385047be38707080ee068cb55702ede0330f35782d9bb12fe2c15eadc64a99def3707b05705805b0c6ee9b5c1df4daf1fd420433af01841855e9cd056bf2c037e6b8becb69680b1967e3f33e7f97426af4d4c0700cc5e33c8b122af763e6865b88264f46b98283ea5150d6898d13250f8f4a8ec12505cf124681a133bf722281399f0aa600f30f93c3dbfbfaa27b500c6b549a95d58549eba4f88e777301aaca7d3ad00e653b4d0afb201ac6e26a5ef2c4d110364d60a6ae3bd1d8a32faa0349093c5101da8e37a33222ecd1603e677bdcd2180f0fe7f2e7c98ddc0ae9654f609e80acfc27a087e54fb9753a12f12e6ff06465d6c01fa87cf628875e2b6a355d5d7346665890419d04c6469d091e460a0a972182c015b21caf03e0a06b3582a7ed7c557bd9da3fd2a78e7ffc5977a77701af9bc2af3f79bf6a753da9f79a5294947c07a2caddf3d6a0d4a8a3289ac1e6fc5bec4f66596f54be641f5064e87b7a3c754e26e4f66cb84bde870f55a79a7577abffd3c3ce9895271c12e80d05c3345cf0c8064bcd8a760ef3e534c06c3b02b992f4bccc3fc7ff364a07183b43a60cb6666ee69547ed53895f6be2083b11d5017246a103478034bf600f5f9f1d9130b6c3195f6165f3d050f794e7499f76814718e0e81477f50e2b8719d1d9a3c1a55d5460711b88eb4f2c8df8d17d38832de9d4251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "00000001994b105a9224e5edcbc265ef0a76939005cecbe87097134c0b52f1d1a41270c255121e197631cb280ebf573fb6155eddb1d968756805f3edc4c4e3ea0008c8e5a998998f7d69207bd41bfe452ed03ee118aeab3430c65105ef59c113ee601c9ac28b498cfa0e628dd0b8f92d5547c68f4f3ed66cba1b8623ad63ddd7976b15538c55b94c63e6ffe9296a28232bd9a784f68e026437ddc807746fcc09c9d68076282cb4988c8bc501a461225fc8de39336d798d9032dda887bad268a57ba0196c6befff554b7a27de52da2dee7fe0b77850a050f87f0287b1c0c055d6610df95a64507b9724e9c3d89b4c41c8f3f3fbc25d20b268ddb8afb41b4132afd20c5a37c1c5d50f6ba64525ad06a238a3959a84c2a1e95e95a59582089114ac497f13b279830417cb255cd7ffb386bd24afcaf6cda74a3d5d7c75e9b04066b2d379eec1f9d5e7d74aa71807f200608aca913f933238c71fb3acef64cb76499517be047af9a8cef1afe16c7a0a282999f4d672603deab8a59dcfff2ce13398b7deccd2e4f1bc1ce02403c5b0f7930033dd6f813ebac9335865d03893d99caae239c17ff91c961301c52576890f6937194ad4c7e74d19f71d3b8d7aa967ff45b9e3a1324e63496d719d1c7c6b4eb56e52cdb3a64f44b87da76e1f1e85a1d891ceb88a3e64addf0d84b8f2d3d9010a3be169f41296afe814f2aed28076479140d2cbe60c232ca8a272b4197b155e6502e6a1861b7597f240bd2479c7544d6a85992d2d1d44ae26cea1ffb29952ca2ce8312626a00d00f4c84dab58d087f55c822ab3444170fdd75d9bb247d6588dd21d7a156f3423bbfbca4a92faa82c1d0e24ced7c6e5b6c543465bfb2966874561c246ef224d0719aea811b63a91191990213cbfd8ae467dc0f1aba9f42d372a98d3cd993d2a1a5ba6f2c7bd385467084c049489648d34b0d2703e5b220b62862da2af2cd7eecd7d4483246ab87612bbe1401ed07decb7bbffd2d774b6a40598e9f3f86826861bb9e9693bc5ba9809cfca4695af4dd372c4ff56957ae29cbf197dd7a218d753541c2f632772219c3d25c2e900ff7d685d6750e01c6f4b88ab346d955db2de4e4bbadbfa46c40443c9402543656d2dd0670484b6872518d85b3f556d01aa0c01405a89cbc15aec2d11731007393d930e0cd10afbeb9ada0162b37519733523bfd5adb677fa1c316966fda4daf7a42d0d857983ed923d61b54553a79105cf4e3595c5bc6878367ef52f8c13018d86f380d42100740f8f0b55c90ec1573e660079653bfb0a45d17f5396aa2220e55c1d23dd1cb78ec51365e26884a08171f10acf2c503ccd1bb7ece8ed5333d466fe3db702ac207a9f15f32db4802e781beb41be0b97653f1c36daffde0345e6b74fadce3a189e65597d049a60ba58913d02285d8cc011ccddf9a5cbb905074ce1986a3776f7fa26dcf4ca02462e800279d363e25949f2b1b8a7f1f9373a3d487121c6e74abd8642ed9721ea7c5b32ee78cdf663f8ece0b99aaefe3e607a58deb53e2de6e6db3394886f8d1cef3ef6762bdc6dea1f93446273d2f6926a920d641f58c742bbd61c056e741543da9a3d4e8bc6237c5c6bf1888cf24d83f3e0ee9009faf1effb8884b6f5aa6e55caef5bedd5a51e4e467dc5229919cb843f09b206edf896e0f77fb6c698b36ca0f146f37c07903cee49ed4c498ad4199d3ef05ecfa3d2575b8871114660ab681a5d159d5c62561d72812bbd8ddd6ceb955d1e91c8b284429891da0040ba5ae2e6593147f41b44a0f2804b1805585f93d2be23666a7a28212982660bdd9e88771397b26e46dccfccad87ac926a444f79070bc8da116c167fb39aecc6e02a1b3bdfee70a61ca2df9a234e7d094aa84f51005df5a18f3df16b64348cbf7eb706a19d7d302bbbd32f1faf8e69300649c46e2e32fabb41e57eb8816f10dc65054943c8db35f6c9960f8ed853bd38c131abe9481bc1afd3f181a04f2c4382f3ad45a1062c53109a2a494f1b9e719bc384842bc9370ab81e8cd6a0955f13babc2361be35127742dd0551a5bc342b9721d7f1bd629c59d67b01fed65350b3c5da71fe6713b51a6fa17a6f0b2caed679a15ff38ef120df61704119d0cb2134b608d6c004a7aee902381550b94361366137e2ff0c7a937b85ce096e45fb245d9e8646e68e85d4b9d87f1c2bdc4193c5e691542894ba42197cf1bbacea1290491572fdfb5052f4b1c461b66e324b6da6264a8c25e1d194e7fd94697159629563aa61fa3bb9a5996432e76562b86a0317ba91771f0c1eaaa4ec418fc0690d7fd0a326319563a6bf2c29dc36318e9318f4caa3714309286bdbbff016c8bcc855352789d1a1159098d7bced399e731b80f83ccac1ad619d9bfcaedfdba9c6600b7a05393b5342182ea2b2cc2a9fdbb502e2c2ec2dd4ff4e84fbcb28e3d8c07feb1be042620cff4710f4d650507232f292f380d993ca55ae66abb6bf0e09c14393fcfd7989483cc9bfa24c2033b555bf0f01811be2b6af12c138b02d02cfd85bbea2ac405fe0420217ba9bff2d6ef4c7cb0ba4faf36344b96846c699b5a9873d81a93b79aab433be2bb1642e3ef44908d7e561ee74e7207d0a405d18544d0c579a8112550894deff455015938377f767f2d1bfb0f10386559afee4f20d678bb550b1bd0083941306a6c0b736fd08e5a36d8aa589d12b170d587a78728d97f5c308e2162f3bf61329b847641e95d83ccbd42a482764d6a9a784245f5333027f53bebb6fa3af629c78db6f8a174816b04d72e02ed956f565b53f3ae7d2e5286eac691dc23bb5d3bc5c4c211fdfb8e455357740e382e63af71856e485cf44221cf3a9c5625d11c850f63c8992cdbaf49ebf5d28701d338ecb57471a2fa50e008b2e6ecdd62d3ac2e5bb30ed330c71af066701d7e2437eacb2fde321751e1a4ffddf0bce43524857313a22a32b58d2b68fbbd99fff526793ab2ddecdabac479b14dad1572235bd8650548854c13049e201eaae30c46ee46ee8fc06ba8c9e4ef1e3f309bcb2076d6293d19fcea9b90c00398e7740910f425703a0da099490e3b0472db2a5b2d2934fff8302e88ee8c6fec5456a05676c14a53ca7eed5485f4e4ea42198251142aa2cdb5a9d81a0c7a98e2c3c95bf96e73c68c78d467186eeac1dffce3853a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "000000029200f6d2d656430e2c49eda24923d91c484885f9e626e8e76cad5fd2f7d875cc1a024d58a92556c86b25cd3eb57e4c90806efe4324e6240d3794284d4c682f3af62d6389cbc9738ce8cff101e0707d6850ce003b9b884fd3c669647d697bfd3a992e0c3ff8b2dc7d51f3b3b8ebb1d42e933c89c44655a602fee6909fe9e972f503cc9a155d185e6638188f2bb8385a519563384778cc15284d03647265b64de909da1ca2871d66495f944abaaec13730a7cc63ae95e0edd4c8887950fae41e02d930b84c3a0f6cfbc49d8c9900dda13b6a7e9273cba461e52af92372709d8d35d29057792ec2ea5e7ce48c03e42cd71595f6bc21878b2d0755af9f0c6e203761059df223016065153d1ea2458fb1c7e4bd881ab3b3c777765adbcff7327270c203f0d3abd11fcd7eb17dbad445d7d13c94d3d75d3cd0dea624e231e867a2f1c26ccc79944785df6ee955296fd29a555df7d83feca277dba523187c83c480c83833346cc9e9b75ac25afe60dd3a8f07a74fee7db3519c0d176d66c47ba8945f843cd1926c5791139f4c9852013b0ae8acfd8ce7114ae6d45b321d245a3db19a087128109b19fd393ca52327af307ba656132e5837cd861783668a8eb0a1039a30e8ca5c22f9b77a008758cf9e92d49ba7d5ac0afb6dcc71572c3b9e6c58fd5b91f34129133d4a4fe1a84debb1072dad543ce99a66ab0a8518d68650ff20ce95cd0d99725065625fcb8a8731de017b1ddcb6df613177280cebb62d004d73b56ed3b2550983f215f5ad25149e3c65aa92d531b9435aba210a1909575d5aa580cd9bf6b2f648620b17ec5b90da73e4d8978fa5cb361a46e4390c8713a3fd2c92026155cbeaa3e9ffa03249f9819b9c114d900ae376b48040cab1ee669d61751f82c1ff9fd61578d4ecb15963d5c62d6c9b64e649b33843c87f4b0bf92c192cb3c3d1c9a867f30afbba4feb549710a1060a6784262baf647fe22e2d16c8f58ddd8bf80aa6c2e7e08a9aabd6171a6a9b49697fdd8c896a7cf857789670d79306c3550d44f535f1e83b7e669ec1a7c7bb49f655bfe7d152e7a05a3c0a9659e2fa5ef951ab3d7d2d30712c561764c765c592e047722c756ff186c3094b041376c484489a0d4049b6f1532e52f97a852f5b810fe43160616be53f3d781d8a4b2d4c039bf5028c2f7057b0523a6617f98e25fc99229c8961488867c11899998cea957c2d135c46ec6de295d9017a5c1436abef766777da7876a1d0adf411863ba4e576d1d613eeca592882601723a1d9dfe5c6b04646c7945a2624cfdfd8417cfdae769ebdd5cb9d2a4016fa8d186910bfc806aed6bb0fb1debb259fc1e9c7362eb36f0913c8a35d1b8800f9b39d5a0844dbd281696b54900d4226939ffe49b07a48952ea9fddf5f0b8ebb0e79a19f83bec3c51b6e4ee8853ddd80becc8ac7a0e214b3f6f6e2a75c09e8f144428df8e2a9aaa930a7fe38ac1a5847933cc5b4c2804eb3833566da3568eccb46cf6a439ddb3304487370dfdd20d237c09613eb22bdf2bb36651ed4b564890c478b720d60c59e07aeff8101ff1b0ffbb9c894cac3c1f78ff49602f3b7c55e664011dc235d2c6b833bb790e2cb06f419a98f43e419c925d30e4984d868fd55b955bc7a73c3d3b8ba7b1befe54d67236e4e42f99e54409cd5cda875df13c0c787285c03d89d6241cc7a992732292e2ce4a6698a64527aba1d7011a09a2f21dfd24b86e16a30ec471bfef6675e72ed2d450309144da61655c6c3706bfd3ab7025d37daf17d0e852ad884a98394ff7a08bcb63f6c3a2b1c9a06c494167207a672aecd7748b34e68fb3719635308f90e01bf61a51df786544f2dbfadfbd6e8509bf6744c906b6a96e535865165ddb175b46c3b7258c32802c10166f2bbed48a8080933030a581ed12ae57483c3e08b48b48d817ea84f71a980b4028de31e01b543be905e9a998991fb44e82b8e6bcd88d09ba15ee58b380a44ebc1821c0dc6eba1d5aa00b9dee768ebdca34b5e757e43047688bd6a3aa03b047b171bad8ef66a6f739c1a11777381c66beef7be94260948a3b774656c75bce0b08515391488b850b3430530613f277c1ee7bed2a7bc272f3b8c6ee177c727e69bea31eb3edad0afb76151d30fe31d6649fa90a36f5903d6bee31b2cf7410896977d7aa4ca5f9749402f7bda44a33aa1211e392493640e2f0df751e769ce000aff1a60f9ba91d17c7cf6bda9f3186dcb1b87e7157b8cd264319404a909222036a836dd3af06c03fb52cf3c9a8afbe25ff9dec5c5e7c542b6bf8f5c535ebc653bbf4597860d1fc57c25cce1531b7ee5e86a990067a5db54188addba92b4e82fdbc9c5c781ab83f33d267abc0ffa02a8e3a7bd91876e7cbbaea246f5a8d3c1372e802ec929353f8a1136447ac61e3f81b37532ee969be56df9d5be0048ba38ee17ae460d640eee02be69de6e4d18606b2bf8c604de3028b42812f9cc4385924240c8f3426d5306aac11092c750985a226fd6cbe125f68133c12882ee31e48f5c2781e27a7aeec7b419c655c8e85830d403fce77a59e5fcb7701d2f24716b63d313a6f2e2e8ad76bf14ca509717319387b69a937d39d2edde293bb663b72b93aa58285c3669cc0d6835e8ea7e1a66f24236b2cb1879a0b391d7cf7b2e65d6c4849d9c365a21bec8d77091ca5c510f8dfd50f46cbe18ff05728f27d4a1977be024ce364f708b81a07f4d60853f01cefd48faf34e4b7ef2cfb5a098e5ddc8e758aa2108890bfb0f6392c1bd0b2a78164a490e2cdb88abac7c1d4829ecab6e639d6a44935d29593c1558cf5d36e9a59d275f88fef060f3aa60ac06e0c15f4ca46901553d27ebc5820896fd6aad46294e40ab0d2c6c6458267cd017ef82fd5fb8cfef2be9535952d6bcdb029abaf13be1e868e90265baa6f044eb9c95ea709f375272ffa245668c944f61f837d00baa017230a5630ba293c3f1f2c0594c6e8a9f55e995f38d8295eb37955f4d3c29f81426636afbb4a1551cfb6c8bfb521941c4f167a30fa2aa5bfeceab0fed80c9cf813e3b75aee9162054ed94243cc2998739b63bb3ba08e79be4aa2007955dfe8862983d252d8b79a33e9b1c531003f1cd3a8bcc6d29ff8aac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "0000000386edf8472409c6a0fe2f3da07457868477f3f745d6717aec4a4ba8c7ab1647486e381abd8cbdf2430a1be34c7dbb65422fb1f261079d086b4212eea4fc20e39638c6098e1fe17f4df9c51ff96f6db6c6f7488f167cdd09f52b191a85782ca73e42d7800af123d2ac2d705844b093404f2072dd430f48bdb09438a21119e1b3730ad685a39c4e8c67750bf56795a43d93be6d5d6181d05922a13aa08d95fba85b6653716610269ce617146ff16b1b781fe1d512b1bac6705ba24cab5e5389ffbc038a751e77edc1a927d33fb3449cc2a639ae6052b59f5a868b370fa8778e939a30149264898b8c050a093e1d345f3076feb8c50c080a2e0604c45995c7482c23a12400a3318a18d8fc93668acfa68a45c8797e3749bb221a3584f97445a6c6ad215be5a12e582d3fdfd2eb5f74032a6e7aa35718389304989170bcbe1c85d36d714bb2fff05f88b85ced919c9bc94dd004804e460818411821e51a6f69731d54acf5e29c8f2090b360fa4afde8008c0e7085d2989badb92df58f45044aa257ecd9857d4f20b3658c49c0a09b2a53ec1451a5e3a8e4d60432504475992c2099d3866bdafaa6cc626f31e95f20a00d7870a74b957c2db3b93544094c2db82f89cddc95724b541840a32d6ca4465e335c4bc8fdf5aa5ebdc3b3ad9874954771a7340530ab22c6120b65c45bc8c28b3e159bd8511a7e1898c6bd83340ced8c1dae51030a0159a596f08cb761e777e19bf31d9492acbfd5c442ac9df91ae90040a93ad5e935270563f453b35c0ddea505343215f0cafafcc125e56fc9ae09f238440a32ef78b942d9b35d06a9c12257925c3aae2761661df4f3df78db246d1526f6f2f9d007614e239e91a8c449601ddaa9516658c78ee08aba6cf01903dcc72e5f9c9699515a1fb1bbe267a8b9fda3000e6ebcee589a4a868695b965f13086c780ee72169894e3da655014eb0a5a3282d37b6b45b7dab67a62a648e29005075555b29b4de95eeb3656e71807feb351eeb20ddf3757ed8de48784c75f24b772fc8440b1d0fa18f8a747c87b60cf5ae0de49edc1dbcca5fed608f93a8b45cad33e444aaa0069586bf94e5a4546696dd37ad6b1a3835a250f6495424a1adb50a9e436c0bf29623e5b09bbe1d67e87ba0c6f3a2f92a4545cc9b6668c0609dd5149c52f088ede4f9ffcd8624bc6a46996d9a5b78c5533249637996f1ea03c949b27e5434b3b4df286a35c2a427c4f37fb48218cd4f75673e6c902071a289ba3d69de281833193b83cf2bcbff3746bdeb748d2a89c2c5d4bdbd932346dee04b6881c5abd78994c6ddf1850bd389b84892d0d59c311f67777d6f11f3c9191943db01eaf124556705cea4f2c14457ca2f8f25258ada527901e4daabae095388bcece387f1e363c606e131951d709e0312b01094b52374cf9dbfb1f08e2d7566e0f68a5df93e72bf2fc56c04ea1904166d97e957adcea5efad409cd58dcc9a2fffe2efb3ed1ee5a41fb93138765ab5d80c50bdd8649ae2ae4937153bca2c2271c74dc2cf72b26bdb5d7438bc729d535316ba82c1e7ae7b0041a935d91760f7d80c57517c3bbd6165838a478a9f7a17161f54eb729d8055e9db80be9b4af3f172a54a812e9ed513e8bc6ee2fbf814f8f3ee28bc0570e9aa7d134efd571baeb0e384938487f13722486bacce923136ccd3e3c5cc7c1426ac672fc0b0d53bdee953e5314013e566472a6479776fcd6fa892c6851a9baf4531998851a79f1f839b02953ed6d7de23dc8ed442b031394320e0b97e6693142bd8d3cae99af9848b31c1f290fed2de7ff1d1841c30d3c2d44d71c893f150056c454900c122309a9f588f25889833fac1118122f7c102eb466ac4ec724dbc60d431c470226099956f0a51cf5d78229d91af8e4acbce56d1643650c64ecba2243431c06d4065cf738e5cb0980da72a35425ee889fc27c9d514bc581b7e740ec12b4e75927754a0ae3d61fc3c6a8e5b3c93a748fd4cd93f8bf788879da5b82fc4eea69256ee44b5e8cc1c1bf0bcf577a165b84c2ac3dc885413ab490a4f85eceb5e364b6331e7f11b7a832f974f7facc9a377a69a3c9a4ac69684cc8515557402a5d6f70fcc82c20051c7db5cd3d6d7f3bbaa38ef89f41337ec131ab3e3dc3959b78848f2dc51c9db0fd0fcc3d2bd670d1c2fdf3f681280be64a80508f623cca14fdaebd5cf4374182612c2996f79d4a547afeaf0dcb34a8eba293646d6b76725666dfce8fca65a186532ee1b441a24609c80bb045ffefff2e7a1f330bde5ac665eb7296fdec860b6c885143056d739969107fbe50283076a9e6ee68e92a303cdf6ccb2fd9a7e9a25100e15de3c3beb5562e27e88bb3e02064762052fa3b0e69f70e44dcb1410ac1fc604f27e86796c6d381ebc896a463de474e2949d242d1eac60f4f8d36685392adf7bff0c5d579956ff4e71bc8269233834d56ffe830079a082eba25eadc5ec0252994942119eebfa0817627b1731a791c4fb4586c43dc2190cc25ca783104aeb057cddc69f0b1692254ecf21d1638c84831b536dad113c1f6bca25d3ee8562eb572aaf23614b59ed0e9f8ab077f9c0a022e1f65fbaa56c8c1c64457e584416009587f8f57592f96951ad0620c1bac4350d7260e973f6635062dad70f3135515f5144814ae13356918954a53f90b3980c08da3cba1d8f112ef4992b38d1183638125dd62fa05c68cc3478064fb87406a78335c85866fd427079f647bbe439e7f8235ff966d591a5cee90cfbaac4873a30519db48aa1111b27e930b644c4f83da0f0b6116d9dbce9eb962268dd839441cbfa116f502d46d66008bde12ea1e33689b7b870708a05252d44ebd390380cbf245d6434fd972f0c7d09e6612ef7867b2f39feea4bfb39f255e3abd31677071ed4d8a7f394bb35f29f7465b5efa0d8b0ce9f57619bfe1e7e9beb2e49d1b372fd3c38453e9bd59d04ff5409825280d38e100f29eccc045ecbb69ccf8a0977c2d7a74eb22fab6c20c0f614f725777f9d1c2491436f87b65c032302bc24ee25f321e47b2ef1e0b9901037ca8c061281e1ff6080de1fbd2f41f91f32f0e09710e1c93982cf716daa1c350b4dbbaa17a95a2251cf702fdc87c03e410d9ac0062bbc479ad59262cc6bce4048859718988b62e2c4511ed65f659cf8c579a53a6a544a1331bf650758d64bb8fab1104170824894fdd8a284d281aec9d62705683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "000000044798030320ee0d4f25de3b7b6f578e0b4c930b7ed068a65c53dbff8ad4d73073204ca1e248cbd771ed1d69e737a113c705a4790409f43e5b7d70b628b067ce08f4b665ce7bbb9f1949220a19d53947b7b2f5c63592ee0740874634b3b11fc8f440901e5160e2398d80b465926d043481b1378fbc29a6e10576b9dddea2cd568ba15d7bd4cedd5a015e4838a3e37175cd0bd0334e055c4a7b3f6194fa8879fa63aac2da5c29ee03f0f3d9f9f22e3cab561226455b8d36436efabbdd3d17b2accab018f0d6bb46dec2518fa167bad997c0c1bca1153b2ea732243fad0eb56ebe357d7c6ffb654a6536afc80e83f276ede8609cb17298097f8d54b3213df6aaffcf3bbadfe7018eedb8cf284860bea973c298e6ce6c61360cdb1c5c5690f4d5f3ef8741c783be4a2cad2ee0aa2c1a58a17f1944ff11c2fb1e6eaec1190707977f862dc71c722e94d0156eb44ae69efbb7db7ec216f34adc979f7eef301d9561772f5d5c5d7b7820236cfc636216c6e4b223ef338f0d2de04c1474beffb8e8aa172c19bd6ae9bf103b92e5aaed352a3554dcb234edd5b965989453cbe994da7597bc5bccb4f5c07a7d62e56fdfedddabe7d2ed4c41e09a9acf9289547a8233cd3e76f36f613a205b4faf2e17388a8ab5e19559bd370945e138cd18565e4e12f5a6c8b429cbec8b9ed34492f321c1885570ec9022fce4ce6e1f3a657baa8bc62b94db4b9f9ca780adcdc0285ff33e976621ff78d26da6d2b66cebe392290fb0ddd76ef973ea3ad00ce3614b744f30d43a37fae5d9b85fd2a76052f3f4d88c056a9151f4c2a7e9b5749cc9e6155287ee53fe83251048bd8f926e6f21a34fd0f760186572e8c7a307cac394c3968c6e2b0f2a8bc822c9ec2c6ffcca5167b53a5923b6b5919d8344e23379b10b1422776724f37c58b1c7b435277aa340bb574495e87e364e2af1a9cb8d9d451d7781f983f03a63019eca75e0adf3be74ab44e76daac957b546141a368c3eb2c0eb2aee3906bbdae61b612f5b6dbcf78fa7758e2236cdd5d1ec1e4dd18cf36af15f1050ed235ca26663ae0fbf628a759e7545a5d4cbe0ddd4c77d2b5ae38b6e4fdfcbf7c45fac0e76023b02194597369710a479b7d7f4b7d6d28a0227b5eb75a4da7f2f8b8aa985a8bc07997344e1bd926e6b8f1dad15027a6c465d0d1d2b6bc02ca9455ae5cbb095f2e9a48f2d435ca0a70fc8841335d10ed4edb6e6d5fe6c63d7d0fb1cf7685385209d02924a3a42d3024d0b698735f8ea2dce35634a2b5420f8a02ce4dd665f4c83fee3cfdcb48b53b5742c73be3bd04cc0feb243315d48e1eacffc8e39395482b66324ee8a7db13bf0bc42ae9f5c105b393640837210218bc83cc8667718327df81a37e1ebe8921dc9fc4202829297aa47f9c4a4ae2ab44c8187f055513252437cf2181434a1d726390c1ea3eac34cc5f3fe03283be36fe9b043313a9e41b7791ee80a27bb2c81034b3b6b60a2034e778dcac848e9904868ccabd3375f1b193687ecf3309cbd3493fa89ce0cca82065faa5434ae5526a7e7bc037b636cb141484febc0c7ed2e8d53dbbe6e7373c70cc3a71705e7e8ae5c2476d254fcd25f1c17cd27d90da52969b4da165b83394e6bf7513b08c3107d25916db11539095a55d023f271cf1976095dbddb3006f57a7bdb5b42a82682ca78ecbb5e8d2bc99441ed5e9e4325e23470f676acf85b1ca3cb21d07c243e4cab727d11d9652ef8af29e6083a5e5b6b2bc7106a022e4b59e93a71c6fe073af7e1078468ceaebb0d69fe3d53e578f58b3fb5dc70d60eaf567e0020d561412626b9cf91d64934a951758b62737baac608bc6b46b7ccefcb181ab038bc10d39ffb17784158081461ea1ea363a030047713b7d28ae4aa2d0d54b08a1916828d00ab7cbe2843ead819c07718855c955ad6a4e7de489d5f0a345d401a855bf0afea122e8c9bbbae5d2e78607a28ccc1a88716f12e96506f39e4ae5b3e849f96e464239176638874f24fe0845df3f6df3d3141943f6eaf0434742253219f7b1c78bd539a869693fcdfe79606aa7e8d2eb28376ca3541fbe3d2ac509862f6e2929de405555bdd80ca3286868f3ab4ed84904ad6920a8e0be3e5527092fd1b57b9ab35bd66610d61fd8b96095c8903383f6a0f14f5b833a7cc7a8089cc7e17764323018fc5393db5e59ff088d5460e3144f22c5acdbb68e4155fd8f488be729e363d4b828a533ace1f3f39b8010f1667f44a2954cc71fd5dddc051a177fc87dc7025e571795ce8becee88c7da067d605d101234f814c060345edba8d862124620207aaa65ef54b961c449115daa1cbc9b6c7191dafe318b8d464c8b05916bb01de9622bd29bfa332c538ab31fda687239905972d740880df01d9389c5109417914a42c53fe6777cdec131276c357ae8d47cbde2f2ae2ffe621e536091677737a4769835cfac5e085bc6a8fe8f8437d222f4d616f3cb8078c0f00821731533f82096fdccf12381b514b47555e0a25a52b8dedbcdcfdf196d9d108ac98b2cda7f83306459e72856f93d8726a10a9fa69feaa274f811b90d36e643c9244a0ca02d9d54e2bdd9a16c1ab98b8a36664a3dcf48227d73993ff151f9b65d6c438ed52b0dcd2e4f060576b5a3d65a5152504292b97be3f0ba13999a8f21c0241aa99f62a7e841f31423980b359165e4fc47fad9c96450a75c500debc5d2bd2c92c1f961d977c4f460fe4fdecec712a4933a6c5f6c11a259e61528991c11e30216504d40b4de90d75528559a5c0953dfd31ea83b006c15e865b77f3a92e45f3fb6446164eb788bfe7aeead0f35e4b550b682ff160c8c113a5aaf91f38222135110f547abf4f5ff597c5c539bb03e67f457ae18406479d885982794b3377a731549422f5d4e8bc118e785173c580258a8d3ee629ab4362ab83f7a7b788261f6b6d50614f4c4082adde4db31635bf7ea4d5b96721f268e2e87c7677a823f4e20edd4f491f0ff63f660c8a64d7f9d192b1f29b175154498bed32b69479b2381c131585fd25be09b3d47fee2f5fbdfae2db9778ef2362217ca9e177f4ba40c34b37974b8ed57c99c84626730b1c2841fcc0a53b3b8fd5ac637c7ad4897c5b711488c6f93b0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "00000005b8763e46e573e82d661c123228225d57b4b1645954acc7e0455e04594a529b520aab69aba86a4676d6cb0bccda97e23a4feebd480f62f50c298b5f8b4dc8407b337095f7e04735c2b92df7753d6a2f8941cd8357fd4adf17c5a73999a159a2fd0a44787fd817ffae71cbddb5b05cbe70e7f4c3ab21210eb4f4254703cae0f8839220ac1aab57c77ce87c9e23ab080127302d6fde978bc12cd17f6f817a184ea6938293d8f389aaf8f87e233b4677f24b6752838a669b4b9da1f850c02495c30f38ee493aa83b79a44f6de5e7c7d92fe79c782df534566b3e5e8c04c0f449f1902e438395e2d782a64f156498d959518f68591fd86955097b11fc28655a3fc3c0799b5627380067cebf8b80410ff391e6ba1379bcd1762aad2e9976d3a62259cab4093109d5494c7608c58e0beedf22fb15c68ba8de2e0f2a687a5a70e29a59c24c5ea9c3546fd2f63687dfe0f98685acb108447187f0cda6e1b603ee5b0356408ac4ae63b5031f84932d514d15dc9a7b392a523c519f81047731d8df19329fb83cf19ad2e18c4f26d7c6873e5dfa197828fc40089aee779f8105f1349d5d91a0df6a352790f4d01f30a6e30d5a6f54b0db4ec6a2586e245caab461843b6168d58cc27b633c9d38a2d5f5738a8dbbfd9b8899227917c1260a22d0febebf0a30f3854d900aa72bcb95dc5fb5b2e977379969a9c0cdf5b9cc76adb6e7088cdfb4255d56fdd6662d2e66141ebd98c828cf3deba85928cd0df384c3b3fa1c734ba9add6180db65c9f9f4f24a6f2b4b96915645d9fffca2917bda8fe6a0674722181fd05cc8b1f0fd2f4dee22bdef9af5059ccde3b70c73ac34752e2da71fa38122d68a0d133c4ba0ad99e85d8226651585857c8c87d5d4780f7ebc62b58204bf25bee42b859261d88e7612b08edcddbddd79bab723078cafc4e9ce269ea7a69f0fedc5ef319ac44b2405b28bbd90eccd3b8ff749aec2632565aec783394fc7361fa991e6b912ab7e09200fc259c34b40e2aa18a887c297b58ded238dccb0191631b0b6ff47578a81c9d553c25609b5b89cc807d75a880a3ffadfd652dc40cb2364bcc34337a271cb975bfd33f2785467f9aa950f66d9bf9376d27603e2cdaf33afbdca5c17132248f9d9b1fe09348bc21f31ed35dadd3f7d38125b266bd5a83af107abd2d8b71f10ba303ce15206b507ac47fc22ae98a95a0db6d3181d20c77a5a62ff6395d57bea5bc6f77cc23b0daf828c3817ae9c0036f89665f42137b81349c36b78b61d811c8aecfdbe5828a3d551d327c35a0c7fe4876a17eddb09cd8c512a730c682f9b34b7ea8d90977723e016609f3e90dfd933264fcf5c607861d1a73082654b379d8e3bede877119a0c83df65559f7b58e37360e7c114702d54c3d656d413d0be889c0373f096d61b5ff54c3f7173d786d5694206680910c1f39b96101e6b4628c9a8d79a3fd778b44e95a758c88e4f843613b64ee44338061f9c7bae205b8bf1ca1be1c4ffa106f3563ac2f1f35c9a3ea8e29d217fab3264a8367f955bd1c6893534398a5af6addb02719f43fee53c46ff1940f7d9740260869171a3d922fbb753110b7f32b472fadc4855b0ecb22f56771cf04a17e7130c4b1091443761adeb5ea23131f6ad80f9571d2b22886e6185d7a11392950513a98dd34a0388f40027a3b27f0507b2c7dcd0a501fcab1cb5253e2fb8c2f3e2423e38d309e91088db61cf27f8e017e1b38b36869ff3d2cc3204392ac83c127218f79dc2d49fb9a5d222fcd67354a640c9ff0705f54fe9321a99e0874a25a565a86ab67f6a16b7aea94470e73a1a6d1e5776146c5d9990566d835eb91d1fad9c3670dd544f1f10921407dd5b4ffc2c41e8e5e8763e3baa7a67680ff5a86bdf46d3b7cf2e95aa8f706f120ab6a16633bd0ea4d51d06adafb8e48872dd2ebede21b746ce905fac09f7b239afd52c58a78e7065dae7772facc096ce4e7d1eb923028504ca79802e45ad542dbcfd83db22af81591ee48e135c731d609682eae2aac047fc898fbf3f2a8dacf8e97084e468eef53474d9b592abbd2f395af0604af3ef66d4388ff8d75b0382a4be2c255d202ea4ea23dfc2ca72adc87e689b527f45a40003c7e23151f4b14dd223caf446b083d013228cec20197ebdf5928b6bb5841be7181e528e0240306dac032b52c7608ce8fcd92beb24420cd5905407086d8dd6540f8c7f157238c9b8c24a9b4168f25c8d54dd40a5e509c2533a164d6fda26517b953ca659a7ccc7f4cc89d43293674ac2ef2f881943bb63fc8b233f0472dfd50ffdd0123e4296ce2e1e29212159d683d1e2fbc301c86123751a5991ce02fb0dbe2ea4d71241e18de4f6867a8b7bf5fded4beff76a56881e256706e4c0246b09f22ddc1db238c86e028c567c8a56e4832f2d33fe60a933fcebe83a2d6f3ac8a076b8ca4474f1dae5ff14edde0591535116d13ae80d982022d44f6adccd24d374050ab273a64420f3a073e4e18bc11d9437f32107c52ecc861532cb89dc8713d89547b779925b29fce17d419c49dae265a19d154391a70a3fb902a0085869e666a67c754f902ae8ee5242b372b3c1ea8a3410df28447ea8bdb682a179ab8db453d203796f5d891d76f44dd3181201632a5831e0e1c839173c0c61211dfcb7c6c88bcfb8514a42157fd67249b5276fa247798a1b2e364e6432df239e6431106ee416388b8f4c3cba3e9d34147b52b3b433d58d0c1266472b2f02b76a26277c6a900d7c3fb065c04a38261ca02d976a2f6e29caf7d74688d03c899a0a53393e1f3f3df3c206aece0a4edb1375698aba794631332f9df088eb80e53d1575141f4ebdb8238d4672be089e2f35f3b9eefcfaba4366ca03d7e1c5c352fc40579b277ec863384cb90da345cefb163e6d835809aec0e2d6acb03bac1683baf022f123c95d41fbbe59d681b647355c0cdace3b61de1cd55506602015d2d065345371c621634a2dcbe12a342837ea0d9ef11b8fd3b67bb387ebf5b8798c7483636d3620e3e4b57faa37ca95829c761d082562da5465c7b3658e91d54ad9c76c868de603db9ab6244432b32291e5a9cc210f552cb304dd14ec5d92e8c1d7200b2e0a7063dfa3c7b36fd9b0acf0260a3dfec76e39f79af59578604fca6af74968331e4709c7fa58f7f87a838de2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "00000006cf8ff0f024235b55d75057e926a8966475b49d7c6a2cb1b43ce9c6969683526e84ff91613b1c9cadb930b5c719b144e6f3a47110899858c3ea7948a2afa40ac0ef9b68c30f7ea65fe5996d967960d02335cc3caa2d534aa2fc5c86bb516890449243fb8f48f84fd27b7ed92421fa520a91de4fab60e8da754af4b5a8f69befc28b690871cf1b0f68e8dc2df0627e6b95a2bbbca7a2fc851548b112439a36103d360fd042a90f064c36641b5d0b2b634aa423950d1227d61eabc7dc05f5450b5ebcfb89c03f3ffa35b0292ab9e1aa4d701c760aa44b0bd92924c007c74984f3e5e3eaf0b204c3a2c312c8eed0913cfdbe85628d5ffcba18e57f99b99f727430c7a3ac10fb2c822f31b88d36a8f0becad48a1fd07ace6ba08b6792d0c9741385bdfce6ef4e98f6d7b24ace8705aa81b534225a0f2bbbad16c79a07d0cdc9b4fb86dffedb7b0733e29f1c125e31a237586a2e640e6b015145c44a04942f74b404be00f256587eb820d92c36b56eaaaa80f72ff0336986d206a62b7151c74eeedf3fea594dfaa3b6ebee3c8a3058c8e3a1f489ed95acffe0fa9b712137261586c112e5f01c1dfb75e9dad61f956ba521187116036cf6322d323f19b46cae1112147618c2607cdcb34eccdec9dccd3f2ac2688e7261b2d4130325851eec469b378ef1f07545f243b194b954eddd7bb725f35b788bf311ab30c09061055a72d426d7124f0d707c140978152917eb84269b2dd7c7d4d5329adf8aee541abaebc494efb28ac23e82a1d248795151b0c40b500bcce665424ba5d01991b38304bbc0b0d3daaeb73a84a770e9ea8acec2b682145d07778737fe391b2a393580c4989e636dc37937ac660ff1ac740e4fc81f0c82bdfed52633315a2e5bec9618a69d416557df212d6e40f2e6d87b67982fa453b2f067c639afda3d7df632d52ea429fc9ab020efbc24b5845eeceda9dafde9dc22dbc9086b7cf1794bc41de6075f31b1ce7491ee42e2db52d041fe7941df16bee80dc582bdeff303fed9e877094bc2587fb4b653b802cd68372beebdadbb7c8870cba18a778326d01d242eb0b31f48ba17f74b7e42164b3421c8245960083374cc23f0095fd3463ebaa23e42ced60f0e9b083158212333677c9f4b2de3fa9d8908b9a0d0e496985752f8f9de26cb0dd57aefd160ff2fd1418666292dd1ade312b2cd518f9edff5ac18011cc625bd50a7ab746977f085c3f80d588819d625f4691937dc2668e151dd7f5914662fef371d731d20ebe7632fba9e71e79f7d760c8d71c80013743ccffd78664d7bbc32683f6761948b184534ebf0c9b45c96d93f5d955ef7342dd998ce2a466defa300db76fba5496307bad2a6e42cf16a4f273048070800bae4a07d5cd12d06fc0eb617f454fb7911c02fd3165e669d3af8e3ed3caaee5515cab9898b16804d36c9693043892658ce7a326a7edbca9f0c71e43b8852e0ee2e2161fa22392be9ce42515e77161a0a18ada62eff6142734cb5a1edc54ecc751ef632289a989aa34e75e83ac72c6999beb0c2aaf62788aa2eed98f9b2baed00bd27d71f7db088ee2b2918f0425aef16ae8c388afce27b9091d4b6cfea2726146ee2525eb6965222ff7b2ba75665546f4ef60c5eec6734b5a47b1b9fbc2570f817ab8aa19fe9604abc5028efb997cf5243c0ff2e84c1cb944f4cc2ffb801fa2cb3737c21b076cbd1779da83549f164367d0a4674bf152955732380d6bc68ae60db2af948691074296777389011643e75e66045cf30aebcc22e5b10c33f2ac9675b321337e4c895d33ab968a58533bd982c71a051349ca7e39fe79c9efcc81899f61760cd9a11e39de5d58ce48cfca98fcadd1cfa253c6c77ae85e0b2601f9d5865886fa51fa168ba556a878d013bcbd81369c0070d21a284dba6233e824d08d6b9d6281e064bdeb688cded8f967a6b61ce36969477adeccc4b21c42c48c7ab3754b2157867e75d15f4d41e218cadbe29a5375ac4dc65480b8c4ebdfd9f81aa828de1b3938ecdb9477452a41fa772f99eb86442b21288d31df55eedcdcef46e410859ba3ea7945fcc82b63f41061f456c580d76820d6f11c2aa7b6874b002d76ca42cecc7af1a88869d1c756690b6dc37df92c76f31bc923e99485a5dc06bd1c7b877d4ff80b6feeaf6323c548428d39e4e2d9f90fe0b2f971faea87257a2cc8ae33e071b16589969468f3225d52b3841a379546bf9b62474a2e60921629a019bf521a602729393e5460fe82234091cbf77ca483622020ce45cfc02ba5de1d613cacc1ba7c65a0036d0381d2108d10c3ad73dafb2f0ea6605ddce86ee92c970ddbd59c02d2f1991efd7b83f6f43202e36bace3155660223c1a075213d8bb8ffe67e2a0003bc9d4bfda432f5b9eae3dfa8b15a8a55205183b96ccf460610a07d137852693041b45fdb126960c53a798d164f8c832f749b66392214eb8e0cb0a3470f7d2c0b29bf7fe486e58f7f6e8a3e3f5c33f029310c5e1010bd940777f89632520ecc96e2b926ae92bcbd4e4f7037ed795e86e5d80aacea6bab0973607b7d15535052e1cb9989920cbc17e814caab4b93eb87bcba61ffc052b2162d35963990aabccf293f2ce359106eb728a6492ca7db1784f1347d5f806c8136254cda29251f579c1e99a5146f45f1dcd7eaa556dc7f3b6adad506b7cefde4bf438813ca0d3219196417a58650c2c50648401c253f25c9d63d33677ddf2d440ef3761bfa040b02d6e1ae3b0389d8d255b29519f22dbc6114edb0fc5e2b58bfe579a4b8201416bbe9f537e047cf6e83bd67e150b23aecd47e73e2a1912021f5a7577171ce83b7bbf0b967ee367c5016071a6f0a5943a090397d8ed65d3924aaf3cec9b85f6793a4716b24ae2b9d4b67ba390ec404ff1b564273d98f44b6e5de722e33c2ba6f16f30dc78f668287628ac00bb3d092037dffab0af4203762e5353445587cda22c6674d896cf813767dbcffb7c4e877274ae1922de5fc37ed04117448ec2a6c19b0c8cea3bd32603da50df8a25aa0246dc41041258ce47b4ef33b571af7e1d61e90df8863dd5a90bdf41bb2e4e9637d4685c07d21a1dcd9db42f4e6ae0e503a5cf94b2a2983f55a7d3713cc9ed56ce326a229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "000000071df262e3499222639eb7e7246a3c91de4f4e1beb944bcbc52e7507f482631e63f16eca604d463ae28afd19b416dfa56ede6eff684efc59f5ec2930581729d7fbb54a4b5134832ea60918d40e5a201c91bdce9e79a519a10fff50e715f9d7c6bcf4db6e9ea9ffe35aec463e842613a2d4d334a4b44105b03116ff9efbbde7214f7220fa96e6b876241ded96598be27cfbdeb867edf9eea1722953d2e035da6b9a4dec2a6e55958b482c03c0c069f6ce036849f9196b2f079263ce04bbde0ad7565b7a5ad8af21d00bdb12cd3f3f0522510563c53d4b283955ce8c6d415dab7d24a7c00dd71571566fb6580b413bd18e7495e30521493aece215df3ba96c06b4d21bb81d9a30e35e90a230448e9d3a9f3afe650d3d6fcdd284ebbba9b17738920c6c09934f5a4401fc5a8fc1734ca702d1928579bc2cd8934bff459cffa847a00d4a90641412ee8463e5801b23f640f3aa022af6e76b5b9df97ebdd533e6f4f859a54a76ba4c6dcc001742837f533472d3065c0f54415e20b7e9920f8d06eb28cb597dee5ff15685054d2f56ee6029f10e3d9b6a7ee799f295ce961539420352edfa241a174a24d1ffc38128a7328416a79622f32a33eba60e8d8bb09f74ac8b6f100b2e4e260e87622d14b6320404c2279bfeadc24ebb91edd79cfc72dc0a9384b2c1a9eed66c13a7bc9e718be80c7dd0c3104d5cc4f18902d0480e2204942021d01d94bb335d3b92b42a61e969d9148f091ad0f3b7d608180d05c100f05f35b8efc6192e6f05fe47ad5c2bbe318b5ab2d5dc1dec7aab66298af88a6e067ca22998d2692b1d31ba8c20bba980c72514e85147d6e5aa498dbd7ce4bb3e90468cb6d84ab5aab5b6a42db9be5dd15bcc505c569e41bf5b15bbcbd43896d61e70fd757fdb4844fd4b7319484a684fa8953d9f4ea4076e6013a4cff73433703dcaefed49f053be6f6ffc5bcc7b723064812d2a7fad25c1dfa3fc71704c672a2195461aa2d80534f5a6b78affc43568c641d6de3a1dff411da291399699c990286e4342f97c86984498190738522f3e68991dd8088c4ac752263c0652c0aa0954f80a1abb15cd1168f239c44710ab5e672302acd950ab8b8a6fa9ea3b4b9eceaed4ca0ec65758e70aa262daf1774f065688e3aa3c3ad137feb62e962059c06c68b7b0effc8f659614f4009fac8820449bc8bc75d5fa30a3f4f86a2dff0e750820fb5308c546525e3f6887c1cd0f4e44a639931fc9e2982fdcd8ca545cfc2742d3f6d6cbd48949bb4251864f334f5c41e43109b57e88646b356c36bb1bb8941ed9b208fa5ea126a5855c91c4ca28ee6771381002218bee79ac8a63c4f831b62429edaa33bddf3cd6facd728c574daf9a26f78747adcccc94c63e2eb02b21bb029e10daad8a08c2a3d3e3feff98929f0e90e607fe21458289c184f5ed3a2dafe15747504f3166516510543c3d1b457d5ced8431a7706de659b13e748f865768f6bbb403bf72dfb844931fb6abcbb14376a22b1539e37f81ffe65025e46bef48eb7e5cc8ff3390abe263c4cd01f8c19f36f96965f09a2b4dcdc4a10a634c9026609ad8fc331773c2b551cee9d44b306154fe6f7c905907a9e2598ee1801554c703705d015d19e7c5183ff472626e5b09d3f54c2235b0522dc7a961b17a5d1cfa676a0c84ad8db129dafb3369d9724b1da831d90f05572fbf89a701dd87c846cc52ebb4ba54887328050dce3c4c085561d62e0ca4c3d7ef6442c2b3a6675e7886dbea818bc2c802e7ce6a676f0f98632f07103b68740575073944d3d970afddbb73ee7555cfe5a499f1e01784c1bfa38b0be0548bb3d059522da078599a4239b829cf7b79fdc595a9300f9ff07444257d88197ac967b83a0232ed36d9db4235e8b799be35b42a84fd07f17fa8939ed51ad5169852d9aa90598bbab4351be9eb4c4d39badead8f7d680d187324e84fbc690348b7064a8a4a1d07509d4612175d89804ca8ac7c41c041c3ddaad6256462cd86a34cbe0a4c2ddc5352ac243fd59cf3c209fcf23c626a2687bd385bb11097dc5c6bd30dd9bbe0c37d31f74dcc84d53dd346bdb5edf896c4ff7be5375eec2a8c1422c73c04549f38ddbf8ee830d9a2fe46c0470c884ddff28379ccb3427f96113171b1087841d32f6f2a611c8d43cdfa36fb2c9b91b9da136687f5bf646002e37ac79f9838c91b7c86799af40717a1b2a0e07e5f5d0c84f9500c0f741c9806cfdb8d239d88a0e02734b257c9560197d96130be79c030d5de0e4849ecae44ab93c52ac403740dfabcc03ca7e8f94b04c7f1de55fa0e2c9e2dfaa8bca70944a9ead79cd664fa783809ac68a3eff5baab674b268be1e21955bc2507cdda847f424fdb03107cf951bc6877ea61079ae0c7e8c3a0e56eb29c2cb183b13d165e4322c0c379ab0663ec86e669522a0f6cf7283cd4d177619b7ce4fb15c5de902e5f0e9da6458227366002c07eea861376944b301ecb81b98e8f233d97a086414c0227f6944631258fc71769eac4bed57a2601f958696ede6699fb8152fb22d01f9520275ee5529bb867e3fcd59732fb204fb715df81a0e272f58d027e1eab4f4233e6e9d785870d34d274014630ab58916f761189b65676e84129b05b2582102edf979735c2b5cdf9a06247d668980229aa506bd32263400a07591947966ef478609c582226f2d409bbdd42e7a426410c658115dc4cd43cb84979cdd7934e0a6609b40a9129a9df73ba6bc36a78eb3cacd808461fb41da93edd42dee2fb6211422b6ab9fc005ade80c5d846f1a1b83bf92f5884d82f6e8ebeab4ad0bb0d8c27f4c9b290496b073f8dfeff060ce60cc48c499a286d731fea677a00941b7d5c371012d1959f791f08675136220a15fcc9f3aa57698ae034c39c4d390794048a24f667cb74f9279f83aac70ebc364a7ce5e2a356c1bf73f7b8b598a99b6deed765f8867451d62723ddfc04797ff7deef3aa27782813c182c2946ee0106f95f2ad4d7f670ed12208f9d138754a0eba619a6f4fd45d299cd7f0815be4a7938ca8e9040ab2ccb419840e21884447e12839f044fff84a63acee74c17f75800c820b8cef25c2edb7e82dae38e1e5e643eb7045ac256e9e93e471c051aa229a4f448ce6b78ceb4d81bbbd2b36bfe66e393a9d7ed7fb47c2a4117036d6c2949d12550d82e2ae617e0057668b0ee946c373880e5e50eb4a4826b2c83dc905683d8f8de11a5845b9d1b5cedb7fd0bd76da3341e75ddd05f261520228664d0",
+            "000000081a9c735975bb5b31a16c39a9e6768dfae7a3caba528dbd84947896ec2001d70f786b05ed2fad39483479924cd4de178c562fb58819d592853ed98d556f0177761fd0ffefed384c434580a26f10311ba05d555f778ca10ef875ead1baef9a6f8e1bd4549d488388332151f9ab07517875b5c4118b7531b9167827ec9c084c2ae0c56eadbee4c74197bead837f5f50512df4eed350b2282c6e84b1382768677abf5317feb7deae2df6e697d565a625cb7015c2e9e6ebd65ec984dd6d4a15e9d2494c58e9d2b4bb938066dfb81824fd71b9b497a60ed30d8b67607b6f419572538159727a5c0f9b1309ff96a603bd966951716568d66ece21e6fc5097bb88132aa79b429dde6c8e8200febada61a995d1ec9c7e98ca54b45fc211ac617a0b251502b37f1c33fddb06ea636fe4b9f2c953b1063c17c73edcead49f12f746ac4619cd9864bf68ab1badde9b0e3ca25bc7e1277f50fc68a75abe0f1679c71700b67039cb7bb7200b068f619de809c5539236befbbc54b0a5f8d237700e94f0d2748273ccf425e1d1bf542d4da575c4b6beca17d33082b5becd934ee9d7734cda44ccf18b4097777a9817d7e9723407ad6e91050b79b43ea7787d0a9b7b1d228db9811bd9e51f1431cb1ee856980bad69cc2100c575a8cd48a784bb5ac214a6c3da39a12bb80cae219bc2d341a642f3b5f971f02939d451fced664bb2c29303d6d80565c0ec8a0898eeefa8bd7c7c69eb434d13e22e37f40a1a8bcd2c250128344d6b054d6fe106f6522fb907649041a31d16bcf65834812ec987d98617ecf66622600585699c28cf4ae8d7a9ddfd771a1926bbc0df264abb7ad6c7eab8d9853a9ddc23ca41bc1ebc7f83ac5462e5580cf178bfe7bf435ecf0b519d9605d9947b2b59ab9bfb4510e62b89b4d38895ac45e2a1842b8a13ee509a5ced874d34481c06499faa85918dd58b2910488f8288e9723473bdde13dc388e96e16d8041844d4ec9684ace0b3823f4387082d5d38bdc7a25f8575bbd1e7ffc84e47a1fb0f31951b5f7a7e7144a0e645a5f04b08c6ecf14502c1a5976671b2a0bfb188c300413d1e937a3f0141922fa885dd0f7f873e72c8e6605818a92b1f9fd4b9489493e184f1013c2c0fcd11eb60deee7873aa13554530c62cdc6929bc8b967248a13851853bf23baaec0c08ddbbf42142667095615a2818a0e4af39e6a4714806947470cc1dd6c263aee6a254ae603a22c2f68b38cc87d1806b20b0b96cec0c201916c5b0a64329a2e8bfbb2a664060d598461880a1e8e0ecef665fced7016c1177b5aaa69b265d58db6cc353b61f2c52ce784b0c81a68014a362c9ccd1098ea09ef816cc77a8d6d79275df684491a5ea30c98b86d42c0127928980426cf4e17122f69f228611626c1ec23b7dc4559892413feb131bb56925a501b386e638161a2bfc9a70ed096573356f5be73a56c2a90f56155bbd1e0278f3116f9ae78d05b2fc0d9aa0030ae67a781c20e0bca4cffcf91f16527e2d8b8f02475698d54514b39b8101f2a746a6ec223cbbee1c7c6d373d698bd3730e34e894d7d6a50f53662700003e872cfc7b2c21638b61a58cdd27a200f99c4a7fabfa67e96d2a5a5cffbf174dab74d96a70569e02ff93c7a9a4d7b4298289f02e6112b988789bde742098c626ed7ccc512fdff1307dba1df6675e1152fa089165b814288d35aea48ae919f53699ac082bf94f40d1748167e1ac806b13d47be01ec6b0439ba64cf33539a7ffa98aaeee6ed3c0ec110471c1d2e7a2c948db01cf97004b908b9bb98aa20120a868c43a94448063274a6d5a8897346fb2feaba12b8f028e48672bdead35893713dce5b5256b1a17e948dd33ceb2db1992bc152c6799e0d90b37d9f7907848ec91e52f6db9686512c63a2c53d7dc2f12321617a9daa218e934f3951eb91246599f07185af8e521eafb297adda701968711fc82559c7b9ca05b8b7d2309d5acb2fc37897faa43c8358b2ede6a90aa8321f4b152e6a4c50fee02a20930e50d7317dbb0d561c721406bfc57691c79217ab5f5ac6c8ddf40be23bbbfb997ceb9ed82e13b3f356a80ebff7d96b3db77e3f919d8942fb895bf1bc799ca084a6690f5627169aba23c1b7dbd27023af629bbfbb36b64a4d019894f6ba3bfd1a2fb4cfab4928ee56a01c8ae18a622e8520f73fd37fcf959d3caa653c013138475c903f2dbc034648ed0e484b0d84bf6860889d6a31f77592820c5a6d0b8d5fee0398aecc434f7502c4e3c8ae7d9d61b6e4c0a5b017415100ecb2942cbcc8e7c937329751a5d00283f16730dc351884b4a286ab8da699afd9226af80e24843c0018d8f9660cbe0532743a640fca5cc81f355c0b0f2ab264527f7e250ad5114b91a262c831c7993430cc94672db036902c42c69c844a191f4df828d91be1e2373076b09b47f384e364bf8050092a502f73886e9ad3617041807f1b48ff7a588ac581bb136ff01ee67122fb827849832821fba46eec6727480370ae23d9e0a29d89a761e9e79f78f26072363b5b7e0946a3833738d7f9a186d999ed09218637a08b4c5c518f3a117b1878f9f7b7a4a7e04e8f9827d3d210146e8acf450d2cae77b6dd6e9b70c14180ed5770b8836a05fce222c5edece192c6eaf1654765c2a5b5ef87236dc3e088d9505d578777e4bafcaa235edcaf61fa6e0ea15d4fb6aafa10acbbd8b922bfabfab0349f8ee07689d33b9fdb8f1e308f18fc101f3a203a8d8bec51852cf501724ec52ce90e3fb5f0f16a6c78fbcacd92cbbb8ec1bdb6f7b568905142523ec159f4869b4665f8070b81aa664a9c0a4069a16cff6622821d1d6c25504cee1aca831dae6e65994ae20556838ae50279da1a9aa9554d81d32a52744fbadcdfb85f92dffb154368b2d4977dd56ce44374e9127a4dcd5c59a1b3320380d4147d939a6b701f109c36e950091f24a26ffe79f911e5ca15c6d428a1cc1851364140915c768f4373acf166756893997b9500a76e3ee7700ba531fec859eaca35bb3251cc8f2ffecaa8c8537215f4d9e99186c92f635d77e350902de6d23faa517318cc6d759dc78b140492f1a787ea5d0d4d35bae14fe3017bb39d68d372df9122d6ee8252f6bb6133c72704c53b7d03ab01ee7e81eca889180d5453a68d7d7def571e8238457bfdf47102f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "00000009cf09b10e11406f1fbcd4d5b5ad2bcbcc61c1a0804ac9c39a40390c916319c101ba5ad887ab3529ecf7e18637d0bdaf1e55fa0e3e033b711228a50fe44b64c2f068db2dd2510cd97428dddd3e25a6ad9b387c7b714c344c705e2d7f26f9a19d3491399e76069951b8d7b7715b5bb84d82306f56a456a0859f90f815198dcc25565d8cc89f46ba1ed69311337eaf466ec06767e538dbdb5c93ff5321f051c7d4a021d3859201f85887f8ca43d0925ad4aba682833a517d352068cca0959beedc7fef29beb2642a1647351b8026a0f6a768654f67a52e910ff9b7e9a140c1411eb5eb4d828808c48c23e67e9c11a5bac13039142805ec894ecc2809123948664cab5a2b0269ef886a675f6d69b8c43ba6a32a6346e10aca33d3bbe41953263d192b80102ba68edd0fa7d23cad153d1525145b791efedcef04c5525b3a2a86a5addb9819e2d2eaabbcaaf5370e5fc2952ae8a07ecd307fd7d54458e9ca48c6d61614e790151ec277065ad8e438d28efbc93d1de8b616435ba7edfc939e79ffa33662277581c8d0c11358dd9e5f23ee7d9e2a1d08c6f3a500d3da3d822760291266ca6d807fb9ff3b1a852201c8e4604253d3ac1636258cf94e4576deae54db1e23b25801ea631e8754174c03afa4fa28208e9c570a2caf4eef5b67cee3876a83262ad656a7596b184376744f6b5f267fa61c7f020f43913c9e45e8bf558ea4351ed542aa3a1d648d20be0a3d1481990232c0521bf3c0f10d47f7b939ea97663377a9033b888e5cdd1a05fe1fd763906e9849f8980d91eb812b4c805bcda86067f0fb1bb447180374f677fb56b1322dca0a13c67664f3ad57ae32daec3adf4e6a38e905772ffc88a0af567e9e058ba909aa8ef4ae9609707483026dceb42c52efeaab8a23cba5d3999587f7b10b51eb422653271ba4212a8f92e2f9058d4164833cc47810259444a5ff4d58470bb9dceb12e34a63d735b0608eba962c22ad04adba48ee8f55856c9294b0c975bb11a8516aed5bb42318bcfb587d5cf4e228382963282b09b154b1c1fb0da5e3396a2f9b3091b2fceb164fb3b539a1720231bcc4ffafe6ef826c9119fbdf4abc79ed5258da3ae37da042b97927948b0e05c075be20a246b9ffaa256910697124d37e47abd10d2bf6197cfe481895532313e54b30248db61c36062692eb3ab5f3e495db34c82d07633d0ed6b2254e60e35c0ebec9bfa0c398fb9ce899c54d3209525b72e88aae0d2d1d90fcd49718f48bb7cf910ee59fcbb499ad84b969457a6be9baa452577409d9b05591c4c5c4d95730e920ebddb3caee6574ffc0600ba821efc895788ce2a0a0f9351db9578662c381ad6502a9d24a6e88433b0ce7385ac9540744781de943af30655e04ab719509180744339725fa61c110d828fc7c804caa18437c6f7080a66688342ae57400150bbb500537c34b644e5a1457447286dd94f9402af217d5c5ed0813fd645b06190cd85a593831d23c0e1d22739f0a063894d00666eb7917ff161b2618af8184ca3a3d5d242955478fb58708dcd9f3b6e6a231585224a8d2d1c2084487a8cf6f12e780e73a995a449a02f4615b2cba6a3915a7db947cc60022a26fb84c8171d15bd5b7a82200ad0719d13836654e721ea69e51eb157cd5ea579dfd80cd5833842f58bc1423257bdbade6a11e515254d1237ccad9ec61c62ab672ada6a5e26b97f8e4a7a901bb2dc326d1a06386821fc7c803d11bbaa7940b5da5dd9deb7756356bb73cf5828a2ed7b846c1cd4a55f419c4b024c0856b4cd908789a7e8a907f9a8bafa023efbf6dd919b150428d84f3b862357ea68a0305632a4034e0845447b6e066c52a7422cee90e3a5c06e7abecb5b37b002556bb1092d646d836c90e7eceb7c12ac2f2d51080ffe6e28f4c1b4616e7f39883ffda141a1d44f8c113e8f8b6ae4ee3cb9bf29bf060be37e7b4a01b35f2b5b4ab9c0fde267fad655f95befff5845b9e01502b98e5d24963ffdf8ed95f7c73110d1b9d5811045d2fa44d01aacfa8c7df38eed95ff6c8ca21f901979a2e7ef294ebc9744b2a67cc754ee006ec61fc7158392d3b7ba8db9d988bb944c78adb68db99b2bb34b617da65c8038b082730b77352cdc8111973d8ce3fe48857e47e05289006aa6aa263a415532e1dba6eba1ff78fa1fb14f41cab2f1a9c5859d6bf907272733a24f198c1c8b6c68ec47ea37b974d4729b8d199ec1e52e8c3c604774169b9ac2c211f47d6aa3e63081c46b65ca7df9fe50f5ce02481f3188e10aad46bf98d1093acc168131f3286101e1c4e9dc782d05d04a5e769dcac092229efd6e88dfe109189a08912ecba615b1142c41d2ff749fb2abc22b929107f4c6c417576947bc4ed1bc874e3cb6f1df223d4d851ab2ef964e802e52bdb98d7385244c3829cecc0cfd798d5140dfd1cfd64e819558e41a46899b3715f7b5cf9b88f6c2864309a95fcffd87be23e597c7f9e2a92b60d794e8a931200e14e02776dfc5989b7ba0c42b44d9699194ffce90489cb32d69cc75cd2644912aa6d65f901d3003c4ed0589fb5a4753355a690d2218740ba218f829507acd2d38f3095f9a9285d58833622af9066fb7e174226722c7b1f44e29994030388e53726a115670a69f6e7437d9c6fbd3546c76f270fd18a09056e37bce8a73a6d36e446985ef2f3a492877d70e01e6a6fbc1a30252ab624ab20807f6421be4869332ec197b53a9fdea553a6b1b66f34f3c1d6112fb2121826911061c402f9b613e1d5b1b856ede41a3496ac476e4b1690a1c59c3f84460bfb50851995196aec4a7df64ced8074d9a87b05a856e0f677dfdddd5bf536556d2042d30453c3b0b284a0814090763a28f4050de1206253c793eca74fa7c6dbf46515e2467dcc9d44b97ca3fe6f0209d613e33bf754ece4000acdd7fde0a5a3037a2ff62efa36dd771dbdd480c7a1d496fe490a1381ec2646e55271a0bdf82c38071ff8fcd923d4b3d916e007c6ccc7ba1bcca7a4a57122a3721946d20ce0e98040e3c359e03bc707ef0b2b39687d0fd7abca13dd3712a1513843f31f64918b9db4317d37e1220440a6ecc7fe79585bcdad93f44a71512a4f153de1ed6b4a1d79d7d69dc87116150cf32704c53b7d03ab01ee7e81eca889180d5453a68d7d7def571e8238457bfdf47102f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000a211e27887cc2ce5f2b079e955e262546ab48cf80c317898c376721cd65397ebded79c7175889b51032779e434a720a7e952eeff8555b41aa132f717a8d155be80e99c4cd16b4a1f4389d1cf0e2b68d628415b50ba50e2906e291a81eecca7764d6e8108f5a3ab82b309f591d50eb0b62c25869394ea7c229b705620ff23bfe6fa14b6cbf65106ad0ab3998cac6840ef0f9179e5f5134e6fa44223394fd480bf04e3456ec70c7ac9073ae356b590184fc1c0f31d8d262cc3df8534f81dc10ce33d470ef7d03487780bd965e813bdb8095c338e79038903460c75ee612e4d4a6b181893a981e3510d46d16cb5c005d727baaa8b945714dcf25069ea73747f5cfc9c0c545970e97111cc6c18cb5cf3df4bb8980cabed0f3fc3c883d459ad0b65d04ac4436fbf3bb7cc7d0de101fa1cb1d6cf81a5bd70d54730323d3bf7c00ea9fc6a9386969912e520b7730467f2bfbb836bff526d37c972e335a4b885e8586d26feb0e2922375e7f4a2a698957e8acf719acf2cfc08f35371b70637cc8bfaf53b9fda6372bd3ec0ce2b812863fa6d32bdb66ff240608e75c9e8165ad5a242b79159338272dfde63ea781747fff4fd19aaba9d542cb1de83b2acc79d704389928654ad09a3ad53f3c4b3739b8d29a4dd29294c24c41b2104cb6bf7fa8644ad3fe419179e0c62ca9c388e02ec0745e6997855a35bb7cda602b59d0685c24e94b2aa1cdeb16f3b5b02a5f0cefd4e3d6bba9223337cccd4880e346b430957f6f0cb3734ecd538accc583faa2b988d2c8d859e65755c03d6456855ff6238e4147b476ac17f5bfeedc6c241a8defeaa45019793185692ed28952ef275638dfc574a868ea0d1bf93579c19c5a1fbd99c78ae33d401e9c5777e1a505a8262219c969a032471ccc4dd26f9be409fa4a39dc40f9dfe00c55c2c1c3beffb5c77f272c35f128b874d69fbd72aaba25d0dd481032354fdca7bf6ea9ddb8aef1467838a40edd9e4561a41a5c2c4e3bc205c20078995399eb8f619247bca0959ecb79a90cc40aeba36283106e391525a56bba306467f40fe99549587525d957b995fdd53b8010d7573b626f37a5586d0f0baec43fa0457da68090b53b2a8fecbf9906f56c1937ee40d21fdb4a753ac3daa77bfdabc133a9467a022f5067e3a0a75bf8ae530f32bebc38defe85a11b03b07dcaca38032fdf95e5bea7509deb2fdbff4ca2f511944c54be6fb0e02a19c3e510e1dd11e034d1c70ae54ddada061e8130ce24f03d53cc759bd21d6e9572eeeaf441e42615519c7aa68478adaef38cbb671fc872cb5ca8e9d265911ebd999c2d10838fa4a471fb4d1bc750102c6b9d8bd812fa28f17a3e7c8c8f64be81329c5c302dcf10f07806bf94b2aaf462b236041b7ee9d00112021757fc7de1470992b16ebb54a2774917447d477367a9c151bb6177a7528e3ac55ec4b4049601a4a6a477f0bfced763d928efbfa9c0c817c68c7198e7de88a792c1acbba7a18edbcc88e57668ea3579377a7f1b3139a72555c0ecb89b4e7c488acb0645312aa93437b6c331313e9088529b1f311c73f699052ca735456dd54e9c8b2119462fdbbcdb1a2001ec9a3fe99cb35188a2ea4c8f06581d940a9d6b1756d38b48cb92d2a506c23c7426b7f8c57c275f3f0587e7db311abbc9a9677bac9c00811c538dfd389982b2aa22a70e45167914d1d00179df5e35a58155dee4b20d90d6bd839d78de1c7c9cc5c667915481672d583de8fcdb20581d2c5fe6acc8f71415ce01fcd556f28cdb84b9fb96d15c8692387b87c380ad64cc746f5f57d1546f833ae914118670fc63269ca3c82adee51fd918b97c8aab54fd27c86b799686718755e0162c51087573eed4fbe0ad97e407b7b504108124c9c7b0fdbe938f4d9c03ad1bc5c539cd5505fe1b58887f0bcc89cdd7772f1fa63e543f78c051de854b318e6cf5b77a05b61c2960eb686b4129647770c7dd387346362fccdae82acaaf3c7bb184c5500dc110079cb1a3027dd48ae34fbb3d29f880935054dd8173158e48f0b176583377b0f91043dc863f67001713ad078b4062295bbfbaf488239cf31a93b4fdfa4bf3050d892efaef369fcba2c6087dcd157f7cfe0c4141dafab2113ad6a066e6ccc6d60ffe0916ec2dbdad2ed8673b950f89ce7ba20459b8af9ad0e7a5be0d95d4fbadcaa2606cf0bc674db6a75f334bba25f125a0d47f5250130675725e8a7a87c2b875b2ebd01803980c1915de016b6829f92de422b172c0db88dffe4263cf026c7f404212c8dbc6bcfd87af1f250264379beaab70dd96a22eb1fe2ac1126cb5200aea1859818fdc9286cecaf12bc88d44d8e47dc0cc3726a7a9c7ef605ba9920368f4dbe9b93e516e2c17ae0987a94ed76c2006e4a5ae9669738db39604ccfe275c0aa6751878f095dce2a1d0cadccfca2bf40f6245ba8f0b8180abe37f9741910a2eafb8700f4e16bc3fba263ee4bf021402305dde577184a30f9fc63945b89c32097b158bbf8b8306606baa4bac3a2f7e0957f0b37d09aff97428f8d54412ecf5fa3623cd02b8e9f52296281a17e6ed327c7f76b4a4097ce05ace430ea14c2c10b7fb229e7457bc7702e19ae7e63d12008e038d0498c0ba2d74b85733ab7b9ad31308896c42f827599af08c890c3dee15c7978aa7485d99e1294b0c7988822a6cf040c07b01b820b703653805d851ad04ad763fbd5c988d636dda4925f631337963248e78c8b9bdb4122f7a11f9ed152d0cac44d1a39b293a0ac0c196d90ee6fbaea65b42951ef9d2e2d03ef6a688324679da533b96188484cd581fe5d756ecc7fe84cea8767229ab28da5c46d0639f4eec63066a52103319259feef759f4ebad0bdc57c5b510885f0f6a9c837acf5e1703623c4c83ac5558c323c042297c91254b54897662dea67450043c8e3bd5e6cc596f875ee8a7050f49bcdad83528e541c80b1d59a1e54e5475173decaf6ecbd16e7dacdfdc4875b4a6e09ec4cc9c739f6173525e156f11253d3cee122290870d90b571b3d9de4b7e28e59f3456fe728233ea8a336a11c35e39b69238317452be31d2ea13aa2613cd02ee51244fc803ee5258388f665eca59da8c4c80809fd787b51e173f4922822006a7705fcf01a6507b04158d503559eb527a597b5c220ec46ebd092e289f08fb02f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000bd892ca9e8f0bb84fdd9bb9eb8636b6138c66d530bb03c8054c0c66a1063971c0001035fc7109dd60dfc60801fe09bf7878399e17d9b3c2db3c2b8bd98dbacb3aeb3b690cc15703124e7aab1d1fa2a9f60ae80e91fcc4d8309655eae632b7e6e7b73d6f3ee37d501aab764a53ad672b1dace6a4c3ff1c7f8268d8b0b4326cbb529df30299ea46e0e395e4d59a19d1f179f5062fa788a09538d74503ade7cfef60a82789e35e0c9eee947840b8be6ae62666240618754a656fadeec1a76ab1ff8b4bfa20ad709dd00fb311e4fa7e9eb47b69e7a0b1cd26ed16351ece9d347e9c4a549f1d93e0dd72d1d415e08a315da248d02268386bb761e053f3a9f4f7c3f6f9e02efefe04fd241c3679e39d92f921bc04b88512ad0055e6e3da706600b1685956ccc1dcccc9cd64cca828fb303742dffc8b17675cb9ee0e4f713c7588f3aab1d020a666389e77f3481b6ead8e47f44f8b76b3862cbae898fb3afe1eed220f18997cab8d971b9c28752ea65bebfa639e3d41445250eb761dd053fa7b43486a838e07de8b1fa2f5d74c1421cd37b51dd239141bb23eb5aa19a7ca5e2263d3fe0b391196b67171290b6df01072801856e2f07fef7931d2286ede595df731c302a35b62e833c8ea1fc569529ef10cea049f97e921edbb9a350dd70e6416852e480444731687bb6e87eec68c2ace0ae8dc8d5a802a020f15f38cdd7dd35c8f46801713ed7b2cdda0b32b3e1bb79ae6b9ac9d6ecec745789f0f1815b2251e9bf115f0b5b455750c7c1823a8eee68305d324f14f8fd0543c2bed95709f72e32a1c242d706f9389f54b094228ad23a934ba3b41d0793b2dd2a75817fbb3dd78db3b1d5ca8f35d713e6bfeb788a32fb7609664e9c7ee0fcc1aac9d82a44d1df0ac9d7ae3c98235164f2925fbd0b579db48c02fea17def08dc855b7da3d88cdab7a8040e697c015f1dc66b02148aad109fa08fcc3411df6e1bf26215323ef7fe091f13d2e05d21110bac56545a5ce3acc30e9db4175693bff9fccbf9d5766f6ff10834efdc3003d4e65361456d2daec0a24c0e48aae289d382fd307e4792eb149c34000d4c7cba7c9ac73540a651fd09d2658a6dfd91d5d9ba74671728f9be9f3bc316dbecac7f06588490f0e66d781a0a8942bb5057228a741d4154e9e01cdad9de60bd1b33d0564a2f27a12f494c911f51081dba961cc92363e725d1908f338deb91a5b41352f2a807244f0fa08f7edb644194454b7fd4f7cf838528a85f22afff5583c2e4e3fc8eafad4b5e99e11a0da4e7ca14a838b386492a87b14f084c8a50e9f866923717c2aea1b3c9f2d938c3c19036229c12a3679e16aaca780ae435242c3ef8cc65a354cfee7fd69b26f4d4b42ddb198643ee69447ce0cbccbc539090fe3684ef012c9131cb019851381a4def73e82ec78234008775701f0b4e0da70a42a6d558b8d6719de1a6ff92da978eb3a758a57a77e00ffcc5b68ae8ed72c8569a5e9ff2fa248a82a6472ca49ff01282ccb9f7f79ffa8a334f4466bed5ce1beb69b31472101e41eb8a80a95d81ab98074ed1215e8cfbeb60a7d8cc896b72fa79e0e2872b6a6bfc154cdda9a1696bc4babdc957f51daed08b81f49b6498f9558d1317f013a6c62f9b60b51be28d205b68dab2002b2212ea8e0fdaa1fb00f68943f07326402308fec2e7520eb0434f22624d4bdcfd0403f1d3bacdbda7bbe82c677aa4cc247fb7e72f157ddf24e0b5e3c6ef47aed61006edd262fd39711e6fb94bcdcdf2c71f04ed9889e63a8725c5d49bd2f4a875bea9e8e78235f9fae2d99e5e0773d8cb0f8ee2c846d1d240acba0ec0e71f40c03ea2ea874b244934da8c2576676de415c0203cfd461a7b71428de28467797f8204bb568d89cf592770641e5011a16b749ff454fec7492a52c3e5de85c1a5db8c826c463e1dc8730aa21cd1e65af9d1e1aec12ff64851dd4615bdc7da08e49d52fe31e18695358e02bb207467450d743382085b8737fbc95ed1104ea733c20db626b3f8f46c02a03c5462d55df395b9d32326353b4d9f18fd9cd3d5a3ffd633249b3dbc9c86922ad37f8b4517b85be5dfb7c5d2fb4b15c2327e023781280c253c3437a793567b0186c3e3574753d5da8dfdb9b6d01a08d1c1475df807f3a5e6987f0a0e2a66aa2a03bb413a8dafb08f00a704767260c22c654f654a34eb7960e880b629a2e60fd1debe2dc2c2a646baaff36621de529e1de56a3ec9b29fa6df09e2eb99c79af87e7df649b3d9c1392cbbb10efb1155add61a96b16157a4f3a3c54c612edf5efaecd6f9e0268c684d273fb4d346ecbbaf570090ae0bcf31c894d0ebdedcaf6d70d1f7ef954be56ff7d657621519dbbe478e43781c2fcefdd036fd2fd77b606187b76892feea2a4811adfa29bbcefdd19a54899f720261a205123fee7e8a2c36e62f194a1fae7f6f859678534ade46684ce1790ae83e004292e1e5c112fd18fa789b0e2a194a683608be914229636eb8eef01862b47e246d187b21103dcb7762f228b1b2cc5faaedc3f6c9773b218d6100a84d93dff7c86dbbfddd460c7720e8f0ea97bcf115d05d02b5906874801e36ad1ee39b0056a1311087345d63b054548418f0390944ca70110250605f6de0e5a25eb50d2f5978b36f7f5ea9d1bbb79e0f69341eb718a4c4207799552f7c1ed96bfa8b7f427c695933c48e46d4139b8523956fedd31cc41591bcaf61e96eabbf23849992825081334662b720218b1e173b9f045171e897a189eabf56d36efafcdaab10d94ae84b93ed64d826877d8d2fde6e28dbf5bbde94996e56e435ab84a66d4c7244b90d1356c81776006ba59056dc122d4863282eb40b2a4a03104621c38ad3b595b7ecf25666f54e4cfc46dc75fcb0f49864019bf6f2f59de7a2c615d4bc05493e165a253ea7ee2b0a4d38e474d0043c184d2aae4391d19af9a1543573a03c4d046af05e40cf632c7fb1c0e1cdd3b7f1141794620398f5231d78db1bb66b96a2f85e014702e257c1be3337ca27fe5752dee26d79d6cf7c3e372cdb306d6461eddb6289cefe0f504692b8b40c69685579b4aaf5c8ca910ce70a7470559846402e184adf21f8ddc477d1be6b210c237190893dd763a199c06a7705fcf01a6507b04158d503559eb527a597b5c220ec46ebd092e289f08fb02f24f6430dcc51e25b881b57c6827e33fa0a5d4020502a2bc0495220d647e7958f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000c89414abfb71950a59e162325388b50a78a43e1341f141cb6e15916f061021ce7037f7a90c4ff1c6b80e186095b9d4c0a9a1d3fe3c11beb80a75f0ce783ce7d5e13c262063bff669617194ab5f3d9f731f1b668bb21fe7c4675f0ea7e60fd807f8a67ad75e07fb97e5c493eeb0f011feb1e37a1dd67acc1bd04502c9c74b4964fa68248fd36cdaee5512a92165249478695b189d8e317e4f454e82f1458e30ca2e3c5c935258123597d4ef0cecbf5e76d2bfe9ae57e76ef4ee1be11fa087af53956a88281942ed2843005e4b3c497c0cbcc23f2285887fe2b33c5578274e124b5bf279c9fc03d3aef7e71edb0fd0d80e0a9cce5337618f60c01f3480970abe2ec18b00facb520fcb4b1fc1f275135b8866d3a2c8dc078dd1fc2e6905eeb1aded87b2d9ac29f8fe7713d3f029a5dedc405915be2bc8faf1334cd7feaf2decd281ba36786176506ff259cc568059cf9c2bafde6b649bc84b5ddfa03aa9a4c4db58dcfe044b46a554a75f1e4fc70366678f3de50b0769db796272d66c302fbe1e41a760d7d3a080f0040b9de2403bb6708792790b67951cf9cb3784e636852d3f31077655a439552a58b59eccbe2d8fcc97aac41a787dc30040b2d5dc696297b1a3778db8d32e5517e7e4c6f7cab83302af12b79911df99ab5de94e1aacffcb46a15d19374f34a871e333b8d1b6ccb734c757991ce613cfd6c5eff0652c9221497839fe499fb0b6a4b749fddbbf72ab4099dd9a6f4a3bfed2966140c778bb5de5fd84d8fb5216111477443d047e0d02cb392acf655e459e927e9b2ca137d62d67453f2c9d0e7afce761dc36ee6d253650427648284ddeaf1b75837cce84335501b2a5881c83267538093090366b53d04c0fffc3df131e6399528427e5da7c3e03f0c759a4c7f42f0e394cb9cc3b2d689c7185bfdf300e0612fb350272360dbe8b8f346ad73711bdacf95a1aae06172a171ab9555337cde944926c1c06e5cceef8815912414bf72d31d49d7090e99545e04380c6f525919c0ca68980a3943a98a6ec9834ba0479c5ff353f4336f36b470634825cc815c5892ff1cdc45f48a55dc2088394ac44d653d3355a5bee646aebe535262aaa8e8c015f35129d3bf9a0410b3fc46652e5cd354b09759cd17c8f0d945e6874a119702c46dbeebda32f493720efe5d39d5f13f987c4d5776065cd6b01747dd791cb9410abbbed5d419de9016efff221030ddcd7a9d1df2a5e564024469981580786a4b58a5f35f8ef34ec2833ff5180c6870b46f582ee6a82c840a4dfef47b2fae0962652006aed0473fd67cbd4662af23fcf0a46b15b56921df0cc22e4a265a29e6f6d7f9b3c4ace4b4ea9d76c2b9069e7a164cebbb4fe2b78049e887b1c24e053634eb69468fd55809b61e2fd03d035155b74704fae62a7af068962af1fc3e82eb2ff18eea1b4f2dc10e86f4ad425cc28c4ac3d95caeae42ec9fe0e406ee241c8a3e2291eaa7caf0c21adebf4e20a9be73f9468ae2e334809a0fc1d4218495774054be1b07a1994574c7afb30709e96c6bfca3ac35f78602c785ccb47185b0a68bbf6c0b53f40e6a892a88a90daf828bcf18a7333e9c074f93cbf3b19c6837adae9696b75cb2621cecddd1b97c1c4977a90533a5a22073411ca30e4717fdd321c0284b9001ca25de47c66a5d853c9b0dfb762a682c89757ea8ab52062613d5820ebffff9fcccccb916eab16a339ffd987c5fc97840f60de475c96f2e25acfcd6f8402cca754991c8eacb48a546c6ed200f5eb9a39060bf18e05c0a796f55950893864569156d97dd3d905bc914ac8c9ab8697d98def2f8f9c8245855bc856bdb0c29d047f02a5d97b458de2578e68b56f82600f4723a1a5d627c4154e4cdc21b09ce9faf1ab7ef701afc4852b4031112bd462f2ce6ed353c2a0348a08955debb6ceb25bd73c83a006261ca9edbf7c9ec656577022e5b449e0b7a940571e85b941104ff11283027f0168278de2ca8580bd5a52dc65a7897c318760080a7f1df88cda94588fe9cedc3c49a94480c0a7eb61459828249641c09ae6fed24e63e9d9049e394f5c6752f1a529ecc95bc980aab793bf150fe2a577dc47b1cf3222c701326d50be7b79eda0899f3a9e3af6ea076013502faade815fc23f733c513a29587f7354bc98ab6ccae80d07718a281af2df22f3805a790550e4a76a205568ae42237db0d9ed133cb7e154ead15783775ba336e4853ee06e0e925d8ec332fe198d01b678f012a2f25f0fc950d04987e2ec16ae9af0055ac3865acf52086a48af4be746b5b373b62455fd107cd73b1b3941b6179ddd3bb6d86994076dc846208569289c13b63010a1ee8288cba5dba261db6136c3095a1f3f07d7540389794c011893028f95f61f44f86e6fd4aaa6d72ef21222f2749687bd68e94c84f6202241adafda41d60fc9aeae146259fff46343936a59b1834b78197fcfe245f35c21f7bd00d981a1fd9018c598d4af966c6850a06216dd11db3909cf81cf5200303d5ee8f437b533778c2519a1e4b9d2682958a215f6b78b720f460d533e98f2bb634dd7da93e5123d302971b7753b9c4c1e7237f15741cba88ddb2385659a69510facecde6c03aa4b9ba743e1607ca74eca4a80a823d8d57287f59ad4c3efa388b31d9078c7cd036657f1513b1a455606a0a4a15be4dc2981181c3c9eac8921c1c7bcc3ed1e704b8ea97d0aadeeb7a3ce252e6c02f3d23bbc89b481e4a003ea9fa069399529663fbc6f1a15c0a304b8721c916f35ad368edf51780338b684ed6219d4ea7e5a54b21e6562f32ae40ad20d5870f9def3989e18af7e82b9b56d9278af70fc73f6868aecdcb00b9ed367423442f71df2b65942054a233999b2e8d6654f6a946ebcab047e7c1dddcb9a38e5f9c0d660403ea05715488923aaa768600ba575ee01eeb5f4349917e734958bdb7884cc22bd9c82e2d2d2405addc84184e812c889d5ee364bf77ed5964cbc53ca931511aaed59bc2abe7d88cf23786bbb70eb0ff68130e143d35acac03edda59b046e9b566ef2d1d204fad058339b1e3b6d750aabfd556a34b6e692fda2429e4ab41fa90f7c6ac9a1b5efea1be6b62de767020eb4432e725a1c47a68c1b4d91df5e83a9383fba252835f1ddda1b9ead2594a80d91977c594c0ac4f1035a407439803b4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000da714e82f54dfd7f6c178572d168289c560985042e67c45781d7e940aa1a33f475daa7030d66e06aa3af46ca036f84b6e3b64ed6fd3192a9ce620f5646dc3e27cd949e534744afe3763f3766ad1f3040c8765b58938cef23bb6959863450b322a4d6abaacf215216be0ffff2d0032ea3d2c5080b75bd836bd89286193dce3c869c62dcce82a28a7383c7aa44c05bd97e018c56686042d78076739f8a335f3db927f9f02d385370c05d72859ec69a2a361fbe07cb6e2dc6cef48cc253d76fe80c65c2afb2df7a16e2f380d96cc054408fad0e97ba8ef4ab07a40720ec846c43c00b5bfd6359b187ccd9a39c44da45d9858a2e98d65c432199b44b09ba66d4db8908cc8d642bbc41e2353efd83c7b32898791ce4ab95977f813bc8af3eb03598f2773667277aad09e296675de39f5256c2652da25810bfeb575b36dace7ce0da4a188c30ab6cf32dc02eda4518b4811dce8df899556d50382a0df047b9d60cc2fb746da68e2635044bb99aa9b55b0b91c709c273a6b2e4639ec33cb0d89aae6667d2a7548d89930021cf49f417702f0affff0a3643bcd0d01755db385f32c3da9e8a09e1ba753aa8b2604f5e54186877aac6e3bf110ff66219441b84d9c21835bb9c6fcd0d4eb2cf222167d3d0d6cbf5c701c0372c8cece7e54d719cc87d5fdf454fc01be4255d00880c2c43abb6290098207fb9afa3066ad64c6298eea1db4f4d0955d7f5badddaea10a7594fd2cd97c36433489e2cf4c68edfd2b2de08144bcb43a72786e244f235812aeec01da3bf2f0b8b8f5a0594bd206990858361de2e5cdd88d58bf4fbbe4a360fdbb992315ba586bed1aa31993264969f3054b9d182c027aca53b6e44b1866e0f76ce781e42f65f330048ecdb9a0fb41ce5b6303871fe893c4b824f639dfa3fc44761caeacddc61fd5495e9e62697ccca4a25cca3264dba4c2e84c3c9ba0600fbc9418499865654fbfa601114d2bdd2ed7ccb8a98cfe1b01a3b6432a9d5ac508f118be58be1b1511f7da077dadc35efabbad2e8edc1b715f80aded69412833985e0d043cf454d0d74c5f0cd439fcdb14ffb3dd42155c00c6ab40cb975e4fce7e24d27be4922227d42f66860fcf7b2fcab0970c78a5b8354886cd1e0be2473a230f52ec9e9b0d0eba098a498b1f15f53c02bd8eb8b9f977c76d930e886bc1e8d2f60df02de4fa101c9be366c4cf6f12c1826d9ea8a4a68312124db136e3bfb3fddf46fec16739dd5f791d7a3b215cfcf245edb8173ef65faf7674fd461d0b61d9f72ccfa28092b1f879297f7eca38e91624a525d013025190371798a93e7271ca2687d9d8b9d19b152aadbdb3b1086df9d27bbe9700726be574823625561b65d5a4cf7e865228766bbdde67291eba7a33bebd428d52767a8fb9a96f7f7dc977defed8076a4acbc0d1bf78ef933e4d58b1edde90584b0c7a440db4f8a9309170ed54b2c673137a565d6ee62deace26f1ee568939ff5f921cc1e4a8639178d1231d21954ddf2326cd397e456904655e4c00183cdadd8ac2ac33b1e1113de190bfd61df44383358f1ad95fae88b43031bc9be52ba88d65d60f7ed19a49ba8d39ce4e7bf863ffb45bdd4430b76face2669e7e0cd579286edbc414558c56fdc44d79bc0e1d9dcf3c8a8f849a4ea5e429adea28e3be1408dca1d380ba0e06828bc5f33cd9a5bd62675ea79827b00c11a8d4ce2b095fd158f8a1b58907b016065a78a70910f450634e08bd132f2f6c510eb1bd3dfd3304c59676a8d44f87ef19623f2d4ace042c8d12f63f2dc7ef60d89ac00fb5a193cb4e6c40a2d2fbb1a314e78148fecb96437af58dbe7973c63b375788cb8f7a8af9a77b4c4d2010359b36abdbbadce79ff7353d4662fb8c28231f0c0631cccece50f217170093e2fe7063f2d13ef13bcd2cdbaa4a575bda1ba8b73b990ee52cfe27d325a6927de33b3a42b4059d3bafe60936fcf26eb7cf409dc7bb70b95a82a2ca81ae8afa7120d9f9c4c66ed8ad7ed4b02633f11913f23bdbd75d55287010fd027c7c4ce5c5c1f561236dde2ab0ab9156d8c75174d2ca2f5870977ec2cd9b2aa1eb3bfa7f7c828237cbe6217b8c317f27504aed5dfc6045aad43c8d9d20adebf022643500f7bfce324fee49118787b544db1fb511bf1f47200e6f2df8b779c7d9eb629b77912c3665d2003b3951f4e3bc5425a8183c1094de2b4bf417b3d1db7dd35549db5194ba3c34889ce99a13bcc5860c0fdc5853217e10796bf5af771593c47dfa432d83a9544da80102d74f3f920d134244d74646aac0e8bfd3842719dc8a79f15ff8876eb556191d20f66947db08bd93e7f3ad130a10defbe215b5961d15e0b373d3e99f2cb23e9e9873f1df4484d779daf8cdc6f2e0da7855e1f6c67c19c60844abd2cea13a102287a950712768c2e321782ae8eb7434ff239b4210ac0941789464de35420b2518822792f9490a6955c66ce5b9e6b90438adc255851152350aaa57a1c96391bd08c5d98b679db4c04478fef77648b0c41105c86f31cf5e0554ea3f79fa24e8ba6a6e14ae6c029376f651abc1e6e74341338919f9607a119d493acd94168a4b6be73287a81dd1106f3ce877eb7e4f2426355f2a3cf72b97c54de2de4689f9bce7b90f72a7c27cd215661ee2ff2de308848161f7eca05ff28967e1c5676e33d13fb0c1d05cbee97b0f5a2e9a3146db6401ee85a25ddadcb2f3c98a43cfe47391754e0a75fce59f77b4022c07530d86ac217b3d5c864661d650ed9520bf8b5daa2dd5a37a8f0741229c43c866fd5af070c5f41d668a651182c7ba43fab5246eb1c47747aea958946952d61171f2f9758eaa4319f4a84cb5c020034e818835d06a41dbc9d019d6b9971bff8ccb159e5cdbd1fe43e81b0c06246d163c531f0bf3c5e2369e8c5b2bf21925c3c261a500de90b94fda0c85fa8d781486d3085ad7f3745b2f056a4fd641dfb3270c3f1f03b91deb6ce1125cae8aa4afb42db0cfec6228450839f18812edb4cb0edeff8456de65b604b4fa7f6e880139b1a09d4fd94c41ffd2d07496c7f0f30545677b6da3127699a73fbe84a0e0b1bb7e23cbfb473a07adc74cb81454d7cf337c97eaddedf4d2ffe8de3ad645e77fab66ce3a9383fba252835f1ddda1b9ead2594a80d91977c594c0ac4f1035a407439803b4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000e8fc52c68779f742a04ba2effbdbf8a5033169921b204d3518907e5e8ce7b051b315e8c52f1e7fad26e3aac48f95c68fe6e9eaa92ba7a5b9afce1963c4caaa82cc295b4c86e987242c5fb2864fa0c94f9ab41f9650a8aa8a9ffcc182366e6bf33946eb58d4b64cb8e1453a6027638141946601792d523f663b79a1bcd062980ed486b2aab4848f50440c58c9effbaad77b427048d6f8549d31b6ee09ccac94e2b7da0d162759e8b431ba3ed061e0a057c40aeaf868f7646b221da25e3f369a1945b36e52dc77c16665ecfb615f83939685ce209c5559875310961d63085a422bb2847542513316b02dcb814290d28b52005d5c98a4d8e88e461efd45ca24bdded2bab455b60559c06dfed5acee085665741482d45e4d0f983e8af423895a504f05dad8a949357bb742f426e6af11384ef308bbad6d43176cd4857b51d64c1a97898508e1f9ec4357f18f6e8d426d1a56a265546994d030926dd19c6645225d41ef0efcfdbb871e7b108c3c4645dbd705b8fa45e8c0573e5216353208ecd28605ab3e6f5aadb8f4c0c1d9b519a45b62491350ae8d75ec6d50d2bed4504896b2178b95846b878097f90cf472ec998c2e540955adbcbcfac0932a5f7c8a81cd4771a823ab154a881066f4129142abda56f87ca3107d2a0510b8b00fb3dd96c0858e975ab36a566af9303e42485b51ecd4cf549c5ffac67cf238faab30667e3ba9aeda74deefbdccb85b037f9b91b82c12f7b5554767c1e302dd53d7630dd283b3d0d96117acfbb20da7bd4b1464eb57c31daec6acb4476b311b1295ac70735a184ee56482694dc6819a1b249ad865a6b270510f4f719f57627a325106d304abd4e0ef84289d311ecc13e00fbe675a27d5fa28ae3899548b987aae7eec3b74655cd6dc1970fb1a77abc1815777b6b4c97b02f32841ef6f191129bbe7369c24889b108e5dd12522639310c0659f41ecfdeda2e9d55967be9798b2ce9f490288b2ecab9b2bea33c09d89cc500005d05e6cb41abda760b0a1868fb9e89f75d74c40514ee21d87e8f5dbd1e3ce8aee21629b101716d1b4ecf5cea2a8d97effdfb204161b9bef0934e1777610c4f05d2f87c314f011a6dedf6a8ce3d934316f23d49f3c36f43bd55e31dcb695c2de70ede2d4c323807984194d9bcc47bad6917f290b0086f6cd0452bdb62d642ed5a97f9e4c1f6a4d4d15785536a437fa11ee766e39cf4d4ef92727620d33a477135f65cfb138df24d828f943fbf76d5911006130471dcaa81c771c31ac39b4cc668c9da0a7fc5e65c494d83d658ceb72a969b9ce261ac53ca1d385ec87112e963c85f7765af477b07c20e51187686435a5fddff3cc2e7902b408b7fc7b9901950ab9f1994858229bbb2f45e7944c71086d6f56f4bc2ae15d1304ae57b499898a414bbbc7f0644269d4f4ed1f85596985b31e468991b8d8ffd349c94b2418e9166d5ea4389611540cae6239ae91801ea4302bece753989108f6e0efda4a0211e5a43f1fef8a503188a2aa15ae58c2a81359f58b211e7ab646f31652cb7f3ad57e3ec0d3b4dfec1fe8405c32ea502c1a86c864cffd121c1c7cec942c41993b7f2a7d000ca971609ffbb8f59b1431202afe1417397f16eab04d886e76373d6349404b49d89b0890e5f3e45250bc79ecab9ecc6587de39635bfdb3add8c32824172df1e141f572d64267d08d0b63a9d212937d19738aafafc8c0ebdbd98798ba793abd4c8728415f5962abd3bf1f9b2ab225a225fcd0a176a0cbe887a7f3e0511e06a0a7661711ff65582ac1ca59e6afcdcdc21285d2ffd6dac50d7078da618e6f0f9bb3cee3a03c56a81118d03c9dbaedf20d76c2897a12225c3e953c9ef1a1f75f47e029de3b0c1ee2b93c21f2552fc5579da3ce345bb19b07ce351ea1db17ef1bf73f226cee86e7c5b4e9e42d980da500aabd6df7f864e1789e7cd10b185449bb5da587038882522e091436a4794f521068789b6c2410002ce62fabb190a484028a2d31b0a97c6629e12f6d65bfc7be6d245beedff8495bd62dbd1d69f9766df4ecd4d1e23f81d8e6c1cead0cc5e8af21b4d765f7d1af4a5ff1fbfd100e07a5c02a149c59c202d280630f07032998b7aa286755673ee01a210f24d93d7974e8c825dfcf751b416e32d424d2d9a53ce35c68cace06902d97b2cca12f0a5ccd59c7231e8a0455765633a63ad068b1335f62e0ea0a3dc2c9d0f8ce614d4df17b711133e5bbaf45a2956f8bc717ff0f79dba290598917259c0aa76ae867e5f924e182265ad7d5c82f122379fce815e41a785a343faf003410f708aa6fc989884bf8fad6517a340f7a36b8690710f0ba3c583b2ace425fc9af5f9286c662a703bd213b77fb9e0d953a511199c477f810f2f79274fb30dacc6330b61435fe067f44a4a6c7527e751a3598f8dacdc4b0d4ae0c24f5372d808e6cfb1f36efdb7bf8d676c231dc84c70123cad4e69f2d3cb66162c0e604fa43cf23984519965b23b8becfc9970fa946ae7f1d4e9bf237d4c9fe02760bece1f80edbf117e4b06a13a03dd3cdff1a6fa19e05cbb11408fa9a7098c284e60479eaa9d076a5881e87029887f2525cc29c167338a88322189b433965b713239802055ef76ddab6194d27d945810e97881e861050f13812170f832a701501e715edef71548de53ebeecee4f23bbf1468b77e4e5b293acb6aa7d93495994c43c292c004722cdbfaed3f1662609a370f8c41020a6c7ee8dc993b9201875aec1c1c778eb6033b7fbb96133107a1e7d91a19ab613f2b5f8312fea4b40eb6a89e2ca9d8f5e92b35c0e9765aa9bba5567e95ba2db688fe8ee590f4184244614d3dfce26d79485c736ed8fa5dd068c0d7822e75e9dc06ef7e98a461c44cf735ee16cdafdec154cf075f10afc698cb4347cffb4f98ce045a8a24d4a565085cd685dd7de9b5181e29c0fc963d577607be5689840d1408fc894444917505af68fe6edc6c843e38c7347c3560f94e91b91539e99d36e9075a3bf425b9d511ab03beb1603d6639ec212628cf9195d13d2788680509b3f8fc3a3fde61bfae24748a496c06ebdee026de45f8ac53f311401ab9b00db9178f91397b8ffdb170db6b76e700e16e3b2faee734e0516487b09ee81116a7763b0bc919ea062ab18aa9ba1329af73c4597fd612d79e5cb4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603",
+            "0000000fcc0e7a5d3665e526f3bfa0c4f79a87d0345c71d81e75cdc41e9fdd0a0983810337156d5d733a91a9d511129654d5a2c04e8f3b2a7995e23d075dd0a63a1673bbb3148560f41a7bcdfa0bb63a780e41916dc3bd8939d0b72b608b36789f1c785e3e74ce53916966dabf193cae2afba31ed7324eb1c69e20ef3cfee4e54593b8065ed26ba1b6bccc20db8ff5e5f3be8e2e5709dab064ef370d79b2973065d3e87e9547cbc99a5f6503e60becdfebe889f822b80af071b5ac4199c627861881ba14b6127aff5bc9254cd0db3d63e09041e64a3d26e34922d4c42bb00d99c25fa4e3a0485b3f4b28a9f287090bf1c25517334a9b56764e898a1c5f627b486cdeef3131fd959ce625de4f1500a4ab9eee693fead0730cff0eaf872f03bfb319ef51a1e00346d4158eefb68edb0270fe603021e60d28ac6967882ccf65d75f0cf17865deed8715c9e9412ed10ea7d551c2de1652410905e564573122912721636b1f048cf33b1dd72aec11503c06726c14397a801b607da4b266e4a135917d9a002dd29654e9603d883753273aac7007df21464fdc1c3bcee0028e31fb8033c23277b19e970ba7070a0dd3d1ccfd32b8ceaf8efd3c5442256279b7dbde34ceb1444887f4e9ae64787464aea9ea00597bb2c8a67e4ebc565db7fd90ac502bf54b34eb464c50988d895b3b530bfbd7d91b20a1b1484791eb62c64b9dd64ac121865368a6db1b264c4be7c650e8bd0ed6f51935c6feee99c666b1eb95b4d35bcd2d9470a9d9c6380c367c073fec2e270d1fe9a9ac73885b32bccfaedb37b76cf2690b3e5f44611748d297cf37d929b33b2828c4a4f360a60eac4ad4414cb97822c5673b08f5faac36dede224007fcb6d23045bcca880b505f10aa1210dccf4330bcc616bc70cfeab4e23eb8f69c55e184067ceb6790b708ca2e6ae0da65c5c4b22d8e20dd4ab8322179b92347fbc1d944ef51e98ee4783e49c133770a28e77f99bb485a1fa9dbf0ad4a76c8f29745e6527d73dfb9f0d31b733e88931b2d30fbb2bcd18ec462d86b1d6ab4912ad4ef8cff7e0cfdd1fda1b9e80603b2f6afc8fa94dc9613d11af205ac0018d2286639cbc2d9a1285e9017accf5997b5ba94df03f11df3353f96441398198d1ba09a9746547c78a57628ba820fa1b6b8b40a310454cd57d4d4505cb23a937fd3d1a8e4705132b6b69ba446dcac02e3d47ee27aeb2c9739e513137db01f843b44fb62c760a5ed949f78ce4ced311eef5d058ea29ec89f4d19adb98c79e8f5de1b84ad422ac26e3fd9fa2ab96dcd9c57807720b0eabe4fba74f262028eeb0cc97ee200cc9587ecba4c6dee3036268561210063cf8975f04dcc229fb7245bd57ffc76bf2d07eedd956ff2c52f22b0f40d3ae7ba6f4525f85120ac7e582ad921f44d3d76dfd7ba98bcebd2c891fac705bc7909688838a31b2d922b37de1c81289562a9c9dc0b470fc4be6a82c7065e0f998755badd4467f6fba1c57cf849d69aa585d9ae0f04b478c9e3d7b19e1f66f85c672d618784b0b0ea288c8947b4c36bb4867afb71e2ab384d542fbb6a88f73c3751c8a804b65fa524268d7349b402d3a3afd5d272ad742723c61a26bf0e50b801240f4babe293e42d5043ed660a079258752e38aaadab4938854aa5a18fa30807b703726c4137587ecb0579b63f7e6a2ea21858cc977c0e8dfd1ddabe69244faae38d184beca54cd691f175ffc6e8b621a4628e104dc8a9629a1169947d17061680f818eda5f7c5430bd87da506e304fefb9d687e2ff764c0885cd8b9385b5633daa7d4a5d50850fba6498261f76dfdf9e4bf1c431999db1d731e68c1b74a11f4fb5fae6d0745fd3f823e621dc553d1c42b0e0ab488bc4e32d21a2018c292df801aa7976ba9f0c55f14910fa9d9abc13375e93fb8b9135ea4c25ccf2de87ee7388b6cebd99950469a815f510a444f388e1ff5ff57984179e93c2c20de13a69e9c676272f5b64d2736a75be057a470d4f9c825525b9a0935b01834ee82383bcb620ac6096445db5297a7be006252325c0752272ab9904d7c7a00f6661e726268528cbfc3fc13180ee32397ba91a04c81912603ba4e98bab97e415cbb2a7660e88d52d468b2f6ed9b98d4223aa15c661d187282e55bbe0b8d589e528ff9df1cce6302f48ba9253d2be75d0ce398318a97717d14a6e085588265dc0cecf320eab11725d3756019a3d8bf054c8e3d4da6e4108ff4f5e548576f79eeb11b53eecacafaddd2a648bc6d7edbc8c251a43d64a5d738570594873ad3342afb00d8cc9470a71bff591cb6420b4cd36c52ad5612bff07966a0ebb1361a9dbfb09d72230dd48aadf0a9132757591abb55f11da7978454aa414e8cd55d895d74d246a0c8ec98ed3954695f7b36b79e65a2c7af3a755e647887955932861d2ad6cdb14f135958e0858e0be9fd563d692a0a0809f69b49198c5de2318e6af59e7901629e9a8d364051cf5960145646ff0d7436bd05a18db69af2ee8b078938af8ac1c7bc1cd0fbd742cc21697c5e23c3511de7e75c0fcddc3b4c0de91209aa252e7c7853759bfb25a2ed7c71b45c972c6722630f0330fcbad42ab8fbaca41fae5fd37cac2bcc0eb947801b6a545828e3c6ee58c8160f3a8ac65e146783bfe7a42d800f48b6b8e4c10ec5cf996a7f1c9cd88aaede2a44607ecaf714ca38976ec0032c5f070f82b101e84784b1a47124f9509f6e61770c68347d71593ce6619802041e1c168a75e6035b8fa41aab6ee46f1ebd72b68e59e3fc2cc512fa60456f33ff35764be41deea78801443edd9486c45affae9ee6c5b479b6f1a543b11dfa3a88c1169e26add505ac6531ae26e7cdab1a97305d74a78018d09789e058b698cf8d3cf27072e8b74134eaf0322fdda88a2e3c9f905103021ec013cfffbfc0afcad6def43c04b1780fc73cb7c11a61b69a539433e961cdc8bf8146288384383979f0059d587b44eb4591e36b5acf05798c91f8177e3b30a82a837d199e63a9e7c403c2070588afdaab2c766b0a39b7e0cf8d7681d346dbd739d37ea35740b84c80984331eb5093fd0b2fe49d18cc0f7268df4925d3a1884ad7b6eee2f7ba0afbc9e5342024b614f4fbbab5561c31203c4f1fdb6487b09ee81116a7763b0bc919ea062ab18aa9ba1329af73c4597fd612d79e5cb4976821f565bbd0f31126eb6bc370c6f7253908c1b092714e0c984b4afbdff758f38dc4949de1203bb359ff82c3b4b629ae973e1b97ea046596f49282043603"};
+        int height = 4;
+        XMSSParameters params = new XMSSParameters(height, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        for (int i = 0; i < (1 << height); i++)
+        {
+            byte[] signature = xmss.sign(new byte[1024]);
+            assertEquals(signatures[i], Hex.toHexString(signature));
+        }
+        try
+        {
+            xmss.sign(new byte[1024]);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+    public void testSignSHA256CompleteEvenHeight2()
+    {
+        final String[] signatures = {
+            "0000005b5211d3a3a61178505fb4f4abe1c58d7ca7c17d9ae56ceb93762c262e501c4b096ea46a1cf5a874a2822d932a1404caf54ca8279136f09cb89f33a09ff6045c6b073f2b43cec2f1538a2613d6186abaadd4a636b406e89433428a36f93f02eda785eb33fad4393779aa0830407975dd55bdfad0bb84ff3d804f0df2410491a9fbe51ec2b093fbeea5b577e08cf46e05d4a27f7159bf590d19ef3a9aa2e97e1f3de574f30ac6085cc8fe82c22cd4ed7e79f2a0ff91d4bad8ccc1067f10fe5f1fc6c2ef1ce5080039c82974d5512afc55f33f125b7ef7543570d75d4428f038ee1519a22872a5cdb50f0def83eef3ded03a55285c107dbb4ff3bd9c71f1833989821ed98c35854289583b737f12b08a750f4296d49d90016e852d01265ee6fbcd67485a5a1582b5edeae237e155192e8e0a99ccca6f2e0d5a11be271c350c0568f11efb1f8e23dae3039c62dc7a8fb8d75ba9989067d8805eec35a78c51dba7a46c7d22a18220a1e131a157beb4eabf9fe83fd722fc3c8e24293da0fbe91a44f10ae7344934995da41d6e9389c170789d06f79b686a09455b4f335053fe62946db7bf7a91ee8bf6659876939858941372c1ec2f470e8c880a83e20f9750d6a7d4de06488297fb82e72b4831b1e2a7e02369e695d948dbbc6f1f77e031d3a3ddc9f93a8f36a6b1eef632d9c7136cae6658715373a22f9d56b62bb9a4765422423baf1c6628fd5a1508690d22ef143392eaf2a451df58e67d459185daa4a14a8b8aa288cc070a28007845f6621bf4655c92e6a555ad47cb94b86452d6bb4d30d91dfdabf099046bdbbe7f376b4953a566a8593364dd44cc804f9c7ac38dd1f1962f05a7c728253abf52092106d8e95a07c9981aeb49fcd64f3bdb1188c5c7daf7671cad736853865cffc46f90f75e512931221766a03ca980c4c886e8163927b8ab0acdad4ef16ffbf43bc68d4a868e28375bfd5a75be2afca8d4968482159e823810521a20b721ba6ff11df8bcb8fda6563cecc65331d3b2f83e698d9ffddbce7cd39cf955f22bebe1bde95d4629b52c93c3ed1a241a53315009a5c0ce636b0780c0c8b9ac838e3a2e89d19cfe7f682a17d649a4c7293feb38a42faa2c7c949185cca804a73f123699c4c79d8bf4ec4fe69f7192d2ade210f2bdcf660af503425651b85c22abe5e5612b59622a1dfd32d0d301abdba78b79954925f702f10af7c881f2af2d80a5e127c27c73dd098ea60ca1ea7be24a94a57ae10e809a24e1c807b4482ca2d9029a627bfe0d9dcf6643800475306a3eff0c33c6c6cf80935d3e4023d2fb93f8936e0eeb852cae7c2bbd3c87b9bc9bd94325032a755382306d0239d1d07b79c0e50c9fb4fd8184e9cf7b33f196a6a676654e01721a42bc7b75b5bbfe0561ee699b817256edf20234f4e1e00e147c40e4bea38c037e24ae2e3df7a11fcc08e7e3998aa7ed236a8cbc04086b8fc622488df47ef3c51f02443975f5927e9f867ec6d068059893ddf112b27e4048467225dbdda0997180a3725149fb55db004aff67310ba94c25b3003d2a50b39cb44e1744c336db6c7508cf0ebdddc382bef767c8a4feb2ee8c2c84b3195a8d42bb0b045df1e54c5d82108f87ba40e1bc5abbd0e51af7cbc8f605e1afff90eeb753c104984231eb44e2f308424ef8e991efd0a2dd7d66ac7aa9988996bf9c682b41b3cec98872b94bbdbe7e9eaf16f608851a94d65f711855adafb77746a518514d9536a26095048ec8fda2b051be163b5c35e22dc03285b1affd95a8d9d1b44c67dafd04686e1d7ffd742e1937120a72c5fca42df6c5db00fec5d08004ab2dc3351348f525ebd4acaa3f8ad6cec7659dc25afc2e5c84965856698a4714539f63a6e605bffc77e45d8cae8a5281b181dbe9242aa23c216c9866e25105689f7a6dec6687a11e92986edd714c0e0badc86e33533b809a3848888f12b2a38776f69fdf46abfe170049ce1473b3fb5f6391c19aa71f852b6f80ac50a465015ad2989ded9f636b004324133d16783ab55f3beaae8e52e0c194a37f50a3a423019e10a18354a4860bcba06d6906a45c71062ed7533dc584930bf5ab03ec32340b0846cb3879d539e6bc57dc57aa1f4bdf4ccf4d4ae7c61892556cd9385ab4331f853bfa5aa928b982d4f9418262d32a162e1dfebee44fea85d4af241ad66e21ff831789aafe11602d521449f5795de91a77b0661904848f5514c3639a64002eb45f4da11fe35dcd0d29f8db611817d77d4116a3564616648010e92fa33689ee35dd0f2a19dfe0c54b7f0590d2490c5f848b1cf292803d35a271ba4ab7fe42449f3b0712938d8dc4374f76ed125a7fcf738310862d1e5c988f7e9d9f394bb92d97204bb724ec94161d55e1f8c9f040207113fb873eb0f2d50df86fa3e35dc8da193227b78410612554ea5c547a64b4a1d9ebe0247cae5aec850f9e6a44936d1b0bb183228c7372c3ec09e5472b70d2bb6128ec89c36c21777c91d64355bb9bcd4f4f8cd691a0562e37dce60aab74ce96c4146fea0cc249d22c5a0ae399f31c5f0764fe9478b2286b91cc124eeb5d4d00e071162b6fcfe1e78773f3465771b5de35f32ac951561c9a16dae856dd78b8bb02495eb55932d85ed9bf1038e2241ef44edf467b7e38bd9aa41ee6fb3fdda95ed97fac045ecda99ea2b1f82cf2f78b1ca9a93130a6b325ebcdff155ed9070138449abad864074b3ff250c9354e7fd25483f1eb215d24174f40ad297b36950ad90f4e39b5277fc7ae711f6ffc03e0b04f8b3df5a7f9d9dd612cbf366d1d55ad39a59732fad55dc06d87516ae0403caa64accf02d954b507f971fb03baed4f9080bacec1b6959e10b5c33715704ae6bb38593d668f0619805e737eefa2557a5e1c114ac77d228ea7d6bd8ebaaef4b84438b61e6d548cd4e7bd65c1e948b00ff7b408f05522ae7baadd46cbbf9a8c672783b4348123adaabb4b5f4b4f4d30a4a452bf08b2bae6c2daf6c3d3c77e3dfcd1789adabcfb5ef56a795ef1064bce634af82f9b14d93cf4790d231f830fcbb54809beb863eddcb445e316238e1d18fe1beb096b5aa49115f74e3d28f09802cb237e59f0f72abe9407dd1899187d858a9a17e63ea71e3eb69b5f1cf7b30fb55ad5a6bd0fc3596fecb52c183f216b45be8aaa048bff6f56f7c933d29e99b3f25d18cf23f965d9fbbad25d4593fec13eab3582bb080722a88fde63e5e4df8702bdb68dd1b5551acc4ee207b5b657e6553c31d6be48e69a7255bbedbb3dada702f8a7ed9dfecce203d9f522cd201cd13b854991f37e3ec64fb2d0bd12707299bd8aa4c1f0d7b24489363f343e7e53f773e0e49032e80d2e3c2c7bfaf37319537bd7f02203aa761d9cf96fec320c8a17969994ee3b3da9159317371748b85e824bd82284875b7bb65604c94ff0b8816cef677ade31963bd514ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "00000082299a59307212641af3782122ec7aefaa2665be6734f4864d948458aeb50a4f7acc620c905f075b16770526a0ff4fc657eb76df19756af8e3cdec9e766ff1dbe0f6bd129b3b1ddaf30919f4e4399730f2de322348c69e443e0f7b8995d494186cc3c375d13e836bf1b75b5dd40d4c94359e4a3567984a9c1cf5cd50d8b3e095be22f5f9bce65f5b75aa75996cb5e19715a8852373e342143ff8631464a3a8d4792ce675c6aeeca8aeff13f4efd886f2332464463a88b4953010a0986016dda1544f42393ad991e12822571094e89c5d7e9b9cac61ece754fbda2b765ffa211fcb102d5800f6919fc4b6e208d5f1fc47bb41b2dd0de41a42db8846b978f4bcc49ca3a1db5d4ba8781dc3019d3cb1cb0737ca3ba5a8a7f4d2668239fe8c1e8023e3302f3b09245e8d7892160bb09b2acbe6d4a3ad9f2026d1020e799655fe3a6dfecfffeb8c403e08b8e53037e6f93049c68a11acdc3b268e0dff6ca7fb624fce3f04e7dd1258e7c1bf3db9b73de7751fa2b06be2cd6996da0caaa7cc38589eaa376863f07e5402ebf67cdd46dbdf78a3b7a7b0199b058e48a22f7c761f808e05e3fec93041a26ee2d39c875030a89cec39b34b3ede5d2894cbfdd47966dd57a7c0b237d632c3d68a199b4ddfb0c3a73a9d6907c60f11272cce0abf18d90be62eb410dfa806cb753a9fc54dc9181546cb921268a2321ce4dc358a2e6f6cb7ff214886eae3b8b052e5a527bd4a979eb04d79bb8793c0d6f59417d0ca6e2d9d6ec7a7e8171f7ffc4dd598bc04083c9a71e3c8989c834b1246324c3ebce759072466ca4ba44014d30877fd093320b3b4d48f64d107d7e5ff9d203667b42b909b0fdfcd76ace55fb5236e2b2fe91241a5367688791ec5ef6ac32a66420191c3a448d1a216eef1dd4697f2597308f30a540d65b144420a22e1e75d2e8a3939aad78cb2e868f5c074942344e0597194bc56c118715509e85c0370f526ff2fb6c366745e1db333d3fcc754d1f67e1cb5194c1e636a64891329164fc453fdd3abd1362b4824de266d890dcf8d0af7e05ee6055befff07c68c359dbd0615e408c8e14ecdb51dd15f6ee78a8d45b0c3a3478be08b3656b941cbe355c43eb86abb6b92dd034ecdbc5cce187c88988263fec50be9c20e0ae53e1c47077805de8496d649a906b59bb930bf537756218b5ec6e1b1cea9a2204cad1faf02f07a585ef3270512af36dbe7ec4f7e63a071db8a5e753ecd1bae7cae28796759e749c8ca4df97a8de783a73bf0a4c621acf4accc2258ffb23faccdadf74cd0b532ff594b09609c5f68545e96f0ff1be666a8bd1a767b1fa938038afe31259b45c10586f604d77c7944bf4998a24325cab1e2d531a503401a676f440590ed42c7d98e2e2704af3a7ca6a9dcdecae0485566dd83941992d21c8298df3fcc8090149224250efb3827c102abcc06e66cd91f330d5653c8f50cdea1e56dab1e1b10bc68e062fbc7d94d5c8be08c6303112c4b41d12e2ee9aa705526b2217742c8f07d9c7b82174d47da5b7e3a3587dca8737fd223a035b5327f025d6a147181497eda09dc58ac92b2501ee3a071e4e088e8a9fc4e48e3e672a1583a1e90ff76cbcf81425a91c4b4965b2f4e1540f78b299f0b19ec137b767f66e953712c36db40fd09036d3c5402191ee64c17c86fe08b69b30e47af60da9514b3534b9e7baee97fa3d239c3a35de48034c1b859c423835c2689431b1a38f0d01c98237396258abfc451718ab66c84f031fcc7f720a22a952ffc2d527f750ab8adcfd5345a2979897097db3ae6f15d0240738fb0795aee45ee6a4c07a0a9f9b7b98ab107656977ec8ff18f6658265cb01b684903c3cb2cc0233c0fffb21eb56fc0a1fe6f6ec38514c7ce0651515bb975e3e16aa7b0ddb03b8760b57fc7371a4bb232037e1e94aa413a430c95ef60116f632a937947fbc0904b318ce220b71e9cae8452d9bf36dc6c9a840ab6ee7077daddf9aa38929176d117b0f0ec373321e72448e3c53f347c4bf32b2d98950ee8536568d58da9bceaa7b927b5fa048f5978d117668c2a47db89b1f7fd80476121df5e5d6ae1e41a430400b81edb5617ed4ed1ab594da576eb24557a8bd558993ae75d48821b8864a356e703c6b0b87acb7224fe3e59cbe97d61701a81df0590635e05681250a1458a2c93618c82bd91df2746af6c33e4ea348e27eef4e8af5f85212f16d63dde7b5877929aafe71eff5a5edf476f18eda9bf3dc70b9a47c58b8d1b593f502aa67452482efb84e8bb5486699adc2aa8202dd0255904f934770919f92a4433ec801c529752f7e3128d34324c5e04ac08a4975836ba69481d81b651e33c6993f12777659e5475c9d83e6cd08d381d69dd0d13b1cb7ed552d0f3c5c3dc64ec01f893333050cbd9fd2dc9b297e9234d3761bf3af63d6c4c5c2dfc45ef573f07adfaa1f8d869d7e4940c8dcbcbd1f9b262dec4034a5e4f0ae61964b2c1cda30ea85a1e5cafe5aafb162c2dcd601f599227719fb9c2fc4edf744669a7c39e8d19b908686951d4fc91983ad12451ff9822d5937b324de12e0d29976850b9c1b6e88894d42e1bf53d72122d4e6078e16e9ccdeaeb55bae1489b20cf1895e1e7c2a7c22061744c232630a0a6c22dc136d5859a01acafc2b056829a191c53f9e71db7b2e5899d902842f7b8547a72b3ffbf357052ab55acfc63f8a95bf37facdeb46c53c97fc09831a363a26cae6f4dfb84aaee10e61b4972711725a7eff18519af7bf2fe067c6140b9b99f4fec0d4d085fb080fe882556e1bbce61b9418feaf475570f15887feefd839e0528efe433cd180614ba97c15f063df08ef185fe16f61aba686e78681dcc921d693a08ab1887b6773e9d6d31f6221c4e450e528ba5e04e8fc1ea2002f4293b984ab5e4be7b2936c37006db42f1e78c0e6e2195f284357118b2777bf54e2a75940897bf41e6c6732fc9383e56f5cbc7dff2eabb6009e609dc63c41385c76430b98764826b94cda6dca08fef1e94a0b7c06cd769cd63f8534bde1a509ae7addacc7938d8284d333d5df116dd8f23bd5f17ec3bd2148d1391c07699abea5026726e2f24b2fd88b88399690819835b1b7d957a3ef8961142aa03390ee8c2787a3e4a9c976608e055797abed36321593b21c3bbaabd908ba6498d992717fca20824cf66300a5614f9cb1bcdda41ca1fa0cfb05a9ff9dc27278387c7ff082b4e2f11b616bda9017fe666b50d25dec6fd7b59f1da88db41ab4a10f675f10ffc4b539f24bae8a8c3e17f0506c89897afc1bb9221d9da2644a542b5f9e639a2e29b7248b28c51f52a75d4c8ea635e7b066341f942caf368037e9b13476608c1565c80311c8e31436b541591517b401921598033fad05e9b12b913378f4ba6ab6ca59a42ed7ee99bb3a754a8ba4095c260e8f5e9a6a1705fd92e324044d34d14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "000000b0608636b65bd2e93b0790935fe0110644ca7ec7189eb50ec64bffedf64c8a718f30cf1e2297513da5145a6ae6e0dcfcdd4fc08991e0d9edcca95f0f13dc43220211964daec0c73e29fc3bda0a4253e68330a513cc3f86d3c1907c9d6ec271ac038260b375c936f868a312524a9970cbec77667cb6b43fba7deb8fa8844f5530e644cb7faa36222d6d2c8ef45150d214044c36cd5b98beac3a2c29557702f4f38ee1b63851ec492efb1999beaed988c100d94fa42ee3e119c046fede9d3dc7915507c24cc99e7f271a13c861a7eb6b593826b39e99349233071e2ed1fa92caa6960d119729fb266ff297e17865ca3efaa95e5745debd51043be9b4a1ad96462ff404792724c201e1aa965afe71dabe13dc184f9723d69ee51c5f6951ac37cb5310b1860a698670c40f61f0c22b35452db35760fbe65a928493fb219300da1d9cc0ebaf3632fe449443033c309f397998adefd2088abd5043e80371ec45809e71f5de18a85521120c7846665e97e581e2cf160e36cb5d12c87e64ba3fff00cd4693b0e7610746c98c7048b725bfe352203ae581e4c026c4e3bfe8ec10a20794b9e4dac00da44bbab2af4c47611979892605d1ee4c43bc9165d8c45cbc52e38eb1705306e6e3183a73d82d2be435a9426eca1b4b5bc433d881e991bc8c73a3e4232e48a6deef5fbfba9f0c748526bb457d1a4249913a0a549a64ef1687cabb820ea831720daa68efb11ac41d8259df5789b41d8f5af7edda5bd0f65f8ffba79812e56f6d00ee9d57fa629f0649758865f1ffffdc56725eccdf9dd7a2cb1bc1976a799a471ddf1c6d779fbb9f28d1b80ce4f1e34118e876e6eec6674491f545ff648e5bd3b45bc4343fda88ecb7e508a155eec53ed55cc4c3594616eb68dc5bc0cbec815e6de7b2ac9c3062240143c259e1b851dae99825864d7bdd67d94187ce6630db9c5cfe4a93df53e46e22a50e16429c72a2e8449c424695a2e21b6aff8615514392b0291d36eab45ff07005e9faaee761b28e95bdda8a76faa9e9a6b783b926e595781ea36ab04c0404a78141bb9c9a163f5e54b8758107414710936597e1a7a79547ab41ea3d1d578e11044965c2b63de361acb948ba1792740da8960408a773ba9b110a622344c8f4745d17590b3117ab0c5f22dc8f650f9cfecd3baa7777da37fca90edbc9ae07cd8e103dcc315b68842c4f1d752335deabc20708c6ab09b8b5d7c649da79ede708391bbf38483007d8087e15d5bc7e03df2e5031c0673755cf68f7a473987637f189ef08c38e9c434022dff19fb96b5ef1fa3ed564aa7364cda56bdbdeea82605676efc98a0c28451582d27ab1942b05cbcdeb106a62298d39c8e1a44f9ca3c61bd23bd9c5af9bbb1aac00cf177a40e8bd7f6fc4f80278b48cd8938e40be86d09f4d527e5987a4a3494c0d45a6836e11328bee88382b0175b77c0add806cca6ca7296d3d289dd3145adb352b58b67c690e346a5e626dbd0d5ee953b037d0265ecbf3aa43eb737d857b6d3e29591c926f1647ebaabfa75a8fe5658fb913e9aa6b0388fef3e449967a106ea66f7be97f746ea24b165d12f32e22d11aebc5991da331a6c7ac5de98adee512df6f33ea9ac41d7ee67330a91980bf114c2383f3b9f727d582d0cbdb2d09bb7a89a5f0c7c89b8d17ec35ff8adc9ffd3436881e6cdc601fc7d0a7ebf85f82f1597fba4201eb63b778dadcb4f16c78ef29fd903147e6c1e058f2dd4db304e9cd387c8cbce7d3ca7826ed7bcb59c4ff44373904c71626262ffb293cfd74f787490b21da55bc060013741e281652556209ea464547836e1e0adf46d14b61ffb2d9939cee5c00eba8274e724fd3f785240f3f714132679bf501e182ed1d92d66586733b8c979c58d5619f91fa14b720875738539386b0df4d8fa3496ce4619c78444593fd52b513485f994421b8f9efaca61fd4ba457783d9950d050f64e265395c4393bde0f1365704fff7de9e9aebb600bcc795fe994e244fcc7186fb171dd918143f824282b6d9a3f5414db61200fd25f4fc172eae69e85aa71a8afb4f296510b95c41e27e33c573b705bccac633d162967bd0f24039fd33bbb4d051f99185c0083d0db4225881fdf201bf2a9d4d9a913658b175116d9e3ce57101e5d5f4d4eba99aa4e08a813bab8364edc94905cd79d021a2cfd656960edcf77b1a49f5d7e932e9b8c81c7cfee1d561c78d83e8a20257d3e0c33c9743e08fe9d3dd6c4e265619af8d1a9044b4f41fdb0a257ad7aa8688054486d0a43deafc566c4f74de2d91c56f01e2f58b3027b19f82bfd3f622d7355e933777ea81182d68218bf7e0a74cef167e1f5f2b18d4422ba3a34c724c798bd6d6eb0be5ee2dc0667eff91282d54f4acae482705139cb7349d7acfd8c95fdf6d4ced557d48bdc0a049a155420761ce3f4adab176546a0846258968eb1d6ab67b9fce931e4aa1cb9762c7eb7509a8a1b1cfe5d17b9b3c055acef1f2698ca063207f0a358ad034d1d4d55e38ba0d979c13aa17be6c555a3f959557f242c5ca3e2877013deb20d623ecd688418761d208e7808bc014486535f3192b50928f1ce861157fb8891a8da4c09d13f3c791f5d1a8b20ec25f8500075a6fce54afa972938ba838c4e1212f1b07f8aecac78f0c3b088e003f7087ee64c5ff22f12235ec3b9bd7d539408308e10f0b651fe37ea69ae5f860dbad25a4d4c55193ee59336d21e864ec2b4da0d3816b3328da6d22e9feaf51df9af33a5754a4bb67f65bf795dcc9e6edd7430882634d6b8888106b8f68b1b4308def8871decf9d242b89e66dbd669e3a737ce22259c9f5f197bc33abe2f61ff35bc0938b55c3bc1504366aae415fcde7031bf92abf989726ce72c14f200fd742509e4f344d6776796be55c22dce3d5595f1228843b8ae57cd093693872415fc91d8570870151665e11b85c75c9828a66b8114105653784272710917d044a558bd21d48efacc29b07b8847bed52929115b7c8f7a80690085b296092adc2504c6afb5984d78408f66ee23d58b8405a88c4a413bce09d73ba35467c55431a24acfe601ef3402e475c31f01cc9de7eb8ca5b7ac0912b85770411d8ad498b03b2331fdc10fb928d287cd4af01e28b98194a68b39aa8caec883c6d367bd63300910af32c98ea678c4c5ceb546c2c77e10e96bd0dd03bf5c3d8d38fbc5c8605826470a71137ee4365d8b384d8021a8f433c6060a90f879cc463108eea5d066246d3757a3aab6c4fb2e84598ed7a81f004889771d506ba5e22acf84abd03ac3cfa833f8530c68674ad72d9cba048c0650e313343434417257f951c14019cefcfd2643f6c52b8c8b431a59b0c0349c1565c80311c8e31436b541591517b401921598033fad05e9b12b913378f4ba6ab6ca59a42ed7ee99bb3a754a8ba4095c260e8f5e9a6a1705fd92e324044d34d14ba59e3d998735a963d018b840a80be11a52ccdadaceb7ed45f0763e6cd49ca6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "0000016dfe3b6ac0fbd2514e3449cea6c8ff65c128c1f144e64ba73e4bbae7322bf6f22158c1ed378a1f960ae78ab934ab5c47f25679fcf2a503ac113bf7698e00397adb0f65f04143f012bfb31a7cf50df8f404d7e744256bfaf8cc54f140b187fcb56ea081aaf8336ed6617218f47da2ec97714516f20876edb8795afcab4f1b7af4c75d29f35dade047c746800eb2c01f90cd7fca7a2cc3cedbba6fabc509a230f951b05883cb3afb37641c48f359e0b2361d3ab98c7ec62f1c7b04a5fd773db58c7dcd55b6748379c735ab7443f07594a28348fe3b74fdab37e9d58a4a69c5c324d6e6384a61192cf23c0c564ad988afd2ba14297b441322483c5a07fc07b65d7e1c16daf8f9ba1af043d6ca706c0f49cc2c78c551613e2f73aa84a085da3a0cb5db387e39f552063d31ea40ca36e76d9f70cccb4645f24a12409338ecfd2f4f2757d9327355b49286cbcce7cd8445fe059a0b4093f08c1a326f6ea17d3821535464de238a40f0f867626443652fb1155cbd49ef0d0baaa284a54853035c6181e16bbde611d048d7452255cb45d1c91c5618566a972eec3336d76cf9b1bc85c057f756c55ab442159cb5fab99fd9e53d69611a9785506b57a5eca14096f74a8204a880eb9bbb9223cb13a563bc3c55c4b9e9af481cab8364d36cb19e8727e5fc29784f122b399918efa6253284d91c468f2800417cbf25fc1a5e8986071fb980119b70e77d8c44df32c147ded0790fc74c829b3a98e5b1d7f33e56f1a915a0ccf855d281ecd867a1ec96d59ed96906369548525d97cdb060233a89d000117a18057ea3826d73c0462dec997598dbf41bfa6ce4cc4ab42600a1c4fbcaae8eaa535955c456917e1a9949783a48790e8387a12e813047c4df33647d5184b33ad85c1174e338db84a490776101eb8def21cd090020c7dc7b4b8fd311cdc5c6bb4790353b21cc12e5264b81e626820562f8f40952a92cd31c3c34b884fbf3fb3a82eac566de40c1593ed67519b69ccb9dd3c12793039c91b1fc273d3b6e5173a54a69c088f52814c1cbc9db34425788441825c29246da976592186df899ac681a7572c4d5b43ca86a70df98e0206c3099c1649e7af116460dcd7a23490252f93519f02373400436ca5d84137ddb9b79ab2d9ce2d68cd9fdfc9f9ded8d224236a992635e448e611987ee061368782639444b852f5579693aab334e00d4bea4aa0ab8410827a3f2c80819e6fd8e19016aefde55ea96d12d97e1432134c68343e6c6e241bba990a07182c8c898c7f58c4486b6b1f72f0205059c6f7dfb640d770598ca6c5128d0adc833aa5458c46d8417dfd27779f9bada125bc8b52a1f22b6540bc53215234aa9a5aac15a4f9ce82641b1f12461b28cba637524c27233a359f01816cc1f9ff1d9dd2112b291fa2b572705c73efce4fda27f90cb5ff8d847f720d2f1a5eff800331ba166a791113ac90cfad41fb9fe89bbc0c0e2a95a9438bf5751aedbc654e88666a2179aca638e5ab10f53cb195dab9e37720958413bf59909f91c98ccf87a36f3e42618c86e3e2b5cf270c211d5bcbb45683bd9676df3edc7ba6905b06d942d3398f9765d168544155937be426d9d4618f50f6869e808582abd14d3bf306a48b74da1104f53bff753f35fa061c66bebd7ff906697567f11b9065d93a8c03175ad8485d08c038f944b33f9543b5c3c57fe06f69ec1302fa5bcd475007c78f7e8f683db89b73a09413c0d15c96130aa02082916a8ec80930a378cc4eed87e184a7805e2385d09eaa9dcc6024e832040b264fadfdd817682e3767475add5e0c08bf6083b4046ca56f268ca4366c1e786b4735a288b8ad32f45b33beb2a9910b535819a052e6a80864ba177aaf0818a2af851baf6829096fca10d6011027e8d142375cc1922068414e58e8418d9855ba3b15ef9a76a97918f00a25ad0a69255af0e46be6572b5acab9474820bb7bd43a66401a14b63753e6c7881d87a967b816fdc814d6b4030c7673776c5c406b9baf2d42b0ee9976d6522613f703dcd09206f875a3c25709a552d11d6713824d4011c75a6d128cacfb388c2282bb7a086bab96b2111a882f12740d415ea3a45a3b8add4cace6852d085ab5f26c54f681bd23d9a17fc46a58ab95f9b41389baae09be46b4ef5b86ba716b6d1bf2e72bea129b6408e5453e5d355e46bea0c82631d25dde6ebd7b18b11083b8551afa8df60701af75c76a435f03c7fe191bc7d643e01dd4835e6eb7951896910f5ae310d1b04401e76afeaf55d0a9fbd4a4ff66fda49928a82cbbee04f617d00ba783bca6cb6832cfc73eab2cf5fd66c604aeaba0c65d193beb57af7674d31cefec824035b97ee24d9916f2a88ffaee98bda22ff5dd00035328b98db66ce3f9bfa296558a9e77949e67589a7b4377a91195f10a07086d7c1325e9c4f058350f6fce1cfacb73e53051a563d338e0ca9f8d3e66ea72d8a2f3a7f939bcfe3ae71a2a3c0579d05fb12d6bda6aab3ba13bf0d35833caea068a5a1c4de4cb652e23a61189a3dff10698ae89942f5a6624bb068f697996c6f1ee903ffcba03971d5d49b145eda824aac6a2027905d9bbf5912542db90b02af512a4a65df73ecb2a61bf8a129e12e14d802b063bd9f8123890604bf257c47526c83dc054662d347b83a68ce377f202e0300f222f9e1bbac42c37d1f64c6783e5263099965088948fb13d272c67062a93e1b1b6475eb2a633335c365f2df3beb89f5daedd95433a0d6472a33099006f36704f60df2f62bb74b3ac5fba744e2ab5c09ba59068ecc6facd0b34329ed4f5b620fc01e237d175eeb455243c523e0221fbb49fa5fae5bfe567a5e9f05b26239da30cf456aee0aa359b2660a71d8d4e14979c202af96ef4385d8ea3a22d1f0ccbab1d036d3e2f91fde24c0675bbd5022f3d9376a38f9f07beb62765635bd27e3356efe40d211deb5340b329c3f5a3e163d6456effc2fb68fbdc43bee3bc600912a8c6e5f585d7ebbea1b8ed43ad93bfa74559461d09b402dd0bc5360ed11275e1859e19b3dfb95eea79fd4377a53d1dd746d11a42ad3c98955a64fe8304f242ada82aaedcfad1906c6334145888312ff09d86da3ed40ffb719f4c40ca4f10c04629697906aec52ddbb4f629b4ce36e5f5a2ef5dc768a9191fb4a8ae3f1daa3e9008adf17e59c1b8e30299a11c0d764b74725ea60890a113155fa3938f9a757807402213bdaeef69aa67940f52e7f177e739eecbf49ba6f62faac6c560fcb85adc3ebde724f109223ea68387bd213727e1228962a5154cccb248c76a1e09a17fc1d828de1b2d5007321ae418c060d92537535afbdcb25921af73b487c32fdffb2be04518b8ddc7e34f6924464729d08d1e5052d75ab9717a9376262e6840351863523bff546f02a4e761f52a33ecbab0da332c4b250708f3cb43e29284d3cb1e45a1e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "00000189488bda2da8c08b95768a0349a05a6e374630b142aaae7f5be79b7e5b8f43d7b39db46560a8a1b29001f61a5f819613a71b97ec48c2c8d9b1ac893719e7ca967f1d8397571339facc1514d940b61547796e943f02ff22bdde4e209a3ccffe03a1eac9bffdaafcfdc126ff02df4f6cc9d74c062d0dfc7435f40b53afb4708a9f7f766618a77077c8c122a7b7dabea6ddfc8841f8ab312289d1ef163a66435522033643279179b72505a162d55903116d20a221d85d403aa1fb26338b05bcc10c74c6eeda9dda790f14e89855d0c00c15b3be886c92bba3618c325ab964a855abd962feb7739d47fd4c88694b7f5fb12c4ce7a8399c99d8a1b503a70380ff634add1e21dd0cb5653cae5d200b8520577db845186257705e95799f55a269e5bf4e9daf7653ee36980b056f1688a63cea18d27816ad5300af012efe22a1a992584fc4426cbf4fe99f06007d0402ff5a89dcc08fbe8740e62d21227aa96df01b25bb143e65d106ff136d52a38ac4a66f1962719a3938b36ff349d7bc417a892d452c450b5b3a3d7eb4fbc6380060ea2ae323e12b8c7c22bcdb1e48823ef92c919a0b477bdfb8b549318ee03f630738d1fedfc1c0aefe0d40958414f975dc679e5cc79cf7c9643aef94308950c864b79098f77bef7c6a8b3ff2a4b7754edd4034b522056203e4199a155d1bd0f5d22e8dce513451b40b854a2fa8d401e519b0c10c2b71053ca2d4afb71de065343ebac83aedc46b6f0d9c88f8b600df0c77a8c14a6147f6de550530a0cb3a7b1f3046ee2105b59689bf80c9230e4dceeee7029926245f735c20f13ffa8d6ed0828369839b63316c8a29a171f204cb1d905ca0e9cb7149142fffc534de366b96c238a76a85ea83b06fccda2310b87702832727159d024204e5058230abe558c76625f8d5280fd5069214061a0c1407ca8b3b6acc90ce9c1905397b38cfffd47ebc8ec7cf5b8aeb78f25e1409ec34841f3d47fefe7b23e6e8a739fe3e8e59f2285167a7d98539558813eb542f400dada96efb676e398820dae60c3952660217939a614183777510cd855bb9a6783dbdc259645338c5e691f13868a222ac674a3cc18b9b47dbaa667fc46db5e359107d63a8cd87d4a96dd377af8d869d90218a15d934e18af814b1795d06d084d0a0b47d9ff30b681fa8afa770d60a63127d7dce42c77849dbafe96f0b7964b15300d8cdc8f1a91a286bfa1c735a743d73da17a01e7c2e7d39a2802fd40f98cb8a2a54ea950dd364a32fa300a5c6307915d61730c0046ddb696278854fda1e42f44d6fd982f812fa120548d723edb768fa48e34ab7485df3575a8de13f4984514e2921a35d43615419eb2d7a72418de5857e9cd0dc92285d659427073b96b021e2dfb53858a6b920176c70338cbbd63b0c864b3daf7e339a87083a72858b3c23b17b8ad5a446c93960d92bc9fcc678ab19be30d7b539ca9e45f8d33608338d53e5244428e3487bc2e3d9079b7123972fde4add7338342e7046874d52311d3bc5695d07b0dd8caa667dee6f165f704ae1765009af49315bffef3fe3e0cfc0b5de84586f3959de739cd8a9a00bd7689133fb03daa07bccc69c64796b398392a1399caeae318f0ae538b4296787f5adfc70f29829aa04673189bcf4e06ac52935220ba55de3c91a28eeba8e87628a8cb79faf82d8bb103317d89e9bcb74266121539ed2be97b2c1d8b95a99cba47bcd3f31a28ca04b2c7007b23a819838884483ffa6ba6d25df1497f957b9650bc53e9599d5b7ddf1757ea3d1fcf6cfc15e46fb8234b098596877ef70e6743dbbadd753310c94648ad3abf1d6bf91871e5fb2ef7e8a0a99c0086ee5e7099bc89b008a1799851fc6225fddb8ec0ff4d8cd69f6532fa776a410a1f1ac6aeef561a80b4900086aeb763f71de59ab508d33b48ff63908736c0836bf0909bf9727b2bc3d39dd85b9fa00ab83c7858f3cddb87bb8ec062efaf168639c81b8f7ad88bc85e276d70472c009b4d3a42df77da085467fd13b220974d3ca9804ee8456b439bbe134973c212b56fedab37f1bfaeac9c6152cb49de2e6467318084f81c28cec904c531730d86fbbb1bf8a848b1575fee7367f12b9e46b9639c1db23e2cc0505c6766ac18ca2bf7a0fa09eb4a58929e04b70ccfa6cf63042d88fbae3ce259cc00e9b9893de0b5f40b0da0abc49bd1d5ad455cea276857f8ed1a9c88b9a4ff26762e67504ae169e149a8701268fee7afe30b00917b1797f1fc6b1244111955e7db27b584aa2a77525909711a6be80fedf29fbe6b2426217db854591924d645e524dc85676263f37cf761936ba9e4de9a468babb0726c80b8615e039a6f4df19377209f6f585b51fbbba017510ee9012d8eebf6970bc702ddb048d7b5d2bc0c294d0f3bb6dc6cf1086048841b0c03b5f352b27370cf6e3eae5713f4ab0663177ecd9e42fffeb662ba9fdecf10256f1d2d552a37843dc1e0f200bc71256e7cb4f1f36944b5c55082b4f1f040e8c9d5917d01b6e1e1609486ba6a1255a343020e31742b20273726648a46c634a78f62ffc88660409e43e77855de390b9eef1964a52bcc38ffe014a55d4664ff5e04a2cc2ad0c7fe48ac6846711a3bc015442adce5b819d12be2ce8a5de7cd7c0379b86b4fa5346ffbaa477a9d68170b44edb447c7ba0bbb9a799a97f2750eb3e6f2c0f05064e85239f9e5b9e5437579f2607d029e57d7f84d7b7670c33cb87ff92b041666089c7edb3909cd91e8289365e0a8a68ed57cd4c9890f1444f81303ac657b504a5a1abf049244fe06c586c3cec9016567811e9756f815d466cc375e70ed3ae80a9fa0a0179a1c5f77e58a69ce2806ff935cba360ca480b14a2d95cd1e9653265617ffb2629c5dbe6d90ef493a4d68fbda9b8fd84d04de71486c248acacfa2f698c239d4966fa3f073cc5d4135a0007637963b5efb0936100767585d3dc44e703c34bc370aeb90cc9b844db7e7590d2db191a7694178d06b79272f71ce42841f79242ef843196001639e095da7e57de75f2aa7ca05942ed371244db7bdefe455f2623981fc76c9aca7acfcdf23edb09778f9b71ae724efeec438c94e307bed9c1e6f9d275b121515e851b155506ddc853bd3bbb1692ca7ee89e14f3dfca44cca15cba1f50cc42d5545fcd947be7d76c15e969411219a477216e2f74ced2655ad95b9b8b5137ddc4aeb62b3dae5e4ba4b1f9e2162b64d517dceeb780108ec8ef04ea0b948660bb035f6ee1bdf89d247759ade8cdf1571aca25881bf922b0079b6487b7ac02714a8cea20c3d67a9993847b6b2017c695ca77ea73f4ec87298356506064b7969b46b7ec70baa9473df1f21298d5d04b1f1841e43b16c7cd8ed3acac4217451b2e0051cf310fd51899627ad041f2ef8007e0c4430d6bb8c968f25238aa2669e8fd09689007b97f258f3f4e4af01e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "000001f9471714edaf6ba829296c75394d1b4975480f47144472cfc36a08b3ef435033d62d7528ca09b07f99f990124e3c53fe8d575c3ee2c100a8005adf5165f33afe41a8e975c16d6c71cf3b117e539daf2d1a48e6c5bdae3c8ac70cc06d158e2dc0e872005aafec694ac4399fa16a83993b5cb6a21e88f2cc3f7bc426600a0d50d73cb4ce024649728a36b4ca6c9e11d59bf7ba4288a204c621974188e5620e9592f68f21eb4dc56e0af5ee78863607f004d442a87c45699e0562aa41e82a0bec06712f630260c74c01a3a8a3f6d29cdf8b2a08edd72ef7cbb46d339b52d2c2c0ad509d7c01bf639636f9cda1453186905d68cd94975c42203e2a0a8e24c2ac17a1f57b9a7de3cc0b7da5b81d7b6bac2be5f1b9cdcde64d78acefd7e49d17d68fdd4bfc4eb6bac15b0ad509e13d03cb66b852aa63a2529348d6d604f3628799a21b288ccc0470aef69043e476f1d821634949dfb1497e4b7efdb60ff6a4ba57c0bca3a3f607403b30270d624ba6df629e1013fb8f82d0e49e975aa5793407df465ae1cccfe9c2ce9f9657845071feddc7f6f0e592e9d59d7ef591b5fa9f96c58d3cfbd2975a4c19b35d19a5fef201ccbbb1eef15f08c26386aee0a1452329570f22064697fe56de3884f1b3f22a1aa39da0896fa6a046fa549bcaaf593109efede2dd521deea1de8fe6501c995d9e2e84443be1958d4383a5348bcae5809e7c44416e042d1ae1b7d1d3e1ccf99f733df6d218a0582898f3dcf1526c1a1d8c4e6b59663e3d73ec30bde655eac9751751596fb6f1dbfc85a2849a1e404f441f172b101daeab15b8ed06d0840ffd0f5704648be6b02e3cd6061ea081561300a1481f709992dbd5e8e617a9f66578abb2914ee8d7f796180f4d0758a8c3b216f8c5909c3e92469b7b0431d21e213370ba901a9e9916399be0659e6bdd841d050aabddcb0948fa22bdeb5318919e31ad2c39e5a29ec65f39294b0d08c9d88e71ed438a23b84b9fcf5d3d35788f3f50684d6d70b63decd3a87179b4690374f3e7d16db8e971e838c74f24c8d4d218a3dc4a437d03f3a543cea3f230cfd333b0d6855568dd0c361eeef818b783b6ccacfe4154246c99aba22c94787c028c73a6e35a84a0383c3ad02e278363d9d6fc1649c74e376be25f726862a1f5894fce3f7c496580c09dd92b10ab674cee9174f2ba6ee532a37ab95047d5f1bdb003d887478de60a4dd10de67fc0e644e367dccd475af7569b6fb78d7816565e1615a537cab3dda5258157af66bd4fbaeb198a4ee450f9863392a7bad50b7f4d9d9c3c94b8ba4e0c437ac3b73672e62e8ab2f0517629d7d95bc9e28a765c3d54b43081fc3e6eba895081b532de4310b83043dabf9c1d1eb1db6695e136f32f8c59f9dfd713ce5bc82e8195b5ad8b57ff18866beb470396fa87a7394455e5e98313cf370be6721b77ed679679445cc97620d6d497d6505f6dc1876d7e7e4519f793fcbfb2e65163220013a483041b84854c64ae9abc53fcf45742202bd3190cdcf868502a79f82513d89dc8a9046af0408291db828a77328f88ebee7d7fc042eb0450d369571644ca1a893d5486e2e63ae3f25d5c684b276fe2237744e12a93d63ada8960a4e96f18f9fc45110354f8286f9366791249c56cd4d2532d0e9d1968a6fdab6108b7180d7931fc930c6f2c5c58c3950145545894b64525f5a6b201176bbb5a69b7f1d99afd8ba5de64f1556b2c000388cd1b88c55c0cc3270f36d7fb2cafbe25dc66ff72835865feb56e8734b3eceb0d882fc53293c4b81154b1aff10ed287b5f47435e908b01f2af29c2d70e4945a5e38131577c1edfd417e15837834253b4d6758625f0a37087fbb4fb07f19fbbccbce534976fe8ba870159da334e2b289cede259b35110cad468bf393c5a7af387fbbf8efdec65e7755bb178eaa4fa9932e4ecf28a018c908e51d447bda1a88e9ec3b73f9d97c1522d3edc681b1b2e4c0ac55918972f088fa46f687f911064c8baa0fb92b690bd0f54705723c2997b1a41096d92ffe3e5925176d79b2870953942fe631e7358028893632b28bb6373a56c435451f29b5cdb08c83260156284ff3cc5c3beb01ea79d59462c070564a8c72fbf0e8d9c46d8c4fe54058a65cf0432f08495f65bb6c14ef5e927aa11385fb59d5713280c2df49b9bbd47c4c5a1a774a543d09dc59b4aa7ad592e34a2feaeef2cb221a71b4532ef1ceb072e1a5451e98ba52e18716912babc06cff8b3076984ccd72328d68438cba61c18f45ab74d90784dfc1b796ebb29dfb55d1ec7c30bfb2502ede2298b35ce1c7b3b58cd8d13dd306188ca353bc8d5077137948f805c91963aea36f541c63ba35d139aecb49fc65a839501c37432e147731bf42bf5226cf0181f1f8de771f2d6fb0d6fc5e367aa7e697111aff2c853198b458f258d9ed76649110d1f68f45eb6632b3960228f1d70a410b98c91d5b749a7ad528f9547e017646fa9a3292b1d7d009d30d2d0f10fabd4c4be92eaff3a494b243a7db2e8300e46acce816094cc74a99cc1e061260f10f781fc7f5fca8ece34cfcffb411a3fae20e29c3a710afdc30c9849873d7163368b241370d081c39522837e00315e1be6a9c44e776927759f28aed74551a4bd7860a106ffcaac56d8cccab104de169a44e6d3efe6f0d3f0e4779b9b3a548c0e305fbec8d58814c2a2723131dcafd34f8372670313946b62a3baf1303bf3749135cbf1398f5448eb3b5f1a50070841b61851528e960ac6b71a866dbed01613272490cc31db8c9510c828de8e9e2e731958daab1a2cd2a2e9accebd30fdc08c22a4bffa60ce7baf05978b615be01c369e73c45672bc72993f57cec918b0f055188b7652b74be2e1a68401c2def306c7a3ce15a04884dd789a5b6e43cd5133d62280d46e557d7eddaa0d32ad72891d7fa59ee9b9082bf4e052056b6934cc6f771aed4fb0e906ec0dfed6936b4bfa2b01393b2ecb93bd07ce6f1a187d60d36b24f54a2bd353c1308a2b705cbda7eff5102f238ad5deed3813e7d1a13efff09c9c7f23d87befa946915a53866cbbaf457e52a27183260fad640ce75c85d06793fcd6c7d2a9a0eaa83e56cf5a9bbb9c80764f15223751e352351bf6192c648af260a9a80c24e4a329106c0340e72075b5e4845a51e3b1df891d85ae14b80e7f87160bf5bfcfa92dbacb0a358552f7cf6fad82806fe0d699c4d643ebb7e99a79e0b2facbc69d8ad866e7f2432501c70e18d7ccaaa983c16730875a6f9f7eb927f557df00e832d7ff8a5e9d95634fd9fe75de430da29eae6d08c2a7a96f3102e3825e68154d52a4164f84a8baa39909699c996ac354c1edd4e31c398ec283713f0129690641330519c61ba6ec17ce29aca5b8667e1c8eb4100acabf2ef8007e0c4430d6bb8c968f25238aa2669e8fd09689007b97f258f3f4e4af01e88a53f2f7f1979d8bc110022fa61cbfd56c3cdcfb3f2f15ec7de96bc54d65e6af97f6bde85e4569178192bab4fd59d8c2b1fec181f27a8fa2c17552c16e3b5",
+            "000002a10219b7e1173dffc0bb0a2706246e95d218846b5fca2d4aa52288409697f43c473817b6b2fe2c1ff766512730dd558ad3844aa5d52345da6348228acec9206d88ff1137ca95d63369ca51d04c41e3b611ac3d83df8c2da2e263e498c8916231c1132408f030c1bb326b372a3e9b30ab00c246cc16d3867b9f3dfcd90b22831fb505e0a839a84395f2747458cb445cd865cf41f74c74960d169336cb4cc3e91ccc68238609f4fda677bd11f0796edf4738a00bba32fb4cd5c8fb7b5694949f0dd219aaa2ff6e315b75e487f4b8ca4a0db48bcf9dbeef53a7904fbe8df0f59ad618bdd175578fcc29cfdcd8b45c0d9e492d72b89a94a60100f70d17b91b3209b074e48b6fc8299ffe6801fa8bcd68e1e9ca2c39fa511eb41e494601b70be2aed5748a9cfe67f048cf24cc6f545717f03b3909fd76e35cedf344ce1fd1cf0b698d76438e670df5a9b7bb30f6fa5b0971b9267617e1aa450f63e006c555a56ee2b964d6be465fc2e333e8647f18af31a3632654194bd9a8b92b4b4c4480e2539b60d611feaf99fcf24ffaaf328baea05fbfffe395a21a47dc6453b08ccb00aac1728af898f18822f28c18f502feb3c1c1f0f4ca7605b32ccad8e4ad0ebfc2e97b7b183671cdffb5b37dd94a39a62d9c4ea7eacd5e86569465712876a55fde7349403ebbabef0e768037d22e652c243785ce15aca6889972ef18a05c741c35a125e4499f6d4cdba492a10dc6d98e25bf77ebd86f762d7b1c1cab34c92ee239504268b0fe8d2f1ec4788816932441b5d13abcae0a2619771d42d79761415775baef162e7f5a7d8ef3f34ef298250b9738307e9adba17afca264c0502cbf73f2f5fad115cfbb46578a22517293614b66765792434cab5bad52578bd500a55bf9f81a1a6a75ac22cd7b1d3c5dcec50e50353b0dc5d3a55b1e4ade1146460afe41f000d3eac8af6a75873b6ad174acc50caf8a2aaa9f4ccfc3461fac4b3810c5a4ea7e4aa14da9d0eca36ed33630a6082dc2ebf2665a1d7f2decb7a6f623d9735555a748aa077005e0d19d174bb071ad511990e56ec704eccd5cfef38ed0b059deaeb1eed0ec0e0ef2ce26ffd7777110630662d1fb7b89a70a2bdc5b6b4efca37512d7570a9ea6bf157ada2914f682e458652693c48fa3058e5731124746af96e7716059962da68e37c8ccfa8e5f79428d520c99356255b037eb7f4d00ca1b591f947a0cc288792441ee35a367d865b2c352bedbed1a040fb97533925fbf38a47a2f3ccbef00788c7e53af1580a94240c8dee54f8687725c0000e84faaa0bd7330404533771038dc99bb2ca663f84b70e46a9f773dd42ac9f5e8e22de47b5d231eb784f7b13a9e97737e5f76aed92a5c0a6d497ca3b7d9040a3c30fedee0fe9c45e2396541f7763a78716b0e3b9d13bab0fefea7e790fae52bef6fba83cbf4fe1631c7adc77381538b43ea1e944b6d333c927909ad424fcd951cfe5824f1191b74efb38e1454a31d4b5ff04f87030220ce716392e515784cb7609287704ec08eaa2c8a008a5de8e6bb379190308dc5571b249f3f6763a00d6f121ca3381ede5e30ddc4d70f8c07cd00ded09def65834cb9516993b950ce989dd9bfa48cf00ecfea49f0486de49b548e574ae81b3889a58cf0834f5173c81b510d6a3f0d75166e8b500973531c38fad0242fe7e4fa915bc2d6b3de83c6558870944888b63b5486c4dec79eed70229c0fab41fd360fd3092bb7f8babe53e01a9ea990823c698cb8af114713c9c797b88a04ebf1b8245805dbb763602a59660b3435787a49e505052ad88cfdd0d1c5c9f4d08a7caa54588daca668b3e310a9ddbcf670a0890ca8a57070165852ca7593ac0d15be8b5e3edf31a8f950567074dba2e24d2bfa91f74922d0106b4753f6e004c751a0d9ec9fab6bf494eafcf2c92d53c7e3cd4e2188bdfd17b1e6f16c4afb8e9f5af8875d86b33b49a633619e4286136fd3f85ef1c2162898c78790ba5b12c233109ee810db5efea4eab6d0cf4b824f45c8c611523d88f5b1b672f7515f45df527e26b6407326a9d7b3309101a7f774fc0fe0f3c1a9531ebe749ff589945410bdc73fc2515ff0454588b7afa505bd78cc1c13a2efffb6d27ed13e8118bd4409d216f1a0608ccf7f7f232054fbd1ce07d3ebfb5be8564bce04a014e5a2575197ce4caf3ec460d887b0656085f86d112c2f601780c37bf1e5429be0a88b6cf1e4bc1d4260d9aa41a42de72312dd4773dfd6ab8675b0ea58977bde5484602c4463c2ba4e02203ec3a5986237076e017473cd71a7434b0999cb4ec72d554140ecacbf1c2dda1e5a4c8397a168d34284672209836c2dedd828455a526380ab2b61ffa78e7a0ce02c054e71818a73c5a8725527ebdd072d92fd65a4c84c2db9027c97d24c0ebb15e98e516fa6d5fa088af5e3561a4447e8561649e6faa059ec00b79121ca1de364924de4c8abdecbb5024a679bdd4581087529525d28b74ec7880c8c207fc940e6898d7b21562361bda0fce8a0917eb0c8bd4dd98864a8dc0c8c0f1de8f4a972719458726c3885676fc798a0134a218dfd6e08b405131a7388c694015826e1ad25e2620f4c22d211596e76f5561396cc91f2d0f134c5c6d5067649b4053603d9dea26babe6ec9dabc4e408f8fd6d2d4fbb9a569bdb2beeddf37e43ba287a6c8fa11130f5e6cc97defd7d202d2d6e44ccb0c2c3b00b8c285466ed15abe51516c0e1b3f9f156a88d272bcf61a48f7aef9abb1aadab1e4becf9cc48e0f4eacf2a9b08fb7d4beba7816f1dfce96809d3d084bd24d944e0b9166d5c95bba98e3e398aae0fff7047b3cb13832add0b6e3cc4025ea817122fc367fa961f4d47c117dfa8157e426123b07d7ab4714bc9264e57db3fb56be1585487bca25f24fad3ab84007b6b7cae5656045be786a2e6a1f35cb39f41499cd4c7d3c36fa09499eb9e37d3831f4058abfa3afc68348431c155e9aad69c58b4984d0c2aa6001896cf579529845fe58f2f473d865c1c6d58bebbc98a9a4c7d40fb9c600c85ae552556fa249ac1fbec93800c56920bd5116f604e8edfd2db8b4782488c401514890b5b088576bce8eb5042facec9cb6d1ae4a53fff7545e7b3f60382a29a13d61ee6e7da164d11c9b473d1dcb830437e9c5542958723e4548991670fc09af20277526cda0010c47889e9746759826b6caa59348d813bb6bce2d04e97f32f96821e41343b87219abbab4b60413e205a0b075a550c14a7de4f4d8442d0e8d643f863e394f43b481bfc7d1c257f00835b689002f87e25405a638f546c422bc411d1a96ab1df0027f28444d6336308931432cf7b720b394bb6e95143e1b287013e66596abef8126e253e3c82f9765bab31fe1de5590003939fedfd2830b367438e5393ccec4ed617a889ba511891c5110f209f416d35e545cb976c281c4040eccad072ed0707f298269d325ac79cd7c10f1d5cb945c36bb32374cc0d43e0112e6ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
+            "0000038bd3eac53d4b89d846bec2a1207f52d572c1e16d53962b55337903826035a8b07d0a962cb60cd7e095ac127e689bfd5bf2abb7b07a07a059631c529043a97562ab2d60e5623baaa9901b78fa38a61848af03bc0914f56c8ce935e60173e1b0f2f7b7c036b3e7ad29fa159bc3650295634342275977ad16e2ad0e1687e176afadd0e88b0927026a111e9d88ebf416b98ec5a28acb4870b38bdcabd8a9f1ac6c47cf10ec9e100fb5d488d24357bb1af2d1540057a437e0c7914df5f40659667340a88ece0e56a28382859874238d522bfea708b4492fe6a477e557049548c6d9eafbca8fedc598d0ea74dfc589749175066fedf49a1dfd55305febb49f59827034b4c22e918281d7164d4ca513e652459d8ee6469b9daee5b9abdc0cd2ba3b55d6f4f5b144c449cd36abf5510b352ea8f441cb4a47a6a87b0f007e496f3ffe74b2f7284de4a4f11f897bb0ca560dfc32f16ef7842e1a00868dbffd2b9b55a6f7ec0d46c7cb2a313f5fefff5d46c48a3eba0228b5fcf106faf1b7ffa4bd08c143205c1d22bd7b1570eeaa9853b13dd204ffd70787e6b366d5c2a3acec691fb43f367f5b49e9d728853029549fb5bb91787ce69e1d63f34de4b4a20ed250e0402177ff72f86e3014fc7b273740b49101e39142ea95a71b9e874a558b47ef6fd563c12485d809dbafa9392fcff2fdac9206d47cb379caf127e8d2d182b42c08637315d6d717f80d12f8739416e4dc75c1a5d670375cab29051d93dbecc6ab963dc41d31fe70c3491146c51d51240872d167c0a8352694c4f7cd4ee518f34c6721d3d02112162880173be028e7b88fb52e2329548aeb2415dc823342935b12e572e8cb10023298cf3cbb7420d1e5acd6c8546bd18ccc123391d46b267f6675609cf339bb2ffe806db156117aeb9157b39157038f6cb3fecdeb6e0c3974f9385148d41916a576215914e731a623d1cdab46fbb24bcdd839d2ded7d8bf9e01393870d3b8dfc3982ed7cf62ff1e4a4cd583a7f833a0224bb42a66822306122284bcae9f7b3399f7615eb8979bc3046f2ef5639bf27a2b9efb9e7b072487fd30793b65c334ff07ad9f9813bc8d8e7f33200e262677cf79081b9d09a9ba78883d45f128a215ab7b1b68c6f416a40a92146aab2a9104870c7191a105d60540b465caf080d65c910a95900e098f75157ff767e03c8be577bfbd4c3c9d5328aa1ddc8c009fbe2d111efdf7b11785c13f8958d63b45e6f74398a62e2d676340f75e020b1a66ed863488246c1a28d8877623f5485dc9728650c9dbcce430c0505a45b6b77523dc737e24339c42a1b2575e80c2ea0f7705fed08cdb21316a4475560458557572952b1c3d6fcfcb29e25d2b1db95f8c27c4226a2d4399526464c782ea15bb2c041278c438eb19b2c0a1f8c8583e5fca2c10e38c4abc6046cef2e46bb76d908101f9dd9536196b9c11a676c0ac679871fedd60a42fda2f2df55a4ef04c911e16740f4c41c3599f656f1184a5fb1af694d64a9f89a1203f7e0588a46a999a83646678395b0b9a753555d1793358e668ebfa4df12fd6aad633163e881936982197b9124d96efc0715693bfd082c8e251999fde10173e15c90d11ee01d3a2ddc18528fcb8275d4521c1e7366044dcb7991badc306515ae3cb5015c6546b3eca47704af8b4cb7aaadc67491b4ac83bfe51ac9bc45aa4f536833e07736862ac17f03b4dde13e1b880842fca6688aa34e65692c5f188f013ce92fed50414630c295e2c795441968e069c7ef1a8db2108aa549dbd6026eaa167f1cb80131a04b126de57ec8f129a9211bc6d490a04c8d2c93c6c3cbe01dbf735a9547ca97744523106330265b42d3a01cda4f00f0c7858a1a53de8e5cf5ca73bafdff17af82e83297fa9cb9e4a9f5b7924eeea188240cb7c7e6e6fdfe7e6f16221fc12ac6ee82c71218df5c4812d0ac7ae7057be06927bec1fbf6bd25dddbd35c7dcc697312958009e303342a27ab1c1dcc0556cab52acb92685dcbb1a00de29cee6e31a6a5ec853d5aa4611b172e432cc89f9097e105cb1cba2077bf5fa1a01cacbd54e1914cc44b1a9e842c254b68fdb756eabf14276853e44e934297a1e0f61b30777828afbd717d26419bf30b99d7776f0113f46f4e4b294f0dbc8e5d61f4b44374fa687ecf54025163cac93c5f1a5fd5357251d2c4ac1e8926ba89a9064153a10d4f1df87122d33e1f23bd4cd5783861a310818c8d6864f3a5a7e806e1bce2c0b07456f93a3abdeab5a9ac2d5df76835579e3d95f065bc991a9fa7bd99c112c5daf9bcc801970ce2db4c653e26dd1b464831ba2ad06c18920f520e7a0e83be89a2ecbf4d349361753aa5db31b71fa9d528261b0d42034a86dca89ebcde11762c71c60479e53de607b3ee903159c53fa65c42db41fddf67b9ac57d5d2904d5695c707a4532ca67f2a1c68c5b132a1d20cbc1f6ea7fe9a03e68d7c142684edf0b91f515ab4156db31374e12603a37f31c1027bb0e647c334130e4033e6adf3a2bfe52ae2dc848a3f8a744da0584ec94a54626c8daaecb40ad8dc2cee8f7f04f5eaa8205d1a76fcfe15d305aa538c3a42fe4c2ad4f6445a4a4293a6d11e8b4bacfee60440f435e0a81c53d9ade7704ede7ade996cc954e47c62253400138919f615e9a7a364e9cc479d89e4f26ad3d91400370d5333883f06a0ad5f92255f0ce43f42d07c448cdf90251549a2d3aa00e23d6da5c74a0e699130eda781ec1ebe87bf366c1118804205879db3b04da6cc9c3a89c07101e81ee7629f50a82b97af2217dd13bd9c69a4b5d77dc8d68a2e857da88c1b3996f0b363faa49c9340f711590c5da20c8fcd7b5d2e3b86fdb904de3287008d12c17d45ef45e5bbf4f2f6df48a881eda11c9c94db555778f565fb120df68b63009204366e0e973d9cd9a8d8eb4641d48c6c7340e5febf83af0d16754ef7287a4aeddeada65b398ecf948fc0042ef01ed8f86aa61b2925e79ba12db63186e7e7965a9503e965db2fb2b91c25b92da638c4484089576c9c05686153a40ca69b1bbd298d1a3f369a9c43b3c321a809df4128d7da40afca5d47d49dee4e7fe72d8e7d09116fc089ba37a8c8a669536e7954788fc8605c4b993b3109bedd1e6ba6089469bafb794703bbe181f49da226e91e43ac114a4ea6787ea60094f7c56fe0fec97b56fb5a22771c4c76b3c8e844bce4d5221a70dab8424b997b2b8c7e99a016f7dc7bd0e7643088b6384a6258f76844ab642a08c2baa62a9c1d184aa09bb547bdc66013b3f27bc4c73e608feed4d7788abff75df9619cc9d4c271b7b76772e2262dff41566fe0957cb5548077224b8237c5b7ff5582eb5cb07e66dd0d4fb8170b31b3f10a52278b964cccf0420698473c67c304f431009b0ab7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
+            "000003fe19e89df20c2fd4b7227948357829765b81d88c2212de4197d1700a69b3abdcb5033e93b5b82622c868902172e85b326aafca090456b499500bd7487748e0065c21d3100b95852ebb2cde9fff508085caee33d9265e5cba68715c660e930a49c77b66bed414d6d456a163e3eadcba78561246e1754bcfd44a0e61e6bfa90c6fa6344ff9c2befc0748d1d84814da4cf291bde892c7c3b4a1eb5c6464fcd3b8095d21a0422c0b9f25654f65f4f5453b1fb5b2136d312ab19b5e0bdeffca0ec190f0edc270d854e931cac8d984c19b048f279bfeb3fe318000553a3c6b1b201c86a3b803f3c291a2cab9bc351afaa64779d83f3b8c1fd5b083a7838c0f4d4417170d8e9fef533369383b751aa1940ae67c29156768d2daf5f2aeb97d39d9ddd034764de4d53826a75edca1b545c0448858511423351555300d251011ad4412afc3bc1aa9ff0aea6d0f7cebd4025e0003b5f25e19ca79195db64fa8ff3459e6861dfdb5e0941464e9d1a8c98e1df84ce1f54b532e328399f59bad26bb9019190524aed50c93fc1a29f243658bdaadc6b5c24a052aa0cc248d04e5a65a11b3657b7185194f416337c2b6275711a79873bd2867806ba690ca6292e2a3dff1258f5fc3d747b5c093df03857799dca7cf028b5abf5d1a165c41065ef2edee7d0ed5d8f573f19676e581570d209e42378c437b408dadbc59864c9e404434896e66d084174db91bb7f8c8f0b1359e9b8783ed33560b5a6ecb7a871e6acf99c65810439e463c71c5775fd0b6297569d17b35588136339c613415de29da583c5923bed18fdac04145aa9521c06d06d8e32d085016168776feb484dff2f44601aead607923aa957a7ca686071c27cc4a473cdd6ec3cab9cfd9f606838b3b7f3dc7f1ffb3ad5448535fb4883c222430015af5e02a98e40a96116cfd19f7a3ce1931ff9bbab10a1cce0728c7d9ed397d42af29d61d5812cf03c88d4b1ee5824b1286705d540bdce4c25b84003dc861efb576d52dd6701d9d99ba02288591d40357dbaf1bb741d8d50cfc642cab9720f3530e8a211c6cdec132a042d3a242abb93750802c9a4b0da29b0f7a3a09971ca2cc0676e87caa02704d2c14ec8371c1666d6c0ba8cafc2126dc753ea89a944e156d301c307bfb3f69cbb0d3efc87496b0124e7b7464106d58a6729b5a830e3a24521f241b389b494488ffd559e02822c8d212d1ef277674e14ebf639d682fdbe2bbbcc9bd9dd1ceb70e445e66cb1cbfefbf1045043e402caee3510f90d6e9752c4713b5cbfbe0349059a5d69835a85d4488dfcf55a76c8aba58cbfa8d6bb94993482799c154f67c019424404005c117aeb9dd9a5f48488edf3099ce07b1bfbb5b82244399df6822e8a30efed3daf1b48683d33260b5c0eab17902df15d48701ccab1c5a81a29ae1cad220be97b747ef54bf3b8bff33fb0aafbcd8c3f1897af8e4673e4b186aaf7714151311e8a610234275219f7e2c8f7ef5b11090b688b563f35b42e4b6b3bbd4e1a39f19bb397015338ebdb2ad4665474e04e53a90aaef44d9798060d1db7a2690226762f4aa23cd687f07a8bb3a23c72780890bd6f0c01be74167c9f39b83f9672474bc0cb4c295e089d943a81002f1e75a4454dafe871561415a7c2193b5e719f002fb9f6649173c19681d3acfdc9de88ae4345504735dec213104a0f809cfee1fc980341563c4987bcb0e6e9d825abd1f6dfabb3acf5ccfa04e7c928cdd2dbd8d012abace8ef343d4637bf7f68c1370036e74f406209126bb6339afc5987abeb8cf709d96c84ce1c859de654a6cd501bce2f4746ed6224a458be21bb66a71ca9a614e52262d7620ee430661ab52fa2bc97559f4c56155c3be5230cdc4f3985792b37428fd8db2c8d0535670fb198191ebbbaedb239228720dfa58535c7aedba80cbda98b93c99cabf9e215e1d093a5f49ef4dd3c83fcc3265d0807b50c170a2f2610d77fe7e4283e774d3e3da31dc6415c54be70cf9c7b155f59dcfb027423821f39f1fdbd49115d9fb369d85dcdfb3f94a79652e9f409288d48013ce20e39a8de41f8e3718397e2c18e1ba9809a6585ea7d5021ed2fbff47a51ad586678992997f4ba1e72b5a75d7e3d3703b66682a56a6b04e21f69c6b862edba1afb065fc410e85c0ddf8f2179ac9aa92405bdc3e00e08e9757f45c97cd2bfc1fa25f85f2ad57217f158208157d74ffc85520437fcb6e8bb8a9891c5a230555d12b273e76d29ed3d8d57a041e9188242b37b3f982d1edf82da410ef800b252db25575bb4f69862d38249049424938da3511084892b7d40c13268a3451528064845538e6e43a07ea529549858f3fb131cceac1bedae3a2ba5a1b3a445f3a513be725c7652d6c51405d6bbbd94325afb47a900991efb1c72b876c855dcce7787de4c488049051f67b99bf9ff9ed68b6cc46bc08436131a01c4412503df90eff7ecf25434a1816ffef1e7da02c587da4107ce5167eed39f9f078c09a8c301f5d532e9523c501030f5b5669a1ff911a798ca0019b923a1349ca1bf8354e2c72acfdbc7b48d4bccbc2b7b1a0d7528487edb86eeda97d10b1c7f2950c9d38aac5d221e7cd712c92ac0a49aef3cf4c9f8a60e6969d47a19f42530183a6ae7789cf16d793d91aeb45db516f69f006fc66f1f6bd215d95b07eadc8ac830d6df966cda8a6ddaa0bbf98e13d45e7c6d38e2524ac78fa383bc5814ff81d47a9b016f76d52ffb85f0e23cc9aaad36b686936f83e9d5671e0fbe52748ae93f2480abe482249a7f7c8e1d3dcbe527a0745a8d0115325eb58f63e57b53f9369e7944d1a737d44406d522644d197b58b36e1ba2dfc201fa0ef12831e96e18106e4e204cb560814e8551813b2ba3fedf343389dd0491021ff7e9cf70eef3fd5999105034b974211dbf5391215173a3296c3deb3df8af6f2089431ff6351dcea6e0168a42a4cd7473863fa13147ca7ca6bdb9b2dcf8f8eb5adb2773cddbfdfe3c167dc5a07eaef8b3061e45fb3147ca7a84f6fac7f70a45d3004ae13d022a6990c15ae415c62657f11746c5efd1f547e1b0023bd577c0ab4990ace6a8c11b093912b4ea719e2768ea337e8807efd7472a572dcc6f033d1f3629f540aa2e226316c94cae7bed61420d86e06ca011af74312071086db5c50f0605a443b877341f9ce54f761e7876cef992d1458f62753366f6989838823192add70fcc2c96aaa6ab6d10cd7235e7a1db837c378fe231f280cb9f0a95b6f27e2741d680da80836fb3561b3b155f750b86288eb9ab996de45b9c7d4d18ceae200def16034243602692246d7f4b1dab8a7d53242cd2467d758c7482a525b2aaba7f41e05eafb369735f476a27dce4e1adb09cf2c09a4670c5f0a134d239425682b040e828339bc7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67",
+            "000003ff3c180f191eb9214326a3df038f5b168f96539b2af6bac89088a2c521534b4ac109a6c0211ae758222942d48290816f48bd47213a677c3abbf5a0760d7ef064ce370d2616576ed3304672fac02edf44256d37bbbf79688b80b8b3a3282ca55984fc9464ebf4db7b5c2987e642f7b1a528f2a5aa8f69c1e03e3320964fd12f607653eb32b64cba17b9a187385dc5f524ad53207b3f212cd2b43c558899b37fdf69b54bbf4b58c7484c2976079f23649b1171ac0d60213a9d21d68512e655472f3e6bc03e23d9f4cc0fdd46d41f15c396f40459a70f69d2d52a5a8c58e85507bc6f84d73111169ec37dfd1e8a792b13a17ac64c32bd656e2fc17c3e7a278f2f13b8b1a28614545d69f5b20633f6a6a3e442793710ab21f77e90cb070bf40083c49cb769c5146873d1311b6d07e7b3ec37d6281e0c09de8a37bd428947d7f5f6de29c068ccb43129dcf5e66673be8b60c04156590ad40dce51baf706b23dbcda54cf562ab615f58d9d98ae2610e51182a843c7f2eee26b6ebdff235a8914b5519987ed39cffb0fc5ee4c8546515abbe827b076627f1c7e2c55aee9b52ec8e4c0c3ed31583f38d9b8f32bf82f479521397537f4ecf0a1fcb4b0508de3b123e0bc764fb29eeeb621eac55967abaa62e02df8ce6c6dcfc1400ad996bfcc7d3633e96789896c4f804050d66af5517e98666870043b24a1f69e4f40e49a1d2512226441dfe83b10b1bbcce79f066f694fb5e4e4138056ddc7eea42072437e0b137c6f1edec173cb8677d4d52b121c8b6a610b5909a5e1d8d4cd9749948b6d8e40dfdb800f2ea1b517c993858323e7b1c08284743112a7654fae111778096694669d8d020913d22a86cf97a036de0ccdfaef435069b784e3c8eb8e73fe53e0f542286dfb9e240c4a3e09132545f316dce3b284913ded7ed7ac0eff2325ca23a999fd85129c4c7a5ec5f6afd91bbff4a0e8d9d7c0ac09951545bb752ef297ebc03a542010bef3e06070f7d0ef14c73f0daac37347e788614fb25fe4019e6ce2d8780f4e3de935af59b2d8fda1013d5b7f1ee8a3dbcbbdf63281ba0b501478b53871d79508e768308535c5c5426e29cc2bfa1c2702d49deed76021f1783861b18be7067e5b6e28605243c3bd7ca22037678d0d8bee639e69f9a7b5fadeb8b5afff414a4fd89908a0b0705cd3315c278b29d241adf57e02ba384709eb3d50c50264c82739adcb9c70289c220fe87702cddd3150d6ad1fb4b7f734fabbd41518e4c795e9efc4a57c9d25222d140cb624708567b326fb560e83f155fb5a328d523e938e2f7d8636f135014f7c827ce744f0044f1ac00cacc225fbfa6a7198dc04e62e8d1e55384f6b9bd2fff09f5c63d7116384d8255fdc32c047d2b2042f1eafdd3b4f075ed643a6ef8fc4dc2aa806db4b0b4e00b19e089dfda52798555856174f1e13045a16d5196adf61b36be71b90a3cb8abc4836e1b6e1b92cb63d844e7eb4d128612f6b447ea1aaa9d86de95ef07196f5524b3a8a9e06728a2dd5591f41ed08375e159563d6828d4de32e46d874e6fc8e5141f9f4cb51d84860ba45c9a98d62c1c152a045e4024bcdf1fdb76b94edf3fd234ebb962c37c0514080158e78ad8c83edf58e0af1ce7bf0bff266291aea00478c8505e51adaac2729929816cbf6a1d26bf8bfbbbec523dc2cea4e1cbb93a5eb8eafd2baa1f2b473b6430c8b43795d7cdaa1c124597960bf2509eb19b6c7b83923ced37cfbcf0abfb87483363b9b236d52a19863e475a6e3fb8b9e535ebb092e76f91dabc5acae01041d8931be12d3340c9ad9e75f7ab82fd5d3582c2e1d070e4604ae2e16bbb70cfd7c33fca0452e5d46fff311331387c336897396b3fa26227e2287b2589e321e003d1fadf3fb3130cd363363d8bc16d3569662eb33927ca396a1efd55b82eb76102a908e505fbfd0456dbd3152f20d223322847a36dedd70518d9f61bacb123acb471a992f0371825a39056ee1a2bd5c6723c1dec830c38bdc5299c4bea07763a6f0a1e417670559878c67a324d91f6f33c66e3a3028837bf21a240959f9f069781ca83cb6f0484577dea4f88d9bac1f18624c16895fb097caf7869fcbdde5b2fc2d45f5981120c23f5f9488b03952aa436a993f0f79e40be170e79ab73e034ca4ebb30f45f4734d959954443503dd30a7cafb74ea8a695626e056b967df4ca0e2f3d9f60f8802b3457b5aff3abce70247e881649eb90f6c55c1a5959ff9fbbbbf0121f9c12824f2246c198ad8139aca5694a92afb467ee148032eaff73f85e10536312d94e1658f510ed9dcebf29c8b11f672adf856153e916ec68996a317c8ededeb1bbc274653f03be02e9dca745d39495b26093f1d98fa9565278284f110744f9be6e34dcc70cb77007a2c65853156800763f15e850b8a01e122b8ffce5d3fa4417b5361eeacb7a0b183a0b835781779e8ad63906dbf5dfecf0716524c60bafcbe49510ed015f1f1750a00d07dca5aa3634671104df1c79236a23410bdca01dce11bba3d9aeea7d06faf7b16b18a4da9c0622e4c17c4392262293a514d0bb0ff8458c5366bfcf3bbb6a04110e67e7e91dbe9185e60282a67204b8360b5fa99f26d1300cd511901cbb6f59fd7545efe47776e736fd1af2d845ba343f4296246a19e874d875f7aa4584da888526665f233c6ceaed05057063fa5d833278e3d07df84fb482142dca6540b679840a38f035cae800f32849da78eaedd04b463dde31bf61b752f6e69cc795f6c7515defc881662b1be2f74b1889e456d9be54b19e49c4dce025bc95ff63d5e9fa5a10d46ade9f36c78f5f22f27e955fb5331284185bf8961a57f1d6113fd868f645efebdc441cc8d671c5546ab2a701e35aaa4977604fbcce6fbddcc9f493be42f974c299ddf74321e0884ba2c3708ef62d00a0e28edf6dbe22c94e351a0501e81a645d761b8875f46d79df128f9df0d869775b18036bfebcb5a5e10f733dca63ed896936ade6c0d4bebc064752fc113823d8bce45d3b2648f01b1702b274e5674c76e8bd121ab67e05a77b5c6a68a4a7f63c1aee14e9cf33eee417e9f4df70045767a0b8a162e362efe51cce26eb2351cbcd239bc3be2ea75c7767474d2799b26316c94cae7bed61420d86e06ca011af74312071086db5c50f0605a443b877341f9ce54f761e7876cef992d1458f62753366f6989838823192add70fcc2c96aaa6ab6d10cd7235e7a1db837c378fe231f280cb9f0a95b6f27e2741d680da80836fb3561b3b155f750b86288eb9ab996de45b9c7d4d18ceae200def16034243602692246d7f4b1dab8a7d53242cd2467d758c7482a525b2aaba7f41e05eafb369735f476a27dce4e1adb09cf2c09a4670c5f0a134d239425682b040e828339bc7a1c38a5e6abcc9d5c296f05f5a66ad387c39b43251f13eddde0cfc713b7259e8f9f348e10d62478e98590f838881b8e956915b4b30ad9c802888da4280f3cb66ef54bf173d7222b3a0c89290a5a902a5388995f0d7c0bad68d1578227bc0f67"};
+        int height = 10;
+        XMSSParameters params = new XMSSParameters(height, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        for (int i = 0; i < (1 << height); i++)
+        {
+            byte[] signature = xmss.sign(new byte[1024]);
+            switch (i)
+            {
+            case 0x005b:
+                assertEquals(signatures[0], Hex.toHexString(signature));
+                break;
+            case 0x0822:
+                assertEquals(signatures[1], Hex.toHexString(signature));
+                break;
+            case 0x00b0:
+                assertEquals(signatures[2], Hex.toHexString(signature));
+                break;
+            case 0x016d:
+                assertEquals(signatures[3], Hex.toHexString(signature));
+                break;
+            case 0x0189:
+                assertEquals(signatures[4], Hex.toHexString(signature));
+                break;
+            case 0x01f9:
+                assertEquals(signatures[5], Hex.toHexString(signature));
+                break;
+            case 0x02a1:
+                assertEquals(signatures[6], Hex.toHexString(signature));
+                break;
+            case 0x038b:
+                assertEquals(signatures[7], Hex.toHexString(signature));
+                break;
+            case 0x03fe:
+                assertEquals(signatures[8], Hex.toHexString(signature));
+                break;
+            case 0x03ff:
+                assertEquals(signatures[9], Hex.toHexString(signature));
+                break;
+            }
+        }
+        try
+        {
+            xmss.sign(new byte[1024]);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+    public void testSignSHA512()
+    {
+        XMSSParameters params = new XMSSParameters(10, new SHA512Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] message = new byte[1024];
+        byte[] sig1 = xmss.sign(message);
+        byte[] sig2 = xmss.sign(message);
+        byte[] sig3 = xmss.sign(message);
+        String expectedSig1 = "0000000025fc9eb157c443b49dcaf5b76d21086c79dd06fa474fd2b1046bc975855484b9618a442b4f2377a549eaa657c4a2a0dc9b7ea329a93382ef777a2ed402c889733b98d7c21a6298d40c9e790fa75c064f12cf9e2634d74c86959085cb090b9dd912f6f72fcf8f4e796de983f473854c6134c4616df07df9923eb421fabc45cd07c78f027f46a88dcd851882205160c7a066335203e50f415f7d907b8eba64d5d436b05b43cac92afe459e7d7e8cc9c0b9f973c4b7b103cb4c042b595c2f6cf206ae6835d0a4adb9d85adc76974b3f8d097de7ecd7a806b93c943d66421a83e61b5588ac2697a2ddf0b22ba1dda387522e7142bc6dbf86b8f6d8de68bca2f1d03188a629a33e568818b022996aad87afcd59c5c8203e6d6b4ab83e12bf00bded2a705ff3701754a4a993af74b361760ceed8baec5ad4543282f9e0574d00bbb6e9c98b2da32d463e2bab0394bf60e34ac5edb9b5196445e1db3afa25c3dde5f2489c6c5136e4bbe4151a6692becff2250474277ac1c4626bd1bdb6310ecfa8dc8f9664d61991023ce7a04039b8bd46057f36a1a6d657b530f11fe14dccaf9eeecc7382af507ff64a464ff6e8437c7040a17409317181b2333bfe781945c47af565abf5bbeb06c0c6fd1810c872bf3ad6db9382b048a5765debc946b867c8e10705312c74b568991b4e1bd29cb05132304c9fa11e6104fe6f406a66fdbe3e2f031a68340c6b849cedd32d8aeff20ec063b862c4915c0bc4745029d2dfe23003d5dc9f8f7328b2e21ec494da726d70cb4400edb99ddf0f9f5aa8ed4069a99769e7e4b905e0cfba8255304dcd9eb4e7d4cf9ee92f0b1a4406e01d1c0754d56f6e14b74da7373eeafc687a4820623c240999ad143d9ceeec76cb600406a8f4eb2bb196e8b53d7655a889a4a25d65aaf6eee6f309ebb5ef096a563fe5c231889037b3f0d33a3a212ba4f1851fd44bd367717ed6909a032ce36f1d7eae489f462993c41ae4333528bd373217c98acdfe9522474f7255c8089e6422de79c65112c6d6ddd98d53d306436bca504f8603fa6beb9925d84c104d374cb4c681dc2b304ccff8f1b1b3b10f58fa701445d83810b853f17834017ba9a21206fbbb066b1563a606f1c844800712db2cb64b07f9fc834f2242d8145e6144c6096cdfd1cf5a2c467e961a3cc02ee2b7e103c8d54f38f0082823ce663b26c78f84678556cfc42104b533cb7587ef1f1567d50a4f4543de5e3f8235e91aca91f003d53b9e800eab741588e2c6498dc6fd4a7a67f16708ce7eb683a867e2448f72f770c189aa4740848f17776d093f24e633400da20fd8270e62073fedabf4b891a25b4cabc40fdc9207af2e41c99396cafe08a3d68443a643aa55ff7853330bdd80e1260953aadaa30d1e1a1342674a29813fba0bb5774fb3714a4fa8b170569cd735d36a053399f73d057c40cb86cd397cc173b3a76911df7123029a09513db529beb1f74439c364c388562138730e5f0e195def2c30727dbb43ed42b133f3e7637b4d6131d8d8fdf8dbf78fc1da3db2ecd003bc3e28e065b53917513083e354fe7251f509c87c29536eae80f2aaf2fc7e4fc045ccbe4973d2f7bbf627c19a8aac11017b1e00403075e81965d1b3bb5e723099f9a5f326a1cabd480ff12eb7c33c2aa5c1ec410ff6689a559c61e4409ca1867161c1c7fa5f87de53e164c0d9dc2b7941af54988c21ed676e005482ff38c83a6a9d66410a9252bb6dddc50cf8fed31df5b2580745bbe76fe024f0ce7888152cf6ccb75f359c7ca914ea93fadaeb7c43c25464eec9e23fcb51a6d17178f4949278783117c88c86d10a30e191e0dcbc1b0309e25927804ff449866ff5f9c4e42f29c06ea2921c4c7f6ebc862f71349e70b6a84fa9c035fdbc6529de608da3e64e23e9dd8cd93a05d9b3d1c0a816b1d67ac97b70a0ee3bf0872fa585e7c66dbd1884bedea39b19413e7c9bba6a9aaed65215407220697a8d986ee5bb76725c945433d84057cc83ab0526e85244b2a91c5eed1784a0eb5d8d5b287c5e0ef372e3ce8865d20881a5fb63d3bcf9c2272f950f81426a1fe3fee8a1190f760d0842e2a624a202e22ee6f1bf3d742b8c3c568af108be2bb627eaf154dfcb32d6f2950ac8ddeafaa4a6d03d38b6d06ca7c60c3ad5ca770da8f5bb6b2936780d75e25ebcc3266ddf4de5aba712dd9d30bead884a0b477b22af3c97edb94c131861700394d83bdad35b4f1feff2e612abbdbf0a0e09fc3d3af8f3a1c26225a9f39d0be38ae6a16b54b2c9f271588a353fa121bd690496d5af5d3b83dafabb292713beb23d076f6215281d2f0f4c851e738a39fbb075d883e3e2d96d436734311aa46ba605bb4f0ce1c42fc2648b83d7ada023a4775d4f98a4c9f2df015f50291eda9bd3a4b24a46280c90a1fc79a9555b51d15fca1904dd56257f189e2833a4c778ee26c4fd2c682c84d0621e3acecd392600ff68a4c15c9d903eda18bdb07ebdde86d31853dd06f1b65c5004647c0f9c22433f98a40b02d50294ad98f3660bf79610aa182aaa59d0f9a113bbc58ed58c02226516ecd977286f8d58fec91ae665ffeb7c0b449b024a1adb3d3ed33438aa60724bdedc8c7d847c704798c22153233e6e9e1f32142b710d57f7f99ba352c9923775e049417449a60d32fb4fe4a816a6b7c7f31491e2bef5b17f2c0d5f60d28a673219721bbce70e2c3c9f235e70aa1ddf27c9f499d31b9fbb1156742005f6509706b7ba176254e1889a427e07334136eafa1b39ca52cb8cb1c24cf49d1a2b2c07fd8907f4a6be01275cebdfe8c3f804bf7ffdff2a5bde0ebfa4f0add7b3d37db819de93e5eff1ea91abd27bb76b4201e9607d5cefa144fa48590ec3ab339fb206b275bc314f4b44854141c5bfb19fd00ed8dc4c928f585b07289fefa5dfd489e78f3602672d0a33beedc85bb873dd8fa43222173881bbef88775cb2d37d2353fa955f870553c0bab49c8d17bb1ae20f5f08959ffd4e77a848c820607ddf6df88ca91a057ea9eff0131652b8e99fa5e64d27068d3c79ff370410af3a7f9f53d3c75b0fb865388864297a8582ca79bdf47c56f4035d8bf99a0377aba30a0f1ae7f540e0b9bd97c63f75fb2ac8280af823f91c32428a0a5940dec0db8edf0495f3b1776e2ece83f0ce4c43b592ed70c5c8b14056df1050f38ef1d1975a57abf365f21d7db03413a820595010598028af4c46aa718e643df002e8d5fcca43056468e91758005d942c2f7eef5fb2c58c524ca0006dcdaa9051772af7ca50d4d3e4c194775a127ad8d974d7266c749af5b63f32a8f93e711a6990d06bbcbe1edbe42d7e180117687ed09af96957db605e32822f2ec6f7e8c45b6b1f22e2ba84725fef839d5ee08bd0bd3d393c17753c21e0f6952300a04a5df9e16c7d9410250d13a02237c212e0dae3cc4a4d04a67cfb3b02e7af63c65b01fdf73183761a72c5bbe42f02f407ec34fb451cfb785004de387d935fec886d2101d26e4be221072562a6af51db288321c8ed79f157aef63681e1b21f4c55c06bcd262ac382371f14c9ddd9ea87bccb5f015e591b7fcd3868972b6ad30091dc59bc6adf197f4da4f6f7ab7de7e928c8f7a3c38fb851b9598c350eede7c0e71dce689fe9fccdda4a00e3a67675ae2482b97d27deab6a1959e64612e244fbe8b3b5db8d8321f2c73355eace7a988fc75ee2d0b67acf9eb3de0630a661141285b5aedd0fcc92d82cfc70f91ee2a41301f726484b2c5259c347add1408b0f5325b5eb2cfe4866d2f5e245b542b290173efa7043115e60f3adcbda09776975fbca448f0de92ddb0ce444ac9079d6be43bff2a996d645bcb59e7bd467236d1476e7866308aef87fd7fd236d1967569290fc613e285cb33ae657c22fb8768701aaa3544206e77890f726d9639c49073885be27c23ad1ca54c3f5ceb47d2a7b11758873f3362ae9c40324f188ba2066ab61d32eda690bc2a747150f01e5fb7007c4711a84d47224bf28b90daeed7d57631c2146e9ed44b4157084fe02886c1a9e49dfde6be129b006f17a1887ecd1a15b36e5e211074ed7d6cae345467b954efa70fe2398df5a345b942e779bfe0f21e8e2d6c0a67d7dcdb7954335d4fdb3b4cbfaa6331bc33bc81f5f9fda1dc0c947ceeb7c56fad107bd925c78498e5872328b99792cc073f066c39c5e54c05e821ec8e8305bf5434f961f4c9bfbe2cc0a0727e4205013160e3a0e2ef98fdb8d1f96c76786b4229e7f2798dd53db36c31bd6839d62c0bdaad428e0fd3ebe9ea2567da61c0e5315eb3d6f2aa76f87cb6203f6bcb1d341d4d5c58c6a458f45d2ca4692f932f09a685069cac65762d98505cc36e3ad293fb9e11df2fca7c492a5d2567c6cdabacd5ecbb13e2ea0cbcb59b51385dbb3de30f9e619a62487ff6470b491f23a93c2d5407de37f4c39c0b936076c0dd7097b8c9e413b1f386ee52b4f33de5366554a292607f72a59d21364590a0f5516611a4ebe7c64358407b2c6b6fad66cce0399db60fab84abeb023e9bbdf7d1aee52a104a71ffaf75cc0ccf827b10a372eb44eecdfd795be15bee70edaa70df6430f2bcbbe437d1f7112c19eba74d887d2b61f5955c055e63a9efe45a0ea355bf5c18adc79031676151f907c08b7fb2d6f2f002d8d003d19afb8aeecc72d77a33cfc06f4239e3d7f0737b79981054c0c71625ab4c5e0aa2a8d72cbae38ab281fcdacf478a6e50b018ddc5d69d7a73bf8e576844e8e3d0d7299416143ef6067240b9f2cba3a5e20cc9a03d7f6567c75684a868172499609e76c07caca24704178aefb067809ec83c1a8abd500de894e77166faaf9baa7ead78105c03d4008527218fc58f1f7ddfe2461b90fa31bfc6f953beac2efad01e8a03dd6131c6d5b22bc1bef3ba1ab23acea334ee26b35515eecea74d31877da71a904dec0dbc30e3019a4deabb4d03ce5903c3068e668ff861e739ca51285222e7a79d0009b775e8de27153a59db2602a7c717260f793547d52edb5e0bae9080be48a3830762ecc0c7b89fd11ed1015205d193bee79808e5e115fc7eac4af8f841e66a1ba95863d8731be2309e3ed622fdbff61d96c3b5a8720dbb34304e9a899adef0c7be7766b413eb58878f0fac1c6518efc7ccd2dac7d6328487c36f78b79459f068aed29ea2a27d08e4d054a8457bbd9d6dbc396b7ff6fc69896910dc7ad319b01fadf46e776671d352578ee2a959718d960e24b8a565ddaa2f0ee9d201bbd55c4211d85744c5240c4fdfe98e9426e1175e68b975dece12e4b7d00ddc3a9aae64fd060c5faefc1a3d45571e80b687be83c9715970b960763fc1eaa9f2ec84d8bcda8bd771a2f1a1dc8b927d1efbc671a0829b8f0a5db3195322c634eb9b44ae8ff3d4b36d75b26bb22dd4bbeab131b815c2a9fc0034b0c9a2ad5a1847d4fd2a344a7dc3220f9546a80cace35a7bc48ac4422833e5762082e7aa331cc1d6698578a4a3ed8c2a33706029398590da4b399f09e88bf3024dae196983dc32b26bf24e0eb9b8e0b5581076636951cace43297f0aa968c104a25de43917f84bdf73b95110d0f99c95b05e7cea82b1cf8dbac2214d79f0bbfdab8db2984d7322bf6163bacf5b3c0abd3dab2c10894da62ff8c3ecb4268210071b0aa4e48978394ac053113465d4e3d14190408c44e95942d95fdcb22683e68c4b9d4ab36980c12df3405194c857c3da2653d9eb7866e6fd5a4f89ebdf7193141bb26df35bfc44962ea206fcf95e9db615761a8fbb203262959b7c731e20d4cccb6c30c4ea82799ac1fd3a64c442f85c12a70c2c62d543bd362bb6ec6d3f3c1a87f555c1f73b7d1f92c3fd9e1ebd2bc8942c887efeffe65e3098abacdcaf0c626a97931a2953e78554d850614a0673741410373b5b2a67086e20faf7bca17a7e3e74f2158876e1c9d0d8816be4a3b517aaf52600153995465daee1a2bd71ff572267c18cc9ab2e5e481be4d1646683cfc0167b8e7eb6781b09f593832b4a1e8a72c59aa805a30579c528f2e7665c8954c3e509d4705da201f99b3cc3d763800aad1f699339cb767776134b9618bf06e4142089b845adb526ad540444b035fa852777a993b741461b8ada57583a77ed45c08be19a1352515be5fce1d97f227e1c880cdecc45e43190b46e1778144204b77fc858cc7cbca258a3383adfd71080e64bc0d7cca7437599cabefff26de5238ed8744ea96ff5a29cf1a30dead85d073dd5ae13aeb140ffd0c4a41ee3332c2e2bea34467494bb8a6169bad610d20f62c2fee042b02bdfa47a7bc4fbc56756718e5c9acd0144b0892016f82b5d3a43b8e9db8c23ed42604b123f046d029263cc661f658aa0318caa6883daf6af3c1f4eca0aaed6c8c59434a31dbafa162417ae3855a1afd7f98a3235dbb0e83837133be6bc3212dbe7158967f89f4f56ce7dccc8a1d56b96a5747947ceea53a2a8422f8d073beebb94e3621ac546c77170dfd6f682af631876a1989e85a273358cd41bafefa48af4e8f8cfb21689a89c2b6c696859f726cfecd458ff1d0311df8b671bfd6dc5e849f386cc9f6d076bf0d42a982d80cf488d2b8b9c451f4cef63754c948250a34a95a86187d4b0d1c92473e9de35f760701267d761556dd5d9f99e8a5bebb84705800bf99bed16f132f77a10959ff72d357a9d276db60fa199c27bed7cd40fb4b12ef3578be414a5b92ece6eb830de5ea734087dc44abbc38e400584a980518a8ff98345de4a1d80e74b97a1d0a1a3872a8259dd4720e9bd2f113aec31c911bcfab1b0e79fb810491ce74b31f631561753826771e2fe137425beb2317a5a480b66ce163018a3d8eee1bf2d99c4d0c0d4560c2f7979f9bf438f11c9b695d490b8317ab529d5401a5af788c495fb81d2d4d12ae76bcace10ccaeef81b48bb09a408a2e76330a77aa3fef5e53760be73daeffe8d53966cd2b6644d7ae92f3fd13fb1b4adf01fe15931b136c8301908c92adcbdc42ee7bc844a2a2e191d5f05190d8d29416738882de66cb58a63310507501275280e7e4f3f290047c050db2f8047eca1a2a697ff386f9187a574fca5834f219fb0b79dfc1d6b55298f4d9900457705c749a4490b7aede2e69485767aeedb235063b3ae75b0a8a8721031ef6659df4fba5bf2478326544c708c7e25e5ca7ac99bd568d849196371de12ac4e7754bd35bddf7c1509d1a32002108eabde06055bde6bd4dd92173d765ec45cb57244019d3a5c537e4f8e3751ef208948b774e4ef17731b8c4257ef2efeaa4770cf8b95cf291291825b2d374af17fe0c16aa05edb318a9c201a63a30832f8df66637e6243f6e23c83782bb6ad6443a623a9125afdb70ab50bb5494faa5c8c2ffcacdcce931aeb2acc2a8e5d35af2331c9506e2f1b4ea9afaedd48644c0c642946b31330a140e59c59a7e53de994abfadda7b7e041d1ef27bc8ad33db485cb20726edacb43f9e92d8601fb8f1b65f00b75d5631c34564f8464b43c69312fd30612586fbc0f4a5bb9002ed13451963262ae44afffebe38d1f35768dd678a875ccdabd86561d57b05147bb519d9ef27326da505efd370d44c7eb023dfd36d1224e0e214bd14de142e4529e09c1b6caf11162233ffd48af3368cc3b989c4d6316b010a7c582276929daaa3f27c81ac7732e2008befd537c9ea2a95e862f0bc17295766c7415a9cbc6725ef08ebd0df08942224c4ed4c7372c82c28ca7a7e485287ce7807b3df47a3ba3cb6f18fcaf6bb216ec91d99e642fef4e59d9e150ed09fb906e2f4123060cc4d55a0f319bbae0151b68e02aa5c6f430fce57ec994781b90a8eb0064e046b8a6acbaace6a3ea9165e95ebe8116baa05098e49d3aaf76a45f9e27b8f92c621c15ae84c9b0ef9e1bc51d6d9d9dc9b3d3e67213763776f7f6384a6b30fdfb27260d0dbf26508b17cee5bcfc6e63de06ce8e9286552baeeace047aea95f9c0c267705a55b2dcdbfd63dac433d2f6ced9048ac5be3e712f49b272f54d02e37b6698eaba31eb9cf2b51bf8d8b47376802da992280c8cbd7d632949efed734a7b0b11ef9921204a2919a3fdad9cd03c1e08418167512aeea932e8f0016cd461f749bae6713eac495d995547527723cd54b0e1418519143772f72632a319ccd1e521c6f354661d0a894bb728e49323e84c3896032e987f3865d14a317b1a23e11487ccc7bb9e0c1d0092d84b849cb732df32f62480cada8e555341b127617da87f802c25926a82d982b094129d4532529a4bc3d3041a6adfa9aa2a86b147a0ae253a728f4870aabdbde73be2ebcf9d1d374c68f36177012ab5b25bf710f39ca6586a7820e69061b4edd2f37f3512df2fd0364290e07d284baf84423dee7bb7db241d127bf172c99560de1196f860026b9f622347f6f1780de232248bbea8a441f3a081b59f4a490072f19b8febc0d75069c9e60980a1d2a300fce1c1b6c78351cc09d9af4b57e9ed47737e9d51a403fc19475065ffe76a8755cf60fcd124d941fedc482bfc3536c58406b72d69513a9c993caf05f683f7975857cff2af0eb731597242f114d544bedf1871244a072f0b955077f102220aaec4ece8e0ac7ea5b7ad5452c054b9bbbf1256d5bb9709ce00f63a7df49a26ab355d689df1e6c06633a74522f4a6d5308bac8a87f4791bf663659d880c978db38b05040b56d9d6c5f810391f25ad157dbf7552cb542cc89c2da49ca2d735e91c06475f894f4619863eabd4fc75b285d8f03501b7a6c49148b72ff152670f14940ad01923b62899298ae44082db5559861e6a95fc159634c0688f68ae09df5afa2ee7ad6506ec9b0e428f0764798c2ee49a5eb6a1728b64e460827f3d648101d3c2e75d8fc02cf50918b85c71c564b59dd6a51749dfd317ff1ae8d27925df0828cf56ba3e2416a6a11b041ed78b031cb8af85cc2994e24cbbbe015549d4453faaaeaadba9a1f81641dcbb0cc90b602b1fba3ede41781ba00c30fc5759110ab400d022dc3e9c59072bed45649ca704fc25607eedfde1314fe3f3025f7e1a6e7bb9f87943749b1864592201ca26487572253cfc0d2840745bfdc54fd1705111e06fb29b3e63cea6f90f962335945cd217fe72a004c66e27852be515b6f5a417ee6880fbff188d1f4b5e4ec16d29727444fa884a0599bcb4e31821275a75b0080857f275fc0cda849b8819b3d3662974bd0248cb2f5dbb89a3328f9e9807cc836058d018827c380e9e1dba15a25edbe077cafc345ed6ade7fe215a7036b33e8f3c51d51d2b866204ab6bf7d79a586efea08f86aa0b5f7a584cf7ae3d8c2280d907c123aca43cd559e3bf6f5fe36754318b6aa7e33ab5ce90f43722118582a778474dec978e0f275605d2cf350bb48504368cb2c2094b7fb941f92a7497367f308937f15abb8213a9b43044d0088679880982b10448c651f270b84a0f58f8721d9268d2dc2079e569a48ec27390032287c003760eb30d18a4b9f6d0ce4172c201bb5559122608f9124211f2bd40542f5b8fbc70ef88ca3945558030b84cca8d99b81187abe7d1593ca45c11607eec2460031c0901f6156929f53c1b4391e24024be06ca0d85dfe60df55ccd3b2e4627f882fc3eb50de259710a48be91f83afdb1b21885833db92e2a8f151ddc2dea5af3ebcc8255dcac14e7c15e19d748641448aba6969cb218db6d10c1867bcf4f4faab66bcb26f5b6d0f99fff2dcb695d29d73e00e58c06f4bcec09a45bac2d2347f9822e08dfa36e5bcb0d5443b69cb3acf4b1cf4142989e75ab12703699510fb47173897c7c2cb00ca456a17478ab2d18104267ba01cfc4486a5e00e3f4fd513f649989b50b9fac2a50429aeedfd606b3d88a491cb263d55ad8755f449ff360f84a52780c3d9dbe2ab067a6212fa7b410d0ca38899a77fc51321d850605c8e06642f5b752f168d548cbee3252d9856ad342d5bf10215e9f3bb4539816c3dc5a546324e228fa535237a9c723b2affbb3dffbcc1f0f3f649036e1d3f95e0ef0f7b3827bb554dadf68fc6b1d3eac298dc139fc7da4aebadbb56fbbbf6c4e47ad36c36cdf1c78b673e40fe1e19bd631ef24e15379d5b1f5b66e2061761f806a0cc8982f79a1396eb3e06782384ebc852b647c7b71c15b7e6a156ac0032499c399b131ebf9fc4624bcfeb903176f64007291ebd408979016327a8529d1f7cea27e54c669f83775276bd003566b7fb170185a904bb0cf0929678a2111e5d18b824c9178fb83174fb35718e1a7f5c7463867bb9754b55f862cd3b9e41b550b062a199b5316c0268cfa3b066402db89906143c06b0fde74466e53ada95be048878d126969f9ef50976b35f55a27531a654b695bf00b969d73a923477f260eb7e31821452f31d42e7e246f65924c45e6558ca0aba67ddd18debeaee48f838f059aa6b9ba664c978bba5dfa130b13fb2ef5cf9d7a3e4117022e21f2686830583612d9b912d2efaa2ff29ac9acb45289d091c8a38952e78af4690511f2d807f571e3d79b0363942769926c840a9eaf6c1d78811dd0bb9fb973145cb36e3f827e4461a396136be978e0fd80ba4d9c8d5d8d3598b36f509c691d0b84cb7c3d1a0c998ab3e4f119143f52aa3b169f914957e173617a0b332e8625a781cd94438c4c47bb6e1a004ec0604a99459d9283d0b8b809ece8a07a1b3d5b5b12f53df055bce84d7828d2c2e498f842e65632ad4f354a5957333bf68bcea3af440609402c275ff7d993eb1254c9dacb7d07af9b211d695258cba2eeea7c76043d9e697e3de87cffec138a566f52fe779ff67796a2c3dd4230fbeb1ba300aee40c9eee8c0894c45051fed1c2f7c419b219bd845df48582733c4e818811b26baf6d133ed12bae7dea0f3bbd23c599d62efac4d175c774068586459dc625e5176b2ee74680ba07f1d7cd11d7cd231e19232c0f73a26a97153c2c2b76b24a4f4b33816511878bc4c4522fb65aa8b37b7e70ed564662e4687b20ce192926fae3d01365cf21f29589990d0adcc64566ff2928f021b170399b76e8cf384d44024d4a8d982d37f534e0bb15dc77f84fa22ce02aa40eca6c4e27c6edbfcfb39547c53905bda30c95774c287534846373fd396d0961489601360f393f37b2be3c837ad046e0c217aaffac41442d2a79f77111bd968ad937d834d6c59735b527a67d8cb700585bdb5ab1da6ed784aab0a1fdcc5dbdb0c351310502cb902006761f98fa1ebc139791dd40b3766a5650f18938b0c87e590c4b7b460acd0c966cef59cc63c39cc3adfa42b67e6abed944a6b63070058c1191859f9de4ee05d8722aa0b1bddd8418508eb7d62cf9964114b20272c1230e1595081af4b5337fe83977faa35f5c1e9b8c5eec369cc9c3502aaacda1ae6cf78f0440fcef9e2a9af1068e985d8d03669ccd78454c51d0da23e6882e7ed6b67a3205ea90240001f896599b17088821ea16fb904cb49f4b4096773713ae7943e8106d59cff23294207d7af9803e2792b5d9661cc4952a227a5f7b6d471275c7c44d7f288360f0f79c6ed3894b0c7171ad2769c8c09c72e64ddfec355cef10d45f64fcbd1f3b874aeb2f8e4372ec2f80ecbca38d46c8b78345c3472a2c6af14a89c65e9b8270c66332659491dcd4df2ef4255a417b2804c1659ace85d558a439a6d463ee9f73aa75f3ea97ace4e76af0fc9c0c309c2710007b06aa04cea65cb3792e6400821c7feea655c5061002c027fdc405acc396306627c97041abb1dbf46613ea28daa3898f466f02b2865b9f952d62f5c216250a0cbdd04950347187854a273b03f985e85709c95875f4d07c9c1b195285adc7b3342c9d45cbdec27e11c368909cc74dcca9eda8126d92245ea74ed8e3ac8837354e85779f8b01bb03f2f83b4a8cbe437a9164a95f8c9c4cb316e8790dc22536b818381f2087cd6e7052d8bb447b1c98d7cf100c67f9bca8c83d156116e7710296b72f531408e59816c22d6048409b1fa8c9d6227d24d31b7ed3fa792988f1dce1259d721a5926fafd81578b4376fcb7476ba565db4e685a2d6be116b07d5b44ea9981a5e580c55d3e07202619ceae9daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd50026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
+        String expectedSig2 = "00000001e40f5b447267c994bdc775ce6d98b0e559cba26c4261d70927adf1bb11b3336858fe064267d0514a6d971b69939256b8ce8b3d8ba0d796413f561c6825e720e6c0c15632b943606aad0b427ca04a6808f4e783807111ec1baaded34ef7ec2cf40ba763a4555ae307171ad202f421548e7361dd06b9145e6fbdd34eace57be4829117e9c99bf3bd00dd4e0591c74d4e488e90623c6a4cabd5bc903252eb81eefe8b52b2d9b3df37bcbeb5bcff64203211d1cf9e1bd3250cdcaa429c2ddf2eb1bc7e7d366be2dc0dd774f99937859c40fe04a98ba532744ead3c6199ffe6f740399602658f65bbf67010e99fc57f841b91bc29ccf4b6bed49747b459b9835e4fef8a95cfd67eef5110d71e386a557562cde703369a4380718d957ea7c1377c7230800918136d3e9755bf99941e5e857a4d66478d81a89683de426bb466f9b660a6dd9e4dc28bb25e0afefd311c68355753be9f3c12f85458eede1cf39442c8d51a4f9297a91864cd98ee8ce77584174e594072df3c776dfddaf36cb1c14161965b516230db575b7e2a7306c135a61d6a4e9c8d297bac90828cd6cb55389ebbb5610d52415e7c8cccc5510dfcf4cecfff5104b75c57c20f3d3b2503f1d78e2574003902d66683c906542a5b0243cc29ab6f62535db4478dfb2dd2f30cead0ea0a579d594242f8b1d612444f126c26d878f8c0081df90971be86d6a7f29c4e3de77f7130fd2126038c5816d5d90055f85375ad8874e02e485067a791d880dc0f41e05eae9309da935d9fccbabd6141c4f8c1e393c7fb7f7f94976515a1b9a0e3b141df3ffda079a8866843425c2929550dac4f59e1ec47a4a0b25b651f6613ba08e31aff45a365e5d77c8b7bd8b88bb10fc4ca0b2d236cf36ccfe9fc95fecfd62795091676bcdc39381ffa6b53c067b666264eb738b3848969b28c3c8384212d679d164b07af95f3863445ce4ec9408f1652182396b5dc3261aaae84f6eb6e552899b0c3d8906004683326b64397220dd556c91d30e43b5ef2045d23b99faa517bf8972d6eb4afa48da48543b37bc4e2dbfdb12f871492f202c206aff2a607cb9c7e0fc580123f540525a0acafd4962bf48e3705d6800c0732944a1e71d8ed2c884b512fa1e9ff0907397681dedaed90b71eedf9d8369f751dcfe49a1cd376461e8ec3f9589f0fd59ad202d8dcbc21018811993e41cd5c3b43054175b2e4bd6b09cf27197a26e381f7f8656537ce1113085dfc90e8b97629a3785fb6ed85f442b39640a0525c3094b55416fa73898a228816461db31fc370897f0070aa42e35b21e1984feed7177691236854c8e2d1bfad5055e0e02acf04a343a6fb1b2e93b9cf002b55ac1092c2ad8bc7d54ec001aaf4473f44c6f3cacb435612f3c6b683fdac02e32e11ce1e7d5587bb5abf2c8d3ff5400123eff17098460f563b6ad8b01df3593bc35bf12ead873db365676af907174eba5f174da46690cde0c16eba96edefc3a49be70cee7dfe019bfd48965ea9b914b9d4a1f36c10301b37193216687b86847b9d72a9dba2a119a18d707ef64b1116b9cce49db2c4b949efe4c641e9d3ab4da9b66d0e3728890247d68b68fa4fccaee33225857d7fe860d4df1e3772fb0e2856a35538bfcdeba4c1b12ac51a508bac14f583207f9add69051faa5a079419b555be96aeccceef73e0a8d8c12e1afd7b7735b63aecd119a3f20fdd13687f8d5dd20f32d0047497b8fefa71884eceac49d37085a04a662083d001855cb3180b8e156df84f3813af16ca0d09d455e7262f19c5ac02eca2042098fc686990fcab671d536be0084446658f45bf308f44de4b9e68f1a0b7d0e116e9dc0933b320e47b99072d422c3c2b5bff63c65ff62051221d06d43d44aab22b68f436efbd4eee342d0e96919f89f4a8931cf88bcd3d431ef59da0bf1ef68a3b63f675093c4c7c4c421d4ff54ea6b8bc31b3e69cc05df0d0fb378a54e1e90645db9e35590b59549463d3a338f81ab6a32f52ab7e97fa1c3b808198a388a19e624214462524274009d5d84d5beb6e827f9b04a5ae5d144128d6d84f0212ffad5099d97f16d5ea51907fbedd76daec03cf1182f517be0df13df722801b217a909f90e76596b142c5c7d35ee085f1eaba2fb8c0b1db480f6264141dd3fb5b18cc4033f23d9867304f2e988a064da34cfb92aff8884df44a15a9c5303bc2ed00f9ca55c9abe751a43d7719c1d1b1529ecb198754b3d4a003ec37f11d0c1c76d41efd36c64c13f215fd86e8fd02b1bc30e9d2eed8d6a88b859fdeb4274bdc60d99eb0f8f9a2e380032a83a9db84bf1befe38b4922076cc2cfc83d9690e3b5fcb27456f142b11093dd7bdb7f1d51219ae0527c3d7aa990755877a9071d65f69c0b04753a9f3b3b00aa381ff38adfb9b98cc573ed3984e7f6dc9e629c6f2ec57885f80d816cff0d8aa4027bf132448cda4a5d0e1cffae3e88d0900d1f2dc4fede24d77168c9218c80b87f1381f4b2d84c5c7f2a4729256a64d8baf4ac4de0bd395f47654b029563c6066b59ad42ea5bd80edee2a06a62b04c0930442063fd443ebed52662325e0d610870e9ecb571edcaa992afc6bba6b7059c8d3cc0ff4f22109cc36bd33a3a53efeb16c377bfb5c42b2d60218c67fa22b84ec7fb8d44dafb86fdd7da314d357a9772365aa1deb68c786d818ff73d342bff6e5d138819f4cf0563a90c698a56925b6da78c9d07cb4d4a3f4d6b78f1efac2659d387d49cbc69d803e1acdf9155218afdae393d50d6346383a34cd286b68f5e8bdc6d8b99a1d1d7d2702906e2aa9a9a04d7abd96d129c99be31656eea08e8c3347bef29e2b44dff01358f72c949ca56f680a90b73e3c5597f8390f87fa2858a037845ee7764f56f3310cc2796168ea43a7db267e4ee36bb8a11bd3c771337941deef7676ec15ff3314a29b4ad0a679ed4bf90264401a71fdfdeada3666604fb05958fd46be35c63f1729efbd18ee60a3173ef7a709f6345986b77dd9f9b719f512270399206834ada721bea6fa1ae82c44874680b18ff57000619c7f09213e7e52bf33be4150b09cde207aaa6b47a772e077ef104e684561dabf41f74fcc864cd4e2296aa08297cd8fb99918a7b2ae59d3798d5ef19a3a98f416ecd01f42a58f8ec4d17c794cbc635ac38043159871d2699bff14697512763e9c692f6f45f07067dda04d494b47cc0f7fc62351726f83a5ea99a37505a7947d5a3306279f99599dd66255a06dd6f49728584ca036cf1952a9b469ea4549e762c077b6ff0a25a3b6dc5fe4cc67b254a1552e80471a4d2a38e992cc09a9deb211907a1d68b118c6f1430c18982810b080560e2c6e76c17e010b2b6c7f3e329707c3bfb2cc83249923483e856bd880c6133d23cfd7e5c2beb1a15a2d88ecb39539b645572c1dc9db4e2579c8a9caeb96f03ff91ed41ae668d6c8c16bf6c9167f8cdd3c0e9a5215965f6cfdbddfe7cd00b4ca702155878f9aa9141b72ee3305a48b8af6b1da38e639906c35de849233fa171c021bd15c4aa0df67ed2a519792c9e420d3a543150747db0b19b2935df78a47aff348d3796b9261baa850a8aafb7a215bb578a17939f44eb89f1cbd9dbbfcdfbd0a519a471578f7582408768d878bcc83d31f7b596bff1cabf4fe2bca47cdba816a1f3ec155686487bebc37c93a3944a497214cf3caf4b910b1a7452cc6781972422b73abca8e0571e7c5ade4effb63daf60e5f44dc1837f24ab5e1af86d025b6ffb5f6236cb7fd02520ce834d7f31d39e4cbf7d7d17b583941bf6a0bb5099d7033bba1fa6f5cf0ef47b4a5ebc2e8f66922d76a9be0bf2faa2ac172707f8a19bfeeadbe4f4c811ae8ce86bc636028f55a38013c4e39315be4a9e260cedb043122a1a4da3c6f50676ded55216fabd0dbaa659a6ac47e48e784bc2e4f7b3148739d6384bd525da874ec4c4c2d8d4e62b60be4f47f20f40eadaf54accbb106acbca466701e84db4be82be84c8c5736dace8a190519d9bdd5b71604934371cea43eafc76d833e3ce10819d882b4ee33718e74bdf95a0fc040c519ae5f2f47df4dde3996bedf64e401f85a3a01d3f51bef6c076181069e40af6c06c3a16c444322537e400a1b0ec9abcf65a5e7b39938e61e05ebbf79b6e6c100d5a30f23d446fcef89a4037cf459fcbe81e1d4e73cec2c32ef08e173d98766d10dedbae881f6d4efa8ec3fb8920ca77082aebe94851fc518ad791ae27d547fc6709b6e659a0e15e48914dd7f5950533db954c90db131b0d5ce3811fb3b5b6d41670fe450b2bcf99cde54412ad3d77c1585ec32c85d72a47817c63dcbd1ecf2c0f6bee6ec67bf82bf01a63da499df995eef91703c55bc950bf34369c929da4a5a6f4d4b38484b2ed9600927570c9fb4fc19da7f3b72c0a6479685220dbc332dd0cb37d9c9e8d8ad3df586efe65b9cacaf0aee6f27b045e40c22a305964365f4096f4cb28014560aaf0228edc507a62f59c815e17a774b63d57836c1d4b55cadc2d267a3419805372e3ab917f1ee1e8176d0d730361cb62cd89d8d6024d8fb007dccd58c6b2a15747634c705bb1f0701155c6ba5ffaac18954f74abfd27bd157d60e2e403ecd7a9a92da9c536dfb06f7a722441b5f6feb97a770da168947a88c7aad3cfa9f4c18727e6090646d4f9b18fb0534e0d5aa626ab73c5f996a15704503a9c5c6a2d28b683d49f777957120b73a99ebf040a20c1ff04a3dac545595a7f2d14e3f3d5cbd432dc6c1a8495ac16f94bce458caa2aae08cc44118be7275ee894403eb074030aba8f4e886d7011bc74b03596196906c6e0a8c3ef73c2ed6d92e71598a99e2b4b2b7e60ca462933e3decdc9461535680f65071c17d329ae91c6bef8471b12af776d1d273c5f3d39c9b23855389a643a8a9f0127f6b581e15fb5aa29fe8d60b75a2a65e219c003ae60c614c8c488693d5e95f89a5884c6b881efceb8bf666f177b5df88c606fd45ab193e0b273850b7a52daab18a692c1d016877fece51b9d73abda8471672c6fd603de198d8f9e7185789272ef7225a196a025a21a3210ec775b2ea3068840a34eab6f7d35c8b0e25c8eec6da99957c0e25b171b8e518471ef63fb0e96f22d1ed66efee3ea7f1911f2cd4384d01cb57f538030b640486cfdce7f9bf0881c8cb11055ee05eb2e9fc2d7b167e7c2fda3c80533a3e0b353eec3cc5cd90ac744606f9b2009573d00b73e11f083ed00f3a6a4aeaa456abf6085da1f95ceb6b3dc84f2660038e728c9c4b94d1b57f5194d0bade8a33367619ae8bffe710b3d422b578320d5115192a922ddd0a1f48d63477014b5bebfc948b2a2b59ee893524e060a7e816ceb664f5cdf7759cbe95714072f357e0079af966e402eebfb1a8e1c090f9ad2ab0f883c3083205f597f72d723ca8b9c30833b373e27052eb2e08377cdcb49350aa13b16b35dbb7194eae6501c9b2dd71ad873a51292a55eeab15d9850dae731aafd19cbe7837be83633fa312d7bf0e7c212b774e6be6b664b0777f01d557cffa41c23ea7737b552994096dd6f06843aceeb3c3a17f637cd5865641d90c0c6d1d5b439689a19f5182885496f553abb9b4123a956ca0dcddf5257242246636c8ea73d0cfe5db1d9fc759e723d0071cbda5b6ac483ea5f156a365b5b82e0a3273ee4293e1c230a29b5d06f9ae83f4429492ac5b764be6f209ba17d0ae0d6de2b1cb9b72bfde885ada2a91818ebba779af1d4ee4a4edfa0399c5d6c418bd702f1ef0588d5bb3653b3d7eb8786cc90b8d6ef6ab9b7a4898dc6681d4f000708a558ef157149e3fcf5f197d6ccf2599a97dbc066103f491ed424bd11c684b4e9ff262e4096a252785490536b676da12617ce12107f64a8b988c3c8d65ece1ea921d5f8c655b91e78f8e2f9f19969f31f5b806cb9898ee53ee4e9976f2b2a6730f8a77fcc4f4eeeb4f780de1019b2ea5862f7d53e35a06b45466cb8b40b68cbee1087b35415f023f86956454b053213e368565befceb5f0745fc8178dba8933ff8862c35f286447c8147a3d63ca4bbecfc873a2b49788090579b66c2ee25cbc4208ac0c1313188b37ff9a3f5c53103a79d8b3b92008044f6769dd45b4f05909d096638e38794f3a1b54b4aefd381e1b9173366065731c581c44fb970cb16f42ce1d6c0612d1e88fb3b444c58e25559de7f632ef95a9baebcc5e7ea0d7ff013ab5ac55789749eb9f250845f9f258115fb3ffe9b22cd13fcca1bd837cab33081fb3f2866ed2b0cd3bf3777054d1f61f7d070894eda7f59abaf7f0a01cb69bd765eb4a153488bf3e6c5b0f4e47dcedfb40affbac56ee8291c5cf339d8bce560312bda813aa4b48add4c9235b42541a7a723643a0d7ad7a1b680ad589c89b77571d40ebb8c05f6250355bb169e0bf3ce4a64689401be7ff53796e415c1c3264c7c85fc13aa40f35077d95d05098d9c962061dd6a2afab39f5145fe69a0923b82da0e04615639d74a2235715a7e51cae966bf4ecc94d27293cdc1eb9b8b49488a436a24ce0d8523741b52bc71799b5264612dfc0150fad48a7699d95ea382a9b0a61855c9e0ea688000a325ceae92dc744f01c034fd347ce1087a7bc518926810302b0d186babae019d8e1693a2a097ed1a291646e1a85c03c94c932eceb06085a2930c71b9063637650bf7e09ad7fbb5f5e7e429a273fd4f1a7c3d288a8d53330f2d3f0f7c9100f36cf2aa72a68226cc758f05c5643643ba33df59077a63556861bf1f491ac58c481f430a0d7984db2de3726ead1e97476476321bae08c0d99ab6bbafe7e31203f675b779a6007fbd98453715c6b64317e2d8d01e23e25afa164bae43a732508780f38236ce25e8a31121e650a93e9205f832437343901926d5121c633ff078388f21a9e59df4ac67aff27a660cf2fd407bdffcaddbb90a78652e5419069e104632299cb04bc8b9fdc1277cde95e70a9fdcedab5f41042b79e21f8362de123a813f5891c48d7281e2f7808db16c05a47c9fc4cd170f094e6502b9405aebaed273ac1ad5a5936fbb9796c9f6901846d1962da6e1a4c83ac5773ee2e6974fb97149467c644a50c7253320a9fcc3b790ce135cf130d6a9577f7b8749eb7ed892018b3b899e4be66a56fef8062c79cd169738589c744de81550381b3d6744155a793552a8d66cdf364fbc76eb55f47228a81052b7a7762f5727053196440a93ffdce13dfaed06e4977fa17817758d527d9de3baac9023c7cd1d33177868445ad9c5c809bd1fe64a2cb7754e0fc83731f3dfdc2b86ea248ae5da1a89bead60f64147a7062fdf6b5decd6234d19fabb23915a4e6b473b1fee95b68e5f74a1d3830cd3875859b4fa755a4fd9119f16dcec0f381e676cfd5a26b35c82ddcd300acd05a169b703e6cce39548de36c9eb26e1c888944164957387d0fd5fd4205f20785663858b8072b1e41044bbf84838829d5867a00490b9e547e3b7562f2cb1b866e1f032a09110166e31c522ab21eab4357012e74938c4b5c21a1a4dfd32f8c8dba7c7259087b8f19d71f94fe0476176f364d8bfa135c737253454ff96e38b628776b8fc10e05badcdf731d23e948bad72df75c3cbf14c38d745a25cc81e635e3dd46da05226a263c3000beea6dc6430085b622e90a59fc19835c3b74ecc26273620756684ceb37c2204051170adc21d0485c3f5e2356ae7562f02611fa57ebdeb0b632b96d0ad2ec503fd1c06a227b3705fb906dbc059ae84814dd61c77d5a0e0b96e3f48a0e1ad7f1f2ed564d50f683dc89bbedf99ec0335d51cee3806b12ece600e13bdadd445a2bf7f28db90052a523c28feac941ee550bfc6246a5a85dc2c7bad637e0f729b37bc73471dbae1b643e5d87e259131020605ab502b9ef0c17cbb657c53f069823efc4217fffb3d691e8873299d8266721d51c758b4078598c9c1d11d2685370d155ec5ef84b6a5d201675861ceaf658d4109f92a9e72fd83253082e1a5380600c8c36ea35457d8785e1185083dac9dcf8db9597d3a08d8dbb2611ff62d6991a8bef98e666ffcb46266fbcb8ee19f69dc369cb0b03abf5dc43e395391d7e52041bc3bb0495b3121c3a4a76f03c35d48ddbd7305bfba80e75a765d43ef16da9cc8faf3ef743c794466c52fb90ac730b8b2ada23faa3a55eded6793231f7b2518c69a37387b84b19ffa982c705eac7d6ecc0e57b017291ef6990e3a2f05fe09a314ece9b501d4f222140a2faa25261ef1aed3f31bc51c3673af200c1b3c8c858f0855d9bdfe9ea95e4443525991bbd50dc52460d0c10f8562bc2cd5fe0308f745b0bceb6fcabbc1081a1d14e935c03dfc9817e5dc5107abe901bb975f07b3818d495a1b38ddb4c47aa0a9dff86ba18d34044e6594daf49d792fff17da9f721084f8dbfce2614317fc7834ae807ecab516e7da751489558b3a5c649380854db75b657d5196e430fa64d6259875ead90cebe3120ed3a98447a45556a73ebc2f508e3b3b56cb2e5eb5fceefc1dc84ad1867567698266b42de80f67fe1f0f82d40e2d48d4779b26594cc8048cca2a209c88718a33555c38f26892455a3c808b3e3a6ad49ccfa23a609460db1f7928fe6f330626a662cdeddb0a14b643600f0db1f67d88d604ba544dee6c9ec18f23c066a0ecd51e1a78d167d3ec360effa37c661b40d911e13ab1454541851d0047a4e6074d3f81b87d3449b413d86e323c01aa5ec67683733f05e421dee3f6ee0692115f74f21094860d2b3f6ee9eb7a74014e852bacad1ed98fde6834a1230b8f16ce28c0232ff1df788f91ca8f0bdc53307b8a46eb4022c038f410214d09c68888081a6f08431827e03a2a5456ab6361098df0ef106c9112f18c917f97836725d01388d06e0637cba404ebdcfb45320a2ab10499b4c8d512882fc5caa5ce0d15982edaa1d03fb872e8b61b833d3f02bbdb3c894bfbd7a9910ce11eb95da485a88dafe60076c15458aa25ef3fda187a1364860dd207c0709767b344c8895d06b66f88a37db7dff8d42d6bc2acf1ea5f1ce6f9e9a273f0c84e0e8dcc9cb0f4af2d1968ef88961b80865bda7db91ec44d7797e6e62be3c77f6f5945ca04eb9f0154d016d5e4bdeb2b0f9c2f814920e10c7588eab1c859117ca0718be186cdaf7b7c2e949fe4818300a188f59e45d77a51efb0a9d89a9f99a5bf7e40d591d85b4b65298e4f223f00e9e4d6045d0e3a10c620859bdb4d5500344be5d2e9fbde2f01bd899b2c5932a85093aebd734eb20ca16f6efcd0ef26e453a3684db3643eb1631061dcca69cf525f117300135f0a4820651322c6ab72465f4fb114a50e990e3743e71df915c9b4435cdc4bc1d63ffc942132cd1cf8cc80aae9099e609dbb18f9e1c60c178e864a5bea1040b4e95fee935285ac7930c52a71c427278eca43f2120bb0fe2ee7972aacdc6d3002c8aee37aae7b8379c290b2fe4fb1c3eb7b9494c4dc55cdfbf4b77d217ac6d72c00acb01822c22fd8ac972fff63fef88edc638f8de8185beb7edb10afec1f344c4a46ab59408fd1bf46da0cadf7fab69d70da53b08d883312027d867dc708a2d60a9dd210f2ac53ea5288bbdd33737a9d1f34bc8f48aedad9f5792bda6dbe2da86c2ad54b0c78e0e9d1a97b612f4067a623005089352501cc4e6237fb5352c418d5c36f5ea1299df8053610ff37c6de68262744323d77e31f1551b86c04178c96d4e662d6ec3421a09661033ad4462a23550cee84e70e3019278d81410ff0098ee5604bfc32afdb688da229baac6fda3ae620b27602af50f1b34d0b41f71c1f441a5b6ec067503b0a8ae2f0e33d0b45d647252552cedeb9888ddab3c4d45c1a4a021afe1ec1c4ab0ff0fb5dbba385a9415e8cb5fc9927bfdebd93de14c4971e5e88dd493736125cf657e859c0a3df6c138e91014ddf556dae5333797a9b189c6f9a447c93f7b2d99504a3fcffb84a5fa577e2b13e678f08f072f3eaace5499e246b4c823efea98ec52517072870dcd3f35512dfdd37981fbba8e7429bad74a918eb179a8c4f8757423919371e7cd978eaf1ca264c2b4cecf629bb6c9e5d7b498e3808029e002a1cc4cd8001d69ee2598643b25be14a898121927afa0d239e4321dae55b4d9b1ba53d2b90b44e1710db107b74cb314d34ef33789727a2026d04b14bb017a23cb7c8300fee87d5a967a5a00644c1d52fa17c4c3cd605cfe96f01ba40ffa7a52d4885a97929e2d7298fac9cde983af32322b6afc19755d04932a024a45622b671980109fe3034ae9b815aecc5e328d6d57c5c179553924329b21243b4196a241b7877ed86b388f7a8906f242f9bc8e9819ef75c712f48089701554031351ad55a5c2628b398fefe40c8003ad728c5bb8f6f64e667843bc3b928e4ee76bcfdffc31e94fe59b7cffd1e386b3001fe6471d8841278e912ba4e0d7c32aa4ef338977963f2c14459fa23715871c55f2ac2a22cc9e7099cbc29aca43d314104f7463aa006a8a27ed229ea90e213b32c95c0b78040a41dac51b66bfa9a4989ed77b0b2114ceb6df08c176aa04169a011a469fbcd5b6792c3d43aff49cb2e0606f6db136c09d87674e028fd37d56ff9ac55e0f31efa4ccfd93bc0d5341db8a2d1ce5ebd2628b2c676c991321b621ee28726c18bae3a2a5a73a09a5af38b680c37ad71b051d0cd867643e83975670c92676ffd6277fb4d0c33403d06f7f21de22d76a84fe3b607a0250b1f22127ded8756b265f788cbda44e554b002eeca5d3b0622e78f284ce86c76f47f108154b7457baa011f3a3b340a1d0ca277f853f32ad050a78c0ffeb39ecfbd26630c5ec0b039ef7ac5deb63183fea1341b6b6cc0c160bc0420a0dd65035515539c3ec787d324a66ef80a5a9b8c819b7a2bb9ea3a98d14be83dc87cbac5b41ec7d280140a13c89ba6099db301d1e0f584d8c4f29a1f59412421c1f82f44083807c209ea44a68f3e1358b1336857ef1bcb1fc09b1e0fcde3381ad1eda3a5ede702e4b7d51f14b25ad88de858a2e6449eb415a846b0cfb8d0ae2cc177700f81a83730e6e084df7352969e64b2fa410a32993f58903c0990c39b8df17d3785224d468ddf6bbc3d13aceb3d6554d72cae7bd4a5b02cff0201ccfac1b426a4644f1b146f5f8661ceb22713c3cad94725c8a7d02acd5f40e66213fa7563ddef29bee2a99e470b96b0e650ee8ee27b445694e1d1a05e53659b8bcfffdefa9909183cc92a36c9efb86bfcb277596fc50ada7f413c0ce5e7ee648ed923260492b19885bf4eb787d7ecd3510f15c10ffe6cd7d14257628dd90fe41931dd3a8a9f307409896e1b8d876e615602c6d023897706c90a4cfb9ed76c1ddff939fa196fca7ad8d82d9ae8ef71a4c67476435af3d06891c10aafa748b72f80a4ce84a549fbdf8b219517acfd851c62aee3268f03d796274c7c0d74c1d95d3ed880ea35d3a76e1dddce2c87348786649ad518f12f00a5db5263a70d324999be93f6caf941e37fbac08edd3d88aeaabf1508781aeff4cae31b23ced8065a4e86e5ee1252b007ff884e63c338111b5a1131ad69a7a0ce8211a6c31f4cdd744b14f1cf824f78ad4e978b4f43b757ed48310d9d2b88b8944f595b065355468aa0b3ccb058d43c26ee8162893810db55f7683d2ff841d20aab2f49e1640d939638299d7389df6ea2d5ad994861c83b7f82160e0683bfed45d8a9c8dffa5e9b55a418b0952c4b0c582f76a57c5c5674214782d1823ea0470f9a399d7dde6a14d66e44010e3538ef8ec7bb31b06ffd4226b278b710518f1fedab126e5521f30f6a2d0b62e89dadee66dee532d8d41fd905b91468d75cb51fda2b0199e799db357aaf10247f8949e62ca965f1d9600a9f8a76fb66a4ec1e45d4dfb93675f1a87f9d22fa14c642ed19ece41335b220f2ccc9c88deb21eaf05690491db36fb61cb5953ee8dd1dff07269208a98a230dda82e88733fab9509be6639ff9a8a3460bb9555157373a9eb313e007da88181a9745ab291b8a25f0aa2cb7ee40efb4e5eb802df9b4e24509c75be717a10b45fa4404c6fe7b31d23aaebbbda44dcc2cc3928b620aabcc4f771dc7947b59daf5e49fb5b13bee641f639ce0ebbb3286363aa12a41540c225b3f081fda83f1eb5bb065a53b2a8f6c105bafe280bef4d84f73f60da4d1291e925731a4bfd50026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
+        String expectedSig3 = "00000002e562f6fc4309848d94edc37e66c4fa704a494576668583b3325e859eb7c67c9f74ad76223a4a61b8f9d6bf9f9d64b74b6a8f41f64ceb5c9166787bec71f1c55119aa1025f6d9136555740a82467b8618bec24f91cc75ecb4d6dd714908847debf0185505d51b9e64c8dc9276186e7dfc6038ea0c686404401d17b5670a50002722097181934ec58ec3d871bb206f4009857a2bfb810d4005b9dd5b32da5a0df79c8d895a97e163947fc96792b806041dbe480a83b0f6c015967961173ccff05eaa59284736ef2309792c04acf3eed3b9840c45cc03ebc337e9a3c7a853f23f9488c91f27fe7440fba480e6955fc32ed4002aed645a61713b0569bbb40e17b648d67b539fc4ae94498ed6e7a8773492c6cf1d76820fde249b3574b50d9ff1f52e36f87389ee9f821d0a41bd3d224a9d12f0bf9fa54692b477c1b4fe7c88ad0191084cf0bde10db91ed8d3e639503b5a919640f63ce9c7008030a10759ad2f029a54d37c8505af0eb90e8e712e7d0276ddc439873d2c73bd75dddb936c6ab4794858d649d5bb291fe26c43bcec71f3aa11a7eb40cb8d103978cc88b9ef2a26092adaf6568ca4716bae7b7c6225feb2bd53b54685e51fe4158f534b1018d99748a2bdd8ce75361633a8a4d3cbf4c46b0b9352d89fe97797cbbddf6fbb211d845d47e4bae5a9bd752af1a055acf9dc3599f0edb292412bbb327993abb33ccee77af9fad2430d03f9d23dbace7d0081b6646ca141b3c1048967874e9f954c1d5ca23474deaf7188469983a7f80ddbcfa107edac50f032d735f38688e890dfa8944d676147a0f17e069ee4e0ed6c51e5c2311bb72786d4040d0120ee7b303f404625e6161994a34ee9a407fee7011ca90e97b54d0d458c397b8fb2c4dd4f54232bde55ff6ae23e7bce71a7096292de6f0a52993d534d513deb7309227c43cf07804af2f3eccb63e0691ec4dc38c8ed53f58822bb26101bc90437aa1c61538f48a73c4f6d0732e9f8ccee475c3d1bc28ec89328d4628a67ef167e721e09d54fb1ac4cd61bb49c0628cd99b56f8b70283f33f6148b0d14e2cb95774b3663b67261539e2b592d248289ac56ab3536fd34a43b3990dedc0052d01c4213df5995b35b0527d895ecb9a7f36da2ee3ade2181debeff39923f86982e3dae1d67edfe27facfdb8ef36ae5e14d417276a549ce352bc22911147ec791cbddf2a3f4191bf79b36221da26d7114e3c167f6d872a80ee39e5820adc14e41e25cef9300df3b93624a183c57138dc1627dbcca3e250aeb9514288d7bea14bba07f53983967cbbac39046a2b00c85b2764a2c87d52c73c1d7b267055ccd2f382f8f39d3016fb9580009a780112e53f748eda91262684512799c8648ab083dedee56fab62c5dc48172af7098e774ac7e369a6149fdc5349eb62677255f243e2c022e321b19a051d1980a35fff2d0d9cfa43afb01455b4802431abba05b6c47a73358abbb0fe2da5a5d7b70866b83dddd42148d614c049fee0d390cdf742a4f76df41a990d2013eb1c6398f4193f1409eb1ca61d3156200c01a90090e27922dc23927d621aa9cd8aa3c7368fdde0d465fd070c3090e955011fbc010d610701b12fbb230296d5b2894e18e538f566071cd5bc6c85915415be70ad983bc57081434a6d470f8322ee2a357be94d405389289aa406dcc4d0fbf58d04b4347138bda515b309fcc16c0e0428dfc9a61e821690bb059465759240c86c6633e75107f7757d89deb7d6d1774f066e37adf136ef1bd4e243299d2806e9b0104b7dff8d2ec617620170c139264697c26f43370cddf3e7d70e255f70e99db56aba6021ee7cb23ca3dfe0eafc659ba6f3318241485ce190316ce163b1e44cfaedbb9dee53e69418b070b8a910e195aaa8a5c627a1a971843aa970e5d3daa41bf628a5acb932412de7a0f757fefdb4b380650a9336a7313aacd7de945883b3807c2f96e741ffc3d04b8377d9715386084028bb4f598518ce31c64455c58dc9f8b8694109e940b9b78bfdd45dbff61d5bb17713d6774078a853241b43dce88f4dd51c07d43037864b05761f045fb3d1f45d80784334f3895beaa40c43d4edb3bf14229d438f8944276916c7b99312ad58a5e295adef49c4ae1582f4077e5e42759788dfd1bb63e1425725991c8b6bbf22597353e65908bc76be7b4f737d716d294349b4d17aad1a4c523b67fdf7c54a627d0e9d459fb9de783c1012d72ca90f58e18c84c8472bac52ea378a44105018f501ec476dd53632928df294fc7eb83d06b381295cfb2e47071d9b6f1a9bc440999771b9f2a89d06b36647dc4f787696c3f09b216f2a2f37567e39fdaa88c44fd2f1ba92969798d6812f2d43cfc7b15efaa706cd8995368b1d73b86551cca835aa6808f56fe42e34df175d653a619a3825ac15f1bab5e4139f73cd555a7319c8221dd9488b0607e9c73320968872c1ef68117f083fc54d4dc4ad87f4d3e1016db6d70dfdc427df4ec80a04ef264e5273615ba83932cbf0805f6bb31fb0d8293912fac7644cb4d8d17d38809a6bcaf27e0c06ca90d5ae6a28d2bb8e9026ddbd8699f43bef324e1bbf252d1e1d476ad4bdc5ee8966149f93471dae07dd2f46e668afa94838c63c432340e0d34fbe77434c945b620a23fb7559758def29ba646afc7b47f9ac58446afe47125e4a1b21cb9cd0e2a296cdfcc23a2bb113605c414d072ffe32cc74b9a6adb1f1045799d657dcf300ce40dd040bdfca91188d31a847d4a684da65eb9bb4ff9cd60dd99581df275e2267af6b9ff913506e39e563f63cacb06fda15c072d4192966ea623305ce759a72fa2f479cc5e46826c297f531e79b8da89b10ef955043aef04976338a9719cb650ba8ca4410f9436e975fe88adc1e66d6c9d2b60d378df2df934cbea13a5ccff890006279477d25e10e11ef86cabf240535e79566e5251bbcd824aea9a39dcbf450c18cebb3f4bb5a1561b1a1ce792074e6b6fa80ac3662a5b921027ca08f8ac48f0607234b7bdb707e99075e1058e2e168f6e4504d12b9d8ab956335429efadcd2db1582c5de2ab1c9a245f87efd381715e7d290ea984c9502b6d25ad570849a831dfca6d891391da1ca4130f05c610dbfc676e55f3d76f9edaedcdfe6ac6d14b7999234af814b6693a15eaf12d4684c767abef5c41c632059b04225c8484c52f856bc2d92008a924004b4606b097d39ecdff3c15d2135f0fbe30d2e81ad2e1369c282752772760928ed83820b6dda6deff91bb6349784fe45393832ed7a63e9da261525e5d53f2912f94e5a113abd635d69f61085621840bc65e252c51e29b722e4ec9211b739ccaf3d1f529ce90ababb3ade832f25b938d246087234423c06d2b67931c6b3ca9046438a54343d6c2c27d8f1b50e00100373ab18fe012b977c210df35bf469f9a0d1d6479361b5d0c0bcc9b2adb4d1795e3aa8b9bee11221f3ab5b3b09e5e021d991e1eb3e783302852493a15b7c1253cb22e1c09fa6bc8519810a00f5305798b3c2f539d8bac5d268eb3527a8b0411f4c676740540fb0a24915c53f05b4a37a9f30024756d0288e144847a2651ff8bcb40d0c2c189ba914fb25d4f413d4d0ef0bcd2c261639037cdea16394e0c42163b61366e4bcf554c291c252b10b3570e32edbc17ba223b79f323da831c83e37961cbe73cc67072bbbe1ccb009bd0c2ae4547ba9587ad05abcf479f74acbb5c7f78150714e356ec88de5176084506126f5b21b861664cedede54eeef7ae78f8a3a61dbab5438163d0190655044c9a5fc848a0ed70b04c1ef62492aa1763e981b30fd96bc84db050aa3aae1c913a2588544e45fa284424835500b183a4e6e61649f0b5fc1ed255135bdc204b2fbdfd35d1bb7e7f243b113d59c165e10f385569d3727f410b5ee0a25f25d863ea28e04d10cd67fb6b2e4792c4ff3ac8442c0f4c48c8a012d5caed2f4291f6b3f8aa94f27f075ae7118170e61b6e5eece5c088278d06d37e2171e2809f64d1a4762cec78a42e427373f63ee54f93557ddd46d093e4af86223800cc640b872ab0d4f8dcd1d86fa5805db002a86f45c50089dc292081840fc5d7f3e5bd82754452849f777d0ff6baded3ac1e8198471b241aeb2b5477aac62f2f5aa39a30580c2d233324d464499d0666793ee80ad0a03356fe077b1d23a2185b9dd122a62860b5c639de77f5d7b502c6db66a1c110dd596095ebca95c2fa003585ff1b1424732f32b1e209eab65049b6909cf5ca81e2556d0b80f8d227ad734e6a9dc5fbeb5fbcbf77cba4139d9f09e4e939280807b22b947f8edec895f1dc7b770f1c341aebffd4e1e4866e51bdb4867b30f600c6af40b86ccb2368eb8a769a879ecdd3d3bd52fcbd8503ec0f30cd4fd0cb2b51b9af3f80023d5459ab26b485a486e3c26de41898c86525dddb8917e58c374d395e85b299b5313122e95a23ed734a2b3ba6575cd8de8d61e6313c3066ccaac5d38271e93a39583f1fa01bda4a0adc14e6030fdedb5b1ba2bf071a8351dc83e9e492d68804bc4ae8d66ed93f5f8f1ead4d13394c155006f3ebba5f184b3aee9f9a0f5da94c1633ba2eafd679b8466056e41fe844f7f86e37f5bac0b69abcb4e9fd9e04f35eca9bcb68e50f28db177b84df8abb52ed988fb06aea6b95120c9a8f3dabb86ef40c99b1de25058071d0624409d540bc557b2b494ddf39ca018af1898eab2bae7693cf5542e7340e355b974c9e6309d681247cca5f5865225a21368cf41be4b0910e0df5be4f2aeea980d3b46b30bc644e6ac3e2eaaf7f3176ed9fd54b3a9d8729204006e266b35c3db40a56f999ca3a03fd6447ccbedd0ae1096c8b6f9ce462c024d22afdcaf0a3f38f8433dfbc494e98316761579e164d730b3a4cc552270d95ee97c38e02bb072cc16b88058e45ed25aa21fcef5ef55dd44406d712981cc3c670f283f80922a787ac8e1c9237700a3c38daf7a9387b4b778b96147daf7296c9929d188a205b73b8891d1a0c58b2bca842eda3064da08abf2d4e6364c1e7bcf3e35fff0ec195fe729725de5db371b033a8d5fa9a707f722477defc729bfd8c324fb9fb9331b91ebbf1ccc99b509c5c554be57ae6ad24cb5e60eb96a40e9790b8065b33168ba10514c08374d5c7b199b6dd9f0615a7914d59f19e9252648d60ad3f423e963f57fa194d7d5568c94a6a99c2a26ea037ded7e89d98568751568d0feed980ffb6d502b139daf32172af2deeb5a25928d20b12ebf4dc041094e870a9b000a94d6d63c9ab93d77cc9f1cf36b5d5f180c4a7e76bb394b78db19fc3e1d3719bbbcd8a5e377394cd7fe10013f754d5ed862a79d1fe1ec5458d5bb10e9f5310547084b2abc4d5d4f4850f268eac72dc8e3e105af4f63550b9b4d2838f0458f6b9b19da852ecf3c4de6c7b6411f7494e447af1899ef0e414938b152951553935ae512c84af5910af4892e8acdd08fe7a77a58cb601a4156fb583bda1eb00fcb1a6a95cd60f13f6248dfd46232bfb6078949b1ea583e8bf5909f57b7f9b4cdf3bf5dd7ddf1e4dba7b7aa6130da4eca58b0f9a07eebcb4019055caded4e32616b13c6b041d459bfb4c7670abf8100c5e30dd001db88f017f7fde72ed9da6928f0865b49ae73d63f6adf88d4ca7258ce4096b8221be737cfb816302a292209d4d7dd885785f51f6bdacc3ed4f9ced9a760560ed341732cd0b78ca888962d637c5d1f01682898af9ac1e56cbd4ada7c3255b3af91e5ea6b0a22f314595d85d040d0b0cfaada746620367516d410ac8e798ac07a34de6a8b6aeb219e1ecfc01e5ddf1b54cf7d32e43ffbfc34da07a6e1abefdd3dcbe814b3b3b885a5378979176d9c0968f753201746764465389ffe8aafe3a338853f6b1da6545efa803d60ecd2ad310b043e62bcb3c58bc2ac7741cc02c68f4c84e1912a67806f82392fd4264f691aec7b324324ce088e6640a894a9e5ee6e8c0a01260eab1b42bd20e004230a1182da42d6ddd57c0c3fadf31d913509e360e1684de3629f39ba8f9eaf8cf4f4ae50a60cd7585c4f5386a46aae959e2161765a65b3d8faf2abe7fb647d25f5b00a1454663e17899a92f382817787879f4d2a219d2fef19100bfef25b735de8bf8d00ce5449551a91ad0bffb6345dccc0705f9fc549ed47a1955c4ddc9aa06e4e51a165ed0ce02f771fa872a6cd82c49b8084a21e6e3b1d0ac6ca46f79b24ce72522965da32ef96f3e7a17f87282abe14c993e097d9988f016db35bda1120c0cc5317206fb659ed47e4adb17afd03a7b0572bef4d10abb0d2c9c8eff110548fd87c6ce1e46506996c0267b2af1c008c20e85c1a4f59e459ace14ac428572b7d9af57900bc5145be26ebe89f17c737c54f0a353bea5c2b5f5175793f9a34dc7b22b84760f36476be33e5a3bd5db623444c97f0256a84d4524941bf5446fd6f946cec82e8d4e47e2c48032a215dc26a664d5b7b800b6da6d6322560f7fa3ab57058ceaadcfc50e91d1581d6a8aef99325967c3f32e58782dac661c6d1c4402d654007f5bae4f41aa1accc50e378a563ea03b8892187bce7ab606e493b0e2c526398cd290d04605f351d4b927dc7f34d3a5ca8d61946d753421c58aa145931d321ff225d26c5abac68755e08af31cc40476eb4d4de8f4e225de3c48093083916d04bc6890c1bcfd07b47fc0001adb1c62c382a96da673bd7e34aa885bda8c91c19cec4f517475f58b2545c8d34176b64b499cd1daa90d2c9e63327ad5343a79e0b1ce052d72bb6c9382cfde957417a90cecc650366cd2f18217cedcce338287c48d2bcfeed7500a618a1b493aad76a442aed3be226ec7db2ba75e384b6c07799699f7ab2f6e86aa5b0bd24bc87719e700aaf66c4962a7856638e5a15a321dbfbcb0c8c38f7058f0baffe0b99fefa97e5086c3389d166753c0be24429b13786aed835bdc227e3c03e7a7c00bd34c526435ff3d6b6b4804e3f83b888a23e364fdef47ae8e0b736a77d95b1f015666d84cb79bd8b42e4cd5b37efca008e883d9cc0650f8132deec4dba392d6966390510d2fd509a1674406b4d90c0787268840e74507478dea704e8b41b7f78b163d649a27c59c75acb515ef41a8ce0f615c7ab05ea6c583b8f6aa82e837a89872d487e21bdb73dbd242628b331810fbced954e44ca161b0082ec013b019cf90782676d9b8e5b5ec63f922f1fb489a57d593e4f93afc0fa725aaa06feec21b3d81e38f57a30fe473a90efd043957dafa3f121a9f981810689bb4ec24376ae897e8f82654043ce6fa2f908ba69aa663b80b192cfc7822f1f06b655246b764593f8ac8c0d8f8bf7083298ca600a32f6cdcac230db8adf388e241b5fa008a77121ac49561429c94f2bda4177755df4d2a480cb4d7cdaae17304219179c2efafa5183a32fe5c4f7835a59e1212177af2fd6d88b87892d7d8a529e68e530dec3739a185dc56c28f0e8f77b016588c4bd4106f2c445f340c9ec4d8f618f7dc1741be067c5689c79bc0b2f8a73eec36ab8fe4d69a9f12c179cb6554f40ecfb06efe62a37e058d9d7c7a79bfc61c708f71c976443ecd9f89df77f02c64eafb76af0bde669a06436abc66fda04cc6da9a966fe16cf7da7ce6c267c8d74e3b1a9b09db419a0d52ca26b0f3b00e212d2312c0696e3e448d8644441f072ee8bed53861df320792680ff0087d460d524db562e363b8efd818b60f1274c9a7afc6b670eaa94980cc0e8b6ea5bd239e7808f1c214dec43bfb4131e9754ff83c99b954acbfb90c5b1cb7b1a297e28f1beeba26e28d3504dfc6b57e68885e5840c4f4746f280ce6b909fde4ab2ad11077ba15433ab6cfa42de6079de9210ee69097895e12ff41777dc94db76e41b59b88c1864f10be6d8aa80abad8018140a94e1c7a47e2a133f6caa56291caccd21237b122c30da82de2274aacb9ec476113daaae7bcd677a970c3c62b9b8c91cb21c9ab0108cfc4dee362f2346cafc43fcb67a0a37f2af638e1996f1e8d591aeb31c79482102dd0fa0ced6c3421e58bffa219566b735a4baf652df736a74183da30f9863876ff3da528971d67648c01a739bad1e026825b84906e2653a12ac77c24c1cd14369f426135c2029a5d9749be5f47c0d87fb953c2782a475dc406700c2b61fb7ab4faa8a3f23fc23aeb964677a1f7d55a2817b122cc9d120f5c3d751fe7fe950deb989fc13ee7d53d1052d322816b844685f122f7fa321bfb3499802aca9c564d41b39fbfff5ff777873ad1e40c509ac2016eaf08736334650d4bdb4a7c895033a16e1c44acced720fbde04982310639ea4e92bfe065ab16dd25edc4554137eeda0a37bc0a312558a8fc6f2f5c8e1a5b3d81bc75eade5a84b32f81a638fce94181bfd540a4a0b7411bd66fa199da76e10dac8abc04a3738e40ac28c4b6b0935e828b3800bd7d21f5f7ef46ee1cf05f12b6cf335c45fd4093df9d87852653a3717843f3693c240fff8ef9b26b76166ffec4b798490a9015d3ec7740465a0f42ebc2192fe2c5c74a4f428e7c9c86697aa05fb719345dd94a4784dcb9165b0f56f650d8242f89aafab44558a964671660da5aef2debc16172924bdd77cc06981478ac1859cdf0e2a24d5d3911906c7eec01d3a2f7cc6c0f05db395d7518073d7d65fa82269ca90fbed3b207beadc8ffb0547d3e321887fdeb208a9d70d1f6756c8fb7fb86fa05ac33a13c208960951922ded8cacf138b6ceed37245f960a7efddaa5d2111eeebae7de3a8704eac7ede1ca90077cc78d0b1c61bab486db05c7f728cb1a3983eb605c82ca1a36d5d4a90ecb7920862594699b3384b33fbdf22cb89ea98bad56b234cc403aa57dbd140cf0a35900d8a6d7948ef2ee5418db27a7499e37db44303ec8c5dba233c7faf1c73005a96a082c3ea5af948b3fed72386f2a460cbce949cbf9732282667ba64dba04728333f0ad5b6fa63e04b5373b8d8abb993841439aa51ef4ecff6893be4f1540985c3804afcb1d079651c03acd305da29de2b8ca4bd28cb422f11f28f43bbad5174ec7bb6dc7cb5b81afdcbbd5e9a84bda0aba6d06a92323eb34a9774c9ea3c8db37280fe7e2a1b8e9afb7844b30c07024ee283d2032df6cb3e4fe6acd1bc7dc69b75bc1015fc71ea0747a076a16101bfa4da39e647f98f753fd87d79a3fbdea6fe8739f460ca9cd2fda2a1b4596441140d42059c5e3b41e6202c8557009ead726912d8da3b728f4d98aa2cd32cb76cbacde8978f0f374fa66ecef2bbba1509d2c37df52274793a586439560be95b4ec9db4f8488165330fc97851fb2b7bbdea5e2765b2223d80783d675d52c115948fa10652869d91cd947ad9cdb27aea8c78f5ebb9e3ae6864f6fec3721412fa875d991017e3e6f56b7d440ccd1ef04daa1b896d947daea3d194591965396d0d257fe4bfb71c4b5c06a76d23fda40b603aba0557cac78062abdd6fc47647b5dcbb6fd8409ab67645dd40e28db17af59bf65694e7bd71a648461d006021cc1b76c4075cb9bd05b5f61bfa22767c6c967081f2554e046a120cedb0da59136a5d316bebd3401a91041d6d3503209e43746b9a3e5ea2dd2e64a15183f5921710659dc615545bead57d72e6336d393dd9a94c8ebfee9de0e1cb30cbb116e770f0464a2972e7eb2a8fda7dbf9acdb4bf7cb6890bf4dd2afaade4cb50e7d51fe4d95235cabb34eb63ebaff19f242834943945a4c525b4f9d5c2af0536cf3628525ca528ddf0cebf6f751313ecb64113c061f59f5b992d830242f88b9f06d8e9d5c7eb73f6f9991d393beaf70a53a83ec0c31074df4e6c99ab531ff2c2177902ab58c726b6e0ef1c69c43134760cb1b158e8bb0bcc15dc54d2a98999cdb78b33a4fa21121a46915888046e2c9d7dbc3632c4aba535b1522d7b6255d2a03cf1a216987c073e217dd997a989369a4e227d1e2b9f2462ad54cfbf31d591f9a798456f92a3097acd4d20199a4ad3e66a00faa9ce0539ba5547f107a164b6d4cc92088f64cb343651b43e67f0aa6bc0a0256d4a314cdf1906cec1857786cfe3a097ec9f3c7cd46929f1396aa9ac631a7925fe1e81179c43670d720e00ff4d5088ef3bf495c01f7b7c649e8bfa87c69749e6e2a51a99b478ff8ab7007ba5b3ca135df8699c448da129dc6140ce34bb672b05fe307bb1633c96e0dd84ae4dc1caf75b2e2b3180ced90eb79e76c1752b77ac5e3aef2f4bdef3c667df53dcb95ca1e18d6405f4b6310108c23cb2c6bfc0c17d9a9c62baa870e3fe7eff300201178fe00462244bbaadf3607e60380db37e3836035904116697af246e5a7253eaa194902f61246a76bfd41e7531bf4db33ca0a3cf8761780edb67e308c93ea5baeae71b368599f7563658c60381c3523999699e86095989b8ff6275dd20402e5b35b831efe8c4755709284c4ea09c6702b209864e63ade180ab0c7ecdb376c510c55bee53d33577a1ae32d0e6e8d714e9c6c40193ef22e771b521ffb0e246176cb09c30d28e98a439d935fd4d2f1be11548c9147425395e99bb7ee0ac880a43c9ae63ee086728e9db1d57e0324dec05f3be6ed1c4d192cf18e8a6704375ab9632ec065773a206b88eb2d1e36e428e4341ad6e300b1c49f636caafdd28b0546d6748b9211df0273ab27921f3a767efa4e0d38cffbbe2f53e77b7c888c5cd81369602d4d2b9ab3a92f8180306b7e339a18c73cec1a253c319c46e1b71ac8910a11df903cf6c4ff5b392a96b282f849819b47a919ebe9966bff73e202267cffed1deb3c9c9df6e88c4b292b8823e2efc111af20b6c25719a3db8e1451e710717d7412cbc08566d55923784fc0f4a70f903377d73b29033fc61a445fd57e2db1eb9be74334edf13dc5b9bdd9256e158ab3642b1ee3bf8b7e07747abd2114c4d8e45443e14480cd3b6998e6a4dec41290d854fe978bda36f2102506dfc7cb75950409bf174bc268e5e6f9192c9531ba98c0e66ebbb963736866e0f2ad703ee08ef9a5746e6dc76d17bcc192bf380e94a493319621e8adcbeefbe80bd0388f8dc84824471f040dbf72e1d1f068c77a695fc1dc47f18c70ee7e697f0106420e8ec0145b8670d4a3642d776ab7b90be5181a1189457afaf701afa3d4cb7e05219f9c031987f6d4c6cc49d8c5c0e6b67dc5b066f86a74d7e7bfc851af05f23d4fb477d6d4e6242bcf58f1d58a5f0f363e15833488fe98a4306efd560550eb4ee755c229f41d69b9947039ceb254fb64a24792c75be2a29c5058ce0cb89eb86647d657c146360943a29047e3fc3e84936d1d35bbc16885640d3510035eef02a36c582c86fb280292880518873d6bd44b8846bd14573c28ff95c7731bde4d3750dff967d336526773dc809657e589c023a96fb23bf79fd41539195abd3e21f538aba3da6b8b276dc69cb9938e57bfd19ee4ca34141a86fd509644c32dd237bdf26538f52ab287570e35ac0ae86e62dcbdc8a3f1c8ad4ac0a756226439b80ed2f3b5664e9093ba4514ad52da6f1f863b67f4a21ca1863662a266920dd7750dd863f22ede785775b89002daca5c8f00e31c0f9c4b2716481d5830269172e3596907aac87f4e81ef66cfd976e523905534c13fd9f006342d4ee39584f7c196f8c6db1d59693229e4010037302f978b2d8ab5079ea1ef4f431bb9e11ecacdf9f58ebfbc24c50f542911a9b950491bd4a6593aed15177981a94e0723a2dba5dd63fb87cf6d475bfa42d0df7d0fc5b3b1bf9621066c1f32a45061363c3175074fed91377556c63e104186f7609b5a06e4c3902a8404e295929d4d786c15a48be408d8745d9674873c5a7ad4756e41e64188016191e8da12aa4b30238b29e4d784e8950e808931f12424fe4dad6d8cb0bb55e8c0440e0148f04d50b06acd17af3a47854344b9a32a471853b0ded34b056bdc956318cc494f920330d23606698bde3caf30fafe14287fd4b2351bdaacf2cd76b30c90ef07a13dfd40e63f8ad23050b3819d08e6aeca55193b23b0e748b9b358aadb3ed7d1e2affac941f7be7fa35005ccfeb02929320d8db088245cf16f5bf811a90b37f8e30c7ee4c8ff0aa018a109e544f5c51e844152315da766f4c19033796bc9e21e981264f9f118947f1775acf373800c77a063be4473994d511c1e4919047f00e988c8912a8647fdde6c3a8a632026893d67839c20f25b7a3f64f52975be7c25a1877d77478b8b8134c57c6d2e043c6555bb6927142b972c3c70a3a7807f83c5f110fcba676248731ac51078580b1a2f605adb892e9b419f1d9509eb4fb9c5f3fd92e47dae7cb3596e6d04c182834e6ba1d37f139b4f89c95113a77c2c1f802b6ce1624cefca77918047582b694ee5d7da62d97efe090a09b6a3e6723cf09714adfa91bd7dce2f6ec35d53db92d7bc779d260372518d57c973cd07f890c6d9ca234116560c1773a7320aea412b04341c29b32254caed021817a12e9dc00740e76b13aad96dd43544ae873175c05982288134aa8460002515ba8779497ffad9699ad248c11556ec8228e2668845b3e64a2d202b6ca76fc89eaacaa6568ea1b9f0aa6b80ba0876ea58441feba47fc37a0d5a4a0c313bcd5a49de1317eb0498d3f17104c46e0674280be32f5c691b5fd9748e37fc0a00a12022559a411402365c37f74cdf90311cdd4d810929f2f8cfedd690ac1fc52e0d51303e43b070fcf8314537ec4a7d97aa19944a5496e919c72b9bbf0addea5855dc99893e0e6236960ee7cc9d496ddbbb040c9773739ae79d9a5cc95aa119a3210d5e18c7f3d9953050929fe2adedc7df10085c057f95473c6bde947df47a4d17c9a934b95aedf0bff303051428610cd57ed321dea4b0ce12684c0803ad5efacebb7a826a6be3732242d67839f86f99a17dcfe5befd3f903";
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig1), sig1));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig2), sig2));
+        assertEquals(true, Arrays.areEqual(Hex.decode(expectedSig3), sig3));
+    }
+
+    public void testVerifySignatureSHA256()
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] msg1 = new byte[1024];
+
+        for (int i = 0; i < 3; i++)
+        {
+            byte[] publicKey = xmss.exportPublicKey();
+            xmss.sign(msg1);
+            byte[] signature = xmss.sign(msg1);
+            try
+            {
+                assertEquals(true, xmss.verifySignature(msg1, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+            byte[] msg2 = new byte[1024];
+            msg2[0] = 0x01;
+            try
+            {
+                assertEquals(false, xmss.verifySignature(msg2, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+        }
+    }
+
+    public void testVerifySignatureSHA512()
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA512Digest());
+        XMSS xmss = new XMSS(params, new NullPRNG());
+        xmss.generateKeys();
+        byte[] msg1 = new byte[1024];
+
+        for (int i = 0; i < 3; i++)
+        {
+            byte[] publicKey = xmss.exportPublicKey();
+            xmss.sign(msg1);
+            byte[] signature = xmss.sign(msg1);
+            try
+            {
+                assertEquals(true, xmss.verifySignature(msg1, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+            byte[] msg2 = new byte[1024];
+            msg2[0] = 0x01;
+            try
+            {
+                assertEquals(false, xmss.verifySignature(msg2, signature, publicKey));
+            }
+            catch (ParseException ex)
+            {
+                ex.printStackTrace();
+                fail();
+            }
+        }
+    }
+
+    public void testImportStateSHA256()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA256Digest());
+        XMSS xmss1 = new XMSS(params, new NullPRNG());
+        xmss1.generateKeys();
+        byte[] msg1 = new byte[1024];
+        byte[] msg2 = new byte[2048];
+        byte[] msg3 = new byte[3096];
+        Arrays.fill(msg1, (byte)0xaa);
+        Arrays.fill(msg2, (byte)0xbb);
+        Arrays.fill(msg3, (byte)0xcc);
+        byte[] signature1 = xmss1.sign(msg1);
+        byte[] signature2 = xmss1.sign(msg2);
+        byte[] exportedPrivateKey = xmss1.exportPrivateKey();
+        byte[] exportedPublicKey = xmss1.exportPublicKey();
+        byte[] signature3 = xmss1.sign(msg3);
+
+        XMSS xmss2 = new XMSS(params, new NullPRNG());
+
+            xmss2.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] signature4 = xmss2.sign(msg3);
+        assertEquals(true, Arrays.areEqual(signature3, signature4));
+        xmss2.generateKeys();
+        try
+        {
+            assertEquals(true, xmss2.verifySignature(msg1, signature1, exportedPublicKey));
+            assertEquals(true, xmss2.verifySignature(msg2, signature2, exportedPublicKey));
+            assertEquals(true, xmss2.verifySignature(msg3, signature3, exportedPublicKey));
+            assertEquals(false, xmss2.verifySignature(msg1, signature3, exportedPublicKey));
+            assertEquals(false, xmss2.verifySignature(msg2, signature3, exportedPublicKey));
+        }
+        catch (ParseException ex)
+        {
+            ex.printStackTrace();
+            fail();
+        }
+    }
+
+    public void testImportKeysSHA512()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA512Digest());
+        XMSS xmss1 = new XMSS(params, new NullPRNG());
+        xmss1.generateKeys();
+        byte[] msg1 = new byte[1024];
+        byte[] msg2 = new byte[2048];
+        byte[] msg3 = new byte[3096];
+        Arrays.fill(msg1, (byte)0xaa);
+        Arrays.fill(msg2, (byte)0xbb);
+        Arrays.fill(msg3, (byte)0xcc);
+        byte[] signature1 = xmss1.sign(msg1);
+        byte[] signature2 = xmss1.sign(msg2);
+        byte[] exportedPrivateKey = xmss1.exportPrivateKey();
+        byte[] exportedPublicKey = xmss1.exportPublicKey();
+        byte[] signature3 = xmss1.sign(msg3);
+
+        XMSS xmss2 = new XMSS(params, new NullPRNG());
+
+            xmss2.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] signature4 = xmss2.sign(msg3);
+        assertEquals(true, Arrays.areEqual(signature3, signature4));
+        xmss2.generateKeys();
+        try
+        {
+            assertEquals(true, xmss2.verifySignature(msg1, signature1, exportedPublicKey));
+            assertEquals(true, xmss2.verifySignature(msg2, signature2, exportedPublicKey));
+            assertEquals(true, xmss2.verifySignature(msg3, signature3, exportedPublicKey));
+            assertEquals(false, xmss2.verifySignature(msg1, signature3, exportedPublicKey));
+            assertEquals(false, xmss2.verifySignature(msg2, signature3, exportedPublicKey));
+        }
+        catch (ParseException ex)
+        {
+            ex.printStackTrace();
+            fail();
+        }
+    }
+
+    public void testRandom()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA512Digest());
+        XMSS xmss1 = new XMSS(params, new SecureRandom());
+        xmss1.generateKeys();
+        byte[] publicKey = xmss1.exportPublicKey();
+        byte[] message = new byte[1024];
+
+        for (int i = 0; i < 5; i++)
+        {
+            xmss1.sign(message);
+        }
+        byte[] signature = xmss1.sign(message);
+        assertTrue(Arrays.areEqual(publicKey, xmss1.exportPublicKey()));
+        try
+        {
+            xmss1.verifySignature(message, signature, publicKey);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+        assertTrue(Arrays.areEqual(publicKey, xmss1.exportPublicKey()));
+        xmss1.sign(message);
+        byte[] privateKey7 = xmss1.exportPrivateKey();
+        try
+        {
+            xmss1.verifySignature(message, signature, publicKey);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+        assertTrue(Arrays.areEqual(privateKey7, xmss1.exportPrivateKey()));
+        byte[] signature7 = xmss1.sign(message);
+
+        xmss1.importState(privateKey7, publicKey);
+
+        byte[] signature7AfterImport = xmss1.sign(message);
+        assertTrue(Arrays.areEqual(signature7AfterImport, signature7));
+
+        XMSSParameters params2 = new XMSSParameters(4, new SHA512Digest());
+        XMSS xmss2 = new XMSS(params2, new SecureRandom());
+        try
+        {
+            boolean valid = xmss2.verifySignature(message, signature7, publicKey);
+            assertTrue(valid);
+            valid = xmss2.verifySignature(message, signature, publicKey);
+            assertTrue(valid);
+        }
+        catch (ParseException e)
+        {
+            e.printStackTrace();
+            fail();
+        }
+
+        XMSS xmss3 = new XMSS(params, new NullPRNG());
+
+            xmss3.importState(privateKey7, publicKey);
+
+        byte[] signatureAgain = xmss3.sign(message);
+        assertTrue(Arrays.areEqual(signatureAgain, signature7));
+    }
+
+    public void testBDSImport()
+        throws IOException, ClassNotFoundException
+    {
+        XMSSParameters params = new XMSSParameters(4, new SHA256Digest());
+        XMSS xmss = new XMSS(params, new SecureRandom());
+        xmss.generateKeys();
+        byte[] exportedPrivateKey = xmss.exportPrivateKey();
+        byte[] exportedPublicKey = xmss.exportPublicKey();
+
+        xmss.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] sig1 = xmss.sign(new byte[1024]);
+
+        xmss.importState(exportedPrivateKey, exportedPublicKey);
+
+        byte[] sig2 = xmss.sign(new byte[1024]);
+        assertEquals(true, Arrays.areEqual(sig1, sig2));
+        try
+        {
+            xmss.importState(exportedPrivateKey, exportedPublicKey);
+        }
+        catch (Exception ex)
+        {
+        }
+        xmss.sign(new byte[1024]);
+        byte[] sig3 = xmss.sign(new byte[1024]);
+        assertEquals(false, Arrays.areEqual(sig1, sig3));
+        try
+        {
+            xmss.importState(null, exportedPublicKey);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+        try
+        {
+            xmss.importState(exportedPrivateKey, null);
+            fail();
+        }
+        catch (Exception ex)
+        {
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSUtilTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSUtilTest.java
index 9e02432..5e11b19 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSUtilTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/test/XMSSUtilTest.java
@@ -96,54 +96,12 @@
         assertEquals((byte)0x39, b[8]);
     }
 
-    public void testIntToBytesBigEndianOffsetException()
-    {
-        byte[] in = new byte[4];
-        try
-        {
-            XMSSUtil.intToBytesBigEndianOffset(in, 1, 1);
-            fail();
-        }
-        catch (Exception ex)
-        {
-        }
-    }
-
-    public void testIntToBytesBigEndianOffset()
-    {
-        byte[] in = new byte[32];
-        XMSSUtil.intToBytesBigEndianOffset(in, 12345, 5);
-        assertEquals((byte)0x00, in[0]);
-        assertEquals((byte)0x00, in[1]);
-        assertEquals((byte)0x00, in[2]);
-        assertEquals((byte)0x00, in[3]);
-        assertEquals((byte)0x00, in[4]);
-        assertEquals((byte)0x00, in[5]);
-        assertEquals((byte)0x00, in[6]);
-        assertEquals((byte)0x30, in[7]);
-        assertEquals((byte)0x39, in[8]);
-        for (int i = 9; i < in.length; i++)
-        {
-            assertEquals((byte)0x00, in[i]);
-        }
-        in = new byte[32];
-        XMSSUtil.intToBytesBigEndianOffset(in, 12345, 28);
-        for (int i = 0; i < 28; i++)
-        {
-            assertEquals((byte)0x00, in[i]);
-        }
-        assertEquals((byte)0x00, in[28]);
-        assertEquals((byte)0x00, in[29]);
-        assertEquals((byte)0x30, in[30]);
-        assertEquals((byte)0x39, in[31]);
-    }
-
     public void testLongToBytesBigEndianOffsetException()
     {
         try
         {
             byte[] in = new byte[8];
-            XMSSUtil.longToBytesBigEndianOffset(in, 1, 1);
+            Pack.longToBigEndian(1, in, 1);
             fail();
         }
         catch (Exception ex)
@@ -154,7 +112,7 @@
     public void testLongToBytesBigEndianOffset()
     {
         byte[] in = new byte[32];
-        XMSSUtil.longToBytesBigEndianOffset(in, 12345, 5);
+        Pack.longToBigEndian(12345, in, 5);
         assertEquals((byte)0x00, in[0]);
         assertEquals((byte)0x00, in[1]);
         assertEquals((byte)0x00, in[2]);
@@ -173,7 +131,7 @@
             assertEquals((byte)0x00, in[i]);
         }
         in = new byte[32];
-        XMSSUtil.longToBytesBigEndianOffset(in, 12345, 24);
+        Pack.longToBigEndian(12345, in, 24);
         for (int i = 0; i < 24; i++)
         {
             assertEquals((byte)0x00, in[i]);
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java
new file mode 100644
index 0000000..34fdf6c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java
@@ -0,0 +1,165 @@
+package org.bouncycastle.pqc.crypto.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSPrivateKey;
+import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.BDS;
+import org.bouncycastle.pqc.crypto.xmss.BDSStateMap;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Factory for creating private key objects from PKCS8 PrivateKeyInfo objects.
+ */
+public class PrivateKeyFactory
+{
+    /**
+     * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding.
+     * 
+     * @param privateKeyInfoData the PrivateKeyInfo encoding
+     * @return a suitable private key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) throws IOException
+    {
+        return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
+    }
+
+    /**
+     * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding read from a
+     * stream.
+     * 
+     * @param inStr the stream to read the PrivateKeyInfo encoding from
+     * @return a suitable private key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException
+    {
+        return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
+    }
+
+    /**
+     * Create a private key parameter from the passed in PKCS8 PrivateKeyInfo object.
+     * 
+     * @param keyInfo the PrivateKeyInfo object containing the key material
+     * @return a suitable private key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) throws IOException
+    {
+        AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
+        ASN1ObjectIdentifier algOID = algId.getAlgorithm();
+
+        if (algOID.on(BCObjectIdentifiers.qTESLA))
+        {
+            ASN1OctetString qTESLAPriv = ASN1OctetString.getInstance(keyInfo.parsePrivateKey());
+
+            return new QTESLAPrivateKeyParameters(Utils.qTeslaLookupSecurityCategory(keyInfo.getPrivateKeyAlgorithm()), qTESLAPriv.getOctets());
+        }
+        else if (algOID.equals(BCObjectIdentifiers.sphincs256))
+        {
+            return new SPHINCSPrivateKeyParameters(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets(),
+                Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters())));
+        }
+        else if (algOID.equals(BCObjectIdentifiers.newHope))
+        {
+            return new NHPrivateKeyParameters(convert(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets()));
+        }
+        else if (algOID.equals(BCObjectIdentifiers.xmss))
+        {
+            XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();
+
+            XMSSPrivateKey xmssPrivateKey = XMSSPrivateKey.getInstance(keyInfo.parsePrivateKey());
+
+            try
+            {
+                XMSSPrivateKeyParameters.Builder keyBuilder = new XMSSPrivateKeyParameters
+                    .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest)))
+                    .withIndex(xmssPrivateKey.getIndex())
+                    .withSecretKeySeed(xmssPrivateKey.getSecretKeySeed())
+                    .withSecretKeyPRF(xmssPrivateKey.getSecretKeyPRF())
+                    .withPublicSeed(xmssPrivateKey.getPublicSeed())
+                    .withRoot(xmssPrivateKey.getRoot());
+
+                if (xmssPrivateKey.getBdsState() != null)
+                {
+                    BDS bds = (BDS)XMSSUtil.deserialize(xmssPrivateKey.getBdsState(), BDS.class);
+                    keyBuilder.withBDSState(bds.withWOTSDigest(treeDigest));
+                }
+
+                return keyBuilder.build();
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new IOException("ClassNotFoundException processing BDS state: " + e.getMessage());
+            }
+        }
+        else if (algOID.equals(PQCObjectIdentifiers.xmss_mt))
+        {
+            XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();
+
+            try
+            {
+                XMSSPrivateKey xmssMtPrivateKey = XMSSPrivateKey.getInstance(keyInfo.parsePrivateKey());
+
+                XMSSMTPrivateKeyParameters.Builder keyBuilder = new XMSSMTPrivateKeyParameters
+                    .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest)))
+                    .withIndex(xmssMtPrivateKey.getIndex())
+                    .withSecretKeySeed(xmssMtPrivateKey.getSecretKeySeed())
+                    .withSecretKeyPRF(xmssMtPrivateKey.getSecretKeyPRF())
+                    .withPublicSeed(xmssMtPrivateKey.getPublicSeed())
+                    .withRoot(xmssMtPrivateKey.getRoot());
+
+                if (xmssMtPrivateKey.getBdsState() != null)
+                {
+                    BDSStateMap bdsState = (BDSStateMap)XMSSUtil.deserialize(xmssMtPrivateKey.getBdsState(), BDSStateMap.class);
+                    keyBuilder.withBDSState(bdsState.withWOTSDigest(treeDigest));
+                }
+
+                return keyBuilder.build();
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new IOException("ClassNotFoundException processing BDS state: " + e.getMessage());
+            }
+        }
+        else
+        {
+            throw new RuntimeException("algorithm identifier in private key not recognised");
+        }
+    }
+
+    private static short[] convert(byte[] octets)
+    {
+        short[] rv = new short[octets.length / 2];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = Pack.littleEndianToShort(octets, i * 2);
+        }
+
+        return rv;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java
new file mode 100644
index 0000000..41e4b24
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.pqc.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTPrivateKey;
+import org.bouncycastle.pqc.asn1.XMSSPrivateKey;
+import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Factory to create ASN.1 private key info objects from lightweight private keys.
+ */
+public class PrivateKeyInfoFactory
+{
+    private PrivateKeyInfoFactory()
+    {
+
+    }
+
+    /**
+     * Create a PrivateKeyInfo representation of a private key.
+     *
+     * @param privateKey the key to be encoded into the info object.
+     * @return the appropriate PrivateKeyInfo
+     * @throws java.io.IOException on an error encoding the key
+     */
+    public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException
+    {
+        return createPrivateKeyInfo(privateKey, null);
+    }
+
+    /**
+     * Create a PrivateKeyInfo representation of a private key with attributes.
+     *
+     * @param privateKey the key to be encoded into the info object.
+     * @param attributes the set of attributes to be included.
+     * @return the appropriate PrivateKeyInfo
+     * @throws java.io.IOException on an error encoding the key
+     */
+    public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) throws IOException
+    {
+        if (privateKey instanceof QTESLAPrivateKeyParameters)
+        {
+            QTESLAPrivateKeyParameters keyParams = (QTESLAPrivateKeyParameters)privateKey;
+
+            AlgorithmIdentifier algorithmIdentifier = Utils.qTeslaLookupAlgID(keyParams.getSecurityCategory());
+
+            return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(keyParams.getSecret()), attributes);
+        }
+        else if (privateKey instanceof SPHINCSPrivateKeyParameters)
+        {
+            SPHINCSPrivateKeyParameters params = (SPHINCSPrivateKeyParameters)privateKey;
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256,
+                                    new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest())));
+
+            return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getKeyData()));
+        }
+        else if (privateKey instanceof NHPrivateKeyParameters)
+        {
+            NHPrivateKeyParameters params = (NHPrivateKeyParameters)privateKey;
+
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope);
+
+            short[] privateKeyData = params.getSecData();
+
+            byte[] octets = new byte[privateKeyData.length * 2];
+            for (int i = 0; i != privateKeyData.length; i++)
+            {
+                Pack.shortToLittleEndian(privateKeyData[i], octets, i * 2);
+            }
+
+            return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(octets));
+        }
+        else if (privateKey instanceof XMSSPrivateKeyParameters)
+        {
+            XMSSPrivateKeyParameters keyParams = (XMSSPrivateKeyParameters)privateKey;
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss,
+                new XMSSKeyParams(keyParams.getParameters().getHeight(),
+                    Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest())));
+
+            return new PrivateKeyInfo(algorithmIdentifier, xmssCreateKeyStructure(keyParams));
+        }
+        else if (privateKey instanceof XMSSMTPrivateKeyParameters)
+        {
+            XMSSMTPrivateKeyParameters keyParams = (XMSSMTPrivateKeyParameters)privateKey;
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss_mt,
+                new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(),
+                    Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest())));
+
+            return new PrivateKeyInfo(algorithmIdentifier, xmssmtCreateKeyStructure(keyParams));
+        }
+        else
+        {
+            throw new IOException("key parameters not recognized");
+        }
+    }
+
+    private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters keyParams)
+    {
+        byte[] keyData = keyParams.toByteArray();
+
+        int n = keyParams.getParameters().getDigestSize();
+        int totalHeight = keyParams.getParameters().getHeight();
+        int indexSize = 4;
+        int secretKeySize = n;
+        int secretKeyPRFSize = n;
+        int publicSeedSize = n;
+        int rootSize = n;
+
+        int position = 0;
+        int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize);
+        if (!XMSSUtil.isIndexValid(totalHeight, index))
+        {
+            throw new IllegalArgumentException("index out of bounds");
+        }
+        position += indexSize;
+        byte[] secretKeySeed = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeySize);
+        position += secretKeySize;
+        byte[] secretKeyPRF = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeyPRFSize);
+        position += secretKeyPRFSize;
+        byte[] publicSeed = XMSSUtil.extractBytesAtOffset(keyData, position, publicSeedSize);
+        position += publicSeedSize;
+        byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize);
+        position += rootSize;
+               /* import BDS state */
+        byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position);
+
+        return new XMSSPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary);
+    }
+
+    private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParameters keyParams)
+    {
+        byte[] keyData = keyParams.toByteArray();
+
+        int n = keyParams.getParameters().getDigestSize();
+        int totalHeight = keyParams.getParameters().getHeight();
+        int indexSize = (totalHeight + 7) / 8;
+        int secretKeySize = n;
+        int secretKeyPRFSize = n;
+        int publicSeedSize = n;
+        int rootSize = n;
+
+        int position = 0;
+        int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize);
+        if (!XMSSUtil.isIndexValid(totalHeight, index))
+        {
+            throw new IllegalArgumentException("index out of bounds");
+        }
+        position += indexSize;
+        byte[] secretKeySeed = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeySize);
+        position += secretKeySize;
+        byte[] secretKeyPRF = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeyPRFSize);
+        position += secretKeyPRFSize;
+        byte[] publicSeed = XMSSUtil.extractBytesAtOffset(keyData, position, publicSeedSize);
+        position += publicSeedSize;
+        byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize);
+        position += rootSize;
+               /* import BDS state */
+        byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position);
+
+        return new XMSSMTPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java
new file mode 100644
index 0000000..e0989d2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java
@@ -0,0 +1,182 @@
+package org.bouncycastle.pqc.crypto.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSPublicKey;
+import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
+
+/**
+ * Factory to create asymmetric public key parameters for asymmetric ciphers from range of
+ * ASN.1 encoded SubjectPublicKeyInfo objects.
+ */
+public class PublicKeyFactory
+{
+    private static Map converters = new HashMap();
+
+    static
+    {
+        converters.put(PQCObjectIdentifiers.qTESLA_I, new QTeslaConverter());
+        converters.put(PQCObjectIdentifiers.qTESLA_III_size, new QTeslaConverter());
+        converters.put(PQCObjectIdentifiers.qTESLA_III_speed, new QTeslaConverter());
+        converters.put(PQCObjectIdentifiers.qTESLA_p_I, new QTeslaConverter());
+        converters.put(PQCObjectIdentifiers.qTESLA_p_III, new QTeslaConverter());
+        converters.put(PQCObjectIdentifiers.sphincs256, new SPHINCSConverter());
+        converters.put(PQCObjectIdentifiers.newHope, new NHConverter());
+        converters.put(PQCObjectIdentifiers.xmss, new XMSSConverter());
+        converters.put(PQCObjectIdentifiers.xmss_mt, new XMSSMTConverter());
+    }
+
+    /**
+     * Create a public key from a SubjectPublicKeyInfo encoding
+     *
+     * @param keyInfoData the SubjectPublicKeyInfo encoding
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
+        throws IOException
+    {
+        return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
+    }
+
+    /**
+     * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
+     *
+     * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(InputStream inStr)
+        throws IOException
+    {
+        return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
+    }
+
+    /**
+     * Create a public key from the passed in SubjectPublicKeyInfo
+     *
+     * @param keyInfo the SubjectPublicKeyInfo containing the key data
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        return createKey(keyInfo, null);
+    }
+
+    /**
+     * Create a public key from the passed in SubjectPublicKeyInfo
+     *
+     * @param keyInfo the SubjectPublicKeyInfo containing the key data
+     * @param defaultParams default parameters that might be needed.
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+        throws IOException
+    {
+        AlgorithmIdentifier algId = keyInfo.getAlgorithm();
+        SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algId.getAlgorithm());
+
+        if (converter != null)
+        {
+            return converter.getPublicKeyParameters(keyInfo, defaultParams);
+        }
+        else
+        {
+            throw new IOException("algorithm identifier in public key not recognised: " + algId.getAlgorithm());
+        }
+    }
+
+    private static abstract class SubjectPublicKeyInfoConverter
+    {
+        abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException;
+    }
+
+    private static class QTeslaConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            return new QTESLAPublicKeyParameters(Utils.qTeslaLookupSecurityCategory(keyInfo.getAlgorithm()), keyInfo.getPublicKeyData().getOctets());
+        }
+    }
+
+    private static class SPHINCSConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            return new SPHINCSPublicKeyParameters(keyInfo.getPublicKeyData().getBytes(),
+                            Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters())));
+        }
+    }
+
+    private static class NHConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            return new NHPublicKeyParameters(keyInfo.getPublicKeyData().getBytes());
+        }
+    }
+
+    private static class XMSSConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());
+            ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();
+            XMSSPublicKey xmssPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey());
+
+            return new XMSSPublicKeyParameters
+                .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest)))
+                .withPublicSeed(xmssPublicKey.getPublicSeed())
+                .withRoot(xmssPublicKey.getRoot()).build();
+        }
+    }
+
+    private static class XMSSMTConverter
+        extends SubjectPublicKeyInfoConverter
+    {
+        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
+            throws IOException
+        {
+            XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());
+            ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();
+
+            XMSSPublicKey xmssMtPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey());
+
+            return new XMSSMTPublicKeyParameters
+                .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest)))
+                .withPublicSeed(xmssMtPublicKey.getPublicSeed())
+                .withRoot(xmssMtPublicKey.getRoot()).build();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java
new file mode 100644
index 0000000..4ccbf4c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java
@@ -0,0 +1,83 @@
+package org.bouncycastle.pqc.crypto.util;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.asn1.XMSSMTPublicKey;
+import org.bouncycastle.pqc.asn1.XMSSPublicKey;
+import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
+
+/**
+ * Factory to create ASN.1 subject public key info objects from lightweight public keys.
+ */
+public class SubjectPublicKeyInfoFactory
+{
+    private SubjectPublicKeyInfoFactory()
+    {
+
+    }
+
+    /**
+     * Create a SubjectPublicKeyInfo public key.
+     *
+     * @param publicKey the key to be encoded into the info object.
+     * @return a SubjectPublicKeyInfo representing the key.
+     * @throws java.io.IOException on an error encoding the key
+     */
+    public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey)
+        throws IOException
+    {
+        if (publicKey instanceof QTESLAPublicKeyParameters)
+        {
+            QTESLAPublicKeyParameters keyParams = (QTESLAPublicKeyParameters)publicKey;
+            AlgorithmIdentifier algorithmIdentifier = Utils.qTeslaLookupAlgID(keyParams.getSecurityCategory());
+
+            return new SubjectPublicKeyInfo(algorithmIdentifier, keyParams.getPublicData());
+        }
+        else if (publicKey instanceof SPHINCSPublicKeyParameters)
+        {
+            SPHINCSPublicKeyParameters params = (SPHINCSPublicKeyParameters)publicKey;
+
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256,
+                new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest())));
+            return new SubjectPublicKeyInfo(algorithmIdentifier, params.getKeyData());
+        }
+        else if (publicKey instanceof NHPublicKeyParameters)
+        {
+            NHPublicKeyParameters params = (NHPublicKeyParameters)publicKey;
+
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope);
+            return new SubjectPublicKeyInfo(algorithmIdentifier, params.getPubData());
+        }
+        else if (publicKey instanceof XMSSPublicKeyParameters)
+        {
+            XMSSPublicKeyParameters keyParams = (XMSSPublicKeyParameters)publicKey;
+
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss,
+                new XMSSKeyParams(keyParams.getParameters().getHeight(), Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest())));
+            return new SubjectPublicKeyInfo(algorithmIdentifier, new XMSSPublicKey(keyParams.getPublicSeed(), keyParams.getRoot()));
+        }
+        else if (publicKey instanceof XMSSMTPublicKeyParameters)
+        {
+            XMSSMTPublicKeyParameters keyParams = (XMSSMTPublicKeyParameters)publicKey;
+
+            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss_mt, new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(),
+                Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest())));
+            return new SubjectPublicKeyInfo(algorithmIdentifier, new XMSSMTPublicKey(keyParams.getPublicSeed(), keyParams.getRoot()));
+        }
+        else
+        {
+            throw new IOException("key parameters not recognized");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java
new file mode 100644
index 0000000..ade70be
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.pqc.crypto.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.crypto.sphincs.SPHINCSKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSKeyParameters;
+import org.bouncycastle.util.Integers;
+
+class Utils
+{
+    static final AlgorithmIdentifier AlgID_qTESLA_I = new AlgorithmIdentifier(PQCObjectIdentifiers.qTESLA_I);
+    static final AlgorithmIdentifier AlgID_qTESLA_III_size = new AlgorithmIdentifier(PQCObjectIdentifiers.qTESLA_III_size);
+    static final AlgorithmIdentifier AlgID_qTESLA_III_speed = new AlgorithmIdentifier(PQCObjectIdentifiers.qTESLA_III_speed);
+    static final AlgorithmIdentifier AlgID_qTESLA_p_I = new AlgorithmIdentifier(PQCObjectIdentifiers.qTESLA_p_I);
+    static final AlgorithmIdentifier AlgID_qTESLA_p_III = new AlgorithmIdentifier(PQCObjectIdentifiers.qTESLA_p_III);
+
+    static final AlgorithmIdentifier SPHINCS_SHA3_256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256);
+    static final AlgorithmIdentifier SPHINCS_SHA512_256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512_256);
+
+    static final AlgorithmIdentifier XMSS_SHA256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
+    static final AlgorithmIdentifier XMSS_SHA512 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
+    static final AlgorithmIdentifier XMSS_SHAKE128 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake128);
+    static final AlgorithmIdentifier XMSS_SHAKE256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
+
+    static final Map categories = new HashMap();
+
+    static
+    {
+        categories.put(PQCObjectIdentifiers.qTESLA_I, Integers.valueOf(QTESLASecurityCategory.HEURISTIC_I));
+        categories.put(PQCObjectIdentifiers.qTESLA_III_size, Integers.valueOf(QTESLASecurityCategory.HEURISTIC_III_SIZE));
+        categories.put(PQCObjectIdentifiers.qTESLA_III_speed, Integers.valueOf(QTESLASecurityCategory.HEURISTIC_III_SPEED));
+        categories.put(PQCObjectIdentifiers.qTESLA_p_I, Integers.valueOf(QTESLASecurityCategory.PROVABLY_SECURE_I));
+        categories.put(PQCObjectIdentifiers.qTESLA_p_III, Integers.valueOf(QTESLASecurityCategory.PROVABLY_SECURE_III));
+    }
+
+    static int qTeslaLookupSecurityCategory(AlgorithmIdentifier algorithm)
+    {
+        return ((Integer)categories.get(algorithm.getAlgorithm())).intValue();
+    }
+
+    static AlgorithmIdentifier qTeslaLookupAlgID(int securityCategory)
+    {
+        switch (securityCategory)
+        {
+        case QTESLASecurityCategory.HEURISTIC_I:
+            return AlgID_qTESLA_I;
+        case QTESLASecurityCategory.HEURISTIC_III_SIZE:
+            return AlgID_qTESLA_III_size;
+        case QTESLASecurityCategory.HEURISTIC_III_SPEED:
+            return AlgID_qTESLA_III_speed;
+        case QTESLASecurityCategory.PROVABLY_SECURE_I:
+            return AlgID_qTESLA_p_I;
+        case QTESLASecurityCategory.PROVABLY_SECURE_III:
+            return AlgID_qTESLA_p_III;
+        default:
+            throw new IllegalArgumentException("unknown security category: " + securityCategory);
+        }
+    }
+
+    static AlgorithmIdentifier sphincs256LookupTreeAlgID(String treeDigest)
+    {
+        if (treeDigest.equals(SPHINCSKeyParameters.SHA3_256))
+        {
+            return SPHINCS_SHA3_256;
+        }
+        else if (treeDigest.equals(SPHINCSKeyParameters.SHA512_256))
+        {
+            return SPHINCS_SHA512_256;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown tree digest: " + treeDigest);
+        }
+    }
+
+    static AlgorithmIdentifier xmssLookupTreeAlgID(String treeDigest)
+    {
+        if (treeDigest.equals(XMSSKeyParameters.SHA_256))
+        {
+            return XMSS_SHA256;
+        }
+        else if (treeDigest.equals(XMSSKeyParameters.SHA_512))
+        {
+            return XMSS_SHA512;
+        }
+        else if (treeDigest.equals(XMSSKeyParameters.SHAKE128))
+        {
+            return XMSS_SHAKE128;
+        }
+        else if (treeDigest.equals(XMSSKeyParameters.SHAKE256))
+        {
+            return XMSS_SHAKE256;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown tree digest: " + treeDigest);
+        }
+    }
+
+    static String sphincs256LookupTreeAlgName(SPHINCS256KeyParams keyParams)
+    {
+        AlgorithmIdentifier treeDigest = keyParams.getTreeDigest();
+
+        if (treeDigest.getAlgorithm().equals(SPHINCS_SHA3_256.getAlgorithm()))
+        {
+            return SPHINCSKeyParameters.SHA3_256;
+        }
+        else if (treeDigest.getAlgorithm().equals(SPHINCS_SHA512_256.getAlgorithm()))
+        {
+            return SPHINCSKeyParameters.SHA512_256;
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown tree digest: " + treeDigest.getAlgorithm());
+        }
+    }
+
+    static Digest getDigest(ASN1ObjectIdentifier oid)
+    {
+        if (oid.equals(NISTObjectIdentifiers.id_sha256))
+        {
+            return new SHA256Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_sha512))
+        {
+            return new SHA512Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake128))
+        {
+            return new SHAKEDigest(128);
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake256))
+        {
+            return new SHAKEDigest(256);
+        }
+
+        throw new IllegalArgumentException("unrecognized digest OID: " + oid);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDS.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDS.java
index 7ca02d5..10516f3 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDS.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDS.java
@@ -8,410 +8,431 @@
 import java.util.Stack;
 import java.util.TreeMap;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.crypto.Digest;
+
 /**
  * BDS.
- *
  */
-public final class BDS implements Serializable {
+public final class BDS
+    implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    
+    private transient WOTSPlus wotsPlus;
 
-	private static final long serialVersionUID = 1L;
+    private final int treeHeight;
+    private final List<BDSTreeHash> treeHashInstances;
+    private int k;
+    private XMSSNode root;
+    private List<XMSSNode> authenticationPath;
+    private Map<Integer, LinkedList<XMSSNode>> retain;
+    private Stack<XMSSNode> stack;
 
-	private final class TreeHash implements Serializable {
+    private Map<Integer, XMSSNode> keep;
+    private int index;
+    private boolean used;
 
-		private static final long serialVersionUID = 1L;
+    /**
+     * Place holder BDS for when state is exhausted.
+     *
+     * @param params tree parameters
+     * @param index the index that has been reached.
+     */
+    BDS(XMSSParameters params, int index)
+    {
+        this(params.getWOTSPlus(), params.getHeight(), params.getK());
+        this.index = index;
+        this.used = true;
+    }
 
-		private XMSSNode tailNode;
-		private final int initialHeight;
-		private int height;
-		private int nextIndex;
-		private boolean initialized;
-		private boolean finished;
+    /**
+     * Set up constructor.
+     *
+     * @param params tree parameters
+     * @param publicSeed public seed for tree
+     * @param secretKeySeed secret seed for tree
+     * @param otsHashAddress hash address
+     */
+    BDS(XMSSParameters params, byte[] publicSeed, byte[] secretKeySeed, OTSHashAddress otsHashAddress)
+    {
+        this(params.getWOTSPlus(), params.getHeight(), params.getK());
+        this.initialize(publicSeed, secretKeySeed, otsHashAddress);
+    }
 
-		private TreeHash(int initialHeight) {
-			super();
-			this.initialHeight = initialHeight;
-			initialized = false;
-			finished = false;
-		}
+    /**
+     * Set up constructor for a tree where the original BDS state was lost.
+     *
+     * @param params tree parameters
+     * @param publicSeed public seed for tree
+     * @param secretKeySeed secret seed for tree
+     * @param otsHashAddress hash address
+     * @param index index counter for the state to be at.
+     */
+    BDS(XMSSParameters params, byte[] publicSeed, byte[] secretKeySeed, OTSHashAddress otsHashAddress, int index)
+    {
+        this(params.getWOTSPlus(), params.getHeight(), params.getK());
 
-		private void initialize(int nextIndex) {
-			tailNode = null;
-			height = initialHeight;
-			this.nextIndex = nextIndex;
-			initialized = true;
-			finished = false;
-		}
+        this.initialize(publicSeed, secretKeySeed, otsHashAddress);
 
-		private void update(OTSHashAddress otsHashAddress) {
-			if (otsHashAddress == null) {
-				throw new NullPointerException("otsHashAddress == null");
-			}
-			if (finished || !initialized) {
-				throw new IllegalStateException("finished or not initialized");
-			}
-			/* prepare addresses */
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(nextIndex).withChainAddress(otsHashAddress.getChainAddress())
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
-			LTreeAddress lTreeAddress = (LTreeAddress) new LTreeAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withLTreeAddress(nextIndex).build();
-			HashTreeAddress hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withTreeIndex(nextIndex).build();
+        while (this.index < index)
+        {
+            this.nextAuthenticationPath(publicSeed, secretKeySeed, otsHashAddress);
+            this.used = false;
+        }
+    }
 
-			/* calculate leaf node */
-			wotsPlus.importKeys(xmss.getWOTSPlusSecretKey(otsHashAddress), xmss.getPublicSeed());
-			WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
-			XMSSNode node = xmss.lTree(wotsPlusPublicKey, lTreeAddress);
+    private BDS(WOTSPlus wotsPlus, int treeHeight, int k)
+    {
+        this.wotsPlus = wotsPlus;
+        this.treeHeight = treeHeight;
+        this.k = k;
+        if (k > treeHeight || k < 2 || ((treeHeight - k) % 2) != 0)
+        {
+            throw new IllegalArgumentException("illegal value for BDS parameter k");
+        }
+        authenticationPath = new ArrayList<XMSSNode>();
+        retain = new TreeMap<Integer, LinkedList<XMSSNode>>();
+        stack = new Stack<XMSSNode>();
 
-			while (!stack.isEmpty() && stack.peek().getHeight() == node.getHeight()
-					&& stack.peek().getHeight() != initialHeight) {
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight())
-						.withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
-						.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-				node = xmss.randomizeHash(stack.pop(), node, hashTreeAddress);
-				node = new XMSSNode(node.getHeight() + 1, node.getValue());
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
-						.withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
-						.build();
-			}
+        treeHashInstances = new ArrayList<BDSTreeHash>();
+        for (int height = 0; height < (treeHeight - k); height++)
+        {
+            treeHashInstances.add(new BDSTreeHash(height));
+        }
 
-			if (tailNode == null) {
-				tailNode = node;
-			} else {
-				if (tailNode.getHeight() == node.getHeight()) {
-					hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-							.withLayerAddress(hashTreeAddress.getLayerAddress())
-							.withTreeAddress(hashTreeAddress.getTreeAddress())
-							.withTreeHeight(hashTreeAddress.getTreeHeight())
-							.withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
-							.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-					node = xmss.randomizeHash(tailNode, node, hashTreeAddress);
-					node = new XMSSNode(tailNode.getHeight() + 1, node.getValue());
-					tailNode = node;
-					hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-							.withLayerAddress(hashTreeAddress.getLayerAddress())
-							.withTreeAddress(hashTreeAddress.getTreeAddress())
-							.withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
-							.withTreeIndex(hashTreeAddress.getTreeIndex())
-							.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-				} else {
-					stack.push(node);
-				}
-			}
+        keep = new TreeMap<Integer, XMSSNode>();
+        index = 0;
+        this.used = false;
+    }
 
-			if (tailNode.getHeight() == initialHeight) {
-				finished = true;
-			} else {
-				height = node.getHeight();
-				nextIndex++;
-			}
-		}
+    private BDS(BDS last, byte[] publicSeed, byte[] secretKeySeed, OTSHashAddress otsHashAddress)
+    {
+        this.wotsPlus = last.wotsPlus;
+        this.treeHeight = last.treeHeight;
+        this.k = last.k;
+        this.root = last.root;
+        this.authenticationPath = new ArrayList<XMSSNode>();  // note use of addAll to avoid serialization issues
+        this.authenticationPath.addAll(last.authenticationPath);
+        this.retain = last.retain;
+        this.stack = new Stack<XMSSNode>(); // note use of addAll to avoid serialization issues
+        this.stack.addAll(last.stack);
+        this.treeHashInstances = last.treeHashInstances;
+        this.keep = new TreeMap<Integer, XMSSNode>(last.keep);
+        this.index = last.index;
 
-		private int getHeight() {
-			if (!initialized || finished) {
-				return Integer.MAX_VALUE;
-			}
-			return height;
-		}
+        this.nextAuthenticationPath(publicSeed, secretKeySeed, otsHashAddress);
 
-		private int getIndexLeaf() {
-			return nextIndex;
-		}
+        last.used = true;
+    }
 
-		private void setNode(XMSSNode node) {
-			tailNode = node;
-			height = node.getHeight();
-			if (height == initialHeight) {
-				finished = true;
-			}
-		}
+    private BDS(BDS last, Digest digest)
+    {
+        this.wotsPlus = new WOTSPlus(new WOTSPlusParameters(digest));
+        this.treeHeight = last.treeHeight;
+        this.k = last.k;
+        this.root = last.root;
+        this.authenticationPath = new ArrayList<XMSSNode>();  // note use of addAll to avoid serialization issues
+        this.authenticationPath.addAll(last.authenticationPath);
+        this.retain = last.retain;
+        this.stack = new Stack<XMSSNode>();     // note use of addAll to avoid serialization issues
+        this.stack.addAll(last.stack);
+        this.treeHashInstances = last.treeHashInstances;
+        this.keep = new TreeMap<Integer, XMSSNode>(last.keep);
+        this.index = last.index;
+        this.used = last.used;
+        this.validate();
+    }
 
-		private boolean isFinished() {
-			return finished;
-		}
+    public BDS getNextState(byte[] publicSeed, byte[] secretKeySeed, OTSHashAddress otsHashAddress)
+    {
+        return new BDS(this, publicSeed, secretKeySeed, otsHashAddress);
+    }
 
-		private boolean isInitialized() {
-			return initialized;
-		}
-	}
-
-	private transient XMSS xmss;
-	private transient WOTSPlus wotsPlus;
-	private final int treeHeight;
-	private int k;
-	private XMSSNode root;
-	private List<XMSSNode> authenticationPath;
-	private Map<Integer, LinkedList<XMSSNode>> retain;
-	private Stack<XMSSNode> stack;
-	private List<TreeHash> treeHashInstances;
-	private Map<Integer, XMSSNode> keep;
-	private int index;
-
-	protected BDS(XMSS xmss) {
-		super();
-		if (xmss == null) {
-			throw new NullPointerException("xmss == null");
-		}
-		this.xmss = xmss;
-		wotsPlus = xmss.getWOTSPlus();
-		treeHeight = xmss.getParams().getHeight();
-		k = xmss.getParams().getK();
-		if (k > treeHeight || k < 2 || ((treeHeight - k) % 2) != 0) {
-			throw new IllegalArgumentException("illegal value for BDS parameter k");
-		}
-		authenticationPath = new ArrayList<XMSSNode>();
-		retain = new TreeMap<Integer, LinkedList<XMSSNode>>();
-		stack = new Stack<XMSSNode>();
-		initializeTreeHashInstances();
-		keep = new TreeMap<Integer, XMSSNode>();
-		index = 0;
-	}
-
-	private void initializeTreeHashInstances() {
-		treeHashInstances = new ArrayList<TreeHash>();
-		for (int height = 0; height < (treeHeight - k); height++) {
-			treeHashInstances.add(new TreeHash(height));
-		}
-	}
-
-	protected XMSSNode initialize(OTSHashAddress otsHashAddress) {
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		/* prepare addresses */
-		LTreeAddress lTreeAddress = (LTreeAddress) new LTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.build();
-		HashTreeAddress hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.build();
+    private void initialize(byte[] publicSeed, byte[] secretSeed, OTSHashAddress otsHashAddress)
+    {
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        /* prepare addresses */
+        LTreeAddress lTreeAddress = (LTreeAddress)new LTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .build();
+        HashTreeAddress hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .build();
 
 		/* iterate indexes */
-		for (int indexLeaf = 0; indexLeaf < (1 << treeHeight); indexLeaf++) {
+        for (int indexLeaf = 0; indexLeaf < (1 << treeHeight); indexLeaf++)
+        {
 			/* generate leaf */
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(indexLeaf).withChainAddress(otsHashAddress.getChainAddress())
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+                .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+                .withOTSAddress(indexLeaf).withChainAddress(otsHashAddress.getChainAddress())
+                .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+                .build();
 			/*
 			 * import WOTSPlusSecretKey as its needed to calculate the public
 			 * key on the fly
 			 */
-			wotsPlus.importKeys(xmss.getWOTSPlusSecretKey(otsHashAddress), xmss.getPublicSeed());
-			WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
-			lTreeAddress = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(lTreeAddress.getLayerAddress())
-					.withTreeAddress(lTreeAddress.getTreeAddress()).withLTreeAddress(indexLeaf)
-					.withTreeHeight(lTreeAddress.getTreeHeight()).withTreeIndex(lTreeAddress.getTreeIndex())
-					.withKeyAndMask(lTreeAddress.getKeyAndMask()).build();
-			XMSSNode node = xmss.lTree(wotsPlusPublicKey, lTreeAddress);
+            wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(secretSeed, otsHashAddress), publicSeed);
+            WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
+            lTreeAddress = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(lTreeAddress.getLayerAddress())
+                .withTreeAddress(lTreeAddress.getTreeAddress()).withLTreeAddress(indexLeaf)
+                .withTreeHeight(lTreeAddress.getTreeHeight()).withTreeIndex(lTreeAddress.getTreeIndex())
+                .withKeyAndMask(lTreeAddress.getKeyAndMask()).build();
+            XMSSNode node = XMSSNodeUtil.lTree(wotsPlus, wotsPlusPublicKey, lTreeAddress);
 
-			hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-					.withLayerAddress(hashTreeAddress.getLayerAddress())
-					.withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeIndex(indexLeaf)
-					.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-			while (!stack.isEmpty() && stack.peek().getHeight() == node.getHeight()) {
+            hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                .withLayerAddress(hashTreeAddress.getLayerAddress())
+                .withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeIndex(indexLeaf)
+                .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+            while (!stack.isEmpty() && stack.peek().getHeight() == node.getHeight())
+            {
 				/* add to authenticationPath if leafIndex == 1 */
-				int indexOnHeight = ((int) Math.floor(indexLeaf / (1 << node.getHeight())));
-				if (indexOnHeight == 1) {
-					authenticationPath.add(node.clone());
-				}
+                int indexOnHeight = indexLeaf / (1 << node.getHeight());
+                if (indexOnHeight == 1)
+                {
+                    authenticationPath.add(node.clone());
+                }
 				/* store next right authentication node */
-				if (indexOnHeight == 3 && node.getHeight() < (treeHeight - k)) {
-					treeHashInstances.get(node.getHeight()).setNode(node.clone());
-				}
-				if (indexOnHeight >= 3 && (indexOnHeight & 1) == 1 && node.getHeight() >= (treeHeight - k)
-						&& node.getHeight() <= (treeHeight - 2)) {
-					if (retain.get(node.getHeight()) == null) {
-						LinkedList<XMSSNode> queue = new LinkedList<XMSSNode>();
-						queue.add(node.clone());
-						retain.put(node.getHeight(), queue);
-					} else {
-						retain.get(node.getHeight()).add(node.clone());
-					}
-				}
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight())
-						.withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
-						.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-				node = xmss.randomizeHash(stack.pop(), node, hashTreeAddress);
-				node = new XMSSNode(node.getHeight() + 1, node.getValue());
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
-						.withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
-						.build();
-			}
+                if (indexOnHeight == 3 && node.getHeight() < (treeHeight - k))
+                {
+                    treeHashInstances.get(node.getHeight()).setNode(node.clone());
+                }
+                if (indexOnHeight >= 3 && (indexOnHeight & 1) == 1 && node.getHeight() >= (treeHeight - k)
+                    && node.getHeight() <= (treeHeight - 2))
+                {
+                    if (retain.get(node.getHeight()) == null)
+                    {
+                        LinkedList<XMSSNode> queue = new LinkedList<XMSSNode>();
+                        queue.add(node.clone());
+                        retain.put(node.getHeight(), queue);
+                    }
+                    else
+                    {
+                        retain.get(node.getHeight()).add(node.clone());
+                    }
+                }
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight())
+                    .withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
+                    .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+                node = XMSSNodeUtil.randomizeHash(wotsPlus, stack.pop(), node, hashTreeAddress);
+                node = new XMSSNode(node.getHeight() + 1, node.getValue());
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
+                    .withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
+                    .build();
+            }
 			/* push to stack */
-			stack.push(node);
-		}
-		root = stack.pop();
-		return root.clone();
-	}
+            stack.push(node);
+        }
+        root = stack.pop();
+    }
 
-	protected void nextAuthenticationPath(OTSHashAddress otsHashAddress) {
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		if (index > ((1 << treeHeight) - 2)) {
-			throw new IllegalStateException("index out of bounds");
-		}
-		/* prepare addresses */
-		LTreeAddress lTreeAddress = (LTreeAddress) new LTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.build();
-		HashTreeAddress hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.build();
+    private void nextAuthenticationPath(byte[] publicSeed, byte[] secretSeed, OTSHashAddress otsHashAddress)
+    {
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        if (used)
+        {
+            throw new IllegalStateException("index already used");
+        }
+        if (index > ((1 << treeHeight) - 2))
+        {
+            throw new IllegalStateException("index out of bounds");
+        }
 
 		/* determine tau */
-		int tau = XMSSUtil.calculateTau(index, treeHeight);
+        int tau = XMSSUtil.calculateTau(index, treeHeight);
+    	/* parent of leaf on height tau+1 is a left node */
+        if (((index >> (tau + 1)) & 1) == 0 && (tau < (treeHeight - 1)))
+        {
+            keep.put(tau, authenticationPath.get(tau).clone());
+        }
 
-		/* parent of leaf on height tau+1 is a left node */
-		if (((index >> (tau + 1)) & 1) == 0 && (tau < (treeHeight - 1))) {
-			keep.put(tau, authenticationPath.get(tau).clone());
-		}
+        /* prepare addresses */
+        LTreeAddress lTreeAddress = (LTreeAddress)new LTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .build();
+        HashTreeAddress hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .build();
+
 		/* leaf is a left node */
-		if (tau == 0) {
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(index).withChainAddress(otsHashAddress.getChainAddress())
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
+        if (tau == 0)
+        {
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+                .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+                .withOTSAddress(index).withChainAddress(otsHashAddress.getChainAddress())
+                .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+                .build();
 			/*
 			 * import WOTSPlusSecretKey as its needed to calculate the public
 			 * key on the fly
 			 */
-			wotsPlus.importKeys(xmss.getWOTSPlusSecretKey(otsHashAddress), xmss.getPublicSeed());
-			WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
-			lTreeAddress = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(lTreeAddress.getLayerAddress())
-					.withTreeAddress(lTreeAddress.getTreeAddress()).withLTreeAddress(index)
-					.withTreeHeight(lTreeAddress.getTreeHeight()).withTreeIndex(lTreeAddress.getTreeIndex())
-					.withKeyAndMask(lTreeAddress.getKeyAndMask()).build();
-			XMSSNode node = xmss.lTree(wotsPlusPublicKey, lTreeAddress);
-			authenticationPath.set(0, node);
-		} else {
+            wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(secretSeed, otsHashAddress), publicSeed);
+            WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
+            lTreeAddress = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(lTreeAddress.getLayerAddress())
+                .withTreeAddress(lTreeAddress.getTreeAddress()).withLTreeAddress(index)
+                .withTreeHeight(lTreeAddress.getTreeHeight()).withTreeIndex(lTreeAddress.getTreeIndex())
+                .withKeyAndMask(lTreeAddress.getKeyAndMask()).build();
+            XMSSNode node = XMSSNodeUtil.lTree(wotsPlus, wotsPlusPublicKey, lTreeAddress);
+            authenticationPath.set(0, node);
+        }
+        else
+        {
 			/* add new left node on height tau to authentication path */
-			hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-					.withLayerAddress(hashTreeAddress.getLayerAddress())
-					.withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeHeight(tau - 1)
-					.withTreeIndex(index >> tau).withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-			XMSSNode node = xmss.randomizeHash(authenticationPath.get(tau - 1), keep.get(tau - 1), hashTreeAddress);
-			node = new XMSSNode(node.getHeight() + 1, node.getValue());
-			authenticationPath.set(tau, node);
-			keep.remove(tau - 1);
+            hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                .withLayerAddress(hashTreeAddress.getLayerAddress())
+                .withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeHeight(tau - 1)
+                .withTreeIndex(index >> tau).withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+            /*
+             * import WOTSPlusSecretKey as its needed to calculate the public
+             * key on the fly
+             */
+            wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(secretSeed, otsHashAddress), publicSeed);
+            XMSSNode node = XMSSNodeUtil.randomizeHash(wotsPlus, authenticationPath.get(tau - 1), keep.get(tau - 1), hashTreeAddress);
+            node = new XMSSNode(node.getHeight() + 1, node.getValue());
+            authenticationPath.set(tau, node);
+            keep.remove(tau - 1);
 
 			/* add new right nodes to authentication path */
-			for (int height = 0; height < tau; height++) {
-				if (height < (treeHeight - k)) {
-					authenticationPath.set(height, treeHashInstances.get(height).tailNode.clone());
-				} else {
-					authenticationPath.set(height, retain.get(height).removeFirst());
-				}
-			}
+            for (int height = 0; height < tau; height++)
+            {
+                if (height < (treeHeight - k))
+                {
+                    authenticationPath.set(height, treeHashInstances.get(height).getTailNode());
+                }
+                else
+                {
+                    authenticationPath.set(height, retain.get(height).removeFirst());
+                }
+            }
 
 			/* reinitialize treehash instances */
-			int minHeight = Math.min(tau, treeHeight - k);
-			for (int height = 0; height < minHeight; height++) {
-				int startIndex = index + 1 + (3 * (1 << height));
-				if (startIndex < (1 << treeHeight)) {
-					treeHashInstances.get(height).initialize(startIndex);
-				}
-			}
-		}
-
+            int minHeight = Math.min(tau, treeHeight - k);
+            for (int height = 0; height < minHeight; height++)
+            {
+                int startIndex = index + 1 + (3 * (1 << height));
+                if (startIndex < (1 << treeHeight))
+                {
+                    treeHashInstances.get(height).initialize(startIndex);
+                }
+            }
+        }
+ 
 		/* update treehash instances */
-		for (int i = 0; i < (treeHeight - k) >> 1; i++) {
-			TreeHash treeHash = getTreeHashInstanceForUpdate();
-			if (treeHash != null) {
-				treeHash.update(otsHashAddress);
-			}
-		}
-		index++;
-	}
+        for (int i = 0; i < (treeHeight - k) >> 1; i++)
+        {
+            BDSTreeHash treeHash = getBDSTreeHashInstanceForUpdate();
+            if (treeHash != null)
+            {
+                treeHash.update(stack, wotsPlus, publicSeed, secretSeed, otsHashAddress);
+            }
+        }
 
-	private TreeHash getTreeHashInstanceForUpdate() {
-		TreeHash ret = null;
-		for (TreeHash treeHash : treeHashInstances) {
-			if (treeHash.isFinished() || !treeHash.isInitialized()) {
-				continue;
-			}
-			if (ret == null) {
-				ret = treeHash;
-				continue;
-			}
-			if (treeHash.getHeight() < ret.getHeight()) {
-				ret = treeHash;
-				continue;
-			}
-			if (treeHash.getHeight() == ret.getHeight()) {
-				if (treeHash.getIndexLeaf() < ret.getIndexLeaf()) {
-					ret = treeHash;
-				}
-			}
-		}
-		return ret;
-	}
+        index++;
+    }
 
-	protected void validate() {
-		if (treeHeight != xmss.getParams().getHeight()) {
-			throw new IllegalStateException("wrong height");
-		}
-		if (authenticationPath == null) {
-			throw new IllegalStateException("authenticationPath == null");
-		}
-		if (retain == null) {
-			throw new IllegalStateException("retain == null");
-		}
-		if (stack == null) {
-			throw new IllegalStateException("stack == null");
-		}
-		if (treeHashInstances == null) {
-			throw new IllegalStateException("treeHashInstances == null");
-		}
-		if (keep == null) {
-			throw new IllegalStateException("keep == null");
-		}
-		if (!XMSSUtil.isIndexValid(treeHeight, index)) {
-			throw new IllegalStateException("index in BDS state out of bounds");
-		}
-	}
+    boolean isUsed()
+    {
+        return used;
+    }
 
-	protected int getTreeHeight() {
-		return treeHeight;
-	}
+    private BDSTreeHash getBDSTreeHashInstanceForUpdate()
+    {
+        BDSTreeHash ret = null;
+        for (BDSTreeHash treeHash : treeHashInstances)
+        {
+            if (treeHash.isFinished() || !treeHash.isInitialized())
+            {
+                continue;
+            }
+            if (ret == null)
+            {
+                ret = treeHash;
+                continue;
+            }
+            if (treeHash.getHeight() < ret.getHeight())
+            {
+                ret = treeHash;
+                continue;
+            }
+            if (treeHash.getHeight() == ret.getHeight())
+            {
+                if (treeHash.getIndexLeaf() < ret.getIndexLeaf())
+                {
+                    ret = treeHash;
+                }
+            }
+        }
+        return ret;
+    }
 
-	protected XMSSNode getRoot() {
-		return root.clone();
-	}
+    private void validate()
+    {
+        if (authenticationPath == null)
+        {
+            throw new IllegalStateException("authenticationPath == null");
+        }
+        if (retain == null)
+        {
+            throw new IllegalStateException("retain == null");
+        }
+        if (stack == null)
+        {
+            throw new IllegalStateException("stack == null");
+        }
+        if (treeHashInstances == null)
+        {
+            throw new IllegalStateException("treeHashInstances == null");
+        }
+        if (keep == null)
+        {
+            throw new IllegalStateException("keep == null");
+        }
+        if (!XMSSUtil.isIndexValid(treeHeight, index))
+        {
+            throw new IllegalStateException("index in BDS state out of bounds");
+        }
+    }
 
-	protected List<XMSSNode> getAuthenticationPath() {
-		List<XMSSNode> authenticationPath = new ArrayList<XMSSNode>();
-		for (XMSSNode node : this.authenticationPath) {
-			authenticationPath.add(node.clone());
-		}
-		return authenticationPath;
-	}
+    protected int getTreeHeight()
+    {
+        return treeHeight;
+    }
 
-	protected void setXMSS(XMSS xmss) {
-		this.xmss = xmss;
-		this.wotsPlus = xmss.getWOTSPlus();
-	}
+    protected XMSSNode getRoot()
+    {
+        return root.clone();
+    }
 
-	protected int getIndex() {
-		return index;
-	}
+    protected List<XMSSNode> getAuthenticationPath()
+    {
+        List<XMSSNode> authenticationPath = new ArrayList<XMSSNode>();
+        for (XMSSNode node : this.authenticationPath)
+        {
+            authenticationPath.add(node.clone());
+        }
+        return authenticationPath;
+    }
+
+    protected int getIndex()
+    {
+        return index;
+    }
+
+    public BDS withWOTSDigest(ASN1ObjectIdentifier digestName)
+    {
+        return new BDS(this, DigestUtil.getDigest(digestName));
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSStateMap.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSStateMap.java
new file mode 100644
index 0000000..cfed89f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSStateMap.java
@@ -0,0 +1,125 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.util.Integers;
+
+public class BDSStateMap
+    implements Serializable
+{
+    private static final long serialVersionUID = -3464451825208522308L;
+    
+    private final Map<Integer, BDS> bdsState = new TreeMap<Integer, BDS>();
+
+    BDSStateMap()
+    {
+
+    }
+
+    BDSStateMap(XMSSMTParameters params, long globalIndex, byte[] publicSeed, byte[] secretKeySeed)
+    {
+         for (long index = 0; index < globalIndex; index++)
+         {
+             updateState(params, index, publicSeed, secretKeySeed);
+         }
+    }
+
+    BDSStateMap(BDSStateMap stateMap, XMSSMTParameters params, long globalIndex, byte[] publicSeed, byte[] secretKeySeed)
+    {
+        for (Iterator it = stateMap.bdsState.keySet().iterator(); it.hasNext();)
+        {
+            Integer key = (Integer)it.next();
+
+            bdsState.put(key, stateMap.bdsState.get(key));
+        }
+
+        updateState(params, globalIndex, publicSeed, secretKeySeed);
+    }
+
+    private void updateState(XMSSMTParameters params, long globalIndex, byte[] publicSeed, byte[] secretKeySeed)
+    {
+        XMSSParameters xmssParams = params.getXMSSParameters();
+        int xmssHeight = xmssParams.getHeight();
+
+        //
+        // set up state for next signature
+        //
+        long indexTree = XMSSUtil.getTreeIndex(globalIndex, xmssHeight);
+        int indexLeaf = XMSSUtil.getLeafIndex(globalIndex, xmssHeight);
+
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withTreeAddress(indexTree)
+            .withOTSAddress(indexLeaf).build();
+
+        /* prepare authentication path for next leaf */
+        if (indexLeaf < ((1 << xmssHeight) - 1))
+        {
+            if (this.get(0) == null || indexLeaf == 0)
+            {
+                this.put(0, new BDS(xmssParams, publicSeed, secretKeySeed, otsHashAddress));
+            }
+
+            this.update(0, publicSeed, secretKeySeed, otsHashAddress);
+        }
+
+        /* loop over remaining layers */
+        for (int layer = 1; layer < params.getLayers(); layer++)
+        {
+                /* get root of layer - 1 */
+            indexLeaf = XMSSUtil.getLeafIndex(indexTree, xmssHeight);
+            indexTree = XMSSUtil.getTreeIndex(indexTree, xmssHeight);
+                /* adjust addresses */
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withLayerAddress(layer)
+                .withTreeAddress(indexTree).withOTSAddress(indexLeaf).build();
+
+                /* prepare authentication path for next leaf */
+            if (indexLeaf < ((1 << xmssHeight) - 1)
+                && XMSSUtil.isNewAuthenticationPathNeeded(globalIndex, xmssHeight, layer))
+            {
+                if (this.get(layer) == null)
+                {
+                    this.put(layer, new BDS(params.getXMSSParameters(), publicSeed, secretKeySeed, otsHashAddress));
+                }
+
+                this.update(layer, publicSeed, secretKeySeed, otsHashAddress);
+            }
+        }
+    }
+
+    public boolean isEmpty()
+    {
+        return bdsState.isEmpty();
+    }
+
+    BDS get(int index)
+    {
+        return bdsState.get(Integers.valueOf(index));
+    }
+
+    BDS update(int index, byte[] publicSeed, byte[] secretKeySeed, OTSHashAddress otsHashAddress)
+    {
+        return bdsState.put(Integers.valueOf(index), bdsState.get(Integers.valueOf(index)).getNextState(publicSeed, secretKeySeed, otsHashAddress));
+    }
+
+    void put(int index, BDS bds)
+    {
+        bdsState.put(Integers.valueOf(index), bds);
+    }
+
+    public BDSStateMap withWOTSDigest(ASN1ObjectIdentifier digestName)
+    {
+        BDSStateMap newStateMap = new BDSStateMap();
+
+        for (Iterator<Integer> keys = bdsState.keySet().iterator(); keys.hasNext();)
+        {
+            Integer key = keys.next();
+
+            newStateMap.bdsState.put(key, bdsState.get(key).withWOTSDigest(digestName));
+        }
+
+        return newStateMap;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSTreeHash.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSTreeHash.java
new file mode 100644
index 0000000..af7757d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/BDSTreeHash.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.io.Serializable;
+import java.util.Stack;
+
+
+class BDSTreeHash
+    implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private XMSSNode tailNode;
+    private final int initialHeight;
+    private int height;
+    private int nextIndex;
+    private boolean initialized;
+    private boolean finished;
+
+    BDSTreeHash(int initialHeight)
+    {
+        super();
+        this.initialHeight = initialHeight;
+        initialized = false;
+        finished = false;
+    }
+
+    void initialize(int nextIndex)
+    {
+        tailNode = null;
+        height = initialHeight;
+        this.nextIndex = nextIndex;
+        initialized = true;
+        finished = false;
+    }
+
+    void update(Stack<XMSSNode> stack, WOTSPlus wotsPlus, byte[] publicSeed, byte[] secretSeed, OTSHashAddress otsHashAddress)
+    {
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        if (finished || !initialized)
+        {
+            throw new IllegalStateException("finished or not initialized");
+        }
+            /* prepare addresses */
+        otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withOTSAddress(nextIndex).withChainAddress(otsHashAddress.getChainAddress())
+            .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+            .build();
+        LTreeAddress lTreeAddress = (LTreeAddress)new LTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withLTreeAddress(nextIndex).build();
+        HashTreeAddress hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withTreeIndex(nextIndex).build();
+            /* calculate leaf node */
+        wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(secretSeed, otsHashAddress), publicSeed);
+        WOTSPlusPublicKeyParameters wotsPlusPublicKey = wotsPlus.getPublicKey(otsHashAddress);
+        XMSSNode node = XMSSNodeUtil.lTree(wotsPlus, wotsPlusPublicKey, lTreeAddress);
+
+        while (!stack.isEmpty() && stack.peek().getHeight() == node.getHeight()
+            && stack.peek().getHeight() != initialHeight)
+        {
+            hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                .withLayerAddress(hashTreeAddress.getLayerAddress())
+                .withTreeAddress(hashTreeAddress.getTreeAddress())
+                .withTreeHeight(hashTreeAddress.getTreeHeight())
+                .withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
+                .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+            node = XMSSNodeUtil.randomizeHash(wotsPlus, stack.pop(), node, hashTreeAddress);
+            node = new XMSSNode(node.getHeight() + 1, node.getValue());
+            hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                .withLayerAddress(hashTreeAddress.getLayerAddress())
+                .withTreeAddress(hashTreeAddress.getTreeAddress())
+                .withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
+                .withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
+                .build();
+        }
+
+        if (tailNode == null)
+        {
+            tailNode = node;
+        }
+        else
+        {
+            if (tailNode.getHeight() == node.getHeight())
+            {
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight())
+                    .withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
+                    .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+                node = XMSSNodeUtil.randomizeHash(wotsPlus, tailNode, node, hashTreeAddress);
+                node = new XMSSNode(tailNode.getHeight() + 1, node.getValue());
+                tailNode = node;
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight() + 1)
+                    .withTreeIndex(hashTreeAddress.getTreeIndex())
+                    .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+            }
+            else
+            {
+                stack.push(node);
+            }
+        }
+
+        if (tailNode.getHeight() == initialHeight)
+        {
+            finished = true;
+        }
+        else
+        {
+            height = node.getHeight();
+            nextIndex++;
+        }
+    }
+
+    int getHeight()
+    {
+        if (!initialized || finished)
+        {
+            return Integer.MAX_VALUE;
+        }
+        return height;
+    }
+
+    int getIndexLeaf()
+    {
+        return nextIndex;
+    }
+
+    void setNode(XMSSNode node)
+    {
+        tailNode = node;
+        height = node.getHeight();
+        if (height == initialHeight)
+        {
+            finished = true;
+        }
+    }
+
+    boolean isFinished()
+    {
+        return finished;
+    }
+
+    boolean isInitialized()
+    {
+        return initialized;
+    }
+
+    public XMSSNode getTailNode()
+    {
+        return tailNode.clone();
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/DigestUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/DigestUtil.java
new file mode 100644
index 0000000..874ba8a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/DigestUtil.java
@@ -0,0 +1,68 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+
+class DigestUtil
+{
+    private static Map<String, ASN1ObjectIdentifier> nameToOid = new HashMap<String, ASN1ObjectIdentifier>();
+
+    static
+    {
+        nameToOid.put("SHA-256", NISTObjectIdentifiers.id_sha256);
+        nameToOid.put("SHA-512", NISTObjectIdentifiers.id_sha512);
+        nameToOid.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        nameToOid.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
+    }
+
+    static Digest getDigest(ASN1ObjectIdentifier oid)
+    {
+        if (oid.equals(NISTObjectIdentifiers.id_sha256))
+        {
+            return new SHA256Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_sha512))
+        {
+            return new SHA512Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake128))
+        {
+            return new SHAKEDigest(128);
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake256))
+        {
+            return new SHAKEDigest(256);
+        }
+
+        throw new IllegalArgumentException("unrecognized digest OID: " + oid);
+    }
+
+    static ASN1ObjectIdentifier getDigestOID(String name)
+    {
+        ASN1ObjectIdentifier oid = nameToOid.get(name);
+        if (oid != null)
+        {
+            return oid;
+        }
+
+        throw new IllegalArgumentException("unrecognized digest name: " + name);
+    }
+
+    public static int getDigestSize(Digest digest)
+    {
+        if (digest instanceof Xof)
+        {
+            return digest.getDigestSize() * 2;
+        }
+
+        return digest.getDigestSize();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/HashTreeAddress.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/HashTreeAddress.java
index d456cbc..7ea9bb0 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/HashTreeAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/HashTreeAddress.java
@@ -1,76 +1,89 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
 
 /**
  * Hash tree address.
- *
  */
-public final class HashTreeAddress extends XMSSAddress {
+final class HashTreeAddress
+    extends XMSSAddress
+{
 
-	private static final int TYPE = 0x02;
-	private static final int PADDING = 0x00;
+    private static final int TYPE = 0x02;
+    private static final int PADDING = 0x00;
 
-	private final int padding;
-	private final int treeHeight;
-	private final int treeIndex;
+    private final int padding;
+    private final int treeHeight;
+    private final int treeIndex;
 
-	private HashTreeAddress(Builder builder) {
-		super(builder);
-		padding = PADDING;
-		treeHeight = builder.treeHeight;
-		treeIndex = builder.treeIndex;
-	}
-	
-	protected static class Builder extends XMSSAddress.Builder<Builder> {
+    private HashTreeAddress(Builder builder)
+    {
+        super(builder);
+        padding = PADDING;
+        treeHeight = builder.treeHeight;
+        treeIndex = builder.treeIndex;
+    }
 
-		/* optional */
-		private int treeHeight = 0;
-		private int treeIndex = 0;
+    protected static class Builder
+        extends XMSSAddress.Builder<Builder>
+    {
 
-		protected Builder() {
-			super(TYPE);
-		}
-		
-		protected Builder withTreeHeight(int val) {
-			treeHeight = val;
-			return this;
-		}
-		
-		protected Builder withTreeIndex(int val) {
-			treeIndex = val;
-			return this;
-		}
-		
-		@Override
-		protected XMSSAddress build() {
-			return new HashTreeAddress(this);
-		}
+        /* optional */
+        private int treeHeight = 0;
+        private int treeIndex = 0;
 
-		@Override
-		protected Builder getThis() {
-			return this;
-		}
-	}
+        protected Builder()
+        {
+            super(TYPE);
+        }
 
-	@Override
-	protected byte[] toByteArray() {
-		byte[] byteRepresentation = super.toByteArray();
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, padding, 16);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, treeHeight, 20);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, treeIndex, 24);
-		return byteRepresentation;
-	}
+        protected Builder withTreeHeight(int val)
+        {
+            treeHeight = val;
+            return this;
+        }
 
-	protected int getPadding() {
-		return padding;
-	}
+        protected Builder withTreeIndex(int val)
+        {
+            treeIndex = val;
+            return this;
+        }
 
-	protected int getTreeHeight() {
-		return treeHeight;
-	}
+        @Override
+        protected XMSSAddress build()
+        {
+            return new HashTreeAddress(this);
+        }
 
-	protected int getTreeIndex() {
-		return treeIndex;
-	}
+        @Override
+        protected Builder getThis()
+        {
+            return this;
+        }
+    }
+
+    @Override
+    protected byte[] toByteArray()
+    {
+        byte[] byteRepresentation = super.toByteArray();
+        Pack.intToBigEndian(padding, byteRepresentation,16);
+        Pack.intToBigEndian(treeHeight, byteRepresentation, 20);
+        Pack.intToBigEndian(treeIndex, byteRepresentation, 24);
+        return byteRepresentation;
+    }
+
+    protected int getPadding()
+    {
+        return padding;
+    }
+
+    protected int getTreeHeight()
+    {
+        return treeHeight;
+    }
+
+    protected int getTreeIndex()
+    {
+        return treeIndex;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/KeyedHashFunctions.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/KeyedHashFunctions.java
index 10382c8..f428f2f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/KeyedHashFunctions.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/KeyedHashFunctions.java
@@ -5,81 +5,91 @@
 
 /**
  * Crypto functions for XMSS.
- *
  */
-public final class KeyedHashFunctions {
+final class KeyedHashFunctions
+{
 
-	private final Digest digest;
-	private final int digestSize;
+    private final Digest digest;
+    private final int digestSize;
 
-	protected KeyedHashFunctions(Digest digest, int digestSize) {
-		super();
-		if (digest == null) {
-			throw new NullPointerException("digest == null");
-		}
-		this.digest = digest;
-		this.digestSize = digestSize;
-	}
+    protected KeyedHashFunctions(Digest digest, int digestSize)
+    {
+        super();
+        if (digest == null)
+        {
+            throw new NullPointerException("digest == null");
+        }
+        this.digest = digest;
+        this.digestSize = digestSize;
+    }
 
-	private byte[] coreDigest(int fixedValue, byte[] key, byte[] index) {
-		byte[] buffer = new byte[digestSize + key.length + index.length];
-		byte[] in = XMSSUtil.toBytesBigEndian(fixedValue, digestSize);
-		/* fill first n byte of out buffer */
-		for (int i = 0; i < in.length; i++) {
-			buffer[i] = in[i];
-		}
+    private byte[] coreDigest(int fixedValue, byte[] key, byte[] index)
+    {
+        byte[] in = XMSSUtil.toBytesBigEndian(fixedValue, digestSize);
+        /* fill first n byte of out buffer */
+        digest.update(in, 0, in.length);
 		/* add key */
-		for (int i = 0; i < key.length; i++) {
-			buffer[in.length + i] = key[i];
-		}
+        digest.update(key, 0, key.length);
 		/* add index */
-		for (int i = 0; i < index.length; i++) {
-			buffer[in.length + key.length + i] = index[i];
-		}
-		digest.update(buffer, 0, buffer.length);
-		byte[] out = new byte[digestSize];
-		if (digest instanceof Xof) {
-			((Xof) digest).doFinal(out, 0, digestSize);
-		} else {
-			digest.doFinal(out, 0);
-		}
-		return out;
-	}
+        digest.update(index, 0, index.length);
 
-	protected byte[] F(byte[] key, byte[] in) {
-		if (key.length != digestSize) {
-			throw new IllegalArgumentException("wrong key length");
-		}
-		if (in.length != digestSize) {
-			throw new IllegalArgumentException("wrong in length");
-		}
-		return coreDigest(0, key, in);
-	}
+        byte[] out = new byte[digestSize];
+        if (digest instanceof Xof)
+        {
+            ((Xof)digest).doFinal(out, 0, digestSize);
+        }
+        else
+        {
+            digest.doFinal(out, 0);
+        }
+        return out;
+    }
 
-	protected byte[] H(byte[] key, byte[] in) {
-		if (key.length != digestSize) {
-			throw new IllegalArgumentException("wrong key length");
-		}
-		if (in.length != (2 * digestSize)) {
-			throw new IllegalArgumentException("wrong in length");
-		}
-		return coreDigest(1, key, in);
-	}
+    protected byte[] F(byte[] key, byte[] in)
+    {
+        if (key.length != digestSize)
+        {
+            throw new IllegalArgumentException("wrong key length");
+        }
+        if (in.length != digestSize)
+        {
+            throw new IllegalArgumentException("wrong in length");
+        }
+        return coreDigest(0, key, in);
+    }
 
-	protected byte[] HMsg(byte[] key, byte[] in) {
-		if (key.length != (3 * digestSize)) {
-			throw new IllegalArgumentException("wrong key length");
-		}
-		return coreDigest(2, key, in);
-	}
+    protected byte[] H(byte[] key, byte[] in)
+    {
+        if (key.length != digestSize)
+        {
+            throw new IllegalArgumentException("wrong key length");
+        }
+        if (in.length != (2 * digestSize))
+        {
+            throw new IllegalArgumentException("wrong in length");
+        }
+        return coreDigest(1, key, in);
+    }
 
-	protected byte[] PRF(byte[] key, byte[] address) {
-		if (key.length != digestSize) {
-			throw new IllegalArgumentException("wrong key length");
-		}
-		if (address.length != 32) {
-			throw new IllegalArgumentException("wrong address length");
-		}
-		return coreDigest(3, key, address);
-	}
+    protected byte[] HMsg(byte[] key, byte[] in)
+    {
+        if (key.length != (3 * digestSize))
+        {
+            throw new IllegalArgumentException("wrong key length");
+        }
+        return coreDigest(2, key, in);
+    }
+
+    protected byte[] PRF(byte[] key, byte[] address)
+    {
+        if (key.length != digestSize)
+        {
+            throw new IllegalArgumentException("wrong key length");
+        }
+        if (address.length != 32)
+        {
+            throw new IllegalArgumentException("wrong address length");
+        }
+        return coreDigest(3, key, address);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/LTreeAddress.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/LTreeAddress.java
index 3278dca..d7f1a69 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/LTreeAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/LTreeAddress.java
@@ -1,12 +1,12 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
 
 /**
  * L-tree address.
  *
  */
-public final class LTreeAddress extends XMSSAddress {
+final class LTreeAddress extends XMSSAddress {
 
 	private static final int TYPE = 0x01;
 	
@@ -61,9 +61,9 @@
 	@Override
 	protected byte[] toByteArray() {
 		byte[] byteRepresentation = super.toByteArray();
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, lTreeAddress, 16);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, treeHeight, 20);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, treeIndex, 24);
+		Pack.intToBigEndian(lTreeAddress, byteRepresentation, 16);
+		Pack.intToBigEndian(treeHeight, byteRepresentation, 20);
+		Pack.intToBigEndian(treeIndex, byteRepresentation, 24);
 		return byteRepresentation;
 	}
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/NullPRNG.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/NullPRNG.java
deleted file mode 100644
index aa46f90..0000000
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/NullPRNG.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.bouncycastle.pqc.crypto.xmss;
-
-import java.security.SecureRandom;
-
-/**
- * Implementation of null PRNG returning zeroes only. For testing purposes
- * only(!).
- *
- */
-public final class NullPRNG extends SecureRandom {
-
-	private static final long serialVersionUID = 1L;
-
-	public NullPRNG() {
-		super();
-	}
-
-	@Override
-	public void nextBytes(byte[] bytes) {
-		for (int i = 0; i < bytes.length; i++) {
-			bytes[i] = 0x00;
-		}
-	}
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/OTSHashAddress.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/OTSHashAddress.java
index c5890d8..40f67e0 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/OTSHashAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/OTSHashAddress.java
@@ -1,81 +1,95 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
 
 /**
  * OTS hash address.
- *
  */
-public final class OTSHashAddress extends XMSSAddress {
+final class OTSHashAddress
+    extends XMSSAddress
+{
 
-	private static final int TYPE = 0x00;
+    private static final int TYPE = 0x00;
 
-	private final int otsAddress;
-	private final int chainAddress;
-	private final int hashAddress;
+    private final int otsAddress;
+    private final int chainAddress;
+    private final int hashAddress;
 
-	private OTSHashAddress(Builder builder) {
-		super(builder);
-		otsAddress = builder.otsAddress;
-		chainAddress = builder.chainAddress;
-		hashAddress = builder.hashAddress;
-	}
-	
-	protected static class Builder extends XMSSAddress.Builder<Builder> {
+    private OTSHashAddress(Builder builder)
+    {
+        super(builder);
+        otsAddress = builder.otsAddress;
+        chainAddress = builder.chainAddress;
+        hashAddress = builder.hashAddress;
+    }
 
-		/* optional */
-		private int otsAddress = 0;
-		private int chainAddress = 0;
-		private int hashAddress = 0;
+    protected static class Builder
+        extends XMSSAddress.Builder<Builder>
+    {
 
-		protected Builder() {
-			super(TYPE);
-		}
-		
-		protected Builder withOTSAddress(int val) {
-			otsAddress = val;
-			return this;
-		}
-		
-		protected Builder withChainAddress(int val) {
-			chainAddress = val;
-			return this;
-		}
-		
-		protected Builder withHashAddress(int val) {
-			hashAddress = val;
-			return this;
-		}
-		
-		@Override
-		protected XMSSAddress build() {
-			return new OTSHashAddress(this);
-		}
+        /* optional */
+        private int otsAddress = 0;
+        private int chainAddress = 0;
+        private int hashAddress = 0;
 
-		@Override
-		protected Builder getThis() {
-			return this;
-		}
-	}
+        protected Builder()
+        {
+            super(TYPE);
+        }
 
-	@Override
-	protected byte[] toByteArray() {
-		byte[] byteRepresentation = super.toByteArray();
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, otsAddress, 16);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, chainAddress, 20);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, hashAddress, 24);
-		return byteRepresentation;
-	}
+        protected Builder withOTSAddress(int val)
+        {
+            otsAddress = val;
+            return this;
+        }
 
-	protected int getOTSAddress() {
-		return otsAddress;
-	}
-	
-	protected int getChainAddress() {
-		return chainAddress;
-	}
-	
-	protected int getHashAddress() {
-		return hashAddress;
-	}
+        protected Builder withChainAddress(int val)
+        {
+            chainAddress = val;
+            return this;
+        }
+
+        protected Builder withHashAddress(int val)
+        {
+            hashAddress = val;
+            return this;
+        }
+
+        @Override
+        protected XMSSAddress build()
+        {
+            return new OTSHashAddress(this);
+        }
+
+        @Override
+        protected Builder getThis()
+        {
+            return this;
+        }
+    }
+
+    @Override
+    protected byte[] toByteArray()
+    {
+        byte[] byteRepresentation = super.toByteArray();
+        Pack.intToBigEndian(otsAddress, byteRepresentation,16);
+        Pack.intToBigEndian(chainAddress, byteRepresentation, 20);
+        Pack.intToBigEndian(hashAddress, byteRepresentation, 24);
+        return byteRepresentation;
+    }
+
+    protected int getOTSAddress()
+    {
+        return otsAddress;
+    }
+
+    protected int getChainAddress()
+    {
+        return chainAddress;
+    }
+
+    protected int getHashAddress()
+    {
+        return hashAddress;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlus.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlus.java
index 6ef91b1..3216b0d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlus.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlus.java
@@ -3,378 +3,426 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * WOTS+.
- *
  */
-public final class WOTSPlus {
+final class WOTSPlus
+{
 
-	/**
-	 * WOTS+ parameters.
-	 */
-	private final WOTSPlusParameters params;
-	/**
-	 * Randomization functions.
-	 */
-	private final KeyedHashFunctions khf;
-	/**
-	 * WOTS+ secret key seed.
-	 */
-	private byte[] secretKeySeed;
-	/**
-	 * WOTS+ public seed.
-	 */
-	private byte[] publicSeed;
+    /**
+     * WOTS+ parameters.
+     */
+    private final WOTSPlusParameters params;
+    /**
+     * Randomization functions.
+     */
+    private final KeyedHashFunctions khf;
+    /**
+     * WOTS+ secret key seed.
+     */
+    private byte[] secretKeySeed;
+    /**
+     * WOTS+ public seed.
+     */
+    private byte[] publicSeed;
 
-	/**
-	 * Constructs a new WOTS+ one-time signature system based on the given WOTS+
-	 * parameters.
-	 *
-	 * @param params
-	 *            Parameters for WOTSPlus object.
-	 */
-	protected WOTSPlus(WOTSPlusParameters params) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		this.params = params;
-		int n = params.getDigestSize();
-		khf = new KeyedHashFunctions(params.getDigest(), n);
-		secretKeySeed = new byte[n];
-		publicSeed = new byte[n];
-	}
+    /**
+     * Constructs a new WOTS+ one-time signature system based on the given WOTS+
+     * parameters.
+     *
+     * @param params Parameters for WOTSPlus object.
+     */
+    protected WOTSPlus(WOTSPlusParameters params)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        this.params = params;
+        int n = params.getDigestSize();
+        khf = new KeyedHashFunctions(params.getDigest(), n);
+        secretKeySeed = new byte[n];
+        publicSeed = new byte[n];
+    }
 
-	/**
-	 * Import keys to WOTS+ instance.
-	 *
-	 * @param secretKeySeed
-	 *            Secret key seed.
-	 * @param publicSeed
-	 *            Public seed.
-	 */
-	protected void importKeys(byte[] secretKeySeed, byte[] publicSeed) {
-		if (secretKeySeed == null) {
-			throw new NullPointerException("secretKeySeed == null");
-		}
-		if (secretKeySeed.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of secretKeySeed needs to be equal to size of digest");
-		}
-		if (publicSeed == null) {
-			throw new NullPointerException("publicSeed == null");
-		}
-		if (publicSeed.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of publicSeed needs to be equal to size of digest");
-		}
-		this.secretKeySeed = secretKeySeed;
-		this.publicSeed = publicSeed;
-	}
+    /**
+     * Import keys to WOTS+ instance.
+     *
+     * @param secretKeySeed Secret key seed.
+     * @param publicSeed    Public seed.
+     */
+    void importKeys(byte[] secretKeySeed, byte[] publicSeed)
+    {
+        if (secretKeySeed == null)
+        {
+            throw new NullPointerException("secretKeySeed == null");
+        }
+        if (secretKeySeed.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of secretKeySeed needs to be equal to size of digest");
+        }
+        if (publicSeed == null)
+        {
+            throw new NullPointerException("publicSeed == null");
+        }
+        if (publicSeed.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of publicSeed needs to be equal to size of digest");
+        }
+        this.secretKeySeed = secretKeySeed;
+        this.publicSeed = publicSeed;
+    }
 
-	/**
-	 * Creates a signature for the n-byte messageDigest.
-	 *
-	 * @param messageDigest
-	 *            Digest to sign.
-	 * @param otsHashAddress
-	 *            OTS hash address for randomization.
-	 * @return WOTS+ signature.
-	 */
-	protected WOTSPlusSignature sign(byte[] messageDigest, OTSHashAddress otsHashAddress) {
-		if (messageDigest == null) {
-			throw new NullPointerException("messageDigest == null");
-		}
-		if (messageDigest.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		List<Integer> baseWMessage = convertToBaseW(messageDigest, params.getWinternitzParameter(), params.getLen1());
-		/* create checksum */
-		int checksum = 0;
-		for (int i = 0; i < params.getLen1(); i++) {
-			checksum += params.getWinternitzParameter() - 1 - baseWMessage.get(i);
-		}
-		checksum <<= (8 - ((params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) % 8));
-		int len2Bytes = (int) Math
-				.ceil((double) (params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) / 8);
-		List<Integer> baseWChecksum = convertToBaseW(XMSSUtil.toBytesBigEndian(checksum, len2Bytes),
-				params.getWinternitzParameter(), params.getLen2());
+    /**
+     * Creates a signature for the n-byte messageDigest.
+     *
+     * @param messageDigest  Digest to sign.
+     * @param otsHashAddress OTS hash address for randomization.
+     * @return WOTS+ signature.
+     */
+    protected WOTSPlusSignature sign(byte[] messageDigest, OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest == null)
+        {
+            throw new NullPointerException("messageDigest == null");
+        }
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        List<Integer> baseWMessage = convertToBaseW(messageDigest, params.getWinternitzParameter(), params.getLen1());
+        /* create checksum */
+        int checksum = 0;
+        for (int i = 0; i < params.getLen1(); i++)
+        {
+            checksum += params.getWinternitzParameter() - 1 - baseWMessage.get(i);
+        }
+        checksum <<= (8 - ((params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) % 8));
+        int len2Bytes = (int)Math
+            .ceil((double)(params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) / 8);
+        List<Integer> baseWChecksum = convertToBaseW(XMSSUtil.toBytesBigEndian(checksum, len2Bytes),
+            params.getWinternitzParameter(), params.getLen2());
 
 		/* msg || checksum */
-		baseWMessage.addAll(baseWChecksum);
+        baseWMessage.addAll(baseWChecksum);
 
 		/* create signature */
-		byte[][] signature = new byte[params.getLen()][];
-		for (int i = 0; i < params.getLen(); i++) {
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
-			signature[i] = chain(expandSecretKeySeed(i), 0, baseWMessage.get(i), otsHashAddress);
-		}
-		return new WOTSPlusSignature(params, signature);
-	}
+        byte[][] signature = new byte[params.getLen()][];
+        for (int i = 0; i < params.getLen(); i++)
+        {
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+                .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+                .withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
+                .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+                .build();
+            signature[i] = chain(expandSecretKeySeed(i), 0, baseWMessage.get(i), otsHashAddress);
+        }
+        return new WOTSPlusSignature(params, signature);
+    }
 
-	/**
-	 * Verifies signature on message.
-	 *
-	 * @param messageDigest
-	 *            The digest that was signed.
-	 * @param signature
-	 *            Signature on digest.
-	 * @param otsHashAddress
-	 *            OTS hash address for randomization.
-	 * @return true if signature was correct false else.
-	 */
-	protected boolean verifySignature(byte[] messageDigest, WOTSPlusSignature signature,
-			OTSHashAddress otsHashAddress) {
-		if (messageDigest == null) {
-			throw new NullPointerException("messageDigest == null");
-		}
-		if (messageDigest.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		byte[][] tmpPublicKey = getPublicKeyFromSignature(messageDigest, signature, otsHashAddress).toByteArray();
-		/* compare values */
-		return XMSSUtil.compareByteArray(tmpPublicKey, getPublicKey(otsHashAddress).toByteArray()) ? true : false;
-	}
+    /**
+     * Verifies signature on message.
+     *
+     * @param messageDigest  The digest that was signed.
+     * @param signature      Signature on digest.
+     * @param otsHashAddress OTS hash address for randomization.
+     * @return true if signature was correct false else.
+     */
+    protected boolean verifySignature(byte[] messageDigest, WOTSPlusSignature signature,
+                                      OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest == null)
+        {
+            throw new NullPointerException("messageDigest == null");
+        }
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        byte[][] tmpPublicKey = getPublicKeyFromSignature(messageDigest, signature, otsHashAddress).toByteArray();
+        /* compare values */
+        return XMSSUtil.areEqual(tmpPublicKey, getPublicKey(otsHashAddress).toByteArray()) ? true : false;
+    }
 
-	/**
-	 * Calculates a public key based on digest and signature.
-	 *
-	 * @param messageDigest
-	 *            The digest that was signed.
-	 * @param signature
-	 *            Signarure on digest.
-	 * @param otsHashAddress
-	 *            OTS hash address for randomization.
-	 * @return WOTS+ public key derived from digest and signature.
-	 */
-	protected WOTSPlusPublicKeyParameters getPublicKeyFromSignature(byte[] messageDigest, WOTSPlusSignature signature,
-			OTSHashAddress otsHashAddress) {
-		if (messageDigest == null) {
-			throw new NullPointerException("messageDigest == null");
-		}
-		if (messageDigest.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		List<Integer> baseWMessage = convertToBaseW(messageDigest, params.getWinternitzParameter(), params.getLen1());
+    /**
+     * Calculates a public key based on digest and signature.
+     *
+     * @param messageDigest  The digest that was signed.
+     * @param signature      Signarure on digest.
+     * @param otsHashAddress OTS hash address for randomization.
+     * @return WOTS+ public key derived from digest and signature.
+     */
+    protected WOTSPlusPublicKeyParameters getPublicKeyFromSignature(byte[] messageDigest, WOTSPlusSignature signature,
+                                                                    OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest == null)
+        {
+            throw new NullPointerException("messageDigest == null");
+        }
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        List<Integer> baseWMessage = convertToBaseW(messageDigest, params.getWinternitzParameter(), params.getLen1());
 		/* create checksum */
-		int checksum = 0;
-		for (int i = 0; i < params.getLen1(); i++) {
-			checksum += params.getWinternitzParameter() - 1 - baseWMessage.get(i);
-		}
-		checksum <<= (8 - ((params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) % 8));
-		int len2Bytes = (int) Math
-				.ceil((double) (params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) / 8);
-		List<Integer> baseWChecksum = convertToBaseW(XMSSUtil.toBytesBigEndian(checksum, len2Bytes),
-				params.getWinternitzParameter(), params.getLen2());
+        int checksum = 0;
+        for (int i = 0; i < params.getLen1(); i++)
+        {
+            checksum += params.getWinternitzParameter() - 1 - baseWMessage.get(i);
+        }
+        checksum <<= (8 - ((params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) % 8));
+        int len2Bytes = (int)Math
+            .ceil((double)(params.getLen2() * XMSSUtil.log2(params.getWinternitzParameter())) / 8);
+        List<Integer> baseWChecksum = convertToBaseW(XMSSUtil.toBytesBigEndian(checksum, len2Bytes),
+            params.getWinternitzParameter(), params.getLen2());
 
 		/* msg || checksum */
-		baseWMessage.addAll(baseWChecksum);
+        baseWMessage.addAll(baseWChecksum);
 
-		byte[][] publicKey = new byte[params.getLen()][];
-		for (int i = 0; i < params.getLen(); i++) {
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
-			publicKey[i] = chain(signature.toByteArray()[i], baseWMessage.get(i),
-					params.getWinternitzParameter() - 1 - baseWMessage.get(i), otsHashAddress);
-		}
-		return new WOTSPlusPublicKeyParameters(params, publicKey);
-	}
+        byte[][] publicKey = new byte[params.getLen()][];
+        for (int i = 0; i < params.getLen(); i++)
+        {
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+                .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+                .withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
+                .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+                .build();
+            publicKey[i] = chain(signature.toByteArray()[i], baseWMessage.get(i),
+                params.getWinternitzParameter() - 1 - baseWMessage.get(i), otsHashAddress);
+        }
+        return new WOTSPlusPublicKeyParameters(params, publicKey);
+    }
 
-	/**
-	 * Computes an iteration of F on an n-byte input using outputs of PRF.
-	 *
-	 * @param startHash
-	 *            Starting point.
-	 * @param startIndex
-	 *            Start index.
-	 * @param steps
-	 *            Steps to take.
-	 * @param otsHashAddress
-	 *            OTS hash address for randomization.
-	 * @return Value obtained by iterating F for steps times on input startHash,
-	 *         using the outputs of PRF.
-	 */
-	private byte[] chain(byte[] startHash, int startIndex, int steps, OTSHashAddress otsHashAddress) {
-		int n = params.getDigestSize();
-		if (startHash == null) {
-			throw new NullPointerException("startHash == null");
-		}
-		if (startHash.length != n) {
-			throw new IllegalArgumentException("startHash needs to be " + n + "bytes");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		if (otsHashAddress.toByteArray() == null) {
-			throw new NullPointerException("otsHashAddress byte array == null");
-		}
-		if ((startIndex + steps) > params.getWinternitzParameter() - 1) {
-			throw new IllegalArgumentException("max chain length must not be greater than w");
-		}
+    /**
+     * Computes an iteration of F on an n-byte input using outputs of PRF.
+     *
+     * @param startHash      Starting point.
+     * @param startIndex     Start index.
+     * @param steps          Steps to take.
+     * @param otsHashAddress OTS hash address for randomization.
+     * @return Value obtained by iterating F for steps times on input startHash,
+     * using the outputs of PRF.
+     */
+    private byte[] chain(byte[] startHash, int startIndex, int steps, OTSHashAddress otsHashAddress)
+    {
+        int n = params.getDigestSize();
+        if (startHash == null)
+        {
+            throw new NullPointerException("startHash == null");
+        }
+        if (startHash.length != n)
+        {
+            throw new IllegalArgumentException("startHash needs to be " + n + "bytes");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        if (otsHashAddress.toByteArray() == null)
+        {
+            throw new NullPointerException("otsHashAddress byte array == null");
+        }
+        if ((startIndex + steps) > params.getWinternitzParameter() - 1)
+        {
+            throw new IllegalArgumentException("max chain length must not be greater than w");
+        }
 
-		if (steps == 0) {
-			return startHash;
-		}
+        if (steps == 0)
+        {
+            return startHash;
+        }
 
-		byte[] tmp = chain(startHash, startIndex, steps - 1, otsHashAddress);
-		otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(otsHashAddress.getChainAddress())
-				.withHashAddress(startIndex + steps - 1).withKeyAndMask(0).build();
-		byte[] key = khf.PRF(publicSeed, otsHashAddress.toByteArray());
-		otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(otsHashAddress.getChainAddress())
-				.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(1).build();
-		byte[] bitmask = khf.PRF(publicSeed, otsHashAddress.toByteArray());
-		byte[] tmpMasked = new byte[n];
-		for (int i = 0; i < n; i++) {
-			tmpMasked[i] = (byte) (tmp[i] ^ bitmask[i]);
-		}
-		tmp = khf.F(key, tmpMasked);
-		return tmp;
-	}
+        byte[] tmp = chain(startHash, startIndex, steps - 1, otsHashAddress);
+        otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(otsHashAddress.getChainAddress())
+            .withHashAddress(startIndex + steps - 1).withKeyAndMask(0).build();
+        byte[] key = khf.PRF(publicSeed, otsHashAddress.toByteArray());
+        otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(otsHashAddress.getChainAddress())
+            .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(1).build();
+        byte[] bitmask = khf.PRF(publicSeed, otsHashAddress.toByteArray());
+        byte[] tmpMasked = new byte[n];
+        for (int i = 0; i < n; i++)
+        {
+            tmpMasked[i] = (byte)(tmp[i] ^ bitmask[i]);
+        }
+        tmp = khf.F(key, tmpMasked);
+        return tmp;
+    }
 
-	/**
-	 * Obtain base w values from Input.
-	 *
-	 * @param messageDigest
-	 *            Input data.
-	 * @param w
-	 *            Base.
-	 * @param outLength
-	 *            Length of output.
-	 * @return outLength-length list of base w integers.
-	 */
-	private List<Integer> convertToBaseW(byte[] messageDigest, int w, int outLength) {
-		if (messageDigest == null) {
-			throw new NullPointerException("msg == null");
-		}
-		if (w != 4 && w != 16) {
-			throw new IllegalArgumentException("w needs to be 4 or 16");
-		}
-		int logW = XMSSUtil.log2(w);
-		if (outLength > ((8 * messageDigest.length) / logW)) {
-			throw new IllegalArgumentException("outLength too big");
-		}
+    /**
+     * Obtain base w values from Input.
+     *
+     * @param messageDigest Input data.
+     * @param w             Base.
+     * @param outLength     Length of output.
+     * @return outLength-length list of base w integers.
+     */
+    private List<Integer> convertToBaseW(byte[] messageDigest, int w, int outLength)
+    {
+        if (messageDigest == null)
+        {
+            throw new NullPointerException("msg == null");
+        }
+        if (w != 4 && w != 16)
+        {
+            throw new IllegalArgumentException("w needs to be 4 or 16");
+        }
+        int logW = XMSSUtil.log2(w);
+        if (outLength > ((8 * messageDigest.length) / logW))
+        {
+            throw new IllegalArgumentException("outLength too big");
+        }
 
-		ArrayList<Integer> res = new ArrayList<Integer>();
-		for (int i = 0; i < messageDigest.length; i++) {
-			for (int j = 8 - logW; j >= 0; j -= logW) {
-				res.add((messageDigest[i] >> j) & (w - 1));
-				if (res.size() == outLength) {
-					return res;
-				}
-			}
-		}
-		return res;
-	}
+        ArrayList<Integer> res = new ArrayList<Integer>();
+        for (int i = 0; i < messageDigest.length; i++)
+        {
+            for (int j = 8 - logW; j >= 0; j -= logW)
+            {
+                res.add((messageDigest[i] >> j) & (w - 1));
+                if (res.size() == outLength)
+                {
+                    return res;
+                }
+            }
+        }
+        return res;
+    }
 
-	/**
-	 * Derive private key at index from secret key seed.
-	 *
-	 * @param index
-	 *            Index.
-	 * @return Private key at index.
-	 */
-	private byte[] expandSecretKeySeed(int index) {
-		if (index < 0 || index >= params.getLen()) {
-			throw new IllegalArgumentException("index out of bounds");
-		}
-		return khf.PRF(secretKeySeed, XMSSUtil.toBytesBigEndian(index, 32));
-	}
+    /**
+     * Derive WOTS+ secret key for specific index as in XMSS ref impl Andreas
+     * Huelsing.
+     *
+     * @param otsHashAddress
+     * @return WOTS+ secret key at index.
+     */
+    protected byte[] getWOTSPlusSecretKey(byte[] secretKeySeed, OTSHashAddress otsHashAddress)
+    {
+        otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withOTSAddress(otsHashAddress.getOTSAddress()).build();
+        return khf.PRF(secretKeySeed, otsHashAddress.toByteArray());
+    }
 
-	/**
-	 * Getter parameters.
-	 *
-	 * @return params.
-	 */
-	protected WOTSPlusParameters getParams() {
-		return params;
-	}
+    /**
+     * Derive private key at index from secret key seed.
+     *
+     * @param index Index.
+     * @return Private key at index.
+     */
+    private byte[] expandSecretKeySeed(int index)
+    {
+        if (index < 0 || index >= params.getLen())
+        {
+            throw new IllegalArgumentException("index out of bounds");
+        }
+        return khf.PRF(secretKeySeed, XMSSUtil.toBytesBigEndian(index, 32));
+    }
 
-	/**
-	 * Getter keyed hash functions.
-	 *
-	 * @return keyed hash functions.
-	 */
-	protected KeyedHashFunctions getKhf() {
-		return khf;
-	}
+    /**
+     * Getter parameters.
+     *
+     * @return params.
+     */
+    protected WOTSPlusParameters getParams()
+    {
+        return params;
+    }
 
-	/**
-	 * Getter secret key seed.
-	 *
-	 * @return secret key seed.
-	 */
-	protected byte[] getSecretKeySeed() {
-		return XMSSUtil.cloneArray(getSecretKeySeed());
-	}
+    /**
+     * Getter keyed hash functions.
+     *
+     * @return keyed hash functions.
+     */
+    protected KeyedHashFunctions getKhf()
+    {
+        return khf;
+    }
 
-	/**
-	 * Getter public seed.
-	 *
-	 * @return public seed.
-	 */
-	protected byte[] getPublicSeed() {
-		return XMSSUtil.cloneArray(publicSeed);
-	}
+    /**
+     * Getter secret key seed.
+     *
+     * @return secret key seed.
+     */
+    protected byte[] getSecretKeySeed()
+    {
+        return Arrays.clone(secretKeySeed);
+    }
 
-	/**
-	 * Getter private key.
-	 *
-	 * @return WOTS+ private key.
-	 */
-	protected WOTSPlusPrivateKeyParameters getPrivateKey() {
-		byte[][] privateKey = new byte[params.getLen()][];
-		for (int i = 0; i < privateKey.length; i++) {
-			privateKey[i] = expandSecretKeySeed(i);
-		}
-		return new WOTSPlusPrivateKeyParameters(params, privateKey);
-	}
+    /**
+     * Getter public seed.
+     *
+     * @return public seed.
+     */
+    protected byte[] getPublicSeed()
+    {
+        return Arrays.clone(publicSeed);
+    }
 
-	/**
-	 * Calculates a new public key based on the state of secretKeySeed,
-	 * publicSeed and otsHashAddress.
-	 *
-	 * @param otsHashAddress
-	 *            OTS hash address for randomization.
-	 * @return WOTS+ public key.
-	 */
-	protected WOTSPlusPublicKeyParameters getPublicKey(OTSHashAddress otsHashAddress) {
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
-		byte[][] publicKey = new byte[params.getLen()][];
+    /**
+     * Getter private key.
+     *
+     * @return WOTS+ private key.
+     */
+    protected WOTSPlusPrivateKeyParameters getPrivateKey()
+    {
+        byte[][] privateKey = new byte[params.getLen()][];
+        for (int i = 0; i < privateKey.length; i++)
+        {
+            privateKey[i] = expandSecretKeySeed(i);
+        }
+        return new WOTSPlusPrivateKeyParameters(params, privateKey);
+    }
+
+    /**
+     * Calculates a new public key based on the state of secretKeySeed,
+     * publicSeed and otsHashAddress.
+     *
+     * @param otsHashAddress OTS hash address for randomization.
+     * @return WOTS+ public key.
+     */
+    protected WOTSPlusPublicKeyParameters getPublicKey(OTSHashAddress otsHashAddress)
+    {
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        byte[][] publicKey = new byte[params.getLen()][];
 		/* derive public key from secretKeySeed */
-		for (int i = 0; i < params.getLen(); i++) {
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-					.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-					.withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
-					.withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
-					.build();
-			publicKey[i] = chain(expandSecretKeySeed(i), 0, params.getWinternitzParameter() - 1, otsHashAddress);
-		}
-		return new WOTSPlusPublicKeyParameters(params, publicKey);
-	}
+        for (int i = 0; i < params.getLen(); i++)
+        {
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder()
+                .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+                .withOTSAddress(otsHashAddress.getOTSAddress()).withChainAddress(i)
+                .withHashAddress(otsHashAddress.getHashAddress()).withKeyAndMask(otsHashAddress.getKeyAndMask())
+                .build();
+            publicKey[i] = chain(expandSecretKeySeed(i), 0, params.getWinternitzParameter() - 1, otsHashAddress);
+        }
+        return new WOTSPlusPublicKeyParameters(params, publicKey);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusOid.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusOid.java
index febb758..150b7a0 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusOid.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusOid.java
@@ -6,89 +6,92 @@
 
 /**
  * WOTS+ OID class.
- * 
  */
-public final class WOTSPlusOid implements XMSSOid {
+final class WOTSPlusOid
+    implements XMSSOid
+{
 
-	/**
-	 * XMSS OID lookup table.
-	 */
-	private static final Map<String, WOTSPlusOid> oidLookupTable;
+    /**
+     * XMSS OID lookup table.
+     */
+    private static final Map<String, WOTSPlusOid> oidLookupTable;
 
-	static {
-		Map<String, WOTSPlusOid> map = new HashMap<String, WOTSPlusOid>();
-		map.put(createKey("SHA-256", 32, 16, 67), new WOTSPlusOid(0x01000001, "WOTSP_SHA2-256_W16"));
-		map.put(createKey("SHA-512", 64, 16, 131), new WOTSPlusOid(0x02000002, "WOTSP_SHA2-512_W16"));
-		map.put(createKey("SHAKE128", 32, 16, 67), new WOTSPlusOid(0x03000003, "WOTSP_SHAKE128_W16"));
-		map.put(createKey("SHAKE256", 64, 16, 131), new WOTSPlusOid(0x04000004, "WOTSP_SHAKE256_W16"));
-		oidLookupTable = Collections.unmodifiableMap(map);
-	}
+    static
+    {
+        Map<String, WOTSPlusOid> map = new HashMap<String, WOTSPlusOid>();
+        map.put(createKey("SHA-256", 32, 16, 67), new WOTSPlusOid(0x01000001, "WOTSP_SHA2-256_W16"));
+        map.put(createKey("SHA-512", 64, 16, 131), new WOTSPlusOid(0x02000002, "WOTSP_SHA2-512_W16"));
+        map.put(createKey("SHAKE128", 32, 16, 67), new WOTSPlusOid(0x03000003, "WOTSP_SHAKE128_W16"));
+        map.put(createKey("SHAKE256", 64, 16, 131), new WOTSPlusOid(0x04000004, "WOTSP_SHAKE256_W16"));
+        oidLookupTable = Collections.unmodifiableMap(map);
+    }
 
-	/**
-	 * OID.
-	 */
-	private final int oid;
-	/**
-	 * String representation of OID.
-	 */
-	private final String stringRepresentation;
+    /**
+     * OID.
+     */
+    private final int oid;
+    /**
+     * String representation of OID.
+     */
+    private final String stringRepresentation;
 
-	/**
-	 * Constructor...
-	 *
-	 * @param oid
-	 *            OID.
-	 * @param stringRepresentation
-	 *            String representation of OID.
-	 */
-	private WOTSPlusOid(int oid, String stringRepresentation) {
-		super();
-		this.oid = oid;
-		this.stringRepresentation = stringRepresentation;
-	}
+    /**
+     * Constructor...
+     *
+     * @param oid                  OID.
+     * @param stringRepresentation String representation of OID.
+     */
+    private WOTSPlusOid(int oid, String stringRepresentation)
+    {
+        super();
+        this.oid = oid;
+        this.stringRepresentation = stringRepresentation;
+    }
 
-	/**
-	 * Lookup OID.
-	 *
-	 * @param algorithmName
-	 *            Algorithm name.
-	 * @param winternitzParameter
-	 *            Winternitz parameter.
-	 * @return WOTS+ OID if parameters were found, null else.
-	 */
-	protected static WOTSPlusOid lookup(String algorithmName, int digestSize, int winternitzParameter, int len) {
-		if (algorithmName == null) {
-			throw new NullPointerException("algorithmName == null");
-		}
-		return oidLookupTable.get(createKey(algorithmName, digestSize, winternitzParameter, len));
-	}
+    /**
+     * Lookup OID.
+     *
+     * @param algorithmName       Algorithm name.
+     * @param winternitzParameter Winternitz parameter.
+     * @return WOTS+ OID if parameters were found, null else.
+     */
+    protected static WOTSPlusOid lookup(String algorithmName, int digestSize, int winternitzParameter, int len)
+    {
+        if (algorithmName == null)
+        {
+            throw new NullPointerException("algorithmName == null");
+        }
+        return oidLookupTable.get(createKey(algorithmName, digestSize, winternitzParameter, len));
+    }
 
-	/**
-	 * Create a key based on parameters.
-	 *
-	 * @param algorithmName
-	 *            Algorithm name.
-	 * @param winternitzParameter
-	 *            Winternitz Parameter.
-	 * @return String representation of parameters for lookup table.
-	 */
-	private static String createKey(String algorithmName, int digestSize, int winternitzParameter, int len) {
-		if (algorithmName == null) {
-			throw new NullPointerException("algorithmName == null");
-		}
-		return algorithmName + "-" + digestSize + "-" + winternitzParameter + "-" + len;
-	}
+    /**
+     * Create a key based on parameters.
+     *
+     * @param algorithmName       Algorithm name.
+     * @param winternitzParameter Winternitz Parameter.
+     * @return String representation of parameters for lookup table.
+     */
+    private static String createKey(String algorithmName, int digestSize, int winternitzParameter, int len)
+    {
+        if (algorithmName == null)
+        {
+            throw new NullPointerException("algorithmName == null");
+        }
+        return algorithmName + "-" + digestSize + "-" + winternitzParameter + "-" + len;
+    }
 
-	/**
-	 * Getter OID.
-	 *
-	 * @return OID.
-	 */
-	public int getOid() {
-		return oid;
-	}
+    /**
+     * Getter OID.
+     *
+     * @return OID.
+     */
+    public int getOid()
+    {
+        return oid;
+    }
 
-	public String toString() {
-		return stringRepresentation;
-	}
+    public String toString()
+    {
+        return stringRepresentation;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusParameters.java
index b1b3cd3..3a32629 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusParameters.java
@@ -5,122 +5,132 @@
 /**
  * WOTS+ Parameters.
  */
-public final class WOTSPlusParameters {
+final class WOTSPlusParameters
+{
 
-	/**
-	 * OID.
-	 */
-	private final XMSSOid oid;
-	/**
-	 * Digest used in WOTS+.
-	 */
-	private final Digest digest;
-	/**
-	 * The message digest size.
-	 */
-	private final int digestSize;
-	/**
-	 * The Winternitz parameter (currently fixed to 16).
-	 */
-	private final int winternitzParameter;
-	/**
-	 * The number of n-byte string elements in a WOTS+ secret key, public key,
-	 * and signature.
-	 */
-	private final int len;
-	/**
-	 * len1.
-	 */
-	private final int len1;
-	/**
-	 * len2.
-	 */
-	private final int len2;
+    /**
+     * OID.
+     */
+    private final XMSSOid oid;
+    /**
+     * Digest used in WOTS+.
+     */
+    private final Digest digest;
+    /**
+     * The message digest size.
+     */
+    private final int digestSize;
+    /**
+     * The Winternitz parameter (currently fixed to 16).
+     */
+    private final int winternitzParameter;
+    /**
+     * The number of n-byte string elements in a WOTS+ secret key, public key,
+     * and signature.
+     */
+    private final int len;
+    /**
+     * len1.
+     */
+    private final int len1;
+    /**
+     * len2.
+     */
+    private final int len2;
 
-	/**
-	 * Constructor...
-	 *
-	 * @param digest
-	 *            The digest used for WOTS+.
-	 */
-	protected WOTSPlusParameters(Digest digest) {
-		super();
-		if (digest == null) {
-			throw new NullPointerException("digest == null");
-		}
-		this.digest = digest;
-		digestSize = XMSSUtil.getDigestSize(digest);
-		winternitzParameter = 16;
-		len1 = (int) Math.ceil((double) (8 * digestSize) / XMSSUtil.log2(winternitzParameter));
-		len2 = (int) Math.floor(XMSSUtil.log2(len1 * (winternitzParameter - 1)) / XMSSUtil.log2(winternitzParameter))
-				+ 1;
-		len = len1 + len2;
-		oid = WOTSPlusOid.lookup(digest.getAlgorithmName(), digestSize, winternitzParameter, len);
-		if (oid == null) {
-			throw new IllegalArgumentException("cannot find OID for digest algorithm: " + digest.getAlgorithmName());
-		}
-	}
+    /**
+     * Constructor...
+     *
+     * @param digest The digest used for WOTS+.
+     */
+    protected WOTSPlusParameters(Digest digest)
+    {
+        super();
+        if (digest == null)
+        {
+            throw new NullPointerException("digest == null");
+        }
+        this.digest = digest;
+        digestSize = XMSSUtil.getDigestSize(digest);
+        winternitzParameter = 16;
+        len1 = (int)Math.ceil((double)(8 * digestSize) / XMSSUtil.log2(winternitzParameter));
+        len2 = (int)Math.floor(XMSSUtil.log2(len1 * (winternitzParameter - 1)) / XMSSUtil.log2(winternitzParameter))
+            + 1;
+        len = len1 + len2;
+        oid = WOTSPlusOid.lookup(digest.getAlgorithmName(), digestSize, winternitzParameter, len);
+        if (oid == null)
+        {
+            throw new IllegalArgumentException("cannot find OID for digest algorithm: " + digest.getAlgorithmName());
+        }
+    }
 
-	/**
-	 * Getter OID.
-	 *
-	 * @return WOTS+ OID.
-	 */
-	protected XMSSOid getOid() {
-		return oid;
-	}
+    /**
+     * Getter OID.
+     *
+     * @return WOTS+ OID.
+     */
+    protected XMSSOid getOid()
+    {
+        return oid;
+    }
 
-	/**
-	 * Getter digest.
-	 *
-	 * @return digest.
-	 */
-	protected Digest getDigest() {
-		return digest;
-	}
+    /**
+     * Getter digest.
+     *
+     * @return digest.
+     */
+    protected Digest getDigest()
+    {
+        return digest;
+    }
 
-	/**
-	 * Getter digestSize.
-	 *
-	 * @return digestSize.
-	 */
-	protected int getDigestSize() {
-		return digestSize;
-	}
+    /**
+     * Getter digestSize.
+     *
+     * @return digestSize.
+     */
+    protected int getDigestSize()
+    {
+        return digestSize;
+    }
 
-	/**
-	 * Getter WinternitzParameter.
-	 *
-	 * @return winternitzParameter.
-	 */
-	protected int getWinternitzParameter() {
-		return winternitzParameter;
-	}
+    /**
+     * Getter WinternitzParameter.
+     *
+     * @return winternitzParameter.
+     */
+    protected int getWinternitzParameter()
+    {
+        return winternitzParameter;
+    }
 
-	/**
-	 * Getter len.
-	 *
-	 * @return len.
-	 */
-	protected int getLen() {
-		return len;
-	}
+    /**
+     * Getter len.
+     *
+     * @return len.
+     */
+    protected int getLen()
+    {
+        return len;
+    }
 
-	/**
-	 * Getter len1.
-	 *
-	 * @return len1.
-	 */
-	protected int getLen1() {
-		return len1;
-	}
+    /**
+     * Getter len1.
+     *
+     * @return len1.
+     */
+    protected int getLen1()
+    {
+        return len1;
+    }
 
-	/**
-	 * Getter len2.
-	 *
-	 * @return len2.
-	 */
-	protected int getLen2() {
-		return len2;
-	}
+    /**
+     * Getter len2.
+     *
+     * @return len2.
+     */
+    protected int getLen2()
+    {
+        return len2;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPrivateKeyParameters.java
index da5c494..482ff6b 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPrivateKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPrivateKeyParameters.java
@@ -2,35 +2,43 @@
 
 /**
  * WOTS+ private key.
- *
  */
-public final class WOTSPlusPrivateKeyParameters {
+final class WOTSPlusPrivateKeyParameters
+{
 
-	private final byte[][] privateKey;
+    private final byte[][] privateKey;
 
-	protected WOTSPlusPrivateKeyParameters(WOTSPlusParameters params, byte[][] privateKey) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		if (privateKey == null) {
-			throw new NullPointerException("privateKey == null");
-		}
-		if (XMSSUtil.hasNullPointer(privateKey)) {
-			throw new NullPointerException("privateKey byte array == null");
-		}
-		if (privateKey.length != params.getLen()) {
-			throw new IllegalArgumentException("wrong privateKey format");
-		}
-		for (int i = 0; i < privateKey.length; i++) {
-			if (privateKey[i].length != params.getDigestSize()) {
-				throw new IllegalArgumentException("wrong privateKey format");
-			}
-		}
-		this.privateKey = XMSSUtil.cloneArray(privateKey);
-	}
+    protected WOTSPlusPrivateKeyParameters(WOTSPlusParameters params, byte[][] privateKey)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        if (privateKey == null)
+        {
+            throw new NullPointerException("privateKey == null");
+        }
+        if (XMSSUtil.hasNullPointer(privateKey))
+        {
+            throw new NullPointerException("privateKey byte array == null");
+        }
+        if (privateKey.length != params.getLen())
+        {
+            throw new IllegalArgumentException("wrong privateKey format");
+        }
+        for (int i = 0; i < privateKey.length; i++)
+        {
+            if (privateKey[i].length != params.getDigestSize())
+            {
+                throw new IllegalArgumentException("wrong privateKey format");
+            }
+        }
+        this.privateKey = XMSSUtil.cloneArray(privateKey);
+    }
 
-	protected byte[][] toByteArray() {
-		return XMSSUtil.cloneArray(privateKey);
-	}
+    protected byte[][] toByteArray()
+    {
+        return XMSSUtil.cloneArray(privateKey);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPublicKeyParameters.java
index c602d25..9d3ec9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusPublicKeyParameters.java
@@ -2,35 +2,43 @@
 
 /**
  * WOTS+ public key.
- *
  */
-public final class WOTSPlusPublicKeyParameters {
+final class WOTSPlusPublicKeyParameters
+{
 
-	private final byte[][] publicKey;
+    private final byte[][] publicKey;
 
-	protected WOTSPlusPublicKeyParameters(WOTSPlusParameters params, byte[][] publicKey) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		if (XMSSUtil.hasNullPointer(publicKey)) {
-			throw new NullPointerException("publicKey byte array == null");
-		}
-		if (publicKey.length != params.getLen()) {
-			throw new IllegalArgumentException("wrong publicKey size");
-		}
-		for (int i = 0; i < publicKey.length; i++) {
-			if (publicKey[i].length != params.getDigestSize()) {
-				throw new IllegalArgumentException("wrong publicKey format");
-			}
-		}
-		this.publicKey = XMSSUtil.cloneArray(publicKey);
-	}
+    protected WOTSPlusPublicKeyParameters(WOTSPlusParameters params, byte[][] publicKey)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
+        if (XMSSUtil.hasNullPointer(publicKey))
+        {
+            throw new NullPointerException("publicKey byte array == null");
+        }
+        if (publicKey.length != params.getLen())
+        {
+            throw new IllegalArgumentException("wrong publicKey size");
+        }
+        for (int i = 0; i < publicKey.length; i++)
+        {
+            if (publicKey[i].length != params.getDigestSize())
+            {
+                throw new IllegalArgumentException("wrong publicKey format");
+            }
+        }
+        this.publicKey = XMSSUtil.cloneArray(publicKey);
+    }
 
-	protected byte[][] toByteArray() {
-		return XMSSUtil.cloneArray(publicKey);
-	}
+    protected byte[][] toByteArray()
+    {
+        return XMSSUtil.cloneArray(publicKey);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusSignature.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusSignature.java
index b6592eb..1ae0156 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusSignature.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/WOTSPlusSignature.java
@@ -2,35 +2,43 @@
 
 /**
  * WOTS+ signature.
- *
  */
-public final class WOTSPlusSignature {
+final class WOTSPlusSignature
+{
 
-	private byte[][] signature;
+    private byte[][] signature;
 
-	protected WOTSPlusSignature(WOTSPlusParameters params, byte[][] signature) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (XMSSUtil.hasNullPointer(signature)) {
-			throw new NullPointerException("signature byte array == null");
-		}
-		if (signature.length != params.getLen()) {
-			throw new IllegalArgumentException("wrong signature size");
-		}
-		for (int i = 0; i < signature.length; i++) {
-			if (signature[i].length != params.getDigestSize()) {
-				throw new IllegalArgumentException("wrong signature format");
-			}
-		}
-		this.signature = XMSSUtil.cloneArray(signature);
-	}
+    protected WOTSPlusSignature(WOTSPlusParameters params, byte[][] signature)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (XMSSUtil.hasNullPointer(signature))
+        {
+            throw new NullPointerException("signature byte array == null");
+        }
+        if (signature.length != params.getLen())
+        {
+            throw new IllegalArgumentException("wrong signature size");
+        }
+        for (int i = 0; i < signature.length; i++)
+        {
+            if (signature[i].length != params.getDigestSize())
+            {
+                throw new IllegalArgumentException("wrong signature format");
+            }
+        }
+        this.signature = XMSSUtil.cloneArray(signature);
+    }
 
-	public byte[][] toByteArray() {
-		return XMSSUtil.cloneArray(signature);
-	}
+    public byte[][] toByteArray()
+    {
+        return XMSSUtil.cloneArray(signature);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSS.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSS.java
index d0ed3e9..c765237 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSS.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSS.java
@@ -1,625 +1,350 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.io.IOException;
 import java.security.SecureRandom;
 import java.text.ParseException;
 
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.util.Arrays;
+
 /**
  * XMSS.
- *
  */
-public class XMSS {
+public class XMSS
+{
 
-	/**
-	 * XMSS parameters.
-	 */
-	private XMSSParameters params;
-	/**
-	 * WOTS+ instance.
-	 */
-	private WOTSPlus wotsPlus;
-	/**
-	 * PRNG.
-	 */
-	private SecureRandom prng;
-	/**
-	 * Randomization functions.
-	 */
-	private KeyedHashFunctions khf;
-	/**
-	 * XMSS private key.
-	 */
-	private XMSSPrivateKeyParameters privateKey;
-	/**
-	 * XMSS public key.
-	 */
-	private XMSSPublicKeyParameters publicKey;
+    /**
+     * XMSS parameters.
+     */
+    private final XMSSParameters params;
+    /**
+     * WOTS+ instance.
+     */
+    private WOTSPlus wotsPlus;
+    /**
+     * PRNG.
+     */
+    private SecureRandom prng;
 
-	/**
-	 * XMSS constructor...
-	 *
-	 * @param params
-	 *            XMSSParameters.
-	 */
-	public XMSS(XMSSParameters params) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		this.params = params;
-		wotsPlus = params.getWOTSPlus();
-		prng = params.getPRNG();
-		khf = wotsPlus.getKhf();
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withBDSState(new BDS(this)).build();
-			publicKey = new XMSSPublicKeyParameters.Builder(params).build();
-		} catch (ParseException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
+    /**
+     * XMSS private key.
+     */
+    private XMSSPrivateKeyParameters privateKey;
+    /**
+     * XMSS public key.
+     */
+    private XMSSPublicKeyParameters publicKey;
 
-	/**
-	 * Generate a new XMSS private key / public key pair.
-	 * 
-	 */
-	public void generateKeys() {
-		/* generate private key */
-		privateKey = generatePrivateKey();
-		XMSSNode root = getBDSState().initialize((OTSHashAddress) new OTSHashAddress.Builder().build());
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(privateKey.getIndex())
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(privateKey.getPublicSeed()).withRoot(root.getValue())
-					.withBDSState(privateKey.getBDSState()).build();
-			publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root.getValue())
-					.withPublicSeed(getPublicSeed()).build();
-		} catch (ParseException ex) {
-			/* should not be possible */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
+    /**
+     * XMSS constructor...
+     *
+     * @param params XMSSParameters.
+     */
+    public XMSS(XMSSParameters params, SecureRandom prng)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        this.params = params;
+        wotsPlus = params.getWOTSPlus();
+        this.prng = prng;
+    }
 
-	/**
-	 * Generate an XMSS private key.
-	 *
-	 * @return XMSS private key.
-	 */
-	private XMSSPrivateKeyParameters generatePrivateKey() {
-		int n = params.getDigestSize();
-		byte[] secretKeySeed = new byte[n];
-		prng.nextBytes(secretKeySeed);
-		byte[] secretKeyPRF = new byte[n];
-		prng.nextBytes(secretKeyPRF);
-		byte[] publicSeed = new byte[n];
-		prng.nextBytes(publicSeed);
+//    public void generateKeys()
+//    {
+//        /* generate private key */
+//        privateKey = generatePrivateKey(params, prng);
+//        XMSSNode root = privateKey.getBDSState().initialize(privateKey, (OTSHashAddress)new OTSHashAddress.Builder().build());
+//
+//        privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(privateKey.getIndex())
+//            .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
+//            .withPublicSeed(privateKey.getPublicSeed()).withRoot(root.getValue())
+//            .withBDSState(privateKey.getBDSState()).build();
+//        publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root.getValue())
+//            .withPublicSeed(getPublicSeed()).build();
+//
+//    }
+//
+//    /**
+//     * Generate an XMSS private key.
+//     *
+//     * @return XMSS private key.
+//     */
+//    private XMSSPrivateKeyParameters generatePrivateKey(XMSSParameters params, SecureRandom prng)
+//    {
+//        int n = params.getDigestSize();
+//        byte[] secretKeySeed = new byte[n];
+//        prng.nextBytes(secretKeySeed);
+//        byte[] secretKeyPRF = new byte[n];
+//        prng.nextBytes(secretKeyPRF);
+//        byte[] publicSeed = new byte[n];
+//        prng.nextBytes(publicSeed);
+//
+//        XMSS xmss = new XMSS(params, prng);
+//
+////        this.privateKey = xmss.privateKey;
+////        this.publicKey = xmss.publicKey;
+////        this.wotsPlus = xmss.wotsPlus;
+////        this.khf = xmss.khf;
+//
+//        XMSSPrivateKeyParameters privateKey = new XMSSPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
+//            .withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
+//            .withBDSState(new BDS(xmss)).build();
+//
+//        return privateKey;
+//    }
 
-		XMSSPrivateKeyParameters privateKey = null;
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
-					.withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
-					.withBDSState(this.privateKey.getBDSState()).build();
-		} catch (ParseException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-		return privateKey;
-	}
+    /**
+     * Generate a new XMSS private key / public key pair.
+     */
+    public void generateKeys()
+    {
+        XMSSKeyPairGenerator kpGen = new XMSSKeyPairGenerator();
 
-	/**
-	 * Import XMSS private key / public key pair.
-	 * 
-	 * @param privateKey
-	 *            XMSS private key.
-	 * @param publicKey
-	 *            XMSS public key.
-	 * @throws ParseException
-	 * @throws ClassNotFoundException
-	 * @throws IOException
-	 */
-	public void importState(byte[] privateKey, byte[] publicKey)
-			throws ParseException, ClassNotFoundException, IOException {
-		if (privateKey == null) {
-			throw new NullPointerException("privateKey == null");
-		}
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		/* import keys */
-		XMSSPrivateKeyParameters tmpPrivateKey = new XMSSPrivateKeyParameters.Builder(params)
-				.withPrivateKey(privateKey, this).build();
-		XMSSPublicKeyParameters tmpPublicKey = new XMSSPublicKeyParameters.Builder(params).withPublicKey(publicKey)
-				.build();
-		if (!XMSSUtil.compareByteArray(tmpPrivateKey.getRoot(), tmpPublicKey.getRoot())) {
-			throw new IllegalStateException("root of private key and public key do not match");
-		}
-		if (!XMSSUtil.compareByteArray(tmpPrivateKey.getPublicSeed(), tmpPublicKey.getPublicSeed())) {
-			throw new IllegalStateException("public seed of private key and public key do not match");
-		}
+        kpGen.init(new XMSSKeyGenerationParameters(getParams(), prng));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        privateKey = (XMSSPrivateKeyParameters)kp.getPrivate();
+        publicKey = (XMSSPublicKeyParameters)kp.getPublic();
+
+        wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
+    }
+
+    void importState(XMSSPrivateKeyParameters privateKey, XMSSPublicKeyParameters publicKey)
+    {
+        if (!Arrays.areEqual(privateKey.getRoot(), publicKey.getRoot()))
+        {
+            throw new IllegalStateException("root of private key and public key do not match");
+        }
+        if (!Arrays.areEqual(privateKey.getPublicSeed(), publicKey.getPublicSeed()))
+        {
+            throw new IllegalStateException("public seed of private key and public key do not match");
+        }
+        /* import */
+        this.privateKey = privateKey;
+        this.publicKey = publicKey;
+
+        wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
+    }
+
+    /**
+     * Import XMSS private key / public key pair.
+     *
+     * @param privateKey XMSS private key.
+     * @param publicKey  XMSS public key.
+     */
+    public void importState(byte[] privateKey, byte[] publicKey)
+    {
+        if (privateKey == null)
+        {
+            throw new NullPointerException("privateKey == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
+        /* import keys */
+        XMSSPrivateKeyParameters tmpPrivateKey = new XMSSPrivateKeyParameters.Builder(params)
+            .withPrivateKey(privateKey, this.getParams()).build();
+        XMSSPublicKeyParameters tmpPublicKey = new XMSSPublicKeyParameters.Builder(params).withPublicKey(publicKey)
+            .build();
+        if (!Arrays.areEqual(tmpPrivateKey.getRoot(), tmpPublicKey.getRoot()))
+        {
+            throw new IllegalStateException("root of private key and public key do not match");
+        }
+        if (!Arrays.areEqual(tmpPrivateKey.getPublicSeed(), tmpPublicKey.getPublicSeed()))
+        {
+            throw new IllegalStateException("public seed of private key and public key do not match");
+        }
 		/* import */
-		this.privateKey = tmpPrivateKey;
-		this.publicKey = tmpPublicKey;
-		wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
-	}
+        this.privateKey = tmpPrivateKey;
+        this.publicKey = tmpPublicKey;
+        wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
+    }
 
-	/**
-	 * Sign message.
-	 *
-	 * @param message
-	 *            Message to sign.
-	 * @return XMSS signature on digest of message.
-	 */
-	public byte[] sign(byte[] message) {
-		if (message == null) {
-			throw new NullPointerException("message == null");
-		}
-		if (getBDSState().getAuthenticationPath().isEmpty()) {
-			throw new IllegalStateException("not initialized");
-		}
-		int index = privateKey.getIndex();
-		if (!XMSSUtil.isIndexValid(getParams().getHeight(), index)) {
-			throw new IllegalArgumentException("index out of bounds");
-		}
+    /**
+     * Sign message.
+     *
+     * @param message Message to sign.
+     * @return XMSS signature on digest of message.
+     */
+    public byte[] sign(byte[] message)
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        XMSSSigner signer = new XMSSSigner();
 
-		/* create (randomized keyed) messageDigest of message */
-		byte[] random = khf.PRF(privateKey.getSecretKeyPRF(), XMSSUtil.toBytesBigEndian(index, 32));
-		byte[] concatenated = XMSSUtil.concat(random, privateKey.getRoot(),
-				XMSSUtil.toBytesBigEndian(index, params.getDigestSize()));
-		byte[] messageDigest = khf.HMsg(concatenated, message);
+        signer.init(true, privateKey);
 
-		/* create signature for messageDigest */
-		OTSHashAddress otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withOTSAddress(index).build();
-		WOTSPlusSignature wotsPlusSignature = wotsSign(messageDigest, otsHashAddress);
-		XMSSSignature signature = null;
-		try {
-			signature = (XMSSSignature) new XMSSSignature.Builder(params).withIndex(index).withRandom(random)
-					.withWOTSPlusSignature(wotsPlusSignature).withAuthPath(getBDSState().getAuthenticationPath())
-					.build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		}
+        byte[] signature = signer.generateSignature(message);
 
-		/* prepare authentication path for next leaf */
-		int treeHeight = this.getParams().getHeight();
-		if (index < ((1 << treeHeight) - 1)) {
-			getBDSState().nextAuthenticationPath((OTSHashAddress) new OTSHashAddress.Builder().build());
-		}
+        privateKey = (XMSSPrivateKeyParameters)signer.getUpdatedPrivateKey();
 
-		/* update index */
-		setIndex(index + 1);
+        importState(privateKey, publicKey);
 
-		return signature.toByteArray();
-	}
+        return signature;
+    }
 
-	/**
-	 * Verify an XMSS signature.
-	 * 
-	 * @param message
-	 *            Message.
-	 * @param signature
-	 *            XMSS signature.
-	 * @param publicKey
-	 *            XMSS public key.
-	 * @return true if signature is valid false else.
-	 * @throws ParseException
-	 */
-	public boolean verifySignature(byte[] message, byte[] signature, byte[] publicKey) throws ParseException {
-		if (message == null) {
-			throw new NullPointerException("message == null");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		/* parse signature and public key */
-		XMSSSignature sig = new XMSSSignature.Builder(params).withSignature(signature).build();
-		/* generate public key */
-		XMSSPublicKeyParameters pubKey = new XMSSPublicKeyParameters.Builder(params).withPublicKey(publicKey).build();
+    /**
+     * Verify an XMSS signature.
+     *
+     * @param message   Message.
+     * @param signature XMSS signature.
+     * @param publicKey XMSS public key.
+     * @return true if signature is valid false else.
+     * @throws ParseException
+     */
+    public boolean verifySignature(byte[] message, byte[] signature, byte[] publicKey)
+        throws ParseException
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
 
-		/* save state */
-		int savedIndex = privateKey.getIndex();
-		byte[] savedPublicSeed = privateKey.getPublicSeed();
+        XMSSSigner signer = new XMSSSigner();
 
-		/* set index / public seed */
-		int index = sig.getIndex();
-		setIndex(index);
-		setPublicSeed(pubKey.getPublicSeed());
+        signer.init(false, new XMSSPublicKeyParameters.Builder(getParams()).withPublicKey(publicKey).build());
 
-		/* reinitialize WOTS+ object */
-		wotsPlus.importKeys(new byte[params.getDigestSize()], getPublicSeed());
+        return signer.verifySignature(message, signature);
+    }
 
-		/* create message digest */
-		byte[] concatenated = XMSSUtil.concat(sig.getRandom(), pubKey.getRoot(),
-				XMSSUtil.toBytesBigEndian(index, params.getDigestSize()));
-		byte[] messageDigest = khf.HMsg(concatenated, message);
+    /**
+     * Export XMSS private key.
+     *
+     * @return XMSS private key.
+     */
+    public byte[] exportPrivateKey()
+    {
+        return privateKey.toByteArray();
+    }
 
-		/* get root from signature */
-		OTSHashAddress otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withOTSAddress(index).build();
-		XMSSNode rootNodeFromSignature = getRootNodeFromSignature(messageDigest, sig, otsHashAddress);
+    /**
+     * Export XMSS public key.
+     *
+     * @return XMSS public key.
+     */
+    public byte[] exportPublicKey()
+    {
+        return publicKey.toByteArray();
+    }
 
-		/* reset state */
-		setIndex(savedIndex);
-		setPublicSeed(savedPublicSeed);
-		return XMSSUtil.compareByteArray(rootNodeFromSignature.getValue(), pubKey.getRoot());
-	}
-
-	/**
-	 * Export XMSS private key.
-	 *
-	 * @return XMSS private key.
-	 */
-	public byte[] exportPrivateKey() {
-		return privateKey.toByteArray();
-	}
-
-	/**
-	 * Export XMSS public key.
-	 *
-	 * @return XMSS public key.
-	 */
-	public byte[] exportPublicKey() {
-		return publicKey.toByteArray();
-	}
-
-	/**
-	 * Randomization of nodes in binary tree.
-	 *
-	 * @param left
-	 *            Left node.
-	 * @param right
-	 *            Right node.
-	 * @param address
-	 *            Address.
-	 * @return Randomized hash of parent of left / right node.
-	 */
-	protected XMSSNode randomizeHash(XMSSNode left, XMSSNode right, XMSSAddress address) {
-		if (left == null) {
-			throw new NullPointerException("left == null");
-		}
-		if (right == null) {
-			throw new NullPointerException("right == null");
-		}
-		if (left.getHeight() != right.getHeight()) {
-			throw new IllegalStateException("height of both nodes must be equal");
-		}
-		if (address == null) {
-			throw new NullPointerException("address == null");
-		}
-		byte[] publicSeed = getPublicSeed();
-
-		if (address instanceof LTreeAddress) {
-			LTreeAddress tmpAddress = (LTreeAddress) address;
-			address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
-					.withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
-					.withKeyAndMask(0).build();
-		} else if (address instanceof HashTreeAddress) {
-			HashTreeAddress tmpAddress = (HashTreeAddress) address;
-			address = (HashTreeAddress) new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
-					.withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(0).build();
-		}
-
-		byte[] key = khf.PRF(publicSeed, address.toByteArray());
-
-		if (address instanceof LTreeAddress) {
-			LTreeAddress tmpAddress = (LTreeAddress) address;
-			address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
-					.withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
-					.withKeyAndMask(1).build();
-		} else if (address instanceof HashTreeAddress) {
-			HashTreeAddress tmpAddress = (HashTreeAddress) address;
-			address = (HashTreeAddress) new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
-					.withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(1).build();
-		}
-
-		byte[] bitmask0 = khf.PRF(publicSeed, address.toByteArray());
-
-		if (address instanceof LTreeAddress) {
-			LTreeAddress tmpAddress = (LTreeAddress) address;
-			address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
-					.withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
-					.withKeyAndMask(2).build();
-		} else if (address instanceof HashTreeAddress) {
-			HashTreeAddress tmpAddress = (HashTreeAddress) address;
-			address = (HashTreeAddress) new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
-					.withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
-					.withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(2).build();
-		}
-
-		byte[] bitmask1 = khf.PRF(publicSeed, address.toByteArray());
-		int n = params.getDigestSize();
-		byte[] tmpMask = new byte[2 * n];
-		for (int i = 0; i < n; i++) {
-			tmpMask[i] = (byte) (left.getValue()[i] ^ bitmask0[i]);
-		}
-		for (int i = 0; i < n; i++) {
-			tmpMask[i + n] = (byte) (right.getValue()[i] ^ bitmask1[i]);
-		}
-		byte[] out = khf.H(key, tmpMask);
-		return new XMSSNode(left.getHeight(), out);
-	}
-
-	/**
-	 * Compresses a WOTS+ public key to a single n-byte string.
-	 *
-	 * @param publicKey
-	 *            WOTS+ public key to compress.
-	 * @param address
-	 *            Address.
-	 * @return Compressed n-byte string of public key.
-	 */
-	protected XMSSNode lTree(WOTSPlusPublicKeyParameters publicKey, LTreeAddress address) {
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		if (address == null) {
-			throw new NullPointerException("address == null");
-		}
-		int len = wotsPlus.getParams().getLen();
-		/* duplicate public key to XMSSNode Array */
-		byte[][] publicKeyBytes = publicKey.toByteArray();
-		XMSSNode[] publicKeyNodes = new XMSSNode[publicKeyBytes.length];
-		for (int i = 0; i < publicKeyBytes.length; i++) {
-			publicKeyNodes[i] = new XMSSNode(0, publicKeyBytes[i]);
-		}
-		address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
-				.withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress()).withTreeHeight(0)
-				.withTreeIndex(address.getTreeIndex()).withKeyAndMask(address.getKeyAndMask()).build();
-		while (len > 1) {
-			for (int i = 0; i < (int) Math.floor(len / 2); i++) {
-				address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
-						.withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress())
-						.withTreeHeight(address.getTreeHeight()).withTreeIndex(i)
-						.withKeyAndMask(address.getKeyAndMask()).build();
-				publicKeyNodes[i] = randomizeHash(publicKeyNodes[2 * i], publicKeyNodes[(2 * i) + 1], address);
-			}
-			if (len % 2 == 1) {
-				publicKeyNodes[(int) Math.floor(len / 2)] = publicKeyNodes[len - 1];
-			}
-			len = (int) Math.ceil((double) len / 2);
-			address = (LTreeAddress) new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
-					.withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress())
-					.withTreeHeight(address.getTreeHeight() + 1).withTreeIndex(address.getTreeIndex())
-					.withKeyAndMask(address.getKeyAndMask()).build();
-		}
-		return publicKeyNodes[0];
-	}
-
-	/**
-	 * Generate a WOTS+ signature on a message without the corresponding
-	 * authentication path
-	 *
-	 * @param messageDigest
-	 *            Message digest of length n.
-	 * @param otsHashAddress
-	 *            OTS hash address.
-	 * @return XMSS signature.
-	 */
-	protected WOTSPlusSignature wotsSign(byte[] messageDigest, OTSHashAddress otsHashAddress) {
-		if (messageDigest.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
+    /**
+     * Generate a WOTS+ signature on a message without the corresponding
+     * authentication path
+     *
+     * @param messageDigest  Message digest of length n.
+     * @param otsHashAddress OTS hash address.
+     * @return XMSS signature.
+     */
+    protected WOTSPlusSignature wotsSign(byte[] messageDigest, OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
 		/* (re)initialize WOTS+ instance */
-		wotsPlus.importKeys(getWOTSPlusSecretKey(otsHashAddress), getPublicSeed());
+        wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(privateKey.getSecretKeySeed(), otsHashAddress), getPublicSeed());
 		/* create WOTS+ signature */
-		return wotsPlus.sign(messageDigest, otsHashAddress);
-	}
+        return wotsPlus.sign(messageDigest, otsHashAddress);
+    }
 
-	/**
-	 * Compute a root node from a tree signature.
-	 *
-	 * @param messageDigest
-	 *            Message digest.
-	 * @param signature
-	 *            XMSS signature.
-	 * @return Root node calculated from signature.
-	 */
-	protected XMSSNode getRootNodeFromSignature(byte[] messageDigest, XMSSReducedSignature signature,
-			OTSHashAddress otsHashAddress) {
-		if (messageDigest.length != params.getDigestSize()) {
-			throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (otsHashAddress == null) {
-			throw new NullPointerException("otsHashAddress == null");
-		}
+    /**
+     * Getter XMSS params.
+     *
+     * @return XMSS params.
+     */
+    public XMSSParameters getParams()
+    {
+        return params;
+    }
 
-		/* prepare adresses */
-		LTreeAddress lTreeAddress = (LTreeAddress) new LTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.withLTreeAddress(otsHashAddress.getOTSAddress()).build();
-		HashTreeAddress hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.withTreeIndex(otsHashAddress.getOTSAddress()).build();
-		/*
-		 * calculate WOTS+ public key and compress to obtain original leaf hash
-		 */
-		WOTSPlusPublicKeyParameters wotsPlusPK = wotsPlus.getPublicKeyFromSignature(messageDigest,
-				signature.getWOTSPlusSignature(), otsHashAddress);
-		XMSSNode[] node = new XMSSNode[2];
-		node[0] = lTree(wotsPlusPK, lTreeAddress);
+    /**
+     * Getter WOTS+.
+     *
+     * @return WOTS+ instance.
+     */
+    protected WOTSPlus getWOTSPlus()
+    {
+        return wotsPlus;
+    }
 
-		for (int k = 0; k < params.getHeight(); k++) {
-			hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-					.withLayerAddress(hashTreeAddress.getLayerAddress())
-					.withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeHeight(k)
-					.withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
-					.build();
-			if (Math.floor(privateKey.getIndex() / (1 << k)) % 2 == 0) {
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight())
-						.withTreeIndex(hashTreeAddress.getTreeIndex() / 2)
-						.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-				node[1] = randomizeHash(node[0], signature.getAuthPath().get(k), hashTreeAddress);
-				node[1] = new XMSSNode(node[1].getHeight() + 1, node[1].getValue());
-			} else {
-				hashTreeAddress = (HashTreeAddress) new HashTreeAddress.Builder()
-						.withLayerAddress(hashTreeAddress.getLayerAddress())
-						.withTreeAddress(hashTreeAddress.getTreeAddress())
-						.withTreeHeight(hashTreeAddress.getTreeHeight())
-						.withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
-						.withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
-				node[1] = randomizeHash(signature.getAuthPath().get(k), node[0], hashTreeAddress);
-				node[1] = new XMSSNode(node[1].getHeight() + 1, node[1].getValue());
-			}
-			node[0] = node[1];
-		}
-		return node[0];
-	}
+    /**
+     * Getter XMSS root.
+     *
+     * @return Root of binary tree.
+     */
+    public byte[] getRoot()
+    {
+        return privateKey.getRoot();
+    }
 
-	/**
-	 * Derive WOTS+ secret key for specific index as in XMSS ref impl Andreas
-	 * Huelsing.
-	 *
-	 * @param otsHashAddress
-	 * @return WOTS+ secret key at index.
-	 */
-	protected byte[] getWOTSPlusSecretKey(OTSHashAddress otsHashAddress) {
-		otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder()
-				.withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
-				.withOTSAddress(otsHashAddress.getOTSAddress()).build();
-		return khf.PRF(privateKey.getSecretKeySeed(), otsHashAddress.toByteArray());
-	}
+    protected void setRoot(byte[] root)
+    {
+        privateKey = new XMSSPrivateKeyParameters.Builder(params)
+            .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
+            .withPublicSeed(getPublicSeed()).withRoot(root).withBDSState(privateKey.getBDSState()).build();
+        publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).withPublicSeed(getPublicSeed())
+            .build();
+    }
 
-	/**
-	 * Getter XMSS params.
-	 *
-	 * @return XMSS params.
-	 */
-	public XMSSParameters getParams() {
-		return params;
-	}
+    /**
+     * Getter XMSS index.
+     *
+     * @return Index.
+     */
+    public int getIndex()
+    {
+        return privateKey.getIndex();
+    }
 
-	/**
-	 * Getter WOTS+.
-	 *
-	 * @return WOTS+ instance.
-	 */
-	protected WOTSPlus getWOTSPlus() {
-		return wotsPlus;
-	}
+    protected void setIndex(int index)
+    {
+        privateKey = new XMSSPrivateKeyParameters.Builder(params)
+            .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
+            .withPublicSeed(privateKey.getPublicSeed()).withRoot(privateKey.getRoot())
+            .withBDSState(privateKey.getBDSState()).build();
+    }
 
-	protected KeyedHashFunctions getKhf() {
-		return khf;
-	}
+    /**
+     * Getter XMSS public seed.
+     *
+     * @return Public seed.
+     */
+    public byte[] getPublicSeed()
+    {
+        return privateKey.getPublicSeed();
+    }
 
-	/**
-	 * Getter XMSS root.
-	 *
-	 * @return Root of binary tree.
-	 */
-	public byte[] getRoot() {
-		return privateKey.getRoot();
-	}
+    protected void setPublicSeed(byte[] publicSeed)
+    {
+        privateKey = new XMSSPrivateKeyParameters.Builder(params)
+            .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
+            .withPublicSeed(publicSeed).withRoot(getRoot()).withBDSState(privateKey.getBDSState()).build();
+        publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(getRoot()).withPublicSeed(publicSeed)
+            .build();
 
-	protected void setRoot(byte[] root) {
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(privateKey.getIndex())
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(getPublicSeed()).withRoot(root).withBDSState(privateKey.getBDSState()).build();
-			publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).withPublicSeed(getPublicSeed())
-					.build();
-		} catch (ParseException ex) {
-			/* should not be possible */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
+        wotsPlus.importKeys(new byte[params.getDigestSize()], publicSeed);
+    }
 
-	/**
-	 * Getter XMSS index.
-	 *
-	 * @return Index.
-	 */
-	public int getIndex() {
-		return privateKey.getIndex();
-	}
-
-	protected void setIndex(int index) {
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(index)
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(privateKey.getPublicSeed()).withRoot(privateKey.getRoot())
-					.withBDSState(privateKey.getBDSState()).build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * Getter XMSS public seed.
-	 *
-	 * @return Public seed.
-	 */
-	public byte[] getPublicSeed() {
-		return privateKey.getPublicSeed();
-	}
-
-	protected void setPublicSeed(byte[] publicSeed) {
-		try {
-			privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(privateKey.getIndex())
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(publicSeed).withRoot(getRoot()).withBDSState(privateKey.getBDSState()).build();
-			publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(getRoot()).withPublicSeed(publicSeed)
-					.build();
-		} catch (ParseException ex) {
-			/* should not happen */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-		wotsPlus.importKeys(new byte[params.getDigestSize()], publicSeed);
-	}
-
-	protected BDS getBDSState() {
-		return privateKey.getBDSState();
-	}
+    public XMSSPrivateKeyParameters getPrivateKey()
+    {
+        return privateKey;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSAddress.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSAddress.java
index 697a15a..b78bdc9 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSAddress.java
@@ -1,80 +1,92 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import org.bouncycastle.pqc.crypto.xmss.XMSSUtil;
+import org.bouncycastle.util.Pack;
 
 /**
  * XMSS address.
- *
  */
-public abstract class XMSSAddress {
+public abstract class XMSSAddress
+{
 
-	private final int layerAddress;
-	private final long treeAddress;
-	private final int type;
-	private final int keyAndMask;
+    private final int layerAddress;
+    private final long treeAddress;
+    private final int type;
+    private final int keyAndMask;
 
-	protected XMSSAddress(Builder builder) {
-		layerAddress = builder.layerAddress;
-		treeAddress = builder.treeAddress;
-		type = builder.type;
-		keyAndMask = builder.keyAndMask;
-	}
+    protected XMSSAddress(Builder builder)
+    {
+        layerAddress = builder.layerAddress;
+        treeAddress = builder.treeAddress;
+        type = builder.type;
+        keyAndMask = builder.keyAndMask;
+    }
 
-	protected static abstract class Builder<T extends Builder> {
+    protected static abstract class Builder<T extends Builder>
+    {
 
-		/* mandatory */
-		private final int type;
-		/* optional */
-		private int layerAddress = 0;
-		private long treeAddress = 0L;
-		private int keyAndMask = 0;
+        /* mandatory */
+        private final int type;
+        /* optional */
+        private int layerAddress = 0;
+        private long treeAddress = 0L;
+        private int keyAndMask = 0;
 
-		protected Builder(int type) {
-			super();
-			this.type = type;
-		}
+        protected Builder(int type)
+        {
+            super();
+            this.type = type;
+        }
 
-		protected T withLayerAddress(int val) {
-			layerAddress = val;
-			return getThis();
-		}
-		
-		protected T withTreeAddress(long val) {
-			treeAddress = val;
-			return getThis();
-		}
-		
-		protected T withKeyAndMask(int val) {
-			keyAndMask = val;
-			return getThis();
-		}
-		
-		protected abstract XMSSAddress build();
-		protected abstract T getThis();
-	}
+        protected T withLayerAddress(int val)
+        {
+            layerAddress = val;
+            return getThis();
+        }
 
-	protected byte[] toByteArray() {
-		byte[] byteRepresentation = new byte[32];
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, layerAddress, 0);
-		XMSSUtil.longToBytesBigEndianOffset(byteRepresentation, treeAddress, 4);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, type, 12);
-		XMSSUtil.intToBytesBigEndianOffset(byteRepresentation, keyAndMask, 28);
-		return byteRepresentation;
-	}
+        protected T withTreeAddress(long val)
+        {
+            treeAddress = val;
+            return getThis();
+        }
 
-	protected final int getLayerAddress() {
-		return layerAddress;
-	}
+        protected T withKeyAndMask(int val)
+        {
+            keyAndMask = val;
+            return getThis();
+        }
 
-	protected final long getTreeAddress() {
-		return treeAddress;
-	}
+        protected abstract XMSSAddress build();
 
-	public final int getType() {
-		return type;
-	}
+        protected abstract T getThis();
+    }
 
-	public final int getKeyAndMask() {
-		return keyAndMask;
-	}
+    protected byte[] toByteArray()
+    {
+        byte[] byteRepresentation = new byte[32];
+        Pack.intToBigEndian(layerAddress, byteRepresentation, 0);
+        Pack.longToBigEndian(treeAddress, byteRepresentation, 4);
+        Pack.intToBigEndian(type, byteRepresentation, 12);
+        Pack.intToBigEndian(keyAndMask, byteRepresentation, 28);
+        return byteRepresentation;
+    }
+
+    protected final int getLayerAddress()
+    {
+        return layerAddress;
+    }
+
+    protected final long getTreeAddress()
+    {
+        return treeAddress;
+    }
+
+    public final int getType()
+    {
+        return type;
+    }
+
+    public final int getKeyAndMask()
+    {
+        return keyAndMask;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyGenerationParameters.java
new file mode 100644
index 0000000..7c67499
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyGenerationParameters.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * XMSS key-pair generation parameters.
+ */
+public final class XMSSKeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    private final XMSSParameters xmssParameters;
+
+    /**
+     * XMSSMT constructor...
+     *
+     * @param prng   Secure random to use.
+     */
+    public XMSSKeyGenerationParameters(XMSSParameters xmssParameters, SecureRandom prng)
+    {
+        super(prng,-1);
+
+        this.xmssParameters = xmssParameters;
+    }
+
+    public XMSSParameters getParameters()
+    {
+        return xmssParameters;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyPairGenerator.java
new file mode 100644
index 0000000..b9bf679
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyPairGenerator.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * Key pair generator for XMSS keys.
+ */
+public final class XMSSKeyPairGenerator
+{
+    private XMSSParameters params;
+    private SecureRandom prng;
+
+    /**
+     * Base constructor...
+     */
+    public XMSSKeyPairGenerator()
+    {
+    }
+
+    public void init(
+        KeyGenerationParameters param)
+    {
+        XMSSKeyGenerationParameters parameters = (XMSSKeyGenerationParameters)param;
+
+        this.prng = parameters.getRandom();
+        this.params = parameters.getParameters();
+    }
+
+    /**
+     * Generate a new XMSS private key / public key pair.
+     */
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        /* generate private key */
+        XMSSPrivateKeyParameters privateKey = generatePrivateKey(params, prng);
+        XMSSNode root = privateKey.getBDSState().getRoot();
+
+        privateKey = new XMSSPrivateKeyParameters.Builder(params)
+            .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
+            .withPublicSeed(privateKey.getPublicSeed()).withRoot(root.getValue())
+            .withBDSState(privateKey.getBDSState()).build();
+
+        XMSSPublicKeyParameters  publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root.getValue())
+            .withPublicSeed(privateKey.getPublicSeed()).build();
+
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+
+    /**
+     * Generate an XMSS private key.
+     *
+     * @return XMSS private key.
+     */
+    private XMSSPrivateKeyParameters generatePrivateKey(XMSSParameters params, SecureRandom prng)
+    {
+        int n = params.getDigestSize();
+        byte[] secretKeySeed = new byte[n];
+        prng.nextBytes(secretKeySeed);
+        byte[] secretKeyPRF = new byte[n];
+        prng.nextBytes(secretKeyPRF);
+        byte[] publicSeed = new byte[n];
+        prng.nextBytes(publicSeed);
+
+        XMSSPrivateKeyParameters privateKey = new XMSSPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
+            .withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
+            .withBDSState(new BDS(params, publicSeed, secretKeySeed, (OTSHashAddress)new OTSHashAddress.Builder().build())).build();
+
+        return privateKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyParameters.java
new file mode 100644
index 0000000..523805d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSKeyParameters.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class XMSSKeyParameters
+    extends AsymmetricKeyParameter
+{
+    public static final String SHA_256 = "SHA-256";
+    public static final String SHA_512 = "SHA-512";
+    public static final String SHAKE128 = "SHAKE128";
+    public static final String SHAKE256 = "SHAKE256";
+
+    private final String treeDigest;
+
+    public XMSSKeyParameters(boolean isPrivateKey, String treeDigest)
+    {
+        super(isPrivateKey);
+        this.treeDigest = treeDigest;
+    }
+
+    public String getTreeDigest()
+    {
+        return treeDigest;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMT.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMT.java
index c3839be..29abf11 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMT.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMT.java
@@ -1,439 +1,206 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.io.IOException;
 import java.security.SecureRandom;
 import java.text.ParseException;
-import java.util.Map;
-import java.util.TreeMap;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.util.Arrays;
 
 /**
  * XMSS^MT.
- *
  */
-public final class XMSSMT {
+public final class XMSSMT
+{
 
-	private XMSSMTParameters params;
-	private XMSS xmss;
-	private SecureRandom prng;
-	private KeyedHashFunctions khf;
-	private XMSSMTPrivateKeyParameters privateKey;
-	private XMSSMTPublicKeyParameters publicKey;
+    private XMSSMTParameters params;
+    private XMSSParameters xmssParams;
+    private SecureRandom prng;
+    private XMSSMTPrivateKeyParameters privateKey;
+    private XMSSMTPublicKeyParameters publicKey;
 
-	/**
-	 * XMSSMT constructor...
-	 *
-	 * @param params
-	 *            XMSSMTParameters.
-	 */
-	public XMSSMT(XMSSMTParameters params) {
-		super();
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		this.params = params;
-		xmss = params.getXMSS();
-		prng = params.getXMSS().getParams().getPRNG();
-		khf = xmss.getKhf();
-		try {
-			privateKey = new XMSSMTPrivateKeyParameters.Builder(params).build();
-			publicKey = new XMSSMTPublicKeyParameters.Builder(params).build();
-		} catch (ParseException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
+    /**
+     * XMSSMT constructor...
+     *
+     * @param params XMSSMTParameters.
+     */
+    public XMSSMT(XMSSMTParameters params, SecureRandom prng)
+    {
+        super();
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        this.params = params;
+        xmssParams = params.getXMSSParameters();
+        this.prng = prng;
 
-	/**
-	 * Generate a new XMSSMT private key / public key pair.
-	 * 
-	 */
-	public void generateKeys() {
-		/* generate XMSSMT private key */
-		privateKey = generatePrivateKey();
+        privateKey = new XMSSMTPrivateKeyParameters.Builder(params).build();
+        publicKey = new XMSSMTPublicKeyParameters.Builder(params).build();
+    }
 
-		/* init global xmss */
-		XMSSPrivateKeyParameters xmssPrivateKey = null;
-		XMSSPublicKeyParameters xmssPublicKey = null;
-		try {
-			xmssPrivateKey = new XMSSPrivateKeyParameters.Builder(xmss.getParams())
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(privateKey.getPublicSeed()).withBDSState(new BDS(xmss)).build();
-			xmssPublicKey = new XMSSPublicKeyParameters.Builder(xmss.getParams()).withPublicSeed(getPublicSeed())
-					.build();
-		} catch (ParseException ex) {
-			/* should not be possible */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
+    /**
+     * Generate a new XMSSMT private key / public key pair.
+     */
+    public void generateKeys()
+    {
+        XMSSMTKeyPairGenerator kpGen = new XMSSMTKeyPairGenerator();
 
+        kpGen.init(new XMSSMTKeyGenerationParameters(getParams(), prng));
+
+        AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+        privateKey = (XMSSMTPrivateKeyParameters)kp.getPrivate();
+        publicKey = (XMSSMTPublicKeyParameters)kp.getPublic();
+
+        importState(privateKey, publicKey);
+    }
+
+    private void importState(XMSSMTPrivateKeyParameters privateKey, XMSSMTPublicKeyParameters publicKey)
+    {
 		/* import to xmss */
-		try {
-			xmss.importState(xmssPrivateKey.toByteArray(), xmssPublicKey.toByteArray());
-		} catch (ParseException e) {
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
+        xmssParams.getWOTSPlus().importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
 
-		/* get root */
-		int rootLayerIndex = params.getLayers() - 1;
-		OTSHashAddress otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withLayerAddress(rootLayerIndex)
-				.build();
+        this.privateKey = privateKey;
+        this.publicKey = publicKey;
+    }
 
-		/* store BDS instance of root xmss instance */
-		BDS bdsRoot = new BDS(xmss);
-		XMSSNode root = bdsRoot.initialize(otsHashAddress);
-		getBDSState().put(rootLayerIndex, bdsRoot);
-		xmss.setRoot(root.getValue());
-
-		/* set XMSS^MT root / create public key */
-		try {
-			privateKey = new XMSSMTPrivateKeyParameters.Builder(params).withSecretKeySeed(privateKey.getSecretKeySeed())
-					.withSecretKeyPRF(privateKey.getSecretKeyPRF()).withPublicSeed(privateKey.getPublicSeed())
-					.withRoot(xmss.getRoot()).withBDSState(privateKey.getBDSState()).build();
-			publicKey = new XMSSMTPublicKeyParameters.Builder(params).withRoot(root.getValue())
-					.withPublicSeed(getPublicSeed()).build();
-		} catch (ParseException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-	}
-
-	private XMSSMTPrivateKeyParameters generatePrivateKey() {
-		int n = params.getDigestSize();
-		byte[] secretKeySeed = new byte[n];
-		prng.nextBytes(secretKeySeed);
-		byte[] secretKeyPRF = new byte[n];
-		prng.nextBytes(secretKeyPRF);
-		byte[] publicSeed = new byte[n];
-		prng.nextBytes(publicSeed);
-
-		XMSSMTPrivateKeyParameters privateKey = null;
-		try {
-			privateKey = new XMSSMTPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
-					.withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
-					.withBDSState(this.privateKey.getBDSState()).build();
-		} catch (ParseException ex) {
-			/* should not be possible */
-			ex.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-		return privateKey;
-	}
-
-	/**
-	 * Import XMSSMT private key / public key pair.
-	 * 
-	 * @param privateKey
-	 *            XMSSMT private key.
-	 * @param publicKey
-	 *            XMSSMT public key.
-	 * @throws ParseException
-	 * @throws ClassNotFoundException
-	 * @throws IOException
-	 */
-	public void importState(byte[] privateKey, byte[] publicKey)
-			throws ParseException, ClassNotFoundException, IOException {
-		if (privateKey == null) {
-			throw new NullPointerException("privateKey == null");
-		}
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		XMSSMTPrivateKeyParameters xmssMTPrivateKey = new XMSSMTPrivateKeyParameters.Builder(params)
-				.withPrivateKey(privateKey, xmss).build();
-		XMSSMTPublicKeyParameters xmssMTPublicKey = new XMSSMTPublicKeyParameters.Builder(params)
-				.withPublicKey(publicKey).build();
-		if (!XMSSUtil.compareByteArray(xmssMTPrivateKey.getRoot(), xmssMTPublicKey.getRoot())) {
-			throw new IllegalStateException("root of private key and public key do not match");
-		}
-		if (!XMSSUtil.compareByteArray(xmssMTPrivateKey.getPublicSeed(), xmssMTPublicKey.getPublicSeed())) {
-			throw new IllegalStateException("public seed of private key and public key do not match");
-		}
-
-		/* init global xmss */
-		XMSSPrivateKeyParameters xmssPrivateKey = new XMSSPrivateKeyParameters.Builder(xmss.getParams())
-				.withSecretKeySeed(xmssMTPrivateKey.getSecretKeySeed())
-				.withSecretKeyPRF(xmssMTPrivateKey.getSecretKeyPRF()).withPublicSeed(xmssMTPrivateKey.getPublicSeed())
-				.withRoot(xmssMTPrivateKey.getRoot()).withBDSState(new BDS(xmss)).build();
-		XMSSPublicKeyParameters xmssPublicKey = new XMSSPublicKeyParameters.Builder(xmss.getParams())
-				.withRoot(xmssMTPrivateKey.getRoot()).withPublicSeed(getPublicSeed()).build();
-
+    /**
+     * Import XMSSMT private key / public key pair.
+     *
+     * @param privateKey XMSSMT private key.
+     * @param publicKey  XMSSMT public key.
+     */
+    public void importState(byte[] privateKey, byte[] publicKey)
+    {
+        if (privateKey == null)
+        {
+            throw new NullPointerException("privateKey == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
+        XMSSMTPrivateKeyParameters xmssMTPrivateKey = new XMSSMTPrivateKeyParameters.Builder(params)
+            .withPrivateKey(privateKey, xmssParams).build();
+        XMSSMTPublicKeyParameters xmssMTPublicKey = new XMSSMTPublicKeyParameters.Builder(params)
+            .withPublicKey(publicKey).build();
+        if (!Arrays.areEqual(xmssMTPrivateKey.getRoot(), xmssMTPublicKey.getRoot()))
+        {
+            throw new IllegalStateException("root of private key and public key do not match");
+        }
+        if (!Arrays.areEqual(xmssMTPrivateKey.getPublicSeed(), xmssMTPublicKey.getPublicSeed()))
+        {
+            throw new IllegalStateException("public seed of private key and public key do not match");
+        }
+        
 		/* import to xmss */
-		xmss.importState(xmssPrivateKey.toByteArray(), xmssPublicKey.toByteArray());
-		this.privateKey = xmssMTPrivateKey;
-		this.publicKey = xmssMTPublicKey;
-	}
+        xmssParams.getWOTSPlus().importKeys(new byte[params.getDigestSize()], xmssMTPrivateKey.getPublicSeed());
 
-	/**
-	 * Sign message.
-	 *
-	 * @param message
-	 *            Message to sign.
-	 * @return XMSSMT signature on digest of message.
-	 */
-	public byte[] sign(byte[] message) {
-		if (message == null) {
-			throw new NullPointerException("message == null");
-		}
-		if (getBDSState().isEmpty()) {
-			throw new IllegalStateException("not initialized");
-		}
-		// privateKey.increaseIndex(this);
-		long globalIndex = getIndex();
-		int totalHeight = params.getHeight();
-		int xmssHeight = xmss.getParams().getHeight();
-		if (!XMSSUtil.isIndexValid(totalHeight, globalIndex)) {
-			throw new IllegalArgumentException("index out of bounds");
-		}
+        this.privateKey = xmssMTPrivateKey;
+        this.publicKey = xmssMTPublicKey;
+    }
 
-		/* compress message */
-		byte[] random = khf.PRF(privateKey.getSecretKeyPRF(), XMSSUtil.toBytesBigEndian(globalIndex, 32));
-		byte[] concatenated = XMSSUtil.concat(random, privateKey.getRoot(),
-				XMSSUtil.toBytesBigEndian(globalIndex, params.getDigestSize()));
-		byte[] messageDigest = khf.HMsg(concatenated, message);
+    /**
+     * Sign message.
+     *
+     * @param message Message to sign.
+     * @return XMSSMT signature on digest of message.
+     */
+    public byte[] sign(byte[] message)
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
 
-		XMSSMTSignature signature = null;
-		try {
-			signature = new XMSSMTSignature.Builder(params).withIndex(globalIndex).withRandom(random).build();
-		} catch (ParseException ex) {
-			/* should not be possible */
-			ex.printStackTrace();
-		}
+        XMSSMTSigner signer = new XMSSMTSigner();
 
-		/* layer 0 */
-		long indexTree = XMSSUtil.getTreeIndex(globalIndex, xmssHeight);
-		int indexLeaf = XMSSUtil.getLeafIndex(globalIndex, xmssHeight);
+        signer.init(true, privateKey);
 
-		/* reset xmss */
-		xmss.setIndex(indexLeaf);
-		xmss.setPublicSeed(getPublicSeed());
+        byte[] signature = signer.generateSignature(message);
 
-		/* create signature with XMSS tree on layer 0 */
+        privateKey = (XMSSMTPrivateKeyParameters)signer.getUpdatedPrivateKey();
 
-		/* adjust addresses */
-		OTSHashAddress otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withTreeAddress(indexTree)
-				.withOTSAddress(indexLeaf).build();
+        importState(privateKey, publicKey);
 
-		/* sign message digest */
-		WOTSPlusSignature wotsPlusSignature = xmss.wotsSign(messageDigest, otsHashAddress);
-		/* get authentication path from BDS */
-		if (getBDSState().get(0) == null || indexLeaf == 0) {
-			getBDSState().put(0, new BDS(xmss));
-			getBDSState().get(0).initialize(otsHashAddress);
-		}
+        return signature;
+    }
 
-		XMSSReducedSignature reducedSignature = null;
-		try {
-			reducedSignature = new XMSSReducedSignature.Builder(xmss.getParams())
-					.withWOTSPlusSignature(wotsPlusSignature).withAuthPath(getBDSState().get(0).getAuthenticationPath())
-					.build();
-		} catch (ParseException ex) {
-			/* should never happen */
-			ex.printStackTrace();
-		}
-		signature.getReducedSignatures().add(reducedSignature);
+    /**
+     * Verify an XMSSMT signature.
+     *
+     * @param message   Message.
+     * @param signature XMSSMT signature.
+     * @param publicKey XMSSMT public key.
+     * @return true if signature is valid false else.
+     * @throws ParseException
+     */
+    public boolean verifySignature(byte[] message, byte[] signature, byte[] publicKey)
+        throws ParseException
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
 
-		/* prepare authentication path for next leaf */
-		if (indexLeaf < ((1 << xmssHeight) - 1)) {
-			getBDSState().get(0).nextAuthenticationPath(otsHashAddress);
-		}
+        XMSSMTSigner signer = new XMSSMTSigner();
 
-		/* loop over remaining layers */
-		for (int layer = 1; layer < params.getLayers(); layer++) {
-			/* get root of layer - 1 */
-			XMSSNode root = getBDSState().get(layer - 1).getRoot();
+        signer.init(false, new XMSSMTPublicKeyParameters.Builder(getParams()).withPublicKey(publicKey).build());
 
-			indexLeaf = XMSSUtil.getLeafIndex(indexTree, xmssHeight);
-			indexTree = XMSSUtil.getTreeIndex(indexTree, xmssHeight);
-			xmss.setIndex(indexLeaf);
+        return signer.verifySignature(message, signature);
+    }
 
-			/* adjust addresses */
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withLayerAddress(layer)
-					.withTreeAddress(indexTree).withOTSAddress(indexLeaf).build();
+    /**
+     * Export XMSSMT private key.
+     *
+     * @return XMSSMT private key.
+     */
+    public byte[] exportPrivateKey()
+    {
+        return privateKey.toByteArray();
+    }
 
-			/* sign root digest of layer - 1 */
-			wotsPlusSignature = xmss.wotsSign(root.getValue(), otsHashAddress);
-			/* get authentication path from BDS */
-			if (getBDSState().get(layer) == null || XMSSUtil.isNewBDSInitNeeded(globalIndex, xmssHeight, layer)) {
-				getBDSState().put(layer, new BDS(xmss));
-				getBDSState().get(layer).initialize(otsHashAddress);
-			}
-			try {
-				reducedSignature = new XMSSReducedSignature.Builder(xmss.getParams())
-						.withWOTSPlusSignature(wotsPlusSignature)
-						.withAuthPath(getBDSState().get(layer).getAuthenticationPath()).build();
-			} catch (ParseException ex) {
-				/* should never happen */
-				ex.printStackTrace();
-			}
-			signature.getReducedSignatures().add(reducedSignature);
+    /**
+     * Export XMSSMT public key.
+     *
+     * @return XMSSMT public key.
+     */
+    public byte[] exportPublicKey()
+    {
+        return publicKey.toByteArray();
+    }
 
-			/* prepare authentication path for next leaf */
-			if (indexLeaf < ((1 << xmssHeight) - 1)
-					&& XMSSUtil.isNewAuthenticationPathNeeded(globalIndex, xmssHeight, layer)) {
-				getBDSState().get(layer).nextAuthenticationPath(otsHashAddress);
-			}
-		}
+    /**
+     * Getter XMSSMT params.
+     *
+     * @return XMSSMT params.
+     */
+    public XMSSMTParameters getParams()
+    {
+        return params;
+    }
 
-		/* update private key */
-		try {
-			privateKey = new XMSSMTPrivateKeyParameters.Builder(params).withIndex(globalIndex + 1)
-					.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
-					.withPublicSeed(privateKey.getPublicSeed()).withRoot(privateKey.getRoot())
-					.withBDSState(privateKey.getBDSState()).build();
-		} catch (ParseException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		} catch (IOException e) {
-			/* should not be possible */
-			e.printStackTrace();
-		}
-		return signature.toByteArray();
-	}
 
-	/**
-	 * Verify an XMSSMT signature.
-	 * 
-	 * @param message
-	 *            Message.
-	 * @param signature
-	 *            XMSSMT signature.
-	 * @param publicKey
-	 *            XMSSMT public key.
-	 * @return true if signature is valid false else.
-	 * @throws ParseException
-	 */
-	public boolean verifySignature(byte[] message, byte[] signature, byte[] publicKey) throws ParseException {
-		if (message == null) {
-			throw new NullPointerException("message == null");
-		}
-		if (signature == null) {
-			throw new NullPointerException("signature == null");
-		}
-		if (publicKey == null) {
-			throw new NullPointerException("publicKey == null");
-		}
-		/* (re)create compressed message */
-		XMSSMTSignature sig = new XMSSMTSignature.Builder(params).withSignature(signature).build();
-		XMSSMTPublicKeyParameters pubKey = new XMSSMTPublicKeyParameters.Builder(params).withPublicKey(publicKey)
-				.build();
+    /**
+     * Getter public seed.
+     *
+     * @return Public seed.
+     */
+    public byte[] getPublicSeed()
+    {
+        return privateKey.getPublicSeed();
+    }
 
-		byte[] concatenated = XMSSUtil.concat(sig.getRandom(), pubKey.getRoot(),
-				XMSSUtil.toBytesBigEndian(sig.getIndex(), params.getDigestSize()));
-		byte[] messageDigest = khf.HMsg(concatenated, message);
-
-		long globalIndex = sig.getIndex();
-		int xmssHeight = xmss.getParams().getHeight();
-		long indexTree = XMSSUtil.getTreeIndex(globalIndex, xmssHeight);
-		int indexLeaf = XMSSUtil.getLeafIndex(globalIndex, xmssHeight);
-
-		/* adjust xmss */
-		xmss.setIndex(indexLeaf);
-		xmss.setPublicSeed(pubKey.getPublicSeed());
-
-		/* prepare addresses */
-		OTSHashAddress otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withTreeAddress(indexTree)
-				.withOTSAddress(indexLeaf).build();
-
-		/* get root node on layer 0 */
-		XMSSReducedSignature xmssMTSignature = sig.getReducedSignatures().get(0);
-		XMSSNode rootNode = xmss.getRootNodeFromSignature(messageDigest, xmssMTSignature, otsHashAddress);
-		for (int layer = 1; layer < params.getLayers(); layer++) {
-			xmssMTSignature = sig.getReducedSignatures().get(layer);
-			indexLeaf = XMSSUtil.getLeafIndex(indexTree, xmssHeight);
-			indexTree = XMSSUtil.getTreeIndex(indexTree, xmssHeight);
-			xmss.setIndex(indexLeaf);
-
-			/* adjust address */
-			otsHashAddress = (OTSHashAddress) new OTSHashAddress.Builder().withLayerAddress(layer)
-					.withTreeAddress(indexTree).withOTSAddress(indexLeaf).build();
-
-			/* get root node */
-			rootNode = xmss.getRootNodeFromSignature(rootNode.getValue(), xmssMTSignature, otsHashAddress);
-		}
-
-		/* compare roots */
-		return XMSSUtil.compareByteArray(rootNode.getValue(), pubKey.getRoot());
-	}
-
-	/**
-	 * Export XMSSMT private key.
-	 *
-	 * @return XMSSMT private key.
-	 */
-	public byte[] exportPrivateKey() {
-		return privateKey.toByteArray();
-	}
-
-	/**
-	 * Export XMSSMT public key.
-	 *
-	 * @return XMSSMT public key.
-	 */
-	public byte[] exportPublicKey() {
-		return publicKey.toByteArray();
-	}
-
-	/**
-	 * Getter XMSSMT params.
-	 *
-	 * @return XMSSMT params.
-	 */
-	public XMSSMTParameters getParams() {
-		return params;
-	}
-
-	/**
-	 * Getter XMSSMT index.
-	 *
-	 * @return XMSSMT index.
-	 */
-	public long getIndex() {
-		return privateKey.getIndex();
-	}
-
-	/**
-	 * Getter public seed.
-	 *
-	 * @return Public seed.
-	 */
-	public byte[] getPublicSeed() {
-		return privateKey.getPublicSeed();
-	}
-
-	protected Map<Integer, BDS> getBDSState() {
-		return privateKey.getBDSState();
-	}
-
-	protected XMSS getXMSS() {
-		return xmss;
-	}
+    protected XMSSParameters getXMSS()
+    {
+        return xmssParams;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyGenerationParameters.java
new file mode 100644
index 0000000..a2c189c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyGenerationParameters.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * XMSS^MT key-pair generation parameters.
+ */
+public final class XMSSMTKeyGenerationParameters
+    extends KeyGenerationParameters
+{
+    private final XMSSMTParameters xmssmtParameters;
+
+    /**
+     * XMSSMT constructor...
+     *
+     * @param prng   Secure random to use.
+     */
+    public XMSSMTKeyGenerationParameters(XMSSMTParameters xmssmtParameters, SecureRandom prng)
+    {
+        super(prng,-1);
+
+        this.xmssmtParameters = xmssmtParameters;
+    }
+
+    public XMSSMTParameters getParameters()
+    {
+        return xmssmtParameters;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyPairGenerator.java
new file mode 100644
index 0000000..3f76d10
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyPairGenerator.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+/**
+ * Key pair generator for XMSS^MT keys.
+ */
+public final class XMSSMTKeyPairGenerator
+{
+    private XMSSMTParameters params;
+    private XMSSParameters xmssParams;
+
+    private SecureRandom prng;
+
+
+    /**
+     * Base constructor...
+     */
+    public XMSSMTKeyPairGenerator()
+    {
+    }
+
+    public void init(
+        KeyGenerationParameters param)
+    {
+        XMSSMTKeyGenerationParameters parameters = (XMSSMTKeyGenerationParameters)param;
+
+        prng = parameters.getRandom();
+        this.params = parameters.getParameters();
+        this.xmssParams = params.getXMSSParameters();
+    }
+
+    /**
+     * Generate a new XMSSMT private key / public key pair.
+     */
+    public AsymmetricCipherKeyPair generateKeyPair()
+    {
+        XMSSMTPrivateKeyParameters privateKey;
+        XMSSMTPublicKeyParameters publicKey;
+
+            /* generate XMSSMT private key */
+        privateKey = generatePrivateKey(new XMSSMTPrivateKeyParameters.Builder(params).build().getBDSState());
+
+            /* import to xmss */
+        xmssParams.getWOTSPlus().importKeys(new byte[params.getDigestSize()], privateKey.getPublicSeed());
+
+            /* get root */
+        int rootLayerIndex = params.getLayers() - 1;
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withLayerAddress(rootLayerIndex)
+            .build();
+
+          		/* store BDS instance of root xmss instance */
+        BDS bdsRoot = new BDS(xmssParams, privateKey.getPublicSeed(), privateKey.getSecretKeySeed(), otsHashAddress);
+        XMSSNode root = bdsRoot.getRoot();
+        privateKey.getBDSState().put(rootLayerIndex, bdsRoot);
+
+            /* set XMSS^MT root / create public key */
+        privateKey = new XMSSMTPrivateKeyParameters.Builder(params).withSecretKeySeed(privateKey.getSecretKeySeed())
+            .withSecretKeyPRF(privateKey.getSecretKeyPRF()).withPublicSeed(privateKey.getPublicSeed())
+            .withRoot(root.getValue()).withBDSState(privateKey.getBDSState()).build();
+        publicKey = new XMSSMTPublicKeyParameters.Builder(params).withRoot(root.getValue())
+            .withPublicSeed(privateKey.getPublicSeed()).build();
+
+        return new AsymmetricCipherKeyPair(publicKey, privateKey);
+    }
+
+    private XMSSMTPrivateKeyParameters generatePrivateKey(BDSStateMap bdsState)
+    {
+        int n = params.getDigestSize();
+        byte[] secretKeySeed = new byte[n];
+        prng.nextBytes(secretKeySeed);
+        byte[] secretKeyPRF = new byte[n];
+        prng.nextBytes(secretKeyPRF);
+        byte[] publicSeed = new byte[n];
+        prng.nextBytes(publicSeed);
+
+        XMSSMTPrivateKeyParameters privateKey = null;
+
+        privateKey = new XMSSMTPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
+                .withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
+                .withBDSState(bdsState).build();
+
+        return privateKey;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyParameters.java
new file mode 100644
index 0000000..8516ad6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTKeyParameters.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public class XMSSMTKeyParameters
+    extends AsymmetricKeyParameter
+{
+    private final String treeDigest;
+
+    public XMSSMTKeyParameters(boolean isPrivateKey, String treeDigest)
+    {
+        super(isPrivateKey);
+        this.treeDigest = treeDigest;
+    }
+
+    public String getTreeDigest()
+    {
+        return treeDigest;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTParameters.java
index 0ed47bd..1f9e93d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTParameters.java
@@ -1,106 +1,113 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.Digest;
 
 /**
  * XMSS^MT Parameters.
- *
  */
-public final class XMSSMTParameters {
+public final class XMSSMTParameters
+{
 
-	private final XMSSOid oid;
-	private final XMSS xmss;
-	private final int height;
-	private final int layers;
+    private final XMSSOid oid;
+    private final XMSSParameters xmssParams;
+    private final int height;
+    private final int layers;
 
-	/**
-	 * XMSSMT constructor...
-	 * 
-	 * @param height
-	 *            Height of tree.
-	 * @param layers
-	 *            Amount of layers.
-	 * @param digest
-	 *            Digest to use.
-	 * @param prng
-	 *            Secure random to use.
-	 */
-	public XMSSMTParameters(int height, int layers, Digest digest, SecureRandom prng) {
-		super();
-		this.height = height;
-		this.layers = layers;
-		this.xmss = new XMSS(new XMSSParameters(xmssTreeHeight(height, layers), digest, prng));
-		oid = DefaultXMSSMTOid.lookup(getDigest().getAlgorithmName(), getDigestSize(), getWinternitzParameter(),
-				getLen(), getHeight(), layers);
-		/*
+    /**
+     * XMSSMT constructor...
+     *
+     * @param height Height of tree.
+     * @param layers Amount of layers.
+     * @param digest Digest to use.
+     */
+    public XMSSMTParameters(int height, int layers, Digest digest)
+    {
+        super();
+        this.height = height;
+        this.layers = layers;
+        this.xmssParams = new XMSSParameters(xmssTreeHeight(height, layers), digest);
+        oid = DefaultXMSSMTOid.lookup(getDigest().getAlgorithmName(), getDigestSize(), getWinternitzParameter(),
+            getLen(), getHeight(), layers);
+        /*
 		 * if (oid == null) { throw new InvalidParameterException(); }
 		 */
-	}
+    }
 
-	private static int xmssTreeHeight(int height, int layers) throws IllegalArgumentException {
-		if (height < 2) {
-			throw new IllegalArgumentException("totalHeight must be > 1");
-		}
-		if (height % layers != 0) {
-			throw new IllegalArgumentException("layers must divide totalHeight without remainder");
-		}
-		if (height / layers == 1) {
-			throw new IllegalArgumentException("height / layers must be greater than 1");
-		}
-		return height / layers;
-	}
+    private static int xmssTreeHeight(int height, int layers)
+        throws IllegalArgumentException
+    {
+        if (height < 2)
+        {
+            throw new IllegalArgumentException("totalHeight must be > 1");
+        }
+        if (height % layers != 0)
+        {
+            throw new IllegalArgumentException("layers must divide totalHeight without remainder");
+        }
+        if (height / layers == 1)
+        {
+            throw new IllegalArgumentException("height / layers must be greater than 1");
+        }
+        return height / layers;
+    }
 
-	/**
-	 * Getter height.
-	 * 
-	 * @return XMSSMT height.
-	 */
-	public int getHeight() {
-		return height;
-	}
+    /**
+     * Getter height.
+     *
+     * @return XMSSMT height.
+     */
+    public int getHeight()
+    {
+        return height;
+    }
 
-	/**
-	 * Getter layers.
-	 * 
-	 * @return XMSSMT layers.
-	 */
-	public int getLayers() {
-		return layers;
-	}
+    /**
+     * Getter layers.
+     *
+     * @return XMSSMT layers.
+     */
+    public int getLayers()
+    {
+        return layers;
+    }
 
-	protected XMSS getXMSS() {
-		return xmss;
-	}
+    protected XMSSParameters getXMSSParameters()
+    {
+        return xmssParams;
+    }
 
-	protected WOTSPlus getWOTSPlus() {
-		return xmss.getWOTSPlus();
-	}
+    protected WOTSPlus getWOTSPlus()
+    {
+        return xmssParams.getWOTSPlus();
+    }
 
-	protected Digest getDigest() {
-		return xmss.getParams().getDigest();
-	}
+    protected Digest getDigest()
+    {
+        return xmssParams.getDigest();
+    }
 
-	/**
-	 * Getter digest size.
-	 * 
-	 * @return Digest size.
-	 */
-	public int getDigestSize() {
-		return xmss.getParams().getDigestSize();
-	}
+    /**
+     * Getter digest size.
+     *
+     * @return Digest size.
+     */
+    public int getDigestSize()
+    {
+        return xmssParams.getDigestSize();
+    }
 
-	/**
-	 * Getter Winternitz parameter.
-	 * 
-	 * @return Winternitz parameter.
-	 */
-	public int getWinternitzParameter() {
-		return xmss.getParams().getWinternitzParameter();
-	}
+    /**
+     * Getter Winternitz parameter.
+     *
+     * @return Winternitz parameter.
+     */
+    public int getWinternitzParameter()
+    {
+        return xmssParams.getWinternitzParameter();
+    }
 
-	protected int getLen() {
-		return xmss.getWOTSPlus().getParams().getLen();
-	}
+    protected int getLen()
+    {
+        return xmssParams.getWOTSPlus().getParams().getLen();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPrivateKeyParameters.java
index d1bb7c6..14d032c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPrivateKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPrivateKeyParameters.java
@@ -1,284 +1,320 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
 import java.io.IOException;
-import java.text.ParseException;
-import java.util.Map;
-import java.util.TreeMap;
+
+import org.bouncycastle.util.Arrays;
 
 /**
- * XMSSMT Private Key.
- *
+ * XMSS^MT Private Key.
  */
-public final class XMSSMTPrivateKeyParameters implements XMSSStoreableObjectInterface {
+public final class XMSSMTPrivateKeyParameters
+    extends XMSSMTKeyParameters
+    implements XMSSStoreableObjectInterface
+{
 
-	private final XMSSMTParameters params;
-	private final long index;
-	private final byte[] secretKeySeed;
-	private final byte[] secretKeyPRF;
-	private final byte[] publicSeed;
-	private final byte[] root;
-	private final Map<Integer, BDS> bdsState;
+    private final XMSSMTParameters params;
+    private final long index;
+    private final byte[] secretKeySeed;
+    private final byte[] secretKeyPRF;
+    private final byte[] publicSeed;
+    private final byte[] root;
+    private final BDSStateMap bdsState;
 
-	private XMSSMTPrivateKeyParameters(Builder builder) throws ParseException, ClassNotFoundException, IOException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		byte[] privateKey = builder.privateKey;
-		if (privateKey != null) {
-			if (builder.xmss == null) {
-				throw new NullPointerException("xmss == null");
-			}
-			/* import */
-			int totalHeight = params.getHeight();
-			int indexSize = (int) Math.ceil(totalHeight / (double) 8);
-			int secretKeySize = n;
-			int secretKeyPRFSize = n;
-			int publicSeedSize = n;
-			int rootSize = n;
-			/*
-			int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
+    private XMSSMTPrivateKeyParameters(Builder builder)
+    {
+        super(true, builder.params.getDigest().getAlgorithmName());
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        byte[] privateKey = builder.privateKey;
+        if (privateKey != null)
+        {
+            if (builder.xmss == null)
+            {
+                throw new NullPointerException("xmss == null");
+            }
+            /* import */
+            int totalHeight = params.getHeight();
+            int indexSize = (totalHeight + 7) / 8;
+            int secretKeySize = n;
+            int secretKeyPRFSize = n;
+            int publicSeedSize = n;
+            int rootSize = n;
+            /*
+            int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
 			if (privateKey.length != totalSize) {
 				throw new ParseException("private key has wrong size", 0);
 			}
 			*/
-			int position = 0;
-			index = XMSSUtil.bytesToXBigEndian(privateKey, position, indexSize);
-			if (!XMSSUtil.isIndexValid(totalHeight, index)) {
-				throw new ParseException("index out of bounds", 0);
-			}
-			position += indexSize;
-			secretKeySeed = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeySize);
-			position += secretKeySize;
-			secretKeyPRF = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeyPRFSize);
-			position += secretKeyPRFSize;
-			publicSeed = XMSSUtil.extractBytesAtOffset(privateKey, position, publicSeedSize);
-			position += publicSeedSize;
-			root = XMSSUtil.extractBytesAtOffset(privateKey, position, rootSize);
-			position += rootSize;
+            int position = 0;
+            index = XMSSUtil.bytesToXBigEndian(privateKey, position, indexSize);
+            if (!XMSSUtil.isIndexValid(totalHeight, index))
+            {
+                throw new IllegalArgumentException("index out of bounds");
+            }
+            position += indexSize;
+            secretKeySeed = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeySize);
+            position += secretKeySize;
+            secretKeyPRF = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeyPRFSize);
+            position += secretKeyPRFSize;
+            publicSeed = XMSSUtil.extractBytesAtOffset(privateKey, position, publicSeedSize);
+            position += publicSeedSize;
+            root = XMSSUtil.extractBytesAtOffset(privateKey, position, rootSize);
+            position += rootSize;
 			/* import BDS state */
-			byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(privateKey, position, privateKey.length - position);
-			@SuppressWarnings("unchecked")
-			Map<Integer, BDS> bdsImport = (TreeMap<Integer, BDS>) XMSSUtil.deserialize(bdsStateBinary);
-			for (Integer key : bdsImport.keySet()) {
-				BDS bds = bdsImport.get(key);
-				bds.setXMSS(builder.xmss);
-				bds.validate();
-			}
-			bdsState = bdsImport;
-		} else {
+            byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(privateKey, position, privateKey.length - position);
+
+            try
+            {
+                BDSStateMap bdsImport = (BDSStateMap)XMSSUtil.deserialize(bdsStateBinary, BDSStateMap.class);
+
+                bdsState = bdsImport.withWOTSDigest(DigestUtil.getDigestOID(builder.xmss.getDigest().getAlgorithmName()));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+        }
+        else
+        {
 			/* set */
-			index = builder.index;
-			byte[] tmpSecretKeySeed = builder.secretKeySeed;
-			if (tmpSecretKeySeed != null) {
-				if (tmpSecretKeySeed.length != n) {
-					throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest");
-				}
-				secretKeySeed = tmpSecretKeySeed;
-			} else {
-				secretKeySeed = new byte[n];
-			}
-			byte[] tmpSecretKeyPRF = builder.secretKeyPRF;
-			if (tmpSecretKeyPRF != null) {
-				if (tmpSecretKeyPRF.length != n) {
-					throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest");
-				}
-				secretKeyPRF = tmpSecretKeyPRF;
-			} else {
-				secretKeyPRF = new byte[n];
-			}
-			byte[] tmpPublicSeed = builder.publicSeed;
-			if (tmpPublicSeed != null) {
-				if (tmpPublicSeed.length != n) {
-					throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest");
-				}
-				publicSeed = tmpPublicSeed;
-			} else {
-				publicSeed = new byte[n];
-			}
-			byte[] tmpRoot = builder.root;
-			if (tmpRoot != null) {
-				if (tmpRoot.length != n) {
-					throw new IllegalArgumentException("size of root needs to be equal size of digest");
-				}
-				root = tmpRoot;
-			} else {
-				root = new byte[n];
-			}
-			Map<Integer, BDS> tmpBDSState = builder.bdsState;
-			if (tmpBDSState != null) {
-				bdsState = tmpBDSState;
-			} else {
-				bdsState = new TreeMap<Integer, BDS>();
-			}
-		}
-	}
+            index = builder.index;
+            byte[] tmpSecretKeySeed = builder.secretKeySeed;
+            if (tmpSecretKeySeed != null)
+            {
+                if (tmpSecretKeySeed.length != n)
+                {
+                    throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest");
+                }
+                secretKeySeed = tmpSecretKeySeed;
+            }
+            else
+            {
+                secretKeySeed = new byte[n];
+            }
+            byte[] tmpSecretKeyPRF = builder.secretKeyPRF;
+            if (tmpSecretKeyPRF != null)
+            {
+                if (tmpSecretKeyPRF.length != n)
+                {
+                    throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest");
+                }
+                secretKeyPRF = tmpSecretKeyPRF;
+            }
+            else
+            {
+                secretKeyPRF = new byte[n];
+            }
+            byte[] tmpPublicSeed = builder.publicSeed;
+            if (tmpPublicSeed != null)
+            {
+                if (tmpPublicSeed.length != n)
+                {
+                    throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest");
+                }
+                publicSeed = tmpPublicSeed;
+            }
+            else
+            {
+                publicSeed = new byte[n];
+            }
+            byte[] tmpRoot = builder.root;
+            if (tmpRoot != null)
+            {
+                if (tmpRoot.length != n)
+                {
+                    throw new IllegalArgumentException("size of root needs to be equal size of digest");
+                }
+                root = tmpRoot;
+            }
+            else
+            {
+                root = new byte[n];
+            }
+            BDSStateMap tmpBDSState = builder.bdsState;
+            if (tmpBDSState != null)
+            {
+                bdsState = tmpBDSState;
+            }
+            else
+            {
+                long globalIndex = builder.index;
+                int totalHeight = params.getHeight();
 
-	public static class Builder {
+                if (XMSSUtil.isIndexValid(totalHeight, globalIndex) && tmpPublicSeed != null && tmpSecretKeySeed != null)
+                {
+                    bdsState = new BDSStateMap(params, builder.index, tmpPublicSeed, tmpSecretKeySeed);
+                }
+                else
+                {
+                    bdsState = new BDSStateMap();
+                }
+            }
+        }
+    }
 
-		/* mandatory */
-		private final XMSSMTParameters params;
-		/* optional */
-		private long index = 0L;
-		private byte[] secretKeySeed = null;
-		private byte[] secretKeyPRF = null;
-		private byte[] publicSeed = null;
-		private byte[] root = null;
-		private Map<Integer, BDS> bdsState = null;
-		private byte[] privateKey = null;
-		private XMSS xmss = null;
+    public static class Builder
+    {
 
-		public Builder(XMSSMTParameters params) {
-			super();
-			this.params = params;
-		}
+        /* mandatory */
+        private final XMSSMTParameters params;
+        /* optional */
+        private long index = 0L;
+        private byte[] secretKeySeed = null;
+        private byte[] secretKeyPRF = null;
+        private byte[] publicSeed = null;
+        private byte[] root = null;
+        private BDSStateMap bdsState = null;
+        private byte[] privateKey = null;
+        private XMSSParameters xmss = null;
 
-		public Builder withIndex(long val) {
-			index = val;
-			return this;
-		}
+        public Builder(XMSSMTParameters params)
+        {
+            super();
+            this.params = params;
+        }
 
-		public Builder withSecretKeySeed(byte[] val) {
-			secretKeySeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withIndex(long val)
+        {
+            index = val;
+            return this;
+        }
 
-		public Builder withSecretKeyPRF(byte[] val) {
-			secretKeyPRF = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withSecretKeySeed(byte[] val)
+        {
+            secretKeySeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withPublicSeed(byte[] val) {
-			publicSeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withSecretKeyPRF(byte[] val)
+        {
+            secretKeyPRF = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withRoot(byte[] val) {
-			root = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withPublicSeed(byte[] val)
+        {
+            publicSeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withBDSState(Map<Integer, BDS> val) {
-			bdsState = val;
-			return this;
-		}
+        public Builder withRoot(byte[] val)
+        {
+            root = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withPrivateKey(byte[] privateKeyVal, XMSS xmssVal) {
-			privateKey = XMSSUtil.cloneArray(privateKeyVal);
-			xmss = xmssVal;
-			return this;
-		}
+        public Builder withBDSState(BDSStateMap val)
+        {
+            bdsState = val;
+            return this;
+        }
 
-		public XMSSMTPrivateKeyParameters build() throws ParseException, ClassNotFoundException, IOException {
-			return new XMSSMTPrivateKeyParameters(this);
-		}
-	}
+        public Builder withPrivateKey(byte[] privateKeyVal, XMSSParameters xmssVal)
+        {
+            privateKey = XMSSUtil.cloneArray(privateKeyVal);
+            xmss = xmssVal;
+            return this;
+        }
 
-	public byte[] toByteArray() {
+        public XMSSMTPrivateKeyParameters build()
+        {
+            return new XMSSMTPrivateKeyParameters(this);
+        }
+    }
+
+    public byte[] toByteArray()
+    {
 		/* index || secretKeySeed || secretKeyPRF || publicSeed || root */
-		int n = params.getDigestSize();
-		int indexSize = (int) Math.ceil(params.getHeight() / (double) 8);
-		int secretKeySize = n;
-		int secretKeyPRFSize = n;
-		int publicSeedSize = n;
-		int rootSize = n;
-		int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
-		byte[] out = new byte[totalSize];
-		int position = 0;
+        int n = params.getDigestSize();
+        int indexSize = (params.getHeight() + 7) / 8;
+        int secretKeySize = n;
+        int secretKeyPRFSize = n;
+        int publicSeedSize = n;
+        int rootSize = n;
+        int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
+        byte[] out = new byte[totalSize];
+        int position = 0;
 		/* copy index */
-		byte[] indexBytes = XMSSUtil.toBytesBigEndian(index, indexSize);
-		XMSSUtil.copyBytesAtOffset(out, indexBytes, position);
-		position += indexSize;
+        byte[] indexBytes = XMSSUtil.toBytesBigEndian(index, indexSize);
+        XMSSUtil.copyBytesAtOffset(out, indexBytes, position);
+        position += indexSize;
 		/* copy secretKeySeed */
-		XMSSUtil.copyBytesAtOffset(out, secretKeySeed, position);
-		position += secretKeySize;
+        XMSSUtil.copyBytesAtOffset(out, secretKeySeed, position);
+        position += secretKeySize;
 		/* copy secretKeyPRF */
-		XMSSUtil.copyBytesAtOffset(out, secretKeyPRF, position);
-		position += secretKeyPRFSize;
+        XMSSUtil.copyBytesAtOffset(out, secretKeyPRF, position);
+        position += secretKeyPRFSize;
 		/* copy publicSeed */
-		XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
-		position += publicSeedSize;
+        XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
+        position += publicSeedSize;
 		/* copy root */
-		XMSSUtil.copyBytesAtOffset(out, root, position);
+        XMSSUtil.copyBytesAtOffset(out, root, position);
 		/* concatenate bdsState */
-		byte[] bdsStateOut = null;
-		try {
-			bdsStateOut = XMSSUtil.serialize(bdsState);
-		} catch (IOException e) {
-			e.printStackTrace();
-			throw new RuntimeException("error serializing bds state");
-		}
-		return XMSSUtil.concat(out, bdsStateOut);
-	}
+        try
+        {
+            return Arrays.concatenate(out, XMSSUtil.serialize(bdsState));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("error serializing bds state: " + e.getMessage(), e);
+        }
+    }
 
-	/*
-	protected void increaseIndex(XMSSMT mt) {
-		if (mt == null) {
-			throw new NullPointerException("mt == null");
-		}
-		ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC);
-		long differenceHours = Duration.between(lastUsage, currentTime).toHours();
-		if (differenceHours >= 24) {
-			mt.getXMSS().setPublicSeed(getPublicSeed());
+    public long getIndex()
+    {
+        return index;
+    }
 
-			Map<Integer, BDS> bdsStates = mt.getBDS();
-			int xmssHeight = params.getXMSS().getParams().getHeight();
-			long keyIncreaseCount = differenceHours * indexIncreaseCountPerHour;
-			long oldGlobalIndex = getIndex();
-			long newGlobalIndex = oldGlobalIndex + keyIncreaseCount;
-			long oldIndexTree = XMSSUtil.getTreeIndex(oldGlobalIndex, xmssHeight);
-			long newIndexTree = XMSSUtil.getTreeIndex(newGlobalIndex, xmssHeight);
-			int newIndexLeaf = XMSSUtil.getLeafIndex(newGlobalIndex, xmssHeight);
+    public long getUsagesRemaining()
+    {
+        return (1L << this.getParameters().getHeight()) - this.getIndex();
+    }
 
-			// adjust bds instances
-			for (int layer = 0; layer < params.getLayers(); layer++) {
-				OTSHashAddress otsHashAddress = new OTSHashAddress();
-				otsHashAddress.setLayerAddress(layer);
-				otsHashAddress.setTreeAddress(newIndexTree);
+    public byte[] getSecretKeySeed()
+    {
+        return XMSSUtil.cloneArray(secretKeySeed);
+    }
 
-				if (newIndexLeaf != 0) {
-					if (oldIndexTree != newIndexTree || bdsStates.get(layer) == null) {
-						bdsStates.put(layer, new BDS(mt.getXMSS()));
-						bdsStates.get(layer).initialize(otsHashAddress);
-					}
-					for (int indexLeaf = bdsStates.get(layer).getIndex(); indexLeaf < newIndexLeaf; indexLeaf++) {
-						if (indexLeaf < ((1 << xmssHeight) - 1)) {
-							bdsStates.get(layer).nextAuthenticationPath(otsHashAddress);
-						}
-					}
-				}
-				oldIndexTree = XMSSUtil.getTreeIndex(oldIndexTree, xmssHeight);
-				newIndexLeaf = XMSSUtil.getLeafIndex(newIndexTree, xmssHeight);
-				newIndexTree = XMSSUtil.getTreeIndex(newIndexTree, xmssHeight);
-			}
-			setIndex(newGlobalIndex);
-		}
-	}
-	*/
+    public byte[] getSecretKeyPRF()
+    {
+        return XMSSUtil.cloneArray(secretKeyPRF);
+    }
 
-	public long getIndex() {
-		return index;
-	}
+    public byte[] getPublicSeed()
+    {
+        return XMSSUtil.cloneArray(publicSeed);
+    }
 
-	public byte[] getSecretKeySeed() {
-		return XMSSUtil.cloneArray(secretKeySeed);
-	}
+    public byte[] getRoot()
+    {
+        return XMSSUtil.cloneArray(root);
+    }
 
-	public byte[] getSecretKeyPRF() {
-		return XMSSUtil.cloneArray(secretKeyPRF);
-	}
+    BDSStateMap getBDSState()
+    {
+        return bdsState;
+    }
 
-	public byte[] getPublicSeed() {
-		return XMSSUtil.cloneArray(publicSeed);
-	}
+    public XMSSMTParameters getParameters()
+    {
+        return params;
+    }
 
-	public byte[] getRoot() {
-		return XMSSUtil.cloneArray(root);
-	}
-	
-	public Map<Integer, BDS> getBDSState() {
-		return bdsState;
-	}
+    public XMSSMTPrivateKeyParameters getNextKey()
+    {
+        BDSStateMap newState = new BDSStateMap(bdsState, params, this.getIndex(), publicSeed, secretKeySeed);
+
+        return new XMSSMTPrivateKeyParameters.Builder(params).withIndex(index + 1)
+            .withSecretKeySeed(secretKeySeed).withSecretKeyPRF(secretKeyPRF)
+            .withPublicSeed(publicSeed).withRoot(root)
+            .withBDSState(newState).build();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPublicKeyParameters.java
index 87b565e..eddd138 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTPublicKeyParameters.java
@@ -1,129 +1,156 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.text.ParseException;
-
 /**
- * XMSSMT Public Key.
- *
+ * XMSS^MT Public Key.
  */
-public final class XMSSMTPublicKeyParameters implements XMSSStoreableObjectInterface {
+public final class XMSSMTPublicKeyParameters
+    extends XMSSMTKeyParameters
+    implements XMSSStoreableObjectInterface
+{
+    private final XMSSMTParameters params;
+    // private final int oid;
+    private final byte[] root;
+    private final byte[] publicSeed;
 
-	private final XMSSMTParameters params;
-	// private final int oid;
-	private final byte[] root;
-	private final byte[] publicSeed;
-
-	private XMSSMTPublicKeyParameters(Builder builder) throws ParseException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		byte[] publicKey = builder.publicKey;
-		if (publicKey != null) {
-			/* import */
-			// int oidSize = 4;
-			int rootSize = n;
-			int publicSeedSize = n;
-			int totalSize = rootSize + publicSeedSize;
-			if (publicKey.length != totalSize) {
-				throw new ParseException("public key has wrong size", 0);
-			}
-			int position = 0;
+    private XMSSMTPublicKeyParameters(Builder builder)
+    {
+        super(false, builder.params.getDigest().getAlgorithmName());
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        byte[] publicKey = builder.publicKey;
+        if (publicKey != null)
+        {
+            /* import */
+            // int oidSize = 4;
+            int rootSize = n;
+            int publicSeedSize = n;
+            int totalSize = rootSize + publicSeedSize;
+            if (publicKey.length != totalSize)
+            {
+                throw new IllegalArgumentException("public key has wrong size");
+            }
+            int position = 0;
 			/*
 			 * oid = XMSSUtil.bytesToIntBigEndian(in, position); if (oid !=
 			 * params.getOid().getOid()) { throw new ParseException("wrong oid",
 			 * 0); } position += oidSize;
 			 */
-			root = XMSSUtil.extractBytesAtOffset(publicKey, position, rootSize);
-			position += rootSize;
-			publicSeed = XMSSUtil.extractBytesAtOffset(publicKey, position, publicSeedSize);
-		} else {
+            root = XMSSUtil.extractBytesAtOffset(publicKey, position, rootSize);
+            position += rootSize;
+            publicSeed = XMSSUtil.extractBytesAtOffset(publicKey, position, publicSeedSize);
+        }
+        else
+        {
 			/* set */
-			byte[] tmpRoot = builder.root;
-			if (tmpRoot != null) {
-				if (tmpRoot.length != n) {
-					throw new IllegalArgumentException("length of root must be equal to length of digest");
-				}
-				root = tmpRoot;
-			} else {
-				root = new byte[n];
-			}
-			byte[] tmpPublicSeed = builder.publicSeed;
-			if (tmpPublicSeed != null) {
-				if (tmpPublicSeed.length != n) {
-					throw new IllegalArgumentException("length of publicSeed must be equal to length of digest");
-				}
-				publicSeed = tmpPublicSeed;
-			} else {
-				publicSeed = new byte[n];
-			}
-		}
-	}
+            byte[] tmpRoot = builder.root;
+            if (tmpRoot != null)
+            {
+                if (tmpRoot.length != n)
+                {
+                    throw new IllegalArgumentException("length of root must be equal to length of digest");
+                }
+                root = tmpRoot;
+            }
+            else
+            {
+                root = new byte[n];
+            }
+            byte[] tmpPublicSeed = builder.publicSeed;
+            if (tmpPublicSeed != null)
+            {
+                if (tmpPublicSeed.length != n)
+                {
+                    throw new IllegalArgumentException("length of publicSeed must be equal to length of digest");
+                }
+                publicSeed = tmpPublicSeed;
+            }
+            else
+            {
+                publicSeed = new byte[n];
+            }
+        }
+    }
 
-	public static class Builder {
+    public static class Builder
+    {
 
-		/* mandatory */
-		private final XMSSMTParameters params;
-		/* optional */
-		private byte[] root = null;
-		private byte[] publicSeed = null;
-		private byte[] publicKey = null;
+        /* mandatory */
+        private final XMSSMTParameters params;
+        /* optional */
+        private byte[] root = null;
+        private byte[] publicSeed = null;
+        private byte[] publicKey = null;
 
-		public Builder(XMSSMTParameters params) {
-			super();
-			this.params = params;
-		}
+        public Builder(XMSSMTParameters params)
+        {
+            super();
+            this.params = params;
+        }
 
-		public Builder withRoot(byte[] val) {
-			root = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withRoot(byte[] val)
+        {
+            root = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withPublicSeed(byte[] val) {
-			publicSeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withPublicSeed(byte[] val)
+        {
+            publicSeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withPublicKey(byte[] val) {
-			publicKey = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withPublicKey(byte[] val)
+        {
+            publicKey = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public XMSSMTPublicKeyParameters build() throws ParseException {
-			return new XMSSMTPublicKeyParameters(this);
-		}
-	}
+        public XMSSMTPublicKeyParameters build()
+        {
+            return new XMSSMTPublicKeyParameters(this);
+        }
+    }
 
-	public byte[] toByteArray() {
+    public byte[] toByteArray()
+    {
 		/* oid || root || seed */
-		int n = params.getDigestSize();
-		// int oidSize = 4;
-		int rootSize = n;
-		int publicSeedSize = n;
-		int totalSize = rootSize + publicSeedSize;
-		// int totalSize = oidSize + rootSize + publicSeedSize;
-		byte[] out = new byte[totalSize];
-		int position = 0;
+        int n = params.getDigestSize();
+        // int oidSize = 4;
+        int rootSize = n;
+        int publicSeedSize = n;
+        int totalSize = rootSize + publicSeedSize;
+        // int totalSize = oidSize + rootSize + publicSeedSize;
+        byte[] out = new byte[totalSize];
+        int position = 0;
 		/* copy oid */
 		/*
 		 * XMSSUtil.intToBytesBigEndianOffset(out, oid, position); position +=
 		 * oidSize;
 		 */
 		/* copy root */
-		XMSSUtil.copyBytesAtOffset(out, root, position);
-		position += rootSize;
+        XMSSUtil.copyBytesAtOffset(out, root, position);
+        position += rootSize;
 		/* copy public seed */
-		XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
-		return out;
-	}
+        XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
+        return out;
+    }
 
-	public byte[] getRoot() {
-		return XMSSUtil.cloneArray(root);
-	}
+    public byte[] getRoot()
+    {
+        return XMSSUtil.cloneArray(root);
+    }
 
-	public byte[] getPublicSeed() {
-		return XMSSUtil.cloneArray(publicSeed);
-	}
+    public byte[] getPublicSeed()
+    {
+        return XMSSUtil.cloneArray(publicSeed);
+    }
+
+    public XMSSMTParameters getParameters()
+   	{
+   		return params;
+   	}
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSignature.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSignature.java
index 38fc208..34edb2f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSignature.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSignature.java
@@ -1,152 +1,181 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.bouncycastle.util.Arrays;
+
 /**
- * XMSSMT Signature.
- *
+ * XMSS^MT Signature.
  */
-public final class XMSSMTSignature implements XMSSStoreableObjectInterface {
+public final class XMSSMTSignature
+    implements XMSSStoreableObjectInterface
+{
 
-	private final XMSSMTParameters params;
-	private final long index;
-	private final byte[] random;
-	private final List<XMSSReducedSignature> reducedSignatures;
+    private final XMSSMTParameters params;
+    private final long index;
+    private final byte[] random;
+    private final List<XMSSReducedSignature> reducedSignatures;
 
-	private XMSSMTSignature(Builder builder) throws ParseException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		byte[] signature = builder.signature;
-		if (signature != null) {
-			/* import */
-			int len = params.getWOTSPlus().getParams().getLen();
-			int indexSize = (int) Math.ceil(params.getHeight() / (double) 8);
-			int randomSize = n;
-			int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n;
-			int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers();
-			int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal;
-			if (signature.length != totalSize) {
-				throw new ParseException("signature has wrong size", 0);
-			}
-			int position = 0;
-			index = XMSSUtil.bytesToXBigEndian(signature, position, indexSize);
-			if (!XMSSUtil.isIndexValid(params.getHeight(), index)) {
-				throw new ParseException("index out of bounds", 0);
-			}
-			position += indexSize;
-			random = XMSSUtil.extractBytesAtOffset(signature, position, randomSize);
-			position += randomSize;
-			reducedSignatures = new ArrayList<XMSSReducedSignature>();
-			while (position < signature.length) {
-				XMSSReducedSignature xmssSig = new XMSSReducedSignature.Builder(params.getXMSS().getParams())
-						.withReducedSignature(XMSSUtil.extractBytesAtOffset(signature, position, reducedSignatureSizeSingle))
-						.build();
-				reducedSignatures.add(xmssSig);
-				position += reducedSignatureSizeSingle;
-			}
-		} else {
+    private XMSSMTSignature(Builder builder)
+    {
+        super();
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        byte[] signature = builder.signature;
+        if (signature != null)
+        {
+            /* import */
+            int len = params.getWOTSPlus().getParams().getLen();
+            int indexSize = (int)Math.ceil(params.getHeight() / (double)8);
+            int randomSize = n;
+            int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n;
+            int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers();
+            int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal;
+            if (signature.length != totalSize)
+            {
+                throw new IllegalArgumentException("signature has wrong size");
+            }
+            int position = 0;
+            index = XMSSUtil.bytesToXBigEndian(signature, position, indexSize);
+            if (!XMSSUtil.isIndexValid(params.getHeight(), index))
+            {
+                throw new IllegalArgumentException("index out of bounds");
+            }
+            position += indexSize;
+            random = XMSSUtil.extractBytesAtOffset(signature, position, randomSize);
+            position += randomSize;
+            reducedSignatures = new ArrayList<XMSSReducedSignature>();
+            while (position < signature.length)
+            {
+                XMSSReducedSignature xmssSig = new XMSSReducedSignature.Builder(params.getXMSSParameters())
+                    .withReducedSignature(XMSSUtil.extractBytesAtOffset(signature, position, reducedSignatureSizeSingle))
+                    .build();
+                reducedSignatures.add(xmssSig);
+                position += reducedSignatureSizeSingle;
+            }
+        }
+        else
+        {
 			/* set */
-			index = builder.index;
-			byte[] tmpRandom = builder.random;
-			if (tmpRandom != null) {
-				if (tmpRandom.length != n) {
-					throw new IllegalArgumentException("size of random needs to be equal to size of digest");
-				}
-				random = tmpRandom;
-			} else {
-				random = new byte[n];
-			}
-			List<XMSSReducedSignature> tmpReducedSignatures = builder.reducedSignatures;
-			if (tmpReducedSignatures != null) {
-				reducedSignatures = tmpReducedSignatures;
-			} else {
-				reducedSignatures = new ArrayList<XMSSReducedSignature>();
-			}
-		}
-	}
+            index = builder.index;
+            byte[] tmpRandom = builder.random;
+            if (tmpRandom != null)
+            {
+                if (tmpRandom.length != n)
+                {
+                    throw new IllegalArgumentException("size of random needs to be equal to size of digest");
+                }
+                random = tmpRandom;
+            }
+            else
+            {
+                random = new byte[n];
+            }
+            List<XMSSReducedSignature> tmpReducedSignatures = builder.reducedSignatures;
+            if (tmpReducedSignatures != null)
+            {
+                reducedSignatures = tmpReducedSignatures;
+            }
+            else
+            {
+                reducedSignatures = new ArrayList<XMSSReducedSignature>();
+            }
+        }
+    }
 
-	public static class Builder {
-		
-		/* mandatory */
-		private final XMSSMTParameters params;
-		/* optional */
-		private long index = 0L;
-		private byte[] random = null;
-		private List<XMSSReducedSignature> reducedSignatures = null;
-		private byte[] signature = null;
-		
-		public Builder(XMSSMTParameters params) {
-			super();
-			this.params = params;
-		}
-		
-		public Builder withIndex(long val) {
-			index = val;
-			return this;
-		}
-		
-		public Builder withRandom(byte[] val) {
-			random = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public Builder withReducedSignatures(List<XMSSReducedSignature> val) {
-			reducedSignatures = val;
-			return this;
-		}
+    public static class Builder
+    {
 
-		public Builder withSignature(byte[] val) {
-			signature = val;
-			return this;
-		}
+        /* mandatory */
+        private final XMSSMTParameters params;
+        /* optional */
+        private long index = 0L;
+        private byte[] random = null;
+        private List<XMSSReducedSignature> reducedSignatures = null;
+        private byte[] signature = null;
 
-		public XMSSMTSignature build() throws ParseException {
-			return new XMSSMTSignature(this);
-		}
-	}
+        public Builder(XMSSMTParameters params)
+        {
+            super();
+            this.params = params;
+        }
 
-	public byte[] toByteArray() {
+        public Builder withIndex(long val)
+        {
+            index = val;
+            return this;
+        }
+
+        public Builder withRandom(byte[] val)
+        {
+            random = XMSSUtil.cloneArray(val);
+            return this;
+        }
+
+        public Builder withReducedSignatures(List<XMSSReducedSignature> val)
+        {
+            reducedSignatures = val;
+            return this;
+        }
+
+        public Builder withSignature(byte[] val)
+        {
+            signature = Arrays.clone(val);
+            return this;
+        }
+
+        public XMSSMTSignature build()
+        {
+            return new XMSSMTSignature(this);
+        }
+    }
+
+    public byte[] toByteArray()
+    {
 		/* index || random || reduced signatures */
-		int n = params.getDigestSize();
-		int len = params.getWOTSPlus().getParams().getLen();
-		int indexSize = (int) Math.ceil(params.getHeight() / (double) 8);
-		int randomSize = n;
-		int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n;
-		int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers();
-		int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal;
-		byte[] out = new byte[totalSize];
-		int position = 0;
+        int n = params.getDigestSize();
+        int len = params.getWOTSPlus().getParams().getLen();
+        int indexSize = (int)Math.ceil(params.getHeight() / (double)8);
+        int randomSize = n;
+        int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n;
+        int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers();
+        int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal;
+        byte[] out = new byte[totalSize];
+        int position = 0;
 		/* copy index */
-		byte[] indexBytes = XMSSUtil.toBytesBigEndian(index, indexSize);
-		XMSSUtil.copyBytesAtOffset(out, indexBytes, position);
-		position += indexSize;
+        byte[] indexBytes = XMSSUtil.toBytesBigEndian(index, indexSize);
+        XMSSUtil.copyBytesAtOffset(out, indexBytes, position);
+        position += indexSize;
 		/* copy random */
-		XMSSUtil.copyBytesAtOffset(out, random, position);
-		position += randomSize;
+        XMSSUtil.copyBytesAtOffset(out, random, position);
+        position += randomSize;
 		/* copy reduced signatures */
-		for (XMSSReducedSignature reducedSignature : reducedSignatures) {
-			byte[] signature = reducedSignature.toByteArray();
-			XMSSUtil.copyBytesAtOffset(out, signature, position);
-			position += reducedSignatureSizeSingle;
-		}
-		return out;
-	}
+        for (XMSSReducedSignature reducedSignature : reducedSignatures)
+        {
+            byte[] signature = reducedSignature.toByteArray();
+            XMSSUtil.copyBytesAtOffset(out, signature, position);
+            position += reducedSignatureSizeSingle;
+        }
+        return out;
+    }
 
-	public long getIndex() {
-		return index;
-	}
+    public long getIndex()
+    {
+        return index;
+    }
 
-	public byte[] getRandom() {
-		return XMSSUtil.cloneArray(random);
-	}
+    public byte[] getRandom()
+    {
+        return XMSSUtil.cloneArray(random);
+    }
 
-	public List<XMSSReducedSignature> getReducedSignatures() {
-		return reducedSignatures;
-	}
+    public List<XMSSReducedSignature> getReducedSignatures()
+    {
+        return reducedSignatures;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSigner.java
new file mode 100644
index 0000000..1f224e7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSMTSigner.java
@@ -0,0 +1,257 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.crypto.StateAwareMessageSigner;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * XMSS^MT Signer class.
+ */
+public class XMSSMTSigner
+    implements StateAwareMessageSigner
+{
+    private XMSSMTPrivateKeyParameters privateKey;
+    private XMSSMTPrivateKeyParameters nextKeyGenerator;
+    private XMSSMTPublicKeyParameters publicKey;
+    private XMSSMTParameters params;
+    private XMSSParameters xmssParams;
+
+    private WOTSPlus wotsPlus;
+
+    private boolean hasGenerated;
+    private boolean initSign;
+
+    public void init(boolean forSigning, CipherParameters param)
+    {
+        if (forSigning)
+        {
+            initSign = true;
+            hasGenerated = false;
+            privateKey = (XMSSMTPrivateKeyParameters)param;
+            nextKeyGenerator = privateKey;
+
+            params = privateKey.getParameters();
+            xmssParams = params.getXMSSParameters();
+        }
+        else
+        {
+            initSign = false;
+            publicKey = (XMSSMTPublicKeyParameters)param;
+
+            params = publicKey.getParameters();
+            xmssParams = params.getXMSSParameters();
+        }
+        
+        wotsPlus = new WOTSPlus(new WOTSPlusParameters(params.getDigest()));
+    }
+
+    public byte[] generateSignature(byte[] message)
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        if (initSign)
+        {
+            if (privateKey == null)
+            {
+                throw new IllegalStateException("signing key no longer usable");
+            }
+        }
+        else
+        {
+            throw new IllegalStateException("signer not initialized for signature generation");
+        }
+        if (privateKey.getBDSState().isEmpty())
+        {
+            throw new IllegalStateException("not initialized");
+        }
+
+        BDSStateMap bdsState = privateKey.getBDSState();
+
+        // privateKey.increaseIndex(this);
+        final long globalIndex = privateKey.getIndex();
+        final int totalHeight = params.getHeight();
+        final int xmssHeight = xmssParams.getHeight();
+        if (!XMSSUtil.isIndexValid(totalHeight, globalIndex))
+        {
+            throw new IllegalStateException("index out of bounds");
+        }
+
+      		/* compress message */
+        byte[] random = wotsPlus.getKhf().PRF(privateKey.getSecretKeyPRF(), XMSSUtil.toBytesBigEndian(globalIndex, 32));
+        byte[] concatenated = Arrays.concatenate(random, privateKey.getRoot(),
+            XMSSUtil.toBytesBigEndian(globalIndex, params.getDigestSize()));
+        byte[] messageDigest = wotsPlus.getKhf().HMsg(concatenated, message);
+
+        XMSSMTSignature signature = new XMSSMTSignature.Builder(params).withIndex(globalIndex).withRandom(random).build();
+
+
+      		/* layer 0 */
+        long indexTree = XMSSUtil.getTreeIndex(globalIndex, xmssHeight);
+        int indexLeaf = XMSSUtil.getLeafIndex(globalIndex, xmssHeight);
+
+        /* reset xmss */
+        wotsPlus.importKeys(new byte[params.getDigestSize()], privateKey.getPublicSeed());
+        /* create signature with XMSS tree on layer 0 */
+
+        /* adjust addresses */
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withTreeAddress(indexTree)
+            .withOTSAddress(indexLeaf).build();
+
+      		/* get authentication path from BDS */
+        if (bdsState.get(0) == null || indexLeaf == 0)
+        {
+            bdsState.put(0, new BDS(xmssParams, privateKey.getPublicSeed(), privateKey.getSecretKeySeed(), otsHashAddress));
+        }
+
+        /* sign message digest */
+        WOTSPlusSignature wotsPlusSignature = wotsSign(messageDigest, otsHashAddress);
+        
+        XMSSReducedSignature reducedSignature = new XMSSReducedSignature.Builder(xmssParams)
+                .withWOTSPlusSignature(wotsPlusSignature).withAuthPath(bdsState.get(0).getAuthenticationPath())
+                .build();
+
+        signature.getReducedSignatures().add(reducedSignature);
+      		/* loop over remaining layers */
+        for (int layer = 1; layer < params.getLayers(); layer++)
+        {
+      			/* get root of layer - 1 */
+            XMSSNode root = bdsState.get(layer - 1).getRoot();
+
+            indexLeaf = XMSSUtil.getLeafIndex(indexTree, xmssHeight);
+            indexTree = XMSSUtil.getTreeIndex(indexTree, xmssHeight);
+
+      			/* adjust addresses */
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withLayerAddress(layer)
+                .withTreeAddress(indexTree).withOTSAddress(indexLeaf).build();
+
+      			/* sign root digest of layer - 1 */
+            wotsPlusSignature = wotsSign(root.getValue(), otsHashAddress);
+      			/* get authentication path from BDS */
+            if (bdsState.get(layer) == null || XMSSUtil.isNewBDSInitNeeded(globalIndex, xmssHeight, layer))
+            {          
+                bdsState.put(layer, new BDS(xmssParams, privateKey.getPublicSeed(), privateKey.getSecretKeySeed(), otsHashAddress));
+            }
+
+            reducedSignature = new XMSSReducedSignature.Builder(xmssParams)
+                    .withWOTSPlusSignature(wotsPlusSignature)
+                    .withAuthPath(bdsState.get(layer).getAuthenticationPath()).build();
+
+            signature.getReducedSignatures().add(reducedSignature);
+        }
+
+        hasGenerated = true;
+
+        if (nextKeyGenerator != null)
+        {
+            privateKey = nextKeyGenerator.getNextKey();
+            nextKeyGenerator = privateKey;
+        }
+        else
+        {
+            privateKey = null;
+        }
+
+        return signature.toByteArray();
+    }
+
+    public boolean verifySignature(byte[] message, byte[] signature)
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
+		/* (re)create compressed message */
+        XMSSMTSignature sig = new XMSSMTSignature.Builder(params).withSignature(signature).build();
+
+        byte[] concatenated = Arrays.concatenate(sig.getRandom(), publicKey.getRoot(),
+                                         XMSSUtil.toBytesBigEndian(sig.getIndex(), params.getDigestSize()));
+        byte[] messageDigest = wotsPlus.getKhf().HMsg(concatenated, message);
+
+        long globalIndex = sig.getIndex();
+        int xmssHeight = xmssParams.getHeight();
+        long indexTree = XMSSUtil.getTreeIndex(globalIndex, xmssHeight);
+        int indexLeaf = XMSSUtil.getLeafIndex(globalIndex, xmssHeight);
+
+		/* adjust xmss */
+        wotsPlus.importKeys(new byte[params.getDigestSize()], publicKey.getPublicSeed());
+        
+		/* prepare addresses */
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withTreeAddress(indexTree)
+            .withOTSAddress(indexLeaf).build();
+
+		/* get root node on layer 0 */
+        XMSSReducedSignature xmssMTSignature = sig.getReducedSignatures().get(0);
+        XMSSNode rootNode = XMSSVerifierUtil.getRootNodeFromSignature(wotsPlus, xmssHeight, messageDigest, xmssMTSignature, otsHashAddress, indexLeaf);
+        for (int layer = 1; layer < params.getLayers(); layer++)
+        {
+            xmssMTSignature = sig.getReducedSignatures().get(layer);
+            indexLeaf = XMSSUtil.getLeafIndex(indexTree, xmssHeight);
+            indexTree = XMSSUtil.getTreeIndex(indexTree, xmssHeight);
+
+			/* adjust address */
+            otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withLayerAddress(layer)
+                .withTreeAddress(indexTree).withOTSAddress(indexLeaf).build();
+
+			/* get root node */
+            rootNode = XMSSVerifierUtil.getRootNodeFromSignature(wotsPlus, xmssHeight, rootNode.getValue(), xmssMTSignature, otsHashAddress, indexLeaf);
+        }
+
+		/* compare roots */
+        return Arrays.constantTimeAreEqual(rootNode.getValue(), publicKey.getRoot());
+    }
+
+    private WOTSPlusSignature wotsSign(byte[] messageDigest, OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+		/* (re)initialize WOTS+ instance */
+        wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(privateKey.getSecretKeySeed(), otsHashAddress), privateKey.getPublicSeed());
+		/* create WOTS+ signature */
+        return wotsPlus.sign(messageDigest, otsHashAddress);
+    }
+
+    public long getUsagesRemaining()
+    {
+        return privateKey.getUsagesRemaining();
+    }
+
+    public AsymmetricKeyParameter getUpdatedPrivateKey()
+    {
+        // if we've generated a signature return the last private key generated
+        // if we've only initialised leave it in place and return the next one instead.
+        if (hasGenerated)
+        {
+            XMSSMTPrivateKeyParameters privKey = privateKey;
+
+            privateKey = null;
+            nextKeyGenerator = null;
+
+            return privKey;
+        }
+        else
+        {
+            XMSSMTPrivateKeyParameters privKey = nextKeyGenerator.getNextKey();
+
+            nextKeyGenerator = null;
+
+            return privKey;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSNodeUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSNodeUtil.java
new file mode 100644
index 0000000..3faf491
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSNodeUtil.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+class XMSSNodeUtil
+{
+    /**
+     * Compresses a WOTS+ public key to a single n-byte string.
+     *
+     * @param publicKey WOTS+ public key to compress.
+     * @param address   Address.
+     * @return Compressed n-byte string of public key.
+     */
+    static XMSSNode lTree(WOTSPlus wotsPlus, WOTSPlusPublicKeyParameters publicKey, LTreeAddress address)
+    {
+        if (publicKey == null)
+        {
+            throw new NullPointerException("publicKey == null");
+        }
+        if (address == null)
+        {
+            throw new NullPointerException("address == null");
+        }
+        int len = wotsPlus.getParams().getLen();
+    		/* duplicate public key to XMSSNode Array */
+        byte[][] publicKeyBytes = publicKey.toByteArray();
+        XMSSNode[] publicKeyNodes = new XMSSNode[publicKeyBytes.length];
+        for (int i = 0; i < publicKeyBytes.length; i++)
+        {
+            publicKeyNodes[i] = new XMSSNode(0, publicKeyBytes[i]);
+        }
+        address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
+            .withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress()).withTreeHeight(0)
+            .withTreeIndex(address.getTreeIndex()).withKeyAndMask(address.getKeyAndMask()).build();
+        while (len > 1)
+        {
+            for (int i = 0; i < (int)Math.floor(len / 2); i++)
+            {
+                address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
+                    .withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress())
+                    .withTreeHeight(address.getTreeHeight()).withTreeIndex(i)
+                    .withKeyAndMask(address.getKeyAndMask()).build();
+                publicKeyNodes[i] = randomizeHash(wotsPlus, publicKeyNodes[2 * i], publicKeyNodes[(2 * i) + 1], address);
+            }
+            if (len % 2 == 1)
+            {
+                publicKeyNodes[(int)Math.floor(len / 2)] = publicKeyNodes[len - 1];
+            }
+            len = (int)Math.ceil((double)len / 2);
+            address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(address.getLayerAddress())
+                .withTreeAddress(address.getTreeAddress()).withLTreeAddress(address.getLTreeAddress())
+                .withTreeHeight(address.getTreeHeight() + 1).withTreeIndex(address.getTreeIndex())
+                .withKeyAndMask(address.getKeyAndMask()).build();
+        }
+        return publicKeyNodes[0];
+    }
+
+    /**
+     * Randomization of nodes in binary tree.
+     *
+     * @param left    Left node.
+     * @param right   Right node.
+     * @param address Address.
+     * @return Randomized hash of parent of left / right node.
+     */
+    static XMSSNode randomizeHash(WOTSPlus wotsPlus, XMSSNode left, XMSSNode right, XMSSAddress address)
+    {
+        if (left == null)
+        {
+            throw new NullPointerException("left == null");
+        }
+        if (right == null)
+        {
+            throw new NullPointerException("right == null");
+        }
+        if (left.getHeight() != right.getHeight())
+        {
+            throw new IllegalStateException("height of both nodes must be equal");
+        }
+        if (address == null)
+        {
+            throw new NullPointerException("address == null");
+        }
+        byte[] publicSeed = wotsPlus.getPublicSeed();
+
+        if (address instanceof LTreeAddress)
+        {
+            LTreeAddress tmpAddress = (LTreeAddress)address;
+            address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
+                .withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
+                .withKeyAndMask(0).build();
+        }
+        else if (address instanceof HashTreeAddress)
+        {
+            HashTreeAddress tmpAddress = (HashTreeAddress)address;
+            address = (HashTreeAddress)new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
+                .withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(0).build();
+        }
+
+        byte[] key = wotsPlus.getKhf().PRF(publicSeed, address.toByteArray());
+
+        if (address instanceof LTreeAddress)
+        {
+            LTreeAddress tmpAddress = (LTreeAddress)address;
+            address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
+                .withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
+                .withKeyAndMask(1).build();
+        }
+        else if (address instanceof HashTreeAddress)
+        {
+            HashTreeAddress tmpAddress = (HashTreeAddress)address;
+            address = (HashTreeAddress)new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
+                .withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(1).build();
+        }
+
+        byte[] bitmask0 = wotsPlus.getKhf().PRF(publicSeed, address.toByteArray());
+
+        if (address instanceof LTreeAddress)
+        {
+            LTreeAddress tmpAddress = (LTreeAddress)address;
+            address = (LTreeAddress)new LTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withLTreeAddress(tmpAddress.getLTreeAddress())
+                .withTreeHeight(tmpAddress.getTreeHeight()).withTreeIndex(tmpAddress.getTreeIndex())
+                .withKeyAndMask(2).build();
+        }
+        else if (address instanceof HashTreeAddress)
+        {
+            HashTreeAddress tmpAddress = (HashTreeAddress)address;
+            address = (HashTreeAddress)new HashTreeAddress.Builder().withLayerAddress(tmpAddress.getLayerAddress())
+                .withTreeAddress(tmpAddress.getTreeAddress()).withTreeHeight(tmpAddress.getTreeHeight())
+                .withTreeIndex(tmpAddress.getTreeIndex()).withKeyAndMask(2).build();
+        }
+
+        byte[] bitmask1 = wotsPlus.getKhf().PRF(publicSeed, address.toByteArray());
+        int n = wotsPlus.getParams().getDigestSize();
+        byte[] tmpMask = new byte[2 * n];
+        for (int i = 0; i < n; i++)
+        {
+            tmpMask[i] = (byte)(left.getValue()[i] ^ bitmask0[i]);
+        }
+        for (int i = 0; i < n; i++)
+        {
+            tmpMask[i + n] = (byte)(right.getValue()[i] ^ bitmask1[i]);
+        }
+        byte[] out = wotsPlus.getKhf().H(key, tmpMask);
+        return new XMSSNode(left.getHeight(), out);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSParameters.java
index 55061a8..3ef061b 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSParameters.java
@@ -1,102 +1,101 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.Digest;
 
 /**
  * XMSS Parameters.
- *
  */
-public final class XMSSParameters {
+public final class XMSSParameters
+{
 
-	private final XMSSOid oid;
-	private final WOTSPlus wotsPlus;
-	private final SecureRandom prng;
-	private final int height;
-	private final int k;
+    private final XMSSOid oid;
+    private final WOTSPlus wotsPlus;
+    //private final SecureRandom prng;
+    private final int height;
+    private final int k;
 
-	/**
-	 * XMSS Constructor...
-	 *
-	 * @param height
-	 *            Height of tree.
-	 * @param digest
-	 *            Digest to use.
-	 * @param prng
-	 *            Secure random to use.
-	 */
-	public XMSSParameters(int height, Digest digest, SecureRandom prng) {
-		super();
-		if (height < 2) {
-			throw new IllegalArgumentException("height must be >= 2");
-		}
-		if (digest == null) {
-			throw new NullPointerException("digest == null");
-		}
-		if (prng == null) {
-			throw new NullPointerException("prng == null");
-		}
-		wotsPlus = new WOTSPlus(new WOTSPlusParameters(digest));
-		this.prng = prng;
-		this.height = height;
-		this.k = determineMinK();
-		oid = DefaultXMSSOid.lookup(getDigest().getAlgorithmName(), getDigestSize(), getWinternitzParameter(),
-				wotsPlus.getParams().getLen(), height);
-		/*
+    /**
+     * XMSS Constructor...
+     *
+     * @param height Height of tree.
+     * @param digest Digest to use.
+     */
+    public XMSSParameters(int height, Digest digest)
+    {
+        super();
+        if (height < 2)
+        {
+            throw new IllegalArgumentException("height must be >= 2");
+        }
+        if (digest == null)
+        {
+            throw new NullPointerException("digest == null");
+        }
+
+        wotsPlus = new WOTSPlus(new WOTSPlusParameters(digest));
+        this.height = height;
+        this.k = determineMinK();
+        oid = DefaultXMSSOid.lookup(getDigest().getAlgorithmName(), getDigestSize(), getWinternitzParameter(),
+            wotsPlus.getParams().getLen(), height);
+        /*
 		 * if (oid == null) { throw new InvalidParameterException(); }
 		 */
-	}
+    }
 
-	private int determineMinK() {
-		for (int k = 2; k <= height; k++) {
-			if ((height - k) % 2 == 0) {
-				return k;
-			}
-		}
-		throw new IllegalStateException("should never happen...");
-	}
+    private int determineMinK()
+    {
+        for (int k = 2; k <= height; k++)
+        {
+            if ((height - k) % 2 == 0)
+            {
+                return k;
+            }
+        }
+        throw new IllegalStateException("should never happen...");
+    }
 
-	protected Digest getDigest() {
-		return wotsPlus.getParams().getDigest();
-	}
+    protected Digest getDigest()
+    {
+        return wotsPlus.getParams().getDigest();
+    }
 
-	protected SecureRandom getPRNG() {
-		return prng;
-	}
+    /**
+     * Getter digest size.
+     *
+     * @return Digest size.
+     */
+    public int getDigestSize()
+    {
+        return wotsPlus.getParams().getDigestSize();
+    }
 
-	/**
-	 * Getter digest size.
-	 * 
-	 * @return Digest size.
-	 */
-	public int getDigestSize() {
-		return wotsPlus.getParams().getDigestSize();
-	}
+    /**
+     * Getter Winternitz parameter.
+     *
+     * @return Winternitz parameter.
+     */
+    public int getWinternitzParameter()
+    {
+        return wotsPlus.getParams().getWinternitzParameter();
+    }
 
-	/**
-	 * Getter Winternitz parameter.
-	 * 
-	 * @return Winternitz parameter.
-	 */
-	public int getWinternitzParameter() {
-		return wotsPlus.getParams().getWinternitzParameter();
-	}
+    /**
+     * Getter height.
+     *
+     * @return XMSS height.
+     */
+    public int getHeight()
+    {
+        return height;
+    }
 
-	/**
-	 * Getter height.
-	 * 
-	 * @return XMSS height.
-	 */
-	public int getHeight() {
-		return height;
-	}
+    WOTSPlus getWOTSPlus()
+    {
+        return wotsPlus;
+    }
 
-	protected WOTSPlus getWOTSPlus() {
-		return wotsPlus;
-	}
-
-	protected int getK() {
-		return k;
-	}
+    int getK()
+    {
+        return k;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPrivateKeyParameters.java
index b918b91..23b9629 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPrivateKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPrivateKeyParameters.java
@@ -1,256 +1,350 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
 import java.io.IOException;
-import java.text.ParseException;
 
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 /**
  * XMSS Private Key.
- *
  */
-public final class XMSSPrivateKeyParameters implements XMSSStoreableObjectInterface {
+public final class XMSSPrivateKeyParameters
+    extends XMSSKeyParameters
+    implements XMSSStoreableObjectInterface
+{
 
-	/**
-	 * XMSS parameters object.
-	 */
-	private final XMSSParameters params;
-	/**
-	 * Index for WOTS+ keys (randomization factor).
-	 */
-	private final int index;
-	/**
-	 * Secret for the derivation of WOTS+ secret keys.
-	 */
-	private final byte[] secretKeySeed;
-	/**
-	 * Secret for the randomization of message digests during signature
-	 * creation.
-	 */
-	private final byte[] secretKeyPRF;
-	/**
-	 * Public seed for the randomization of hashes.
-	 */
-	private final byte[] publicSeed;
-	/**
-	 * Public root of binary tree.
-	 */
-	private final byte[] root;
-	/**
-	 * BDS state.
-	 */
-	private final BDS bdsState;
+    /**
+     * XMSS parameters object.
+     */
+    private final XMSSParameters params;
+    /**
+     * Secret for the derivation of WOTS+ secret keys.
+     */
+    private final byte[] secretKeySeed;
+    /**
+     * Secret for the randomization of message digests during signature
+     * creation.
+     */
+    private final byte[] secretKeyPRF;
+    /**
+     * Public seed for the randomization of hashes.
+     */
+    private final byte[] publicSeed;
+    /**
+     * Public root of binary tree.
+     */
+    private final byte[] root;
+    /**
+     * BDS state.
+     */
+    private final BDS bdsState;
 
-	private XMSSPrivateKeyParameters(Builder builder) throws ParseException, ClassNotFoundException, IOException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		byte[] privateKey = builder.privateKey;
-		if (privateKey != null) {
-			if (builder.xmss == null) {
-				throw new NullPointerException("xmss == null");
-			}
-			/* import */
-			int height = params.getHeight();
-			int indexSize = 4;
-			int secretKeySize = n;
-			int secretKeyPRFSize = n;
-			int publicSeedSize = n;
-			int rootSize = n;
+    private XMSSPrivateKeyParameters(Builder builder)
+    {
+        super(true, builder.params.getDigest().getAlgorithmName());
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        byte[] privateKey = builder.privateKey;
+        if (privateKey != null)
+        {
+            if (builder.xmss == null)
+            {
+                throw new NullPointerException("xmss == null");
+            }
+            /* import */
+            int height = params.getHeight();
+            int indexSize = 4;
+            int secretKeySize = n;
+            int secretKeyPRFSize = n;
+            int publicSeedSize = n;
+            int rootSize = n;
 			/*
 			int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
 			if (privateKey.length != totalSize) {
 				throw new ParseException("private key has wrong size", 0);
 			}
 			*/
-			int position = 0;
-			index = Pack.bigEndianToInt(privateKey, position);
-			if (!XMSSUtil.isIndexValid(height, index)) {
-				throw new ParseException("index out of bounds", 0);
-			}
-			position += indexSize;
-			secretKeySeed = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeySize);
-			position += secretKeySize;
-			secretKeyPRF = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeyPRFSize);
-			position += secretKeyPRFSize;
-			publicSeed = XMSSUtil.extractBytesAtOffset(privateKey, position, publicSeedSize);
-			position += publicSeedSize;
-			root = XMSSUtil.extractBytesAtOffset(privateKey, position, rootSize);
-			position += rootSize;
+            int position = 0;
+            int index = Pack.bigEndianToInt(privateKey, position);
+            if (!XMSSUtil.isIndexValid(height, index))
+            {
+                throw new IllegalArgumentException("index out of bounds");
+            }
+            position += indexSize;
+            secretKeySeed = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeySize);
+            position += secretKeySize;
+            secretKeyPRF = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeyPRFSize);
+            position += secretKeyPRFSize;
+            publicSeed = XMSSUtil.extractBytesAtOffset(privateKey, position, publicSeedSize);
+            position += publicSeedSize;
+            root = XMSSUtil.extractBytesAtOffset(privateKey, position, rootSize);
+            position += rootSize;
 			/* import BDS state */
-			byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(privateKey, position, privateKey.length - position);
-			BDS bdsImport = (BDS) XMSSUtil.deserialize(bdsStateBinary);
-			bdsImport.setXMSS(builder.xmss);
-			bdsImport.validate();
-			bdsState = bdsImport;
-		} else {
+            byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(privateKey, position, privateKey.length - position);
+            try
+            {
+                BDS bdsImport = (BDS)XMSSUtil.deserialize(bdsStateBinary, BDS.class);
+                if (bdsImport.getIndex() != index)
+                {
+                    throw new IllegalStateException("serialized BDS has wrong index");
+                }
+                bdsState = bdsImport.withWOTSDigest(DigestUtil.getDigestOID(builder.xmss.getDigest().getAlgorithmName()));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+        }
+        else
+        {
 			/* set */
-			index = builder.index;
-			byte[] tmpSecretKeySeed = builder.secretKeySeed;
-			if (tmpSecretKeySeed != null) {
-				if (tmpSecretKeySeed.length != n) {
-					throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest");
-				}
-				secretKeySeed = tmpSecretKeySeed;
-			} else {
-				secretKeySeed = new byte[n];
-			}
-			byte[] tmpSecretKeyPRF = builder.secretKeyPRF;
-			if (tmpSecretKeyPRF != null) {
-				if (tmpSecretKeyPRF.length != n) {
-					throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest");
-				}
-				secretKeyPRF = tmpSecretKeyPRF;
-			} else {
-				secretKeyPRF = new byte[n];
-			}
-			byte[] tmpPublicSeed = builder.publicSeed;
-			if (tmpPublicSeed != null) {
-				if (tmpPublicSeed.length != n) {
-					throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest");
-				}
-				publicSeed = tmpPublicSeed;
-			} else {
-				publicSeed = new byte[n];
-			}
-			byte[] tmpRoot = builder.root;
-			if (tmpRoot != null) {
-				if (tmpRoot.length != n) {
-					throw new IllegalArgumentException("size of root needs to be equal size of digest");
-				}
-				root = tmpRoot;
-			} else {
-				root = new byte[n];
-			}
-			BDS tmpBDSState = builder.bdsState;
-			if (tmpBDSState != null) {
-				bdsState = tmpBDSState;
-			} else {
-				bdsState = new BDS(new XMSS(params));
-			}
-		}
-	}
+            byte[] tmpSecretKeySeed = builder.secretKeySeed;
+            if (tmpSecretKeySeed != null)
+            {
+                if (tmpSecretKeySeed.length != n)
+                {
+                    throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest");
+                }
+                secretKeySeed = tmpSecretKeySeed;
+            }
+            else
+            {
+                secretKeySeed = new byte[n];
+            }
+            byte[] tmpSecretKeyPRF = builder.secretKeyPRF;
+            if (tmpSecretKeyPRF != null)
+            {
+                if (tmpSecretKeyPRF.length != n)
+                {
+                    throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest");
+                }
+                secretKeyPRF = tmpSecretKeyPRF;
+            }
+            else
+            {
+                secretKeyPRF = new byte[n];
+            }
+            byte[] tmpPublicSeed = builder.publicSeed;
+            if (tmpPublicSeed != null)
+            {
+                if (tmpPublicSeed.length != n)
+                {
+                    throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest");
+                }
+                publicSeed = tmpPublicSeed;
+            }
+            else
+            {
+                publicSeed = new byte[n];
+            }
+            byte[] tmpRoot = builder.root;
+            if (tmpRoot != null)
+            {
+                if (tmpRoot.length != n)
+                {
+                    throw new IllegalArgumentException("size of root needs to be equal size of digest");
+                }
+                root = tmpRoot;
+            }
+            else
+            {
+                root = new byte[n];
+            }
+            BDS tmpBDSState = builder.bdsState;
+            if (tmpBDSState != null)
+            {
+                bdsState = tmpBDSState;
+            }
+            else
+            {
+                if (builder.index < ((1 << params.getHeight()) - 2) && tmpPublicSeed != null && tmpSecretKeySeed != null)
+                {
+                    bdsState = new BDS(params, tmpPublicSeed, tmpSecretKeySeed, (OTSHashAddress)new OTSHashAddress.Builder().build(), builder.index);
+                }
+                else
+                {
+                    bdsState = new BDS(params, builder.index);
+                }
+            }
+        }
+    }
 
-	public static class Builder {
+    public long getUsagesRemaining()
+    {
+        return (1L << this.getParameters().getHeight()) - this.getIndex();
+    }
 
-		/* mandatory */
-		private final XMSSParameters params;
-		/* optional */
-		private int index = 0;
-		private byte[] secretKeySeed = null;
-		private byte[] secretKeyPRF = null;
-		private byte[] publicSeed = null;
-		private byte[] root = null;
-		private BDS bdsState = null;
-		private byte[] privateKey = null;
-		private XMSS xmss = null;
+    public static class Builder
+    {
 
-		public Builder(XMSSParameters params) {
-			super();
-			this.params = params;
-		}
+        /* mandatory */
+        private final XMSSParameters params;
+        /* optional */
+        private int index = 0;
+        private byte[] secretKeySeed = null;
+        private byte[] secretKeyPRF = null;
+        private byte[] publicSeed = null;
+        private byte[] root = null;
+        private BDS bdsState = null;
+        private byte[] privateKey = null;
+        private XMSSParameters xmss = null;
 
-		public Builder withIndex(int val) {
-			index = val;
-			return this;
-		}
+        public Builder(XMSSParameters params)
+        {
+            super();
+            this.params = params;
+        }
 
-		public Builder withSecretKeySeed(byte[] val) {
-			secretKeySeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public Builder withSecretKeyPRF(byte[] val) {
-			secretKeyPRF = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withIndex(int val)
+        {
+            index = val;
+            return this;
+        }
 
-		public Builder withPublicSeed(byte[] val) {
-			publicSeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withSecretKeySeed(byte[] val)
+        {
+            secretKeySeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withRoot(byte[] val) {
-			root = XMSSUtil.cloneArray(val);
-			return this;
-		}
+        public Builder withSecretKeyPRF(byte[] val)
+        {
+            secretKeyPRF = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withBDSState(BDS valBDS) {
-			bdsState = valBDS;
-			return this;
-		}
+        public Builder withPublicSeed(byte[] val)
+        {
+            publicSeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public Builder withPrivateKey(byte[] privateKeyVal, XMSS xmssVal) {
-			privateKey = XMSSUtil.cloneArray(privateKeyVal);
-			xmss = xmssVal;
-			return this;
-		}
+        public Builder withRoot(byte[] val)
+        {
+            root = XMSSUtil.cloneArray(val);
+            return this;
+        }
 
-		public XMSSPrivateKeyParameters build() throws ParseException, ClassNotFoundException, IOException {
-			return new XMSSPrivateKeyParameters(this);
-		}
-	}
+        public Builder withBDSState(BDS valBDS)
+        {
+            bdsState = valBDS;
+            return this;
+        }
 
-	public byte[] toByteArray() {
+        public Builder withPrivateKey(byte[] privateKeyVal, XMSSParameters xmssParameters)
+        {
+            privateKey = XMSSUtil.cloneArray(privateKeyVal);
+            xmss = xmssParameters;
+            return this;
+        }
+
+        public XMSSPrivateKeyParameters build()
+        {
+            return new XMSSPrivateKeyParameters(this);
+        }
+    }
+
+    public byte[] toByteArray()
+    {
 		/* index || secretKeySeed || secretKeyPRF || publicSeed || root */
-		int n = params.getDigestSize();
-		int indexSize = 4;
-		int secretKeySize = n;
-		int secretKeyPRFSize = n;
-		int publicSeedSize = n;
-		int rootSize = n;
-		int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
-		byte[] out = new byte[totalSize];
-		int position = 0;
+        int n = params.getDigestSize();
+        int indexSize = 4;
+        int secretKeySize = n;
+        int secretKeyPRFSize = n;
+        int publicSeedSize = n;
+        int rootSize = n;
+        int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize;
+        byte[] out = new byte[totalSize];
+        int position = 0;
 		/* copy index */
-		XMSSUtil.intToBytesBigEndianOffset(out, index, position);
-		position += indexSize;
+        Pack.intToBigEndian(bdsState.getIndex(), out, position);
+        position += indexSize;
 		/* copy secretKeySeed */
-		XMSSUtil.copyBytesAtOffset(out, secretKeySeed, position);
-		position += secretKeySize;
+        XMSSUtil.copyBytesAtOffset(out, secretKeySeed, position);
+        position += secretKeySize;
 		/* copy secretKeyPRF */
-		XMSSUtil.copyBytesAtOffset(out, secretKeyPRF, position);
-		position += secretKeyPRFSize;
+        XMSSUtil.copyBytesAtOffset(out, secretKeyPRF, position);
+        position += secretKeyPRFSize;
 		/* copy publicSeed */
-		XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
-		position += publicSeedSize;
+        XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
+        position += publicSeedSize;
 		/* copy root */
-		XMSSUtil.copyBytesAtOffset(out, root, position);
+        XMSSUtil.copyBytesAtOffset(out, root, position);
 		/* concatenate bdsState */
-		byte[] bdsStateOut = null;
-		try {
-			bdsStateOut = XMSSUtil.serialize(bdsState);
-		} catch (IOException e) {
-			e.printStackTrace();
-			throw new RuntimeException("error serializing bds state");
-		}
-		return XMSSUtil.concat(out, bdsStateOut);
-	}
+        byte[] bdsStateOut = null;
+        try
+        {
+            bdsStateOut = XMSSUtil.serialize(bdsState);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("error serializing bds state: " + e.getMessage());
+        }
 
-	public int getIndex() {
-		return index;
-	}
+        return Arrays.concatenate(out, bdsStateOut);
+    }
 
-	public byte[] getSecretKeySeed() {
-		return XMSSUtil.cloneArray(secretKeySeed);
-	}
+    public int getIndex()
+    {
+        return bdsState.getIndex();
+    }
 
-	public byte[] getSecretKeyPRF() {
-		return XMSSUtil.cloneArray(secretKeyPRF);
-	}
+    public byte[] getSecretKeySeed()
+    {
+        return XMSSUtil.cloneArray(secretKeySeed);
+    }
 
-	public byte[] getPublicSeed() {
-		return XMSSUtil.cloneArray(publicSeed);
-	}
+    public byte[] getSecretKeyPRF()
+    {
+        return XMSSUtil.cloneArray(secretKeyPRF);
+    }
 
-	public byte[] getRoot() {
-		return XMSSUtil.cloneArray(root);
-	}
-	
-	public BDS getBDSState() {
-		return bdsState;
-	}
+    public byte[] getPublicSeed()
+    {
+        return XMSSUtil.cloneArray(publicSeed);
+    }
+
+    public byte[] getRoot()
+    {
+        return XMSSUtil.cloneArray(root);
+    }
+
+    BDS getBDSState()
+    {
+        return bdsState;
+    }
+
+    public XMSSParameters getParameters()
+    {
+        return params;
+    }
+
+    public XMSSPrivateKeyParameters getNextKey()
+    {
+        /* prepare authentication path for next leaf */
+        int treeHeight = this.params.getHeight();
+        if (this.getIndex() < ((1 << treeHeight) - 1))
+        {
+            return new XMSSPrivateKeyParameters.Builder(params)
+                .withSecretKeySeed(secretKeySeed).withSecretKeyPRF(secretKeyPRF)
+                .withPublicSeed(publicSeed).withRoot(root)
+                .withBDSState(bdsState.getNextState(publicSeed, secretKeySeed, (OTSHashAddress)new OTSHashAddress.Builder().build())).build();
+        }
+        else
+        {
+            return new XMSSPrivateKeyParameters.Builder(params)
+                .withSecretKeySeed(secretKeySeed).withSecretKeyPRF(secretKeyPRF)
+                .withPublicSeed(publicSeed).withRoot(root)
+                .withBDSState(new BDS(params, getIndex() + 1)).build();  // no more nodes left.
+        }
+    }
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPublicKeyParameters.java
index 8b8f9f9..a0baa06 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSPublicKeyParameters.java
@@ -1,134 +1,162 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.text.ParseException;
-
 /**
  * XMSS Public Key.
- *
  */
-public final class XMSSPublicKeyParameters implements XMSSStoreableObjectInterface {
+public final class XMSSPublicKeyParameters
+    extends XMSSKeyParameters
+    implements XMSSStoreableObjectInterface
+{
 
-	/**
-	 * XMSS parameters object.
-	 */
-	private final XMSSParameters params;
-	//private final int oid;
-	private final byte[] root;
-	private final byte[] publicSeed;
+    /**
+     * XMSS parameters object.
+     */
+    private final XMSSParameters params;
+    //private final int oid;
+    private final byte[] root;
+    private final byte[] publicSeed;
 
-	private XMSSPublicKeyParameters(Builder builder) throws ParseException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		byte[] publicKey = builder.publicKey;
-		if (publicKey != null) {
-			/* import */
-			// int oidSize = 4;
-			int rootSize = n;
-			int publicSeedSize = n;
-			// int totalSize = oidSize + rootSize + publicSeedSize;
-			int totalSize = rootSize + publicSeedSize;
-			if (publicKey.length != totalSize) {
-				throw new ParseException("public key has wrong size", 0);
-			}
-			int position = 0;
-			/*
-			 * oid = XMSSUtil.bytesToIntBigEndian(publicKey, position); if (oid !=
-			 * xmss.getParams().getOid().getOid()) { throw new
-			 * ParseException("public key not compatible with current instance parameters"
-			 * , 0); } position += oidSize;
-			 */
-			root = XMSSUtil.extractBytesAtOffset(publicKey, position, rootSize);
-			position += rootSize;
-			publicSeed = XMSSUtil.extractBytesAtOffset(publicKey, position, publicSeedSize);
-		} else {
-			/* set */
-			byte[] tmpRoot = builder.root;
-			if (tmpRoot != null) {
-				if (tmpRoot.length != n) {
-					throw new IllegalArgumentException("length of root must be equal to length of digest");
-				}
-				root = tmpRoot;
-			} else {
-				root = new byte[n];
-			}
-			byte[] tmpPublicSeed = builder.publicSeed;
-			if (tmpPublicSeed != null) {
-				if (tmpPublicSeed.length != n) {
-					throw new IllegalArgumentException("length of publicSeed must be equal to length of digest");
-				}
-				publicSeed = tmpPublicSeed;
-			} else {
-				publicSeed = new byte[n];
-			}
-		}
-	}
+    private XMSSPublicKeyParameters(Builder builder)
+    {
+        super(false, builder.params.getDigest().getAlgorithmName());
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        byte[] publicKey = builder.publicKey;
+        if (publicKey != null)
+        {
+            /* import */
+            // int oidSize = 4;
+            int rootSize = n;
+            int publicSeedSize = n;
+            // int totalSize = oidSize + rootSize + publicSeedSize;
+            int totalSize = rootSize + publicSeedSize;
+            if (publicKey.length != totalSize)
+            {
+                throw new IllegalArgumentException("public key has wrong size");
+            }
+            int position = 0;
+            /*
+             * oid = XMSSUtil.bytesToIntBigEndian(publicKey, position); if (oid !=
+             * xmss.getParams().getOid().getOid()) { throw new
+             * ParseException("public key not compatible with current instance parameters"
+             * , 0); } position += oidSize;
+             */
+            root = XMSSUtil.extractBytesAtOffset(publicKey, position, rootSize);
+            position += rootSize;
+            publicSeed = XMSSUtil.extractBytesAtOffset(publicKey, position, publicSeedSize);
+        }
+        else
+        {
+            /* set */
+            byte[] tmpRoot = builder.root;
+            if (tmpRoot != null)
+            {
+                if (tmpRoot.length != n)
+                {
+                    throw new IllegalArgumentException("length of root must be equal to length of digest");
+                }
+                root = tmpRoot;
+            }
+            else
+            {
+                root = new byte[n];
+            }
+            byte[] tmpPublicSeed = builder.publicSeed;
+            if (tmpPublicSeed != null)
+            {
+                if (tmpPublicSeed.length != n)
+                {
+                    throw new IllegalArgumentException("length of publicSeed must be equal to length of digest");
+                }
+                publicSeed = tmpPublicSeed;
+            }
+            else
+            {
+                publicSeed = new byte[n];
+            }
+        }
+    }
 
-	public static class Builder {
-		
-		/* mandatory */
-		private final XMSSParameters params;
-		/* optional */
-		private byte[] root = null;
-		private byte[] publicSeed = null;
-		private byte[] publicKey = null;
-		
-		public Builder(XMSSParameters params) {
-			super();
-			this.params = params;
-		}
-		
-		public Builder withRoot(byte[] val) {
-			root = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public Builder withPublicSeed(byte[] val) {
-			publicSeed = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public Builder withPublicKey(byte[] val) {
-			publicKey = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public XMSSPublicKeyParameters build() throws ParseException {
-			return new XMSSPublicKeyParameters(this);
-		}
-	}
+    public static class Builder
+    {
 
-	public byte[] toByteArray() {
-		/* oid || root || seed */
-		int n = params.getDigestSize();
-		// int oidSize = 4;
-		int rootSize = n;
-		int publicSeedSize = n;
-		// int totalSize = oidSize + rootSize + publicSeedSize;
-		int totalSize = rootSize + publicSeedSize;
-		byte[] out = new byte[totalSize];
-		int position = 0;
-		/* copy oid */
-		/*
-		 * XMSSUtil.intToBytesBigEndianOffset(out, oid, position); position +=
-		 * oidSize;
-		 */
-		/* copy root */
-		XMSSUtil.copyBytesAtOffset(out, root, position);
-		position += rootSize;
-		/* copy public seed */
-		XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
-		return out;
-	}
+        /* mandatory */
+        private final XMSSParameters params;
+        /* optional */
+        private byte[] root = null;
+        private byte[] publicSeed = null;
+        private byte[] publicKey = null;
 
-	public byte[] getRoot() {
-		return XMSSUtil.cloneArray(root);
-	}
+        public Builder(XMSSParameters params)
+        {
+            super();
+            this.params = params;
+        }
 
-	public byte[] getPublicSeed() {
-		return XMSSUtil.cloneArray(publicSeed);
-	}
+        public Builder withRoot(byte[] val)
+        {
+            root = XMSSUtil.cloneArray(val);
+            return this;
+        }
+
+        public Builder withPublicSeed(byte[] val)
+        {
+            publicSeed = XMSSUtil.cloneArray(val);
+            return this;
+        }
+
+        public Builder withPublicKey(byte[] val)
+        {
+            publicKey = XMSSUtil.cloneArray(val);
+            return this;
+        }
+
+        public XMSSPublicKeyParameters build()
+        {
+            return new XMSSPublicKeyParameters(this);
+        }
+    }
+
+    public byte[] toByteArray()
+    {
+        /* oid || root || seed */
+        int n = params.getDigestSize();
+        // int oidSize = 4;
+        int rootSize = n;
+        int publicSeedSize = n;
+        // int totalSize = oidSize + rootSize + publicSeedSize;
+        int totalSize = rootSize + publicSeedSize;
+        byte[] out = new byte[totalSize];
+        int position = 0;
+        /* copy oid */
+        /*
+         * XMSSUtil.intToBytesBigEndianOffset(out, oid, position); position +=
+         * oidSize;
+         */
+        /* copy root */
+        XMSSUtil.copyBytesAtOffset(out, root, position);
+        position += rootSize;
+        /* copy public seed */
+        XMSSUtil.copyBytesAtOffset(out, publicSeed, position);
+        return out;
+    }
+
+    public byte[] getRoot()
+    {
+        return XMSSUtil.cloneArray(root);
+    }
+
+    public byte[] getPublicSeed()
+    {
+        return XMSSUtil.cloneArray(publicSeed);
+    }
+
+    public XMSSParameters getParameters()
+    {
+        return params;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSReducedSignature.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSReducedSignature.java
index f587cc7..dff01dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSReducedSignature.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSReducedSignature.java
@@ -1,137 +1,164 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Reduced XMSS Signature.
- *
  */
-public class XMSSReducedSignature implements XMSSStoreableObjectInterface {
+public class XMSSReducedSignature
+    implements XMSSStoreableObjectInterface
+{
 
-	private final XMSSParameters params;
-	private final WOTSPlusSignature wotsPlusSignature;
-	private final List<XMSSNode> authPath;
+    private final XMSSParameters params;
+    private final WOTSPlusSignature wotsPlusSignature;
+    private final List<XMSSNode> authPath;
 
-	protected XMSSReducedSignature(Builder builder) throws ParseException {
-		super();
-		params = builder.params;
-		if (params == null) {
-			throw new NullPointerException("params == null");
-		}
-		int n = params.getDigestSize();
-		int len = params.getWOTSPlus().getParams().getLen();
-		int height = params.getHeight();
-		byte[] reducedSignature = builder.reducedSignature;
-		if (reducedSignature != null) {
-			/* import */
-			int signatureSize = len * n;
-			int authPathSize = height * n;
-			int totalSize = signatureSize + authPathSize;
-			if (reducedSignature.length != totalSize) {
-				throw new ParseException("signature has wrong size", 0);
-			}
-			int position = 0;
-			byte[][] wotsPlusSignature = new byte[len][];
-			for (int i = 0; i < wotsPlusSignature.length; i++) {
-				wotsPlusSignature[i] = XMSSUtil.extractBytesAtOffset(reducedSignature, position, n);
-				position += n;
-			}
-			this.wotsPlusSignature = new WOTSPlusSignature(params.getWOTSPlus().getParams(), wotsPlusSignature);
+    protected XMSSReducedSignature(Builder builder)
+    {
+        super();
+        params = builder.params;
+        if (params == null)
+        {
+            throw new NullPointerException("params == null");
+        }
+        int n = params.getDigestSize();
+        int len = params.getWOTSPlus().getParams().getLen();
+        int height = params.getHeight();
+        byte[] reducedSignature = builder.reducedSignature;
+        if (reducedSignature != null)
+        {
+            /* import */
+            int signatureSize = len * n;
+            int authPathSize = height * n;
+            int totalSize = signatureSize + authPathSize;
+            if (reducedSignature.length != totalSize)
+            {
+                throw new IllegalArgumentException("signature has wrong size");
+            }
+            int position = 0;
+            byte[][] wotsPlusSignature = new byte[len][];
+            for (int i = 0; i < wotsPlusSignature.length; i++)
+            {
+                wotsPlusSignature[i] = XMSSUtil.extractBytesAtOffset(reducedSignature, position, n);
+                position += n;
+            }
+            this.wotsPlusSignature = new WOTSPlusSignature(params.getWOTSPlus().getParams(), wotsPlusSignature);
 
-			List<XMSSNode> nodeList = new ArrayList<XMSSNode>();
-			for (int i = 0; i < height; i++) {
-				nodeList.add(new XMSSNode(i, XMSSUtil.extractBytesAtOffset(reducedSignature, position, n)));
-				position += n;
-			}
-			authPath = nodeList;
-		} else {
+            List<XMSSNode> nodeList = new ArrayList<XMSSNode>();
+            for (int i = 0; i < height; i++)
+            {
+                nodeList.add(new XMSSNode(i, XMSSUtil.extractBytesAtOffset(reducedSignature, position, n)));
+                position += n;
+            }
+            authPath = nodeList;
+        }
+        else
+        {
 			/* set */
-			WOTSPlusSignature tmpSignature = builder.wotsPlusSignature;
-			if (tmpSignature != null) {
-				wotsPlusSignature = tmpSignature;
-			} else {
-				wotsPlusSignature = new WOTSPlusSignature(params.getWOTSPlus().getParams(), new byte[len][n]);
-			}
-			List<XMSSNode> tmpAuthPath = builder.authPath;
-			if (tmpAuthPath != null) {
-				if (tmpAuthPath.size() != height) {
-					throw new IllegalArgumentException("size of authPath needs to be equal to height of tree");
-				}
-				authPath = tmpAuthPath;
-			} else {
-				authPath = new ArrayList<XMSSNode>();
-			}
-		}
-	}
-	
-	public static class Builder {
-		
-		/* mandatory */
-		private final XMSSParameters params;
-		/* optional */
-		private WOTSPlusSignature wotsPlusSignature = null;
-		private List<XMSSNode> authPath = null;
-		private byte[] reducedSignature = null;
-		
-		public Builder(XMSSParameters params) {
-			super();
-			this.params = params;
-		}
-		
-		public Builder withWOTSPlusSignature(WOTSPlusSignature val) {
-			wotsPlusSignature = val;
-			return this;
-		}
-		
-		public Builder withAuthPath(List<XMSSNode> val) {
-			authPath = val;
-			return this;
-		}
-		
-		public Builder withReducedSignature(byte[] val) {
-			reducedSignature = XMSSUtil.cloneArray(val);
-			return this;
-		}
-		
-		public XMSSReducedSignature build() throws ParseException {
-			return new XMSSReducedSignature(this);
-		}
-	}
+            WOTSPlusSignature tmpSignature = builder.wotsPlusSignature;
+            if (tmpSignature != null)
+            {
+                wotsPlusSignature = tmpSignature;
+            }
+            else
+            {
+                wotsPlusSignature = new WOTSPlusSignature(params.getWOTSPlus().getParams(), new byte[len][n]);
+            }
+            List<XMSSNode> tmpAuthPath = builder.authPath;
+            if (tmpAuthPath != null)
+            {
+                if (tmpAuthPath.size() != height)
+                {
+                    throw new IllegalArgumentException("size of authPath needs to be equal to height of tree");
+                }
+                authPath = tmpAuthPath;
+            }
+            else
+            {
+                authPath = new ArrayList<XMSSNode>();
+            }
+        }
+    }
 
-	public byte[] toByteArray() {
+    public static class Builder
+    {
+
+        /* mandatory */
+        private final XMSSParameters params;
+        /* optional */
+        private WOTSPlusSignature wotsPlusSignature = null;
+        private List<XMSSNode> authPath = null;
+        private byte[] reducedSignature = null;
+
+        public Builder(XMSSParameters params)
+        {
+            super();
+            this.params = params;
+        }
+
+        public Builder withWOTSPlusSignature(WOTSPlusSignature val)
+        {
+            wotsPlusSignature = val;
+            return this;
+        }
+
+        public Builder withAuthPath(List<XMSSNode> val)
+        {
+            authPath = val;
+            return this;
+        }
+
+        public Builder withReducedSignature(byte[] val)
+        {
+            reducedSignature = XMSSUtil.cloneArray(val);
+            return this;
+        }
+
+        public XMSSReducedSignature build()
+        {
+            return new XMSSReducedSignature(this);
+        }
+    }
+
+    public byte[] toByteArray()
+    {
 		/* signature || authentication path */
-		int n = params.getDigestSize();
-		int signatureSize = params.getWOTSPlus().getParams().getLen() * n;
-		int authPathSize = params.getHeight() * n;
-		int totalSize = signatureSize + authPathSize;
-		byte[] out = new byte[totalSize];
-		int position = 0;
+        int n = params.getDigestSize();
+        int signatureSize = params.getWOTSPlus().getParams().getLen() * n;
+        int authPathSize = params.getHeight() * n;
+        int totalSize = signatureSize + authPathSize;
+        byte[] out = new byte[totalSize];
+        int position = 0;
 		/* copy signature */
-		byte[][] signature = this.wotsPlusSignature.toByteArray();
-		for (int i = 0; i < signature.length; i++) {
-			XMSSUtil.copyBytesAtOffset(out, signature[i], position);
-			position += n;
-		}
+        byte[][] signature = this.wotsPlusSignature.toByteArray();
+        for (int i = 0; i < signature.length; i++)
+        {
+            XMSSUtil.copyBytesAtOffset(out, signature[i], position);
+            position += n;
+        }
 		/* copy authentication path */
-		for (int i = 0; i < authPath.size(); i++) {
-			byte[] value = authPath.get(i).getValue();
-			XMSSUtil.copyBytesAtOffset(out, value, position);
-			position += n;
-		}
-		return out;
-	}
+        for (int i = 0; i < authPath.size(); i++)
+        {
+            byte[] value = authPath.get(i).getValue();
+            XMSSUtil.copyBytesAtOffset(out, value, position);
+            position += n;
+        }
+        return out;
+    }
 
-	public XMSSParameters getParams() {
-		return params;
-	}
+    public XMSSParameters getParams()
+    {
+        return params;
+    }
 
-	public WOTSPlusSignature getWOTSPlusSignature() {
-		return wotsPlusSignature;
-	}
+    public WOTSPlusSignature getWOTSPlusSignature()
+    {
+        return wotsPlusSignature;
+    }
 
-	public List<XMSSNode> getAuthPath() {
-		return authPath;
-	}
+    public List<XMSSNode> getAuthPath()
+    {
+        return authPath;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSignature.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSignature.java
index e50b206..e598a97 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSignature.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSignature.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.pqc.crypto.xmss;
 
-import java.text.ParseException;
-
 import org.bouncycastle.util.Pack;
 
 /**
@@ -16,7 +14,6 @@
     private final byte[] random;
 
     private XMSSSignature(Builder builder)
-        throws ParseException
     {
         super(builder);
         index = builder.index;
@@ -88,7 +85,6 @@
         }
 
         public XMSSSignature build()
-            throws ParseException
         {
             return new XMSSSignature(this);
         }
@@ -106,7 +102,7 @@
         byte[] out = new byte[totalSize];
         int position = 0;
 		/* copy index */
-        XMSSUtil.intToBytesBigEndianOffset(out, index, position);
+        Pack.intToBigEndian(index, out, position);
         position += indexSize;
 		/* copy random */
         XMSSUtil.copyBytesAtOffset(out, random, position);
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSigner.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSigner.java
new file mode 100644
index 0000000..5a3d279
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSSigner.java
@@ -0,0 +1,165 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.pqc.crypto.StateAwareMessageSigner;
+import org.bouncycastle.util.Arrays;
+
+public class XMSSSigner
+    implements StateAwareMessageSigner
+{
+    private XMSSPrivateKeyParameters privateKey;
+    private XMSSPrivateKeyParameters nextKeyGenerator;
+    private XMSSPublicKeyParameters publicKey;
+    private XMSSParameters params;
+    private KeyedHashFunctions khf;
+
+    private boolean initSign;
+    private boolean hasGenerated;
+
+    public void init(boolean forSigning, CipherParameters param)
+    {
+        if (forSigning)
+        {
+            initSign = true;
+            hasGenerated = false;
+            privateKey = (XMSSPrivateKeyParameters)param;
+            nextKeyGenerator = privateKey;
+
+            params = privateKey.getParameters();
+            khf = params.getWOTSPlus().getKhf();
+        }
+        else
+        {
+            initSign = false;
+            publicKey = (XMSSPublicKeyParameters)param;
+
+            params = publicKey.getParameters();
+            khf = params.getWOTSPlus().getKhf();
+        }
+    }
+
+    public byte[] generateSignature(byte[] message)
+    {
+        if (message == null)
+        {
+            throw new NullPointerException("message == null");
+        }
+        if (initSign)
+        {
+            if (privateKey == null)
+            {
+                throw new IllegalStateException("signing key no longer usable");
+            }
+        }
+        else
+        {
+            throw new IllegalStateException("signer not initialized for signature generation");
+        }
+        if (privateKey.getBDSState().getAuthenticationPath().isEmpty())
+        {
+            throw new IllegalStateException("not initialized");
+        }
+        int index = privateKey.getIndex();
+        if (!XMSSUtil.isIndexValid(params.getHeight(), index))
+        {
+            throw new IllegalStateException("index out of bounds");
+        }
+
+		/* create (randomized keyed) messageDigest of message */
+        byte[] random = khf.PRF(privateKey.getSecretKeyPRF(), XMSSUtil.toBytesBigEndian(index, 32));
+        byte[] concatenated = Arrays.concatenate(random, privateKey.getRoot(),
+            XMSSUtil.toBytesBigEndian(index, params.getDigestSize()));
+        byte[] messageDigest = khf.HMsg(concatenated, message);
+
+		/* create signature for messageDigest */
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withOTSAddress(index).build();
+        WOTSPlusSignature wotsPlusSignature = wotsSign(messageDigest, otsHashAddress);
+        XMSSSignature signature = (XMSSSignature)new XMSSSignature.Builder(params).withIndex(index).withRandom(random)
+            .withWOTSPlusSignature(wotsPlusSignature).withAuthPath(privateKey.getBDSState().getAuthenticationPath())
+            .build();
+
+        hasGenerated = true;
+
+        if (nextKeyGenerator != null)
+        {
+            privateKey = nextKeyGenerator.getNextKey();
+            nextKeyGenerator = privateKey;
+        }
+        else
+        {
+            privateKey = null;
+        }
+
+        return signature.toByteArray();
+    }
+
+    public long getUsagesRemaining()
+    {
+        return privateKey.getUsagesRemaining();
+    }
+
+    public boolean verifySignature(byte[] message, byte[] signature)
+    {
+        /* parse signature and public key */
+        XMSSSignature sig = new XMSSSignature.Builder(params).withSignature(signature).build();
+                /* generate public key */
+
+        int index = sig.getIndex();
+        		/* reinitialize WOTS+ object */
+        params.getWOTSPlus().importKeys(new byte[params.getDigestSize()], publicKey.getPublicSeed());
+
+        		/* create message digest */
+        byte[] concatenated = Arrays.concatenate(sig.getRandom(), publicKey.getRoot(),
+            XMSSUtil.toBytesBigEndian(index, params.getDigestSize()));
+        byte[] messageDigest = khf.HMsg(concatenated, message);
+
+        int xmssHeight = params.getHeight();
+        int indexLeaf = XMSSUtil.getLeafIndex(index, xmssHeight);
+
+        		/* get root from signature */
+        OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().withOTSAddress(index).build();
+        XMSSNode rootNodeFromSignature = XMSSVerifierUtil.getRootNodeFromSignature(params.getWOTSPlus(), xmssHeight, messageDigest, sig, otsHashAddress, indexLeaf);
+
+        return Arrays.constantTimeAreEqual(rootNodeFromSignature.getValue(), publicKey.getRoot());
+    }
+
+    public AsymmetricKeyParameter getUpdatedPrivateKey()
+    {
+        // if we've generated a signature return the last private key generated
+        // if we've only initialised leave it in place and return the next one instead.
+        if (hasGenerated)
+        {
+            XMSSPrivateKeyParameters privKey = privateKey;
+
+            privateKey = null;
+            nextKeyGenerator = null;
+
+            return privKey;
+        }
+        else
+        {
+            XMSSPrivateKeyParameters privKey = nextKeyGenerator.getNextKey();
+
+            nextKeyGenerator = null;
+
+            return privKey;
+        }
+    }
+
+    private WOTSPlusSignature wotsSign(byte[] messageDigest, OTSHashAddress otsHashAddress)
+    {
+        if (messageDigest.length != params.getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+        /* (re)initialize WOTS+ instance */
+        params.getWOTSPlus().importKeys(params.getWOTSPlus().getWOTSPlusSecretKey(privateKey.getSecretKeySeed(), otsHashAddress), privateKey.getPublicSeed());
+		/* create WOTS+ signature */
+        return params.getWOTSPlus().sign(messageDigest, otsHashAddress);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSUtil.java
index af3b807..bf75258 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSUtil.java
@@ -3,400 +3,435 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 
 /**
  * Utils for XMSS implementation.
- *
  */
-public class XMSSUtil {
+public class XMSSUtil
+{
 
-	/**
-	 * Calculates the logarithm base 2 for a given Integer.
-	 *
-	 * @param n
-	 *            Number.
-	 * @return Logarithm to base 2 of {@code n}.
-	 */
-	public static int log2(int n) {
-		int log = 0;
-		while ((n >>= 1) != 0) {
-			log++;
-		}
-		return log;
-	}
+    /**
+     * Calculates the logarithm base 2 for a given Integer.
+     *
+     * @param n Number.
+     * @return Logarithm to base 2 of {@code n}.
+     */
+    public static int log2(int n)
+    {
+        int log = 0;
+        while ((n >>= 1) != 0)
+        {
+            log++;
+        }
+        return log;
+    }
 
-	/**
-	 * Convert int/long to n-byte array.
-	 *
-	 * @param value
-	 *            int/long value.
-	 * @param sizeInByte
-	 *            Size of byte array in byte.
-	 * @return int/long as big-endian byte array of size {@code sizeInByte}.
-	 */
-	public static byte[] toBytesBigEndian(long value, int sizeInByte) {
-		byte[] out = new byte[sizeInByte];
-		for (int i = (sizeInByte - 1); i >= 0; i--) {
-			out[i] = (byte) value;
-			value >>>= 8;
-		}
-		return out;
-	}
+    /**
+     * Convert int/long to n-byte array.
+     *
+     * @param value      int/long value.
+     * @param sizeInByte Size of byte array in byte.
+     * @return int/long as big-endian byte array of size {@code sizeInByte}.
+     */
+    public static byte[] toBytesBigEndian(long value, int sizeInByte)
+    {
+        byte[] out = new byte[sizeInByte];
+        for (int i = (sizeInByte - 1); i >= 0; i--)
+        {
+            out[i] = (byte)value;
+            value >>>= 8;
+        }
+        return out;
+    }
 
-	/**
-	 * Copy int to byte array in big-endian at specific offset.
-	 *
-	 * @param Byte
-	 *            array.
-	 * @param Integer
-	 *            to put.
-	 * @param Offset
-	 *            in {@code in}.
-	 */
-	public static void intToBytesBigEndianOffset(byte[] in, int value, int offset) {
-		if (in == null) {
-			throw new NullPointerException("in == null");
-		}
-		if ((in.length - offset) < 4) {
-			throw new IllegalArgumentException("not enough space in array");
-		}
-		in[offset] = (byte) ((value >> 24) & 0xff);
-		in[offset + 1] = (byte) ((value >> 16) & 0xff);
-		in[offset + 2] = (byte) ((value >> 8) & 0xff);
-		in[offset + 3] = (byte) ((value) & 0xff);
-	}
+    /*
+     * Copy long to byte array in big-endian at specific offset.
+     */
+    public static void longToBigEndian(long value, byte[] in, int offset)
+    {
+        if (in == null)
+        {
+            throw new NullPointerException("in == null");
+        }
+        if ((in.length - offset) < 8)
+        {
+            throw new IllegalArgumentException("not enough space in array");
+        }
+        in[offset] = (byte)((value >> 56) & 0xff);
+        in[offset + 1] = (byte)((value >> 48) & 0xff);
+        in[offset + 2] = (byte)((value >> 40) & 0xff);
+        in[offset + 3] = (byte)((value >> 32) & 0xff);
+        in[offset + 4] = (byte)((value >> 24) & 0xff);
+        in[offset + 5] = (byte)((value >> 16) & 0xff);
+        in[offset + 6] = (byte)((value >> 8) & 0xff);
+        in[offset + 7] = (byte)((value) & 0xff);
+    }
 
-	/**
-	 * Copy long to byte array in big-endian at specific offset.
-	 *
-	 * @param Byte
-	 *            array.
-	 * @param Long
-	 *            to put.
-	 * @param Offset
-	 *            in {@code in}.
-	 */
-	public static void longToBytesBigEndianOffset(byte[] in, long value, int offset) {
-		if (in == null) {
-			throw new NullPointerException("in == null");
-		}
-		if ((in.length - offset) < 8) {
-			throw new IllegalArgumentException("not enough space in array");
-		}
-		in[offset] = (byte) ((value >> 56) & 0xff);
-		in[offset + 1] = (byte) ((value >> 48) & 0xff);
-		in[offset + 2] = (byte) ((value >> 40) & 0xff);
-		in[offset + 3] = (byte) ((value >> 32) & 0xff);
-		in[offset + 4] = (byte) ((value >> 24) & 0xff);
-		in[offset + 5] = (byte) ((value >> 16) & 0xff);
-		in[offset + 6] = (byte) ((value >> 8) & 0xff);
-		in[offset + 7] = (byte) ((value) & 0xff);
-	}
+    /*
+     * Generic convert from big endian byte array to long.
+     */
+    public static long bytesToXBigEndian(byte[] in, int offset, int size)
+    {
+        if (in == null)
+        {
+            throw new NullPointerException("in == null");
+        }
+        long res = 0;
+        for (int i = offset; i < (offset + size); i++)
+        {
+            res = (res << 8) | (in[i] & 0xff);
+        }
+        return res;
+    }
 
-	/**
-	 * Generic convert from big endian byte array to long.
-	 *
-	 * @param x-byte
-	 *            array
-	 * @param offset.
-	 * @param size.
-	 * @return Long.
-	 */
-	public static long bytesToXBigEndian(byte[] in, int offset, int size) {
-		if (in == null) {
-			throw new NullPointerException("in == null");
-		}
-		long res = 0;
-		for (int i = offset; i < (offset + size); i++) {
-			res = (res << 8) | (in[i] & 0xff);
-		}
-		return res;
-	}
+    /**
+     * Clone a byte array.
+     *
+     * @param in byte array.
+     * @return Copy of byte array.
+     */
+    public static byte[] cloneArray(byte[] in)
+    {
+        if (in == null)
+        {
+            throw new NullPointerException("in == null");
+        }
+        byte[] out = new byte[in.length];
+        System.arraycopy(in, 0, out, 0, in.length);
+        return out;
+    }
 
-	/**
-	 * Clone a byte array.
-	 *
-	 * @param in
-	 *            byte array.
-	 * @return Copy of byte array.
-	 */
-	public static byte[] cloneArray(byte[] in) {
-		if (in == null) {
-			throw new NullPointerException("in == null");
-		}
-		byte[] out = new byte[in.length];
-		for (int i = 0; i < in.length; i++) {
-			out[i] = in[i];
-		}
-		return out;
-	}
+    /**
+     * Clone a 2d byte array.
+     *
+     * @param in 2d byte array.
+     * @return Copy of 2d byte array.
+     */
+    public static byte[][] cloneArray(byte[][] in)
+    {
+        if (hasNullPointer(in))
+        {
+            throw new NullPointerException("in has null pointers");
+        }
+        byte[][] out = new byte[in.length][];
+        for (int i = 0; i < in.length; i++)
+        {
+            out[i] = new byte[in[i].length];
+            System.arraycopy(in[i], 0, out[i], 0, in[i].length);
+        }
+        return out;
+    }
 
-	/**
-	 * Clone a 2d byte array.
-	 *
-	 * @param in
-	 *            2d byte array.
-	 * @return Copy of 2d byte array.
-	 */
-	public static byte[][] cloneArray(byte[][] in) {
-		if (hasNullPointer(in)) {
-			throw new NullPointerException("in has null pointers");
-		}
-		byte[][] out = new byte[in.length][];
-		for (int i = 0; i < in.length; i++) {
-			out[i] = new byte[in[i].length];
-			for (int j = 0; j < in[i].length; j++) {
-				out[i][j] = in[i][j];
-			}
-		}
-		return out;
-	}
+    /**
+     * Compares two 2d-byte arrays.
+     *
+     * @param a 2d-byte array 1.
+     * @param b 2d-byte array 2.
+     * @return true if all values in 2d-byte array are equal false else.
+     */
+    public static boolean areEqual(byte[][] a, byte[][] b)
+    {
+        if (hasNullPointer(a) || hasNullPointer(b))
+        {
+            throw new NullPointerException("a or b == null");
+        }
+        for (int i = 0; i < a.length; i++)
+        {
+            if (!Arrays.areEqual(a[i], b[i]))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
 
-	/**
-	 * Concatenates an arbitrary number of byte arrays.
-	 *
-	 * @param arrays
-	 *            Arrays that shall be concatenated.
-	 * @return Concatenated array.
-	 */
-	public static byte[] concat(byte[]... arrays) {
-		int totalLength = 0;
-		for (int i = 0; i < arrays.length; i++) {
-			totalLength += arrays[i].length;
-		}
-		byte[] result = new byte[totalLength];
-		int currentIndex = 0;
-		for (int i = 0; i < arrays.length; i++) {
-			System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
-			currentIndex += arrays[i].length;
-		}
-		return result;
-	}
+    /**
+     * Dump content of 2d byte array.
+     *
+     * @param x byte array.
+     */
+    public static void dumpByteArray(byte[][] x)
+    {
+        if (hasNullPointer(x))
+        {
+            throw new NullPointerException("x has null pointers");
+        }
+        for (int i = 0; i < x.length; i++)
+        {
+            System.out.println(Hex.toHexString(x[i]));
+        }
+    }
 
-	/**
-	 * Compares two byte arrays.
-	 *
-	 * @param a
-	 *            byte array 1.
-	 * @param b
-	 *            byte array 2.
-	 * @return true if all values in byte array are equal false else.
-	 */
-	public static boolean compareByteArray(byte[] a, byte[] b) {
-		if (a == null || b == null) {
-			throw new NullPointerException("a or b == null");
-		}
-		if (a.length != b.length) {
-			throw new IllegalArgumentException("size of a and b must be equal");
-		}
-		for (int i = 0; i < a.length; i++) {
-			if (a[i] != b[i]) {
-				return false;
-			}
-		}
-		return true;
-	}
+    /**
+     * Checks whether 2d byte array has null pointers.
+     *
+     * @param in 2d byte array.
+     * @return true if at least one null pointer is found false else.
+     */
+    public static boolean hasNullPointer(byte[][] in)
+    {
+        if (in == null)
+        {
+            return true;
+        }
+        for (int i = 0; i < in.length; i++)
+        {
+            if (in[i] == null)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
 
-	/**
-	 * Compares two 2d-byte arrays.
-	 *
-	 * @param a
-	 *            2d-byte array 1.
-	 * @param b
-	 *            2d-byte array 2.
-	 * @return true if all values in 2d-byte array are equal false else.
-	 */
-	public static boolean compareByteArray(byte[][] a, byte[][] b) {
-		if (hasNullPointer(a) || hasNullPointer(b)) {
-			throw new NullPointerException("a or b == null");
-		}
-		for (int i = 0; i < a.length; i++) {
-			if (!compareByteArray(a[i], b[i])) {
-				return false;
-			}
-		}
-		return true;
-	}
+    /**
+     * Copy src byte array to dst byte array at offset.
+     *
+     * @param dst    Destination.
+     * @param src    Source.
+     * @param offset Destination offset.
+     */
+    public static void copyBytesAtOffset(byte[] dst, byte[] src, int offset)
+    {
+        if (dst == null)
+        {
+            throw new NullPointerException("dst == null");
+        }
+        if (src == null)
+        {
+            throw new NullPointerException("src == null");
+        }
+        if (offset < 0)
+        {
+            throw new IllegalArgumentException("offset hast to be >= 0");
+        }
+        if ((src.length + offset) > dst.length)
+        {
+            throw new IllegalArgumentException("src length + offset must not be greater than size of destination");
+        }
+        for (int i = 0; i < src.length; i++)
+        {
+            dst[offset + i] = src[i];
+        }
+    }
 
-	/**
-	 * Dump content of 2d byte array.
-	 *
-	 * @param x
-	 *            byte array.
-	 */
-	public static void dumpByteArray(byte[][] x) {
-		if (hasNullPointer(x)) {
-			throw new NullPointerException("x has null pointers");
-		}
-		for (int i = 0; i < x.length; i++) {
-			System.out.println(Hex.toHexString(x[i]));
-		}
-	}
+    /**
+     * Copy length bytes at position offset from src.
+     *
+     * @param src    Source byte array.
+     * @param offset Offset in source byte array.
+     * @param length Length of bytes to copy.
+     * @return New byte array.
+     */
+    public static byte[] extractBytesAtOffset(byte[] src, int offset, int length)
+    {
+        if (src == null)
+        {
+            throw new NullPointerException("src == null");
+        }
+        if (offset < 0)
+        {
+            throw new IllegalArgumentException("offset hast to be >= 0");
+        }
+        if (length < 0)
+        {
+            throw new IllegalArgumentException("length hast to be >= 0");
+        }
+        if ((offset + length) > src.length)
+        {
+            throw new IllegalArgumentException("offset + length must not be greater then size of source array");
+        }
+        byte[] out = new byte[length];
+        for (int i = 0; i < out.length; i++)
+        {
+            out[i] = src[offset + i];
+        }
+        return out;
+    }
 
-	/**
-	 * Checks whether 2d byte array has null pointers.
-	 *
-	 * @param in
-	 *            2d byte array.
-	 * @return true if at least one null pointer is found false else.
-	 */
-	public static boolean hasNullPointer(byte[][] in) {
-		if (in == null) {
-			return true;
-		}
-		for (int i = 0; i < in.length; i++) {
-			if (in[i] == null) {
-				return true;
-			}
-		}
-		return false;
-	}
+    /**
+     * Check whether an index is valid or not.
+     *
+     * @param height Height of binary tree.
+     * @param index  Index to validate.
+     * @return true if index is valid false else.
+     */
+    public static boolean isIndexValid(int height, long index)
+    {
+        if (index < 0)
+        {
+            throw new IllegalStateException("index must not be negative");
+        }
+        return index < (1L << height);
+    }
 
-	/**
-	 * Copy src byte array to dst byte array at offset.
-	 *
-	 * @param dst
-	 *            Destination.
-	 * @param src
-	 *            Source.
-	 * @param offset
-	 *            Destination offset.
-	 */
-	public static void copyBytesAtOffset(byte[] dst, byte[] src, int offset) {
-		if (dst == null) {
-			throw new NullPointerException("dst == null");
-		}
-		if (src == null) {
-			throw new NullPointerException("src == null");
-		}
-		if (offset < 0) {
-			throw new IllegalArgumentException("offset hast to be >= 0");
-		}
-		if ((src.length + offset) > dst.length) {
-			throw new IllegalArgumentException("src length + offset must not be greater than size of destination");
-		}
-		for (int i = 0; i < src.length; i++) {
-			dst[offset + i] = src[i];
-		}
-	}
+    /**
+     * Determine digest size of digest.
+     *
+     * @param digest Digest.
+     * @return Digest size.
+     */
+    public static int getDigestSize(Digest digest)
+    {
+        if (digest == null)
+        {
+            throw new NullPointerException("digest == null");
+        }
+        String algorithmName = digest.getAlgorithmName();
+        if (algorithmName.equals("SHAKE128"))
+        {
+            return 32;
+        }
+        if (algorithmName.equals("SHAKE256"))
+        {
+            return 64;
+        }
+        return digest.getDigestSize();
+    }
 
-	/**
-	 * Copy length bytes at position offset from src.
-	 *
-	 * @param src
-	 *            Source byte array.
-	 * @param offset
-	 *            Offset in source byte array.
-	 * @param length
-	 *            Length of bytes to copy.
-	 * @return New byte array.
-	 */
-	public static byte[] extractBytesAtOffset(byte[] src, int offset, int length) {
-		if (src == null) {
-			throw new NullPointerException("src == null");
-		}
-		if (offset < 0) {
-			throw new IllegalArgumentException("offset hast to be >= 0");
-		}
-		if (length < 0) {
-			throw new IllegalArgumentException("length hast to be >= 0");
-		}
-		if ((offset + length) > src.length) {
-			throw new IllegalArgumentException("offset + length must not be greater then size of source array");
-		}
-		byte[] out = new byte[length];
-		for (int i = 0; i < out.length; i++) {
-			out[i] = src[offset + i];
-		}
-		return out;
-	}
+    public static long getTreeIndex(long index, int xmssTreeHeight)
+    {
+        return index >> xmssTreeHeight;
+    }
 
-	/**
-	 * Check whether an index is valid or not.
-	 *
-	 * @param height
-	 *            Height of binary tree.
-	 * @param index
-	 *            Index to validate.
-	 * @return true if index is valid false else.
-	 */
-	public static boolean isIndexValid(int height, long index) {
-		if (index < 0) {
-			throw new IllegalStateException("index must not be negative");
-		}
-		return index < (1L << height);
-	}
+    public static int getLeafIndex(long index, int xmssTreeHeight)
+    {
+        return (int)(index & ((1L << xmssTreeHeight) - 1L));
+    }
 
-	/**
-	 * Determine digest size of digest.
-	 *
-	 * @param digest
-	 *            Digest.
-	 * @return Digest size.
-	 */
-	public static int getDigestSize(Digest digest) {
-		if (digest == null) {
-			throw new NullPointerException("digest == null");
-		}
-		String algorithmName = digest.getAlgorithmName();
-		if (algorithmName.equals("SHAKE128")) {
-			return 32;
-		}
-		if (algorithmName.equals("SHAKE256")) {
-			return 64;
-		}
-		return digest.getDigestSize();
-	}
+    public static byte[] serialize(Object obj)
+        throws IOException
+    {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(out);
+        oos.writeObject(obj);
+        oos.flush();
+        return out.toByteArray();
+    }
 
-	public static long getTreeIndex(long index, int xmssTreeHeight) {
-		return index >> xmssTreeHeight;
-	}
+    public static Object deserialize(byte[] data, final Class clazz)
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        ObjectInputStream is = new CheckingStream(clazz, in);
 
-	public static int getLeafIndex(long index, int xmssTreeHeight) {
-		return (int) (index & ((1L << xmssTreeHeight) - 1L));
-	}
+        Object obj = is.readObject();
 
-	public static byte[] serialize(Object obj) throws IOException {
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ObjectOutputStream oos = new ObjectOutputStream(out);
-		oos.writeObject(obj);
-		oos.flush();
-		return out.toByteArray();
-	}
+        if (is.available() != 0)
+        {
+            throw new IOException("unexpected data found at end of ObjectInputStream");
+        }
+        // you'd hope this would always succeed!
+        if (clazz.isInstance(obj))
+        {
+            return obj;
+        }
+        else
+        {
+            throw new IOException("unexpected class found in ObjectInputStream");
+        }
+    }
 
-	public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
-		ByteArrayInputStream in = new ByteArrayInputStream(data);
-		ObjectInputStream is = new ObjectInputStream(in);
-		return is.readObject();
-	}
+    public static int calculateTau(int index, int height)
+    {
+        int tau = 0;
+        for (int i = 0; i < height; i++)
+        {
+            if (((index >> i) & 1) == 0)
+            {
+                tau = i;
+                break;
+            }
+        }
+        return tau;
+    }
 
-	public static int calculateTau(int index, int height) {
-		int tau = 0;
-		for (int i = 0; i < height; i++) {
-			if (((index >> i) & 1) == 0) {
-				tau = i;
-				break;
-			}
-		}
-		return tau;
-	}
+    public static boolean isNewBDSInitNeeded(long globalIndex, int xmssHeight, int layer)
+    {
+        if (globalIndex == 0)
+        {
+            return false;
+        }
+        return (globalIndex % (long)Math.pow((1 << xmssHeight), layer + 1) == 0) ? true : false;
+    }
 
-	public static boolean isNewBDSInitNeeded(long globalIndex, int xmssHeight, int layer) {
-		if (globalIndex == 0) {
-			return false;
-		}
-		return (globalIndex % (long) Math.pow((1 << xmssHeight), layer + 1) == 0) ? true : false;
-	}
+    public static boolean isNewAuthenticationPathNeeded(long globalIndex, int xmssHeight, int layer)
+    {
+        if (globalIndex == 0)
+        {
+            return false;
+        }
+        return ((globalIndex + 1) % (long)Math.pow((1 << xmssHeight), layer) == 0) ? true : false;
+    }
 
-	public static boolean isNewAuthenticationPathNeeded(long globalIndex, int xmssHeight, int layer) {
-		if (globalIndex == 0) {
-			return false;
-		}
-		return ((globalIndex + 1) % (long) Math.pow((1 << xmssHeight), layer) == 0) ? true : false;
-	}
+    private static class CheckingStream
+       extends ObjectInputStream
+    {
+        private static final Set components = new HashSet();
+
+        static
+        {
+            components.add("java.util.TreeMap");
+            components.add("java.lang.Integer");
+            components.add("java.lang.Number");
+            components.add("org.bouncycastle.pqc.crypto.xmss.BDS");
+            components.add("java.util.ArrayList");
+            components.add("org.bouncycastle.pqc.crypto.xmss.XMSSNode");
+            components.add("[B");
+            components.add("java.util.LinkedList");
+            components.add("java.util.Stack");
+            components.add("java.util.Vector");
+            components.add("[Ljava.lang.Object;");
+            components.add("org.bouncycastle.pqc.crypto.xmss.BDSTreeHash");
+        }
+
+        private final Class mainClass;
+        private boolean found = false;
+
+        CheckingStream(Class mainClass, InputStream in)
+            throws IOException
+        {
+            super(in);
+
+            this.mainClass = mainClass;
+        }
+
+        protected Class<?> resolveClass(ObjectStreamClass desc)
+            throws IOException,
+            ClassNotFoundException
+        {
+            if (!found)
+            {
+                if (!desc.getName().equals(mainClass.getName()))
+                {
+                    throw new InvalidClassException(
+                        "unexpected class: ", desc.getName());
+                }
+                else
+                {
+                    found = true;
+                }
+            }
+            else
+            {
+                if (!components.contains(desc.getName()))
+                {
+                    throw new InvalidClassException(
+                          "unexpected class: ", desc.getName());
+                }
+            }
+            return super.resolveClass(desc);
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSVerifierUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSVerifierUtil.java
new file mode 100644
index 0000000..a929b22
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/XMSSVerifierUtil.java
@@ -0,0 +1,76 @@
+package org.bouncycastle.pqc.crypto.xmss;
+
+class XMSSVerifierUtil
+{
+    /**
+     * Compute a root node from a tree signature.
+     *
+     * @param messageDigest Message digest.
+     * @param signature     XMSS signature.
+     * @return Root node calculated from signature.
+     */
+    static XMSSNode getRootNodeFromSignature(WOTSPlus wotsPlus, int height, byte[] messageDigest, XMSSReducedSignature signature,
+                                              OTSHashAddress otsHashAddress, int indexLeaf)
+    {
+        if (messageDigest.length != wotsPlus.getParams().getDigestSize())
+        {
+            throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
+        }
+        if (signature == null)
+        {
+            throw new NullPointerException("signature == null");
+        }
+        if (otsHashAddress == null)
+        {
+            throw new NullPointerException("otsHashAddress == null");
+        }
+
+		/* prepare adresses */
+        LTreeAddress lTreeAddress = (LTreeAddress)new LTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withLTreeAddress(otsHashAddress.getOTSAddress()).build();
+        HashTreeAddress hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+            .withLayerAddress(otsHashAddress.getLayerAddress()).withTreeAddress(otsHashAddress.getTreeAddress())
+            .withTreeIndex(otsHashAddress.getOTSAddress()).build();
+		/*
+		 * calculate WOTS+ public key and compress to obtain original leaf hash
+		 */
+        WOTSPlusPublicKeyParameters wotsPlusPK = wotsPlus.getPublicKeyFromSignature(messageDigest,
+            signature.getWOTSPlusSignature(), otsHashAddress);
+        XMSSNode[] node = new XMSSNode[2];
+        node[0] = XMSSNodeUtil.lTree(wotsPlus, wotsPlusPK, lTreeAddress);
+
+        for (int k = 0; k < height; k++)
+        {
+            hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                .withLayerAddress(hashTreeAddress.getLayerAddress())
+                .withTreeAddress(hashTreeAddress.getTreeAddress()).withTreeHeight(k)
+                .withTreeIndex(hashTreeAddress.getTreeIndex()).withKeyAndMask(hashTreeAddress.getKeyAndMask())
+                .build();
+            if (Math.floor(indexLeaf / (1 << k)) % 2 == 0)
+            {
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight())
+                    .withTreeIndex(hashTreeAddress.getTreeIndex() / 2)
+                    .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+                node[1] = XMSSNodeUtil.randomizeHash(wotsPlus, node[0], signature.getAuthPath().get(k), hashTreeAddress);
+                node[1] = new XMSSNode(node[1].getHeight() + 1, node[1].getValue());
+            }
+            else
+            {
+                hashTreeAddress = (HashTreeAddress)new HashTreeAddress.Builder()
+                    .withLayerAddress(hashTreeAddress.getLayerAddress())
+                    .withTreeAddress(hashTreeAddress.getTreeAddress())
+                    .withTreeHeight(hashTreeAddress.getTreeHeight())
+                    .withTreeIndex((hashTreeAddress.getTreeIndex() - 1) / 2)
+                    .withKeyAndMask(hashTreeAddress.getKeyAndMask()).build();
+                node[1] = XMSSNodeUtil.randomizeHash(wotsPlus, signature.getAuthPath().get(k), node[0], hashTreeAddress);
+                node[1] = new XMSSNode(node[1].getHeight() + 1, node[1].getValue());
+            }
+            node[0] = node[1];
+        }
+        return node[0];
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/package.html b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/package.html
new file mode 100644
index 0000000..117d07f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/crypto/xmss/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Low level implementation of the XMSS and XMSS^MT signature algorithms.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java
new file mode 100644
index 0000000..c6fc80f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
+
+/**
+ * Base interface for a qTESLA key.
+ */
+public interface QTESLAKey
+{
+    /**
+     * Return the parameters for this key - in this case the security category.
+     *
+     * @return a QTESLAParameterSpec
+     */
+    QTESLAParameterSpec getParams();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/StateAwareSignature.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/StateAwareSignature.java
new file mode 100644
index 0000000..4472400
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/StateAwareSignature.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+
+/**
+ * This interface is implemented by Signature classes returned by the PQC provider where the signature
+ * algorithm is one where the private key is updated for each signature generated. Examples of these
+ * are algorithms such as GMSS, XMSS, and XMSS^MT.
+ */
+public interface StateAwareSignature
+{
+    void initVerify(PublicKey publicKey)
+        throws InvalidKeyException;
+
+    void initVerify(Certificate certificate)
+        throws InvalidKeyException;
+
+    void initSign(PrivateKey privateKey)
+        throws InvalidKeyException;
+
+    void initSign(PrivateKey privateKey, SecureRandom random)
+        throws InvalidKeyException;
+
+    byte[] sign()
+        throws SignatureException;
+
+    int sign(byte[] outbuf, int offset, int len)
+        throws SignatureException;
+
+    boolean verify(byte[] signature)
+        throws SignatureException;
+
+    boolean verify(byte[] signature, int offset, int length)
+        throws SignatureException;
+
+    void update(byte b)
+        throws SignatureException;
+
+    void update(byte[] data)
+        throws SignatureException;
+
+    void update(byte[] data, int off, int len)
+        throws SignatureException;
+
+    void update(ByteBuffer data)
+        throws SignatureException;
+
+    String getAlgorithm();
+
+    /**
+     * Return true if this Signature object can be used for signing. False otherwise.
+     *
+     * @return true if we are capable of making signatures.
+     */
+    boolean isSigningCapable();
+
+    /**
+     * Return the current version of the private key with the updated state.
+     * <p>
+     * <b>Note:</b> calling this method will effectively disable the Signature object from being used for further
+     *  signature generation without another call to initSign().
+     * </p>
+     * @return an updated private key object, which can be used for later signature generation.
+     */
+   PrivateKey getUpdatedPrivateKey();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSKey.java
new file mode 100644
index 0000000..5471d41
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSKey.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+public interface XMSSKey
+{
+    int getHeight();
+
+    String getTreeDigest();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTKey.java
new file mode 100644
index 0000000..bda0ffa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTKey.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+public interface XMSSMTKey
+{
+    int getHeight();
+
+    int getLayers();
+
+    String getTreeDigest();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTPrivateKey.java
new file mode 100644
index 0000000..742833f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSMTPrivateKey.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+import java.security.PrivateKey;
+
+/**
+ * Base interface for an XMSSMT private key
+ */
+public interface XMSSMTPrivateKey
+    extends XMSSMTKey, PrivateKey
+{
+    /**
+     * Return the number of usages left for the private key.
+     *
+     * @return the number of times the key can be used before it is exhausted.
+     */
+    long getUsagesRemaining();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSPrivateKey.java
new file mode 100644
index 0000000..fdd46fa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/XMSSPrivateKey.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.pqc.jcajce.interfaces;
+
+import java.security.PrivateKey;
+
+/**
+ * Base interface for an XMSS private key
+ */
+public interface XMSSPrivateKey
+    extends XMSSKey, PrivateKey
+{
+    /**
+     * Return the number of usages left for the private key.
+     *
+     * @return the number of times the key can be used before it is exhausted.
+     */
+    long getUsagesRemaining();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
index f756be2..63f23c0 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
@@ -22,7 +22,7 @@
     extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Post-Quantum Security Provider v1.57";
+    private static String info = "BouncyCastle Post-Quantum Security Provider v1.61";
 
     public static String PROVIDER_NAME = "BCPQC";
 
@@ -37,7 +37,7 @@
     private static final String ALGORITHM_PACKAGE = "org.bouncycastle.pqc.jcajce.provider.";
     private static final String[] ALGORITHMS =
         {
-            "Rainbow", "McEliece", "SPHINCS", "NH"
+            "Rainbow", "McEliece", "SPHINCS", "NH", "XMSS", "QTESLA"
         };
 
     /**
@@ -47,7 +47,7 @@
      */
     public BouncyCastlePQCProvider()
     {
-        super(PROVIDER_NAME, 1.57, info);
+        super(PROVIDER_NAME, 1.61, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -68,24 +68,7 @@
     {
         for (int i = 0; i != names.length; i++)
         {
-            Class clazz = null;
-            try
-            {
-                ClassLoader loader = this.getClass().getClassLoader();
-
-                if (loader != null)
-                {
-                    clazz = loader.loadClass(packageName + names[i] + "$Mappings");
-                }
-                else
-                {
-                    clazz = Class.forName(packageName + names[i] + "$Mappings");
-                }
-            }
-            catch (ClassNotFoundException e)
-            {
-                // ignore
-            }
+            Class clazz = loadClass(BouncyCastlePQCProvider.class, packageName + names[i] + "$Mappings");
 
             if (clazz != null)
             {
@@ -192,4 +175,41 @@
 
         return converter.generatePrivate(privateKeyInfo);
     }
+
+    static Class loadClass(Class sourceClass, final String className)
+    {
+        try
+        {
+            ClassLoader loader = sourceClass.getClassLoader();
+            if (loader != null)
+            {
+                return loader.loadClass(className);
+            }
+            else
+            {
+                return (Class)AccessController.doPrivileged(new PrivilegedAction()
+                {
+                    public Object run()
+                    {
+                        try
+                        {
+                            return Class.forName(className);
+                        }
+                        catch (Exception e)
+                        {
+                            // ignore - maybe log?
+                        }
+
+                        return null;
+                    }
+                });
+            }
+        }
+        catch (ClassNotFoundException e)
+        {
+            // ignore - maybe log?
+        }
+
+        return null;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/QTESLA.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/QTESLA.java
new file mode 100644
index 0000000..78271b9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/QTESLA.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.pqc.jcajce.provider;
+
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.jcajce.provider.qtesla.QTESLAKeyFactorySpi;
+
+public class QTESLA
+{
+    private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider" + ".qtesla.";
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.QTESLA", PREFIX + "QTESLAKeyFactorySpi");
+            provider.addAlgorithm("KeyPairGenerator.QTESLA", PREFIX + "KeyPairGeneratorSpi");
+
+            provider.addAlgorithm("Signature.QTESLA", PREFIX + "SignatureSpi$qTESLA");
+
+            addSignatureAlgorithm(provider,"QTESLA-I", PREFIX + "SignatureSpi$HeuristicI", PQCObjectIdentifiers.qTESLA_I);
+            addSignatureAlgorithm(provider,"QTESLA-III-SIZE", PREFIX + "SignatureSpi$HeuristicIIISize", PQCObjectIdentifiers.qTESLA_III_size);
+            addSignatureAlgorithm(provider,"QTESLA-III-SPEED", PREFIX + "SignatureSpi$HeuristicIIISpeed", PQCObjectIdentifiers.qTESLA_III_speed);
+            addSignatureAlgorithm(provider,"QTESLA-P-I", PREFIX + "SignatureSpi$ProvablySecureI", PQCObjectIdentifiers.qTESLA_p_I);
+            addSignatureAlgorithm(provider,"QTESLA-P-III", PREFIX + "SignatureSpi$ProvablySecureIII", PQCObjectIdentifiers.qTESLA_p_III);
+
+            AsymmetricKeyInfoConverter keyFact = new QTESLAKeyFactorySpi();
+
+            registerOid(provider, PQCObjectIdentifiers.qTESLA_I, "QTESLA-I", keyFact);
+            registerOid(provider, PQCObjectIdentifiers.qTESLA_III_size, "QTESLA-III-SIZE", keyFact);
+            registerOid(provider, PQCObjectIdentifiers.qTESLA_III_speed, "QTESLA-III-SPEED", keyFact);
+            registerOid(provider, PQCObjectIdentifiers.qTESLA_p_I, "QTESLA-P-I", keyFact);
+            registerOid(provider, PQCObjectIdentifiers.qTESLA_p_III, "QTESLA-P-III", keyFact);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/XMSS.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/XMSS.java
new file mode 100644
index 0000000..2e7bfbb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/XMSS.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.pqc.jcajce.provider;
+
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi;
+import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSMTKeyFactorySpi;
+
+public class XMSS
+{
+    private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider" + ".xmss.";
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.XMSS", PREFIX + "XMSSKeyFactorySpi");
+            provider.addAlgorithm("KeyPairGenerator.XMSS", PREFIX + "XMSSKeyPairGeneratorSpi");
+
+            addSignatureAlgorithm(provider, "XMSS-SHA256", PREFIX + "XMSSSignatureSpi$withSha256", BCObjectIdentifiers.xmss_SHA256);
+            addSignatureAlgorithm(provider, "XMSS-SHAKE128", PREFIX + "XMSSSignatureSpi$withShake128", BCObjectIdentifiers.xmss_SHAKE128);
+            addSignatureAlgorithm(provider, "XMSS-SHA512", PREFIX + "XMSSSignatureSpi$withSha512", BCObjectIdentifiers.xmss_SHA512);
+            addSignatureAlgorithm(provider, "XMSS-SHAKE256", PREFIX + "XMSSSignatureSpi$withShake256", BCObjectIdentifiers.xmss_SHAKE256);
+
+            addSignatureAlgorithm(provider, "SHA256", "XMSS-SHA256", PREFIX + "XMSSSignatureSpi$withSha256andPrehash", BCObjectIdentifiers.xmss_SHA256ph);
+            addSignatureAlgorithm(provider, "SHAKE128", "XMSS-SHAKE128", PREFIX + "XMSSSignatureSpi$withShake128andPrehash", BCObjectIdentifiers.xmss_SHAKE128ph);
+            addSignatureAlgorithm(provider, "SHA512", "XMSS-SHA512", PREFIX + "XMSSSignatureSpi$withSha512andPrehash", BCObjectIdentifiers.xmss_SHA512ph);
+            addSignatureAlgorithm(provider, "SHAKE256", "XMSS-SHAKE256", PREFIX + "XMSSSignatureSpi$withShake256andPrehash", BCObjectIdentifiers.xmss_SHAKE256ph);
+            provider.addAlgorithm("Alg.Alias.Signature.SHA256WITHXMSS", "SHA256WITHXMSS-SHA256");
+            provider.addAlgorithm("Alg.Alias.Signature.SHAKE128WITHXMSS", "SHAKE128WITHXMSS-SHAKE128");
+            provider.addAlgorithm("Alg.Alias.Signature.SHA512WITHXMSS", "SHA512WITHXMSS-SHA512");
+            provider.addAlgorithm("Alg.Alias.Signature.SHAKE256WITHXMSS", "SHAKE256WITHXMSS-SHAKE256");
+
+            provider.addAlgorithm("KeyFactory.XMSSMT", PREFIX + "XMSSMTKeyFactorySpi");
+            provider.addAlgorithm("KeyPairGenerator.XMSSMT", PREFIX + "XMSSMTKeyPairGeneratorSpi");
+
+            addSignatureAlgorithm(provider, "XMSSMT-SHA256", PREFIX + "XMSSMTSignatureSpi$withSha256", BCObjectIdentifiers.xmss_mt_SHA256);
+            addSignatureAlgorithm(provider, "XMSSMT-SHAKE128", PREFIX + "XMSSMTSignatureSpi$withShake128", BCObjectIdentifiers.xmss_mt_SHAKE128);
+            addSignatureAlgorithm(provider, "XMSSMT-SHA512", PREFIX + "XMSSMTSignatureSpi$withSha512", BCObjectIdentifiers.xmss_mt_SHA512);
+            addSignatureAlgorithm(provider, "XMSSMT-SHAKE256", PREFIX + "XMSSMTSignatureSpi$withShake256", BCObjectIdentifiers.xmss_mt_SHAKE256);
+
+            addSignatureAlgorithm(provider, "SHA256", "XMSSMT-SHA256", PREFIX + "XMSSMTSignatureSpi$withSha256andPrehash", BCObjectIdentifiers.xmss_mt_SHA256ph);
+            addSignatureAlgorithm(provider, "SHAKE128", "XMSSMT-SHAKE128", PREFIX + "XMSSMTSignatureSpi$withShake128andPrehash", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+            addSignatureAlgorithm(provider, "SHA512", "XMSSMT-SHA512", PREFIX + "XMSSMTSignatureSpi$withSha512andPrehash", BCObjectIdentifiers.xmss_mt_SHA512ph);
+            addSignatureAlgorithm(provider, "SHAKE256", "XMSSMT-SHAKE256", PREFIX + "XMSSMTSignatureSpi$withShake256andPrehash", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+            provider.addAlgorithm("Alg.Alias.Signature.SHA256WITHXMSSMT", "SHA256WITHXMSSMT-SHA256");
+            provider.addAlgorithm("Alg.Alias.Signature.SHAKE128WITHXMSSMT", "SHAKE128WITHXMSSMT-SHAKE128");
+            provider.addAlgorithm("Alg.Alias.Signature.SHA512WITHXMSSMT", "SHA512WITHXMSSMT-SHA512");
+            provider.addAlgorithm("Alg.Alias.Signature.SHAKE256WITHXMSSMT", "SHAKE256WITHXMSSMT-SHAKE256");
+
+            registerOid(provider, PQCObjectIdentifiers.xmss, "XMSS", new XMSSKeyFactorySpi());
+            registerOid(provider, PQCObjectIdentifiers.xmss_mt, "XMSSMT", new XMSSMTKeyFactorySpi());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java
index b04158c..e7834f5 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java
@@ -110,14 +110,15 @@
     /**
      * @return a human readable form of the key
      */
-    public String toString()
-    {
-        String result = "";
-        result += " extension degree of the field      : " + getN() + "\n";
-        result += " dimension of the code              : " + getK() + "\n";
-        result += " irreducible Goppa polynomial       : " + getGoppaPoly() + "\n";
-        return result;
-    }
+    // TODO:
+//    public String toString()
+//    {
+//        String result = "";
+//        result += " extension degree of the field      : " + getN() + "\n";
+//        result += " dimension of the code              : " + getK() + "\n";
+//        result += " irreducible Goppa polynomial       : " + getGoppaPoly() + "\n";
+//        return result;
+//    }
 
     /**
      * Compare this key with another object.
@@ -170,8 +171,6 @@
      *     sqRootMatrix  SEQUENCE OF OCTET STRING -- square root matrix
      *   }
      * </pre>
-     * </p>
-     *
      * @return the keyData to encode in the SubjectPublicKeyInfo structure
      */
     public byte[] getEncoded()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java
index 79ed7db..46004b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java
@@ -21,10 +21,6 @@
 public class BCMcElieceCCA2PublicKey
     implements CipherParameters, PublicKey
 {
-
-    /**
-     *
-     */
     private static final long serialVersionUID = 1L;
 
     private McElieceCCA2PublicKeyParameters params;
@@ -125,7 +121,6 @@
      *         matrixG     OctetString  -- generator matrix as octet string
      *       }
      * </pre>
-     * </p>
      * @return the keyData to encode in the SubjectPublicKeyInfo structure
      */
     public byte[] getEncoded()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java
index 8ba02a8..26647c2 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java
@@ -15,7 +15,6 @@
 import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
 import org.bouncycastle.pqc.math.linearalgebra.Permutation;
 import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
-import org.bouncycastle.util.Strings;
 
 /**
  * This class implements a McEliece private key and is usually instantiated by
@@ -24,10 +23,6 @@
 public class BCMcEliecePrivateKey
     implements CipherParameters, PrivateKey
 {
-
-    /**
-     *
-     */
     private static final long serialVersionUID = 1L;
 
     private McEliecePrivateKeyParameters params;
@@ -119,19 +114,20 @@
         return params.getQInv();
     }
 
-    /**
+    /*
      * @return a human readable form of the key
      */
-    public String toString()
-    {
-        String result = " length of the code          : " + getN() + Strings.lineSeparator();
-        result += " dimension of the code       : " + getK() + Strings.lineSeparator();
-        result += " irreducible Goppa polynomial: " + getGoppaPoly() + Strings.lineSeparator();
-        result += " permutation P1              : " + getP1() + Strings.lineSeparator();
-        result += " permutation P2              : " + getP2() + Strings.lineSeparator();
-        result += " (k x k)-matrix S^-1         : " + getSInv();
-        return result;
-    }
+    // TODO:
+//    public String toString()
+//    {
+//        String result = " length of the code          : " + getN() + Strings.lineSeparator();
+//        result += " dimension of the code       : " + getK() + Strings.lineSeparator();
+//        result += " irreducible Goppa polynomial: " + getGoppaPoly() + Strings.lineSeparator();
+//        result += " permutation P1              : " + getP1() + Strings.lineSeparator();
+//        result += " permutation P2              : " + getP2() + Strings.lineSeparator();
+//        result += " (k x k)-matrix S^-1         : " + getSInv();
+//        return result;
+//    }
 
     /**
      * Compare this key with another object.
@@ -174,7 +170,7 @@
      * Return the key data to encode in the SubjectPublicKeyInfo structure.
      * <p>
      * The ASN.1 definition of the key structure is
-     * <p>
+     * </p>
      * <pre>
      *   McEliecePrivateKey ::= SEQUENCE {
      *     n          INTEGER                   -- length of the code
@@ -188,7 +184,6 @@
      *     qInv       SEQUENCE OF OCTET STRING  -- matrix used to compute square roots
      *   }
      * </pre>
-     * </p>
      *
      * @return the key data to encode in the SubjectPublicKeyInfo structure
      */
@@ -203,7 +198,6 @@
         }
         catch (IOException e)
         {
-            e.printStackTrace();
             return null;
         }
         try
@@ -213,7 +207,6 @@
         }
         catch (IOException e)
         {
-            e.printStackTrace();
             return null;
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java
index e1de330..e3c7185 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java
@@ -112,6 +112,7 @@
      * Return the keyData to encode in the SubjectPublicKeyInfo structure.
      * <p>
      * The ASN.1 definition of the key structure is
+     * </p>
      * <pre>
      *       McEliecePublicKey ::= SEQUENCE {
      *         n           Integer      -- length of the code
@@ -119,7 +120,6 @@
      *         matrixG     OctetString  -- generator matrix as octet string
      *       }
      * </pre>
-     * </p>
      * @return the keyData to encode in the SubjectPublicKeyInfo structure
      */
     public byte[] getEncoded()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java
index 183754c..0c5815c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java
@@ -14,6 +14,7 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
 import org.bouncycastle.pqc.asn1.McElieceCCA2PrivateKey;
 import org.bouncycastle.pqc.asn1.McElieceCCA2PublicKey;
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
@@ -29,6 +30,7 @@
  */
 public class McElieceCCA2KeyFactorySpi
     extends KeyFactorySpi
+    implements AsymmetricKeyInfoConverter
 {
 
     /**
@@ -204,49 +206,35 @@
 
     }
 
-
     public PublicKey generatePublic(SubjectPublicKeyInfo pki)
-        throws InvalidKeySpecException
+        throws IOException
     {
         // get the inner type inside the BIT STRING
-        try
-        {
-            ASN1Primitive innerType = pki.parsePublicKey();
-            McElieceCCA2PublicKey key = McElieceCCA2PublicKey.getInstance(innerType);
-            return new BCMcElieceCCA2PublicKey(new McElieceCCA2PublicKeyParameters(key.getN(), key.getT(), key.getG(), Utils.getDigest(key.getDigest()).getAlgorithmName()));
-        }
-        catch (IOException cce)
-        {
-            throw new InvalidKeySpecException("Unable to decode X509EncodedKeySpec");
-        }
+        ASN1Primitive innerType = pki.parsePublicKey();
+        McElieceCCA2PublicKey key = McElieceCCA2PublicKey.getInstance(innerType);
+        return new BCMcElieceCCA2PublicKey(new McElieceCCA2PublicKeyParameters(key.getN(), key.getT(), key.getG(), Utils.getDigest(key.getDigest()).getAlgorithmName()));
     }
 
-
     public PrivateKey generatePrivate(PrivateKeyInfo pki)
-        throws InvalidKeySpecException
+        throws IOException
     {
         // get the inner type inside the BIT STRING
-        try
-        {
-            ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();
-            McElieceCCA2PrivateKey key = McElieceCCA2PrivateKey.getInstance(innerType);
-            return new BCMcElieceCCA2PrivateKey(new McElieceCCA2PrivateKeyParameters(key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getP(), null));
-        }
-        catch (IOException cce)
-        {
-            throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec");
-        }
+        ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();
+        McElieceCCA2PrivateKey key = McElieceCCA2PrivateKey.getInstance(innerType);
+        return new BCMcElieceCCA2PrivateKey(new McElieceCCA2PrivateKeyParameters(key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getP(), null));
     }
 
     protected KeySpec engineGetKeySpec(Key key, Class tClass)
         throws InvalidKeySpecException
     {
+        // TODO:
         return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
     protected Key engineTranslateKey(Key key)
         throws InvalidKeyException
     {
+        // TODO:
         return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyPairGeneratorSpi.java
index 2de9141..31cb018 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyPairGeneratorSpi.java
@@ -7,6 +7,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2KeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2KeyPairGenerator;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2Parameters;
@@ -24,14 +25,27 @@
         super("McEliece-CCA2");
     }
 
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        kpg = new McElieceCCA2KeyPairGenerator();
+
+        McElieceCCA2KeyGenParameterSpec ecc = (McElieceCCA2KeyGenParameterSpec)params;
+
+        McElieceCCA2KeyGenerationParameters mccca2KGParams = new McElieceCCA2KeyGenerationParameters(
+            random, new McElieceCCA2Parameters(ecc.getM(), ecc.getT(), ecc.getDigest()));
+        kpg.init(mccca2KGParams);
+    }
+
     public void initialize(AlgorithmParameterSpec params)
         throws InvalidAlgorithmParameterException
     {
         kpg = new McElieceCCA2KeyPairGenerator();
-        super.initialize(params);
+
         McElieceCCA2KeyGenParameterSpec ecc = (McElieceCCA2KeyGenParameterSpec)params;
 
-        McElieceCCA2KeyGenerationParameters mccca2KGParams = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters(ecc.getM(), ecc.getT(), ecc.getDigest()));
+        McElieceCCA2KeyGenerationParameters mccca2KGParams = new McElieceCCA2KeyGenerationParameters(
+            CryptoServicesRegistrar.getSecureRandom(), new McElieceCCA2Parameters(ecc.getM(), ecc.getT(), ecc.getDigest()));
         kpg.init(mccca2KGParams);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java
index 19f7ed9..c5f2ea4 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java
@@ -4,19 +4,18 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
 import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters;
@@ -77,33 +76,26 @@
         update(input, inOff, inLen);
         byte[] data = buf.toByteArray();
         buf.reset();
+
         if (opMode == ENCRYPT_MODE)
         {
-
-            try
-            {
-                return cipher.messageEncrypt(data);
-            }
-            catch (Exception e)
-            {
-                e.printStackTrace();
-            }
-
+            return cipher.messageEncrypt(data);
         }
         else if (opMode == DECRYPT_MODE)
         {
-
             try
             {
                 return cipher.messageDecrypt(data);
             }
-            catch (Exception e)
+            catch (InvalidCipherTextException e)
             {
-                e.printStackTrace();
+                throw new BadPaddingException(e.getMessage());
             }
-
         }
-        return null;
+        else
+        {
+            throw new IllegalStateException("unknown mode in doFinal");
+        }
     }
 
 
@@ -166,37 +158,6 @@
         return cipher.getKeySize(mcElieceCCA2KeyParameters);
     }
 
-    public byte[] messageEncrypt(byte[] input)
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        try
-        {
-            output = cipher.messageEncrypt(input);
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
-
-    public byte[] messageDecrypt(byte[] input)
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        try
-        {
-            output = cipher.messageDecrypt(input);
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
 
     //////////////////////////////////////////////////////////////////////////////////
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java
index 85f718d..3bae908 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java
@@ -17,6 +17,7 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
 import org.bouncycastle.pqc.asn1.McEliecePrivateKey;
 import org.bouncycastle.pqc.asn1.McEliecePublicKey;
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
@@ -31,6 +32,7 @@
  */
 public class McElieceKeyFactorySpi
     extends KeyFactorySpi
+    implements AsymmetricKeyInfoConverter
 {
     /**
      * The OID of the algorithm.
@@ -202,46 +204,34 @@
     }
 
     public PublicKey generatePublic(SubjectPublicKeyInfo pki)
-        throws InvalidKeySpecException
+        throws IOException
     {
         // get the inner type inside the BIT STRING
-        try
-        {
-            ASN1Primitive innerType = pki.parsePublicKey();
-            McEliecePublicKey key = McEliecePublicKey.getInstance(innerType);
-            return new BCMcEliecePublicKey(new McEliecePublicKeyParameters(key.getN(), key.getT(), key.getG()));
-        }
-        catch (IOException cce)
-        {
-            throw new InvalidKeySpecException("Unable to decode X509EncodedKeySpec");
-        }
+        ASN1Primitive innerType = pki.parsePublicKey();
+        McEliecePublicKey key = McEliecePublicKey.getInstance(innerType);
+        return new BCMcEliecePublicKey(new McEliecePublicKeyParameters(key.getN(), key.getT(), key.getG()));
     }
 
     public PrivateKey generatePrivate(PrivateKeyInfo pki)
-        throws InvalidKeySpecException
+        throws IOException
     {
         // get the inner type inside the BIT STRING
-        try
-        {
-            ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();
-            McEliecePrivateKey key = McEliecePrivateKey.getInstance(innerType);
-            return new BCMcEliecePrivateKey(new McEliecePrivateKeyParameters(key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getP1(), key.getP2(), key.getSInv()));
-        }
-        catch (IOException cce)
-        {
-            throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec");
-        }
+        ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();
+        McEliecePrivateKey key = McEliecePrivateKey.getInstance(innerType);
+        return new BCMcEliecePrivateKey(new McEliecePrivateKeyParameters(key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getP1(), key.getP2(), key.getSInv()));
     }
 
     protected KeySpec engineGetKeySpec(Key key, Class tClass)
         throws InvalidKeySpecException
     {
+        // TODO:
         return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
     protected Key engineTranslateKey(Key key)
         throws InvalidKeyException
     {
+        // TODO:
         return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java
index f5e349c..8b7fac0 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java
@@ -23,15 +23,15 @@
     {
         super("McEliece");
     }
-
-    public void initialize(AlgorithmParameterSpec params)
+    
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
         throws InvalidAlgorithmParameterException
     {
         kpg = new McElieceKeyPairGenerator();
-        super.initialize(params);
         McElieceKeyGenParameterSpec ecc = (McElieceKeyGenParameterSpec)params;
 
-        McElieceKeyGenerationParameters mccKGParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters(ecc.getM(), ecc.getT()));
+        McElieceKeyGenerationParameters mccKGParams = new McElieceKeyGenerationParameters(
+            random, new McElieceParameters(ecc.getM(), ecc.getT()));
         kpg.init(mccKGParams);
     }
 
@@ -42,7 +42,7 @@
         // call the initializer with the chosen parameters
         try
         {
-            this.initialize(paramSpec);
+            this.initialize(paramSpec, random);
         }
         catch (InvalidAlgorithmParameterException ae)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java
index 8cb8f34..8dc9ba6 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java
@@ -4,19 +4,18 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
 import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters;
@@ -84,33 +83,26 @@
         update(input, inOff, inLen);
         if (opMode == ENCRYPT_MODE)
         {
-
-            try
-            {
-                return cipher.messageEncrypt(this.pad());
-            }
-            catch (Exception e)
-            {
-                e.printStackTrace();
-            }
-
+            return cipher.messageEncrypt(this.pad());
         }
         else if (opMode == DECRYPT_MODE)
         {
-            byte[] inputOfDecr = buf.toByteArray();
-            buf.reset();
-
             try
             {
+                byte[] inputOfDecr = buf.toByteArray();
+                buf.reset();
+
                 return unpad(cipher.messageDecrypt(inputOfDecr));
             }
-            catch (Exception e)
+            catch (InvalidCipherTextException e)
             {
-                e.printStackTrace();
+                throw new BadPaddingException(e.getMessage());
             }
-
         }
-        return null;
+        else
+        {
+            throw new IllegalStateException("unknown mode in doFinal");
+        }
     }
 
     protected int encryptOutputSize(int inLen)
@@ -219,41 +211,6 @@
         return mBytes;
     }
 
-
-    public byte[] messageEncrypt()
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        try
-        {
-            output = cipher.messageEncrypt((this.pad()));
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
-
-    public byte[] messageDecrypt()
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        byte[] inputOfDecr = buf.toByteArray();
-        buf.reset();
-        try
-        {
-            output = unpad(cipher.messageDecrypt(inputOfDecr));
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
-
     static public class McElieceKobaraImai
         extends McElieceKobaraImaiCipherSpi
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java
index e375393..0e4208e 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java
@@ -4,19 +4,18 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
 import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters;
@@ -78,29 +77,18 @@
         buf.reset();
         if (opMode == ENCRYPT_MODE)
         {
-
-            try
-            {
-                return cipher.messageEncrypt(data);
-            }
-            catch (Exception e)
-            {
-                e.printStackTrace();
-            }
-
+            return cipher.messageEncrypt(data);
         }
         else if (opMode == DECRYPT_MODE)
         {
-
             try
             {
                 return cipher.messageDecrypt(data);
             }
-            catch (Exception e)
+            catch (InvalidCipherTextException e)
             {
-                e.printStackTrace();
+                throw new BadPaddingException(e.getMessage());
             }
-
         }
         return null;
     }
@@ -160,38 +148,6 @@
         return cipher.getKeySize(mcElieceCCA2KeyParameters);
     }
 
-    public byte[] messageEncrypt(byte[] input)
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        try
-        {
-            output = cipher.messageEncrypt(input);
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
-
-    public byte[] messageDecrypt(byte[] input)
-        throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException
-    {
-        byte[] output = null;
-        try
-        {
-            output = cipher.messageDecrypt(input);
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
-        return output;
-    }
-
-
     //////////////////////////////////////////////////////////////////////////////////77
 
     static public class McEliecePointcheval
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPrivateKey.java
index 1e76d0d..92d67c6 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPrivateKey.java
@@ -1,24 +1,25 @@
 package org.bouncycastle.pqc.jcajce.provider.newhope;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
 import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
 import org.bouncycastle.pqc.jcajce.interfaces.NHPrivateKey;
 import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Pack;
 
 public class BCNHPrivateKey
     implements NHPrivateKey
 {
     private static final long serialVersionUID = 1L;
-    ;
-    private final NHPrivateKeyParameters params;
+
+    private transient NHPrivateKeyParameters params;
+    private transient ASN1Set attributes;
 
     public BCNHPrivateKey(
         NHPrivateKeyParameters params)
@@ -29,7 +30,14 @@
     public BCNHPrivateKey(PrivateKeyInfo keyInfo)
         throws IOException
     {
-        this.params = new NHPrivateKeyParameters(convert(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets()));
+        init(keyInfo);
+    }
+
+    private void init(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.attributes = keyInfo.getAttributes();
+        this.params = (NHPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo);
     }
 
     /**
@@ -40,7 +48,7 @@
      */
     public boolean equals(Object o)
     {
-        if (o == null || !(o instanceof BCNHPrivateKey))
+        if (!(o instanceof BCNHPrivateKey))
         {
             return false;
         }
@@ -64,20 +72,9 @@
 
     public byte[] getEncoded()
     {
-        PrivateKeyInfo pki;
         try
         {
-            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope);
-
-            short[] privateKeyData = params.getSecData();
-
-            byte[] octets = new byte[privateKeyData.length * 2];
-            for (int i = 0; i != privateKeyData.length; i++)
-            {
-                Pack.shortToLittleEndian(privateKeyData[i], octets, i * 2);
-            }
-
-            pki = new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(octets));
+            PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes);
 
             return pki.getEncoded();
         }
@@ -102,15 +99,23 @@
         return params;
     }
 
-    private static short[] convert(byte[] octets)
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
     {
-        short[] rv = new short[octets.length / 2];
+        in.defaultReadObject();
 
-        for (int i = 0; i != rv.length; i++)
-        {
-            rv[i] = Pack.littleEndianToShort(octets, i * 2);
-        }
+        byte[] enc = (byte[])in.readObject();
 
-        return rv;
+        init(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPublicKey.java
index eec1fcd..0b4dc23 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/BCNHPublicKey.java
@@ -1,12 +1,14 @@
 package org.bouncycastle.pqc.jcajce.provider.newhope;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
 import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
+import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
 import org.bouncycastle.pqc.jcajce.interfaces.NHPublicKey;
 import org.bouncycastle.util.Arrays;
 
@@ -15,7 +17,7 @@
 {
     private static final long serialVersionUID = 1L;
 
-    private final NHPublicKeyParameters params;
+    private transient NHPublicKeyParameters params;
 
     public BCNHPublicKey(
         NHPublicKeyParameters params)
@@ -24,8 +26,15 @@
     }
 
     public BCNHPublicKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
     {
-        this.params = new NHPublicKeyParameters(keyInfo.getPublicKeyData().getBytes());
+        init(keyInfo);
+    }
+
+    private void init(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        this.params = (NHPublicKeyParameters)PublicKeyFactory.createKey(keyInfo);
     }
 
     /**
@@ -60,11 +69,9 @@
 
     public byte[] getEncoded()
     {
-        SubjectPublicKeyInfo pki;
         try
         {
-            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope);
-            pki = new SubjectPublicKeyInfo(algorithmIdentifier, params.getPubData());
+            SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(params);
 
             return pki.getEncoded();
         }
@@ -88,4 +95,24 @@
     {
         return params;
     }
+
+    private void readObject(
+         ObjectInputStream in)
+         throws IOException, ClassNotFoundException
+     {
+         in.defaultReadObject();
+
+         byte[] enc = (byte[])in.readObject();
+
+         init(SubjectPublicKeyInfo.getInstance(enc));
+     }
+
+     private void writeObject(
+         ObjectOutputStream out)
+         throws IOException
+     {
+         out.defaultWriteObject();
+
+         out.writeObject(this.getEncoded());
+     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/NHKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/NHKeyPairGeneratorSpi.java
index ad35cf1..b10f66e 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/NHKeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/newhope/NHKeyPairGeneratorSpi.java
@@ -6,6 +6,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.newhope.NHKeyPairGenerator;
 import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters;
@@ -16,7 +17,7 @@
 {
     NHKeyPairGenerator engine = new NHKeyPairGenerator();
 
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public NHKeyPairGeneratorSpi()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPrivateKey.java
new file mode 100644
index 0000000..9847711
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPrivateKey.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PrivateKey;
+
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.pqc.jcajce.interfaces.QTESLAKey;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
+import org.bouncycastle.util.Arrays;
+
+public class BCqTESLAPrivateKey
+    implements PrivateKey, QTESLAKey
+{
+    private static final long serialVersionUID = 1L;
+
+    private transient QTESLAPrivateKeyParameters keyParams;
+    private transient ASN1Set attributes;
+
+    public BCqTESLAPrivateKey(
+        QTESLAPrivateKeyParameters keyParams)
+    {
+        this.keyParams = keyParams;
+    }
+
+    public BCqTESLAPrivateKey(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.attributes = keyInfo.getAttributes();
+        this.keyParams = (QTESLAPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo);
+    }
+
+    /**
+     * @return name of the algorithm
+     */
+    public final String getAlgorithm()
+    {
+        return QTESLASecurityCategory.getName(keyParams.getSecurityCategory());
+    }
+
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    public QTESLAParameterSpec getParams()
+    {
+        return new QTESLAParameterSpec(getAlgorithm());
+    }
+
+    public byte[] getEncoded()
+    {
+        PrivateKeyInfo pki;
+        try
+        {
+            pki = PrivateKeyInfoFactory.createPrivateKeyInfo(keyParams, attributes);
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCqTESLAPrivateKey)
+        {
+            BCqTESLAPrivateKey otherKey = (BCqTESLAPrivateKey)o;
+
+            return keyParams.getSecurityCategory() == otherKey.keyParams.getSecurityCategory()
+                && Arrays.areEqual(keyParams.getSecret(), otherKey.keyParams.getSecret());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return keyParams.getSecurityCategory() + 37 * Arrays.hashCode(keyParams.getSecret());
+    }
+
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPublicKey.java
new file mode 100644
index 0000000..7324f8e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/BCqTESLAPublicKey.java
@@ -0,0 +1,122 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PublicKey;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
+import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.pqc.jcajce.interfaces.QTESLAKey;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
+import org.bouncycastle.util.Arrays;
+
+public class BCqTESLAPublicKey
+    implements PublicKey, QTESLAKey
+{
+    private static final long serialVersionUID = 1L;
+
+    private transient QTESLAPublicKeyParameters keyParams;
+
+    public BCqTESLAPublicKey(
+        QTESLAPublicKeyParameters keyParams)
+    {
+        this.keyParams = keyParams;
+    }
+
+    public BCqTESLAPublicKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        this.keyParams = (QTESLAPublicKeyParameters)PublicKeyFactory.createKey(keyInfo);
+    }
+
+    /**
+     * @return name of the algorithm
+     */
+    public final String getAlgorithm()
+    {
+        return QTESLASecurityCategory.getName(keyParams.getSecurityCategory());
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParams);
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    public QTESLAParameterSpec getParams()
+    {
+        return new QTESLAParameterSpec(getAlgorithm());
+    }
+    
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCqTESLAPublicKey)
+        {
+            BCqTESLAPublicKey otherKey = (BCqTESLAPublicKey)o;
+
+            return keyParams.getSecurityCategory() == otherKey.keyParams.getSecurityCategory()
+                && Arrays.areEqual(keyParams.getPublicData(), otherKey.keyParams.getPublicData());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return keyParams.getSecurityCategory() + 37 * Arrays.hashCode(keyParams.getPublicData());
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/DigestUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/DigestUtil.java
new file mode 100644
index 0000000..4af55b7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/DigestUtil.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+
+class DigestUtil
+{
+    static Digest getDigest(ASN1ObjectIdentifier oid)
+    {
+        if (oid.equals(NISTObjectIdentifiers.id_sha256))
+        {
+            return new SHA256Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_sha512))
+        {
+            return new SHA512Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake128))
+        {
+            return new SHAKEDigest(128);
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake256))
+        {
+            return new SHAKEDigest(256);
+        }
+
+        throw new IllegalArgumentException("unrecognized digest OID: " + oid);
+    }
+
+    public static byte[] getDigestResult(Digest digest)
+    {
+        byte[] hash = new byte[DigestUtil.getDigestSize(digest)];
+
+        if (digest instanceof Xof)
+        {
+            ((Xof)digest).doFinal(hash, 0, hash.length);
+        }
+        else
+        {
+            digest.doFinal(hash, 0);
+        }
+
+        return hash;
+    }
+
+    public static int getDigestSize(Digest digest)
+    {
+        if (digest instanceof Xof)
+        {
+            return digest.getDigestSize() * 2;
+        }
+
+        return digest.getDigestSize();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/KeyPairGeneratorSpi.java
new file mode 100644
index 0000000..3975cfd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/KeyPairGeneratorSpi.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAKeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLAPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
+import org.bouncycastle.util.Integers;
+
+public class KeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    private static final Map catLookup = new HashMap();
+
+    static
+    {
+        catLookup.put(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_I), Integers.valueOf(QTESLASecurityCategory.HEURISTIC_I));
+        catLookup.put(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SIZE), Integers.valueOf(QTESLASecurityCategory.HEURISTIC_III_SIZE));
+        catLookup.put(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SPEED), Integers.valueOf(QTESLASecurityCategory.HEURISTIC_III_SPEED));
+        catLookup.put(QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_I), Integers.valueOf(QTESLASecurityCategory.PROVABLY_SECURE_I));
+        catLookup.put(QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_III), Integers.valueOf(QTESLASecurityCategory.PROVABLY_SECURE_III));
+    }
+
+    private QTESLAKeyGenerationParameters param;
+    private QTESLAKeyPairGenerator engine = new QTESLAKeyPairGenerator();
+
+    private SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
+    private boolean initialised = false;
+
+    public KeyPairGeneratorSpi()
+    {
+        super("qTESLA");
+    }
+
+    public void initialize(
+        int strength,
+        SecureRandom random)
+    {
+        throw new IllegalArgumentException("use AlgorithmParameterSpec");
+    }
+
+    public void initialize(
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        if (!(params instanceof QTESLAParameterSpec))
+        {
+            throw new InvalidAlgorithmParameterException("parameter object not a QTESLAParameterSpec");
+        }
+
+        QTESLAParameterSpec qteslaParams = (QTESLAParameterSpec)params;
+
+        param = new QTESLAKeyGenerationParameters(((Integer)catLookup.get(qteslaParams.getSecurityCategory())).intValue(), random);
+
+        engine.init(param);
+        initialised = true;
+    }
+
+    public KeyPair generateKeyPair()
+    {
+        if (!initialised)
+        {
+            param = new QTESLAKeyGenerationParameters(QTESLASecurityCategory.PROVABLY_SECURE_I, random);
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+        QTESLAPublicKeyParameters pub = (QTESLAPublicKeyParameters)pair.getPublic();
+        QTESLAPrivateKeyParameters priv = (QTESLAPrivateKeyParameters)pair.getPrivate();
+
+        return new KeyPair(new BCqTESLAPublicKey(pub), new BCqTESLAPrivateKey(priv));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/QTESLAKeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/QTESLAKeyFactorySpi.java
new file mode 100644
index 0000000..46e4847
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/QTESLAKeyFactorySpi.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+public class QTESLAKeyFactorySpi
+    extends KeyFactorySpi
+    implements AsymmetricKeyInfoConverter
+{
+    public PrivateKey engineGeneratePrivate(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof PKCS8EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to PKCS#8 from the spec
+            byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
+
+            try
+            {
+                return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("Unsupported key specification: "
+            + keySpec.getClass() + ".");
+    }
+
+    public PublicKey engineGeneratePublic(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof X509EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to X.509 from the spec
+            byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded();
+
+            // decode the SubjectPublicKeyInfo data structure to the pki object
+            try
+            {
+                return generatePublic(SubjectPublicKeyInfo.getInstance(encKey));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("Unknown key specification: " + keySpec + ".");
+    }
+
+    public final KeySpec engineGetKeySpec(Key key, Class keySpec)
+        throws InvalidKeySpecException
+    {
+        if (key instanceof BCqTESLAPrivateKey)
+        {
+            if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new PKCS8EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else if (key instanceof BCqTESLAPublicKey)
+        {
+            if (X509EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new X509EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else
+        {
+            throw new InvalidKeySpecException("Unsupported key type: "
+                + key.getClass() + ".");
+        }
+
+        throw new InvalidKeySpecException("Unknown key specification: "
+            + keySpec + ".");
+    }
+
+    public final Key engineTranslateKey(Key key)
+        throws InvalidKeyException
+    {
+        if (key instanceof BCqTESLAPrivateKey || key instanceof BCqTESLAPublicKey)
+        {
+            return key;
+        }
+
+        throw new InvalidKeyException("Unsupported key type");
+    }
+
+    public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCqTESLAPrivateKey(keyInfo);
+    }
+
+    public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCqTESLAPublicKey(keyInfo);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/SignatureSpi.java
new file mode 100644
index 0000000..fe3aa54
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/qtesla/SignatureSpi.java
@@ -0,0 +1,194 @@
+package org.bouncycastle.pqc.jcajce.provider.qtesla;
+
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.NullDigest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASigner;
+
+public class SignatureSpi
+    extends Signature
+{
+    protected SignatureSpi(String algorithm)
+    {
+        super(algorithm);
+    }
+
+    private Digest digest;
+    private QTESLASigner signer;
+    private SecureRandom random;
+
+    protected SignatureSpi(String sigName, Digest digest, QTESLASigner signer)
+    {
+        super(sigName);
+
+        this.digest = digest;
+        this.signer = signer;
+    }
+
+    protected void engineInitVerify(PublicKey publicKey)
+        throws InvalidKeyException
+    {
+        if (publicKey instanceof BCqTESLAPublicKey)
+        {
+            CipherParameters param = ((BCqTESLAPublicKey)publicKey).getKeyParams();
+            
+            digest.reset();
+            signer.init(false, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown public key passed to qTESLA");
+        }
+    }
+
+    protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+        throws InvalidKeyException
+    {
+        this.random = random;
+        engineInitSign(privateKey);
+    }
+
+    protected void engineInitSign(PrivateKey privateKey)
+        throws InvalidKeyException
+    {
+        if (privateKey instanceof BCqTESLAPrivateKey)
+        {
+            CipherParameters param = ((BCqTESLAPrivateKey)privateKey).getKeyParams();
+
+            if (random != null)
+            {
+                param = new ParametersWithRandom(param, random);
+            }
+
+            signer.init(true, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown private key passed to qTESLA");
+        }
+    }
+
+    protected void engineUpdate(byte b)
+        throws SignatureException
+    {
+        digest.update(b);
+    }
+
+    protected void engineUpdate(byte[] b, int off, int len)
+        throws SignatureException
+    {
+        digest.update(b, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        try
+        {
+            byte[] hash = DigestUtil.getDigestResult(digest);
+
+            return signer.generateSignature(hash);
+        }
+        catch (Exception e)
+        {
+            if (e instanceof IllegalStateException)
+            {
+                throw new SignatureException(e.getMessage());
+            }
+            throw new SignatureException(e.toString());
+        }
+    }
+
+    protected boolean engineVerify(byte[] sigBytes)
+        throws SignatureException
+    {
+        byte[] hash = DigestUtil.getDigestResult(digest);
+
+        return signer.verifySignature(hash, sigBytes);
+    }
+
+    protected void engineSetParameter(AlgorithmParameterSpec params)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
+     */
+    protected void engineSetParameter(String param, Object value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated
+     */
+    protected Object engineGetParameter(String param)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    static public class qTESLA
+        extends SignatureSpi
+    {
+        public qTESLA()
+        {
+            super("qTESLA", new NullDigest(), new QTESLASigner());
+        }
+    }
+
+    static public class HeuristicI
+        extends SignatureSpi
+    {
+        public HeuristicI()
+        {
+            super(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_I), new NullDigest(), new QTESLASigner());
+        }
+    }
+
+    static public class HeuristicIIISize
+        extends SignatureSpi
+    {
+        public HeuristicIIISize()
+        {
+            super(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SIZE), new NullDigest(), new QTESLASigner());
+        }
+    }
+
+    static public class HeuristicIIISpeed
+        extends SignatureSpi
+    {
+        public HeuristicIIISpeed()
+        {
+            super(QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SPEED), new NullDigest(), new QTESLASigner());
+        }
+    }
+
+    static public class ProvablySecureI
+        extends SignatureSpi
+    {
+        public ProvablySecureI()
+        {
+            super(QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_I), new NullDigest(), new QTESLASigner());
+        }
+    }
+
+    static public class ProvablySecureIII
+        extends SignatureSpi
+    {
+        public ProvablySecureIII()
+        {
+            super(QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_III), new NullDigest(), new QTESLASigner());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java
index 93a50cb..157508c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java
@@ -222,7 +222,6 @@
         }
         catch (IOException e)
         {
-            e.printStackTrace();
             return null;
         }
         try
@@ -232,7 +231,6 @@
         }
         catch (IOException e)
         {
-            e.printStackTrace();
             return null;
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java
index 4874dc9..6a52b3a 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java
@@ -39,6 +39,7 @@
      * are supported: {@link RainbowPrivateKeySpec}, {@link PKCS8EncodedKeySpec}.
      * <p>
      * The ASN.1 definition of the key structure is
+     * </p>
      * <pre>
      *   RainbowPrivateKey ::= SEQUENCE {
      *     oid        OBJECT IDENTIFIER         -- OID identifying the algorithm
@@ -58,7 +59,6 @@
      *     eta        OCTET
      *   }
      * </pre>
-     * </p>
      *
      * @param keySpec the key specification
      * @return the Rainbow private key
@@ -96,7 +96,7 @@
      * supported:{@link X509EncodedKeySpec}.
      * <p>
      * The ASN.1 definition of a public key's structure is
-     * <pre>
+     * </p><pre>
      *    RainbowPublicKey ::= SEQUENCE {
      *      oid            OBJECT IDENTIFIER        -- OID identifying the algorithm
      *      docLength      Integer                  -- length of signable msg
@@ -105,7 +105,6 @@
      *      coeffscalar       OCTET STRING             -- scalar coefficients
      *       }
      * </pre>
-     * </p>
      *
      * @param keySpec the key specification
      * @return the Rainbow public key
@@ -141,8 +140,8 @@
      * Converts a given key into a key specification, if possible. Currently the
      * following specs are supported:
      * <ul>
-     * <li>for RainbowPublicKey: X509EncodedKeySpec, RainbowPublicKeySpec
-     * <li>for RainbowPrivateKey: PKCS8EncodedKeySpec, RainbowPrivateKeySpec
+     * <li>for RainbowPublicKey: X509EncodedKeySpec, RainbowPublicKeySpec</li>
+     * <li>for RainbowPrivateKey: PKCS8EncodedKeySpec, RainbowPrivateKeySpec</li>
      * </ul>
      *
      * @param key     the key
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java
index e64d53b..0979256 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java
@@ -6,6 +6,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyGenerationParameters;
 import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyPairGenerator;
 import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters;
@@ -19,7 +20,7 @@
     RainbowKeyGenerationParameters param;
     RainbowKeyPairGenerator engine = new RainbowKeyPairGenerator();
     int strength = 1024;
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public RainbowKeyPairGeneratorSpi()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java
index e118ed6..86d971d 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java
@@ -108,9 +108,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href =
-     *             "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"
-     *             >
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(String param, Object value)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PrivateKey.java
index d5a1c06..9d9b3fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PrivateKey.java
@@ -1,10 +1,12 @@
 package org.bouncycastle.pqc.jcajce.provider.sphincs;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.security.PrivateKey;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -12,6 +14,8 @@
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
 import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
 import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
 import org.bouncycastle.pqc.jcajce.interfaces.SPHINCSKey;
 import org.bouncycastle.util.Arrays;
 
@@ -20,8 +24,9 @@
 {
     private static final long serialVersionUID = 1L;
 
-    private final ASN1ObjectIdentifier treeDigest;
-    private final SPHINCSPrivateKeyParameters params;
+    private transient ASN1ObjectIdentifier treeDigest;
+    private transient SPHINCSPrivateKeyParameters params;
+    private transient ASN1Set attributes;
 
     public BCSphincs256PrivateKey(
         ASN1ObjectIdentifier treeDigest,
@@ -34,8 +39,15 @@
     public BCSphincs256PrivateKey(PrivateKeyInfo keyInfo)
         throws IOException
     {
+        init(keyInfo);
+    }
+
+    private void init(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.attributes = keyInfo.getAttributes();
         this.treeDigest = SPHINCS256KeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters()).getTreeDigest().getAlgorithm();
-        this.params = new SPHINCSPrivateKeyParameters(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets());
+        this.params = (SPHINCSPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo);
     }
 
     /**
@@ -46,13 +58,19 @@
      */
     public boolean equals(Object o)
     {
-        if (o == null || !(o instanceof BCSphincs256PrivateKey))
+        if (o == this)
         {
-            return false;
+            return true;
         }
-        BCSphincs256PrivateKey otherKey = (BCSphincs256PrivateKey)o;
 
-        return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(params.getKeyData(), otherKey.params.getKeyData());
+        if (o instanceof BCSphincs256PrivateKey)
+        {
+            BCSphincs256PrivateKey otherKey = (BCSphincs256PrivateKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(params.getKeyData(), otherKey.params.getKeyData());
+        }
+
+        return false;
     }
 
     public int hashCode()
@@ -70,11 +88,20 @@
 
     public byte[] getEncoded()
     {
-        PrivateKeyInfo pki;
+
         try
         {
-            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, new SPHINCS256KeyParams(new AlgorithmIdentifier(treeDigest)));
-            pki = new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getKeyData()));
+            PrivateKeyInfo pki;
+            if (params.getTreeDigest() != null)
+            {
+                pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes);
+            }
+            else
+            {
+                AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256,
+                    new SPHINCS256KeyParams(new AlgorithmIdentifier(treeDigest)));
+                pki = new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getKeyData()), attributes);
+            }
 
             return pki.getEncoded();
         }
@@ -89,6 +116,11 @@
         return "PKCS#8";
     }
 
+    ASN1ObjectIdentifier getTreeDigest()
+    {
+        return treeDigest;
+    }
+    
     public byte[] getKeyData()
     {
         return params.getKeyData();
@@ -98,4 +130,24 @@
     {
         return params;
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PublicKey.java
index ee98686..663307c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/BCSphincs256PublicKey.java
@@ -1,6 +1,8 @@
 package org.bouncycastle.pqc.jcajce.provider.sphincs;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.security.PublicKey;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -10,6 +12,8 @@
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
 import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
 import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters;
+import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
+import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
 import org.bouncycastle.pqc.jcajce.interfaces.SPHINCSKey;
 import org.bouncycastle.util.Arrays;
 
@@ -18,8 +22,8 @@
 {
     private static final long serialVersionUID = 1L;
 
-    private final ASN1ObjectIdentifier treeDigest;
-    private final SPHINCSPublicKeyParameters params;
+    private transient ASN1ObjectIdentifier treeDigest;
+    private transient SPHINCSPublicKeyParameters params;
 
     public BCSphincs256PublicKey(
         ASN1ObjectIdentifier treeDigest,
@@ -30,11 +34,18 @@
     }
 
     public BCSphincs256PublicKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
     {
-        this.treeDigest = SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters()).getTreeDigest().getAlgorithm();
-        this.params = new SPHINCSPublicKeyParameters(keyInfo.getPublicKeyData().getBytes());
+        init(keyInfo);
     }
 
+    private void init(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        this.treeDigest = SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters()).getTreeDigest().getAlgorithm();
+        this.params = (SPHINCSPublicKeyParameters)PublicKeyFactory.createKey(keyInfo);
+    }
+    
     /**
      * Compare this SPHINCS-256 public key with another object.
      *
@@ -43,13 +54,19 @@
      */
     public boolean equals(Object o)
     {
-        if (o == null || !(o instanceof BCSphincs256PublicKey))
+        if (o == this)
         {
-            return false;
+            return true;
         }
-        BCSphincs256PublicKey otherKey = (BCSphincs256PublicKey)o;
 
-        return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(params.getKeyData(), otherKey.params.getKeyData());
+        if (o instanceof BCSphincs256PublicKey)
+        {
+            BCSphincs256PublicKey otherKey = (BCSphincs256PublicKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(params.getKeyData(), otherKey.params.getKeyData());
+        }
+
+        return false;
     }
 
     public int hashCode()
@@ -67,11 +84,19 @@
 
     public byte[] getEncoded()
     {
-        SubjectPublicKeyInfo pki;
         try
         {
-            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, new SPHINCS256KeyParams(new AlgorithmIdentifier(treeDigest)));
-            pki = new SubjectPublicKeyInfo(algorithmIdentifier, params.getKeyData());
+            SubjectPublicKeyInfo pki;
+
+            if (params.getTreeDigest() != null)
+            {
+                pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(params);
+            }
+            else
+            {
+                AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, new SPHINCS256KeyParams(new AlgorithmIdentifier(treeDigest)));
+                pki = new SubjectPublicKeyInfo(algorithmIdentifier, params.getKeyData());
+            }
 
             return pki.getEncoded();
         }
@@ -91,8 +116,33 @@
         return params.getKeyData();
     }
 
+    ASN1ObjectIdentifier getTreeDigest()
+    {
+        return treeDigest;
+    }
+
     CipherParameters getKeyParams()
     {
         return params;
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/SignatureSpi.java
index 8d1ef94..30b5a0e 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/SignatureSpi.java
@@ -7,24 +7,27 @@
 import java.security.SignatureException;
 import java.security.spec.AlgorithmParameterSpec;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA3Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.digests.SHA512tDigest;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.pqc.crypto.sphincs.SPHINCS256Signer;
 
 public class SignatureSpi
     extends java.security.SignatureSpi
 {
+    private final ASN1ObjectIdentifier treeDigest;
     private Digest digest;
     private SPHINCS256Signer signer;
     private SecureRandom random;
 
-    protected SignatureSpi(Digest digest, SPHINCS256Signer signer)
+    protected SignatureSpi(Digest digest, ASN1ObjectIdentifier treeDigest, SPHINCS256Signer signer)
     {
         this.digest = digest;
+        this.treeDigest = treeDigest;
         this.signer = signer;
     }
 
@@ -33,7 +36,12 @@
     {
         if (publicKey instanceof BCSphincs256PublicKey)
         {
-            CipherParameters param = ((BCSphincs256PublicKey)publicKey).getKeyParams();
+            BCSphincs256PublicKey key = (BCSphincs256PublicKey)publicKey;
+            if (!treeDigest.equals(key.getTreeDigest()))
+            {
+                throw new InvalidKeyException("SPHINCS-256 signature for tree digest: " + key.getTreeDigest());
+            }
+            CipherParameters param = key.getKeyParams();
 
             digest.reset();
             signer.init(false, param);
@@ -56,13 +64,20 @@
     {
         if (privateKey instanceof BCSphincs256PrivateKey)
         {
-            CipherParameters param = ((BCSphincs256PrivateKey)privateKey).getKeyParams();
-
-            if (random != null)
+            BCSphincs256PrivateKey key = (BCSphincs256PrivateKey)privateKey;
+            if (!treeDigest.equals(key.getTreeDigest()))
             {
-                param = new ParametersWithRandom(param, random);
+                throw new InvalidKeyException("SPHINCS-256 signature for tree digest: " + key.getTreeDigest());
             }
 
+            CipherParameters param = key.getKeyParams();
+
+            // random not required for SPHINCS.
+//            if (random != null)
+//            {
+//                param = new ParametersWithRandom(param, random);
+//            }
+
             digest.reset();
             signer.init(true, param);
         }
@@ -106,6 +121,7 @@
     {
         byte[] hash = new byte[digest.getDigestSize()];
         digest.doFinal(hash, 0);
+
         return signer.verifySignature(hash, sigBytes);
     }
 
@@ -116,9 +132,7 @@
     }
 
     /**
-     * @deprecated replaced with <a href =
-     *             "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"
-     *             >
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
      */
     protected void engineSetParameter(String param, Object value)
     {
@@ -138,7 +152,7 @@
     {
         public withSha512()
         {
-            super(new SHA512Digest(), new SPHINCS256Signer(new SHA512tDigest(256), new SHA512Digest()));
+            super(new SHA512Digest(), NISTObjectIdentifiers.id_sha512_256, new SPHINCS256Signer(new SHA512tDigest(256), new SHA512Digest()));
         }
     }
 
@@ -147,7 +161,7 @@
     {
         public withSha3_512()
         {
-            super(new SHA3Digest(512), new SPHINCS256Signer(new SHA3Digest(256), new SHA3Digest(512)));
+            super(new SHA3Digest(512), NISTObjectIdentifiers.id_sha3_256, new SPHINCS256Signer(new SHA3Digest(256), new SHA3Digest(512)));
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/Sphincs256KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/Sphincs256KeyPairGeneratorSpi.java
index e05b04a..7228b31 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/Sphincs256KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/sphincs/Sphincs256KeyPairGeneratorSpi.java
@@ -8,6 +8,7 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.digests.SHA3Digest;
 import org.bouncycastle.crypto.digests.SHA512tDigest;
 import org.bouncycastle.pqc.crypto.sphincs.SPHINCS256KeyGenerationParameters;
@@ -24,7 +25,7 @@
     SPHINCS256KeyGenerationParameters param;
     SPHINCS256KeyPairGenerator engine = new SPHINCS256KeyPairGenerator();
 
-    SecureRandom random = new SecureRandom();
+    SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
     boolean initialised = false;
 
     public Sphincs256KeyPairGeneratorSpi()
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java
index 4b2592f..95cb5be 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java
@@ -8,6 +8,9 @@
 import junit.framework.TestSuite;
 import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
 
+/**
+ * Full test suite for the BCPQC provider.
+ */
 public class AllTests
     extends TestCase
 {
@@ -37,6 +40,9 @@
         suite.addTestSuite(NewHopeKeyPairGeneratorTest.class);
         suite.addTestSuite(Sphincs256Test.class);
         suite.addTestSuite(Sphincs256KeyPairGeneratorTest.class);
+        suite.addTestSuite(XMSSTest.class);
+        suite.addTestSuite(XMSSMTTest.class);
+        suite.addTestSuite(QTESLATest.class);
 
         return new BCTestSetup(suite);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AsymmetricBlockCipherTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AsymmetricBlockCipherTest.java
index c67d438..c3b6516 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AsymmetricBlockCipherTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/AsymmetricBlockCipherTest.java
@@ -54,15 +54,17 @@
                     mBytes = new byte[mLength];
                     rand.nextBytes(mBytes);
 
+                    int cLen = cipher.getOutputSize(mBytes.length);
                     // encrypt
                     cBytes = cipher.doFinal(mBytes);
-
+                    assertTrue(cBytes.length <= cLen);
                     // initialize for decryption
                     cipher.init(Cipher.DECRYPT_MODE, privKey, params);
-
+                    int dLen = cipher.getOutputSize(cBytes.length);
                     // decrypt
                     dBytes = cipher.doFinal(cBytes);
-
+                    assertTrue(dBytes.length <= dLen);
+  
                     // compare
                     assertEquals("Encryption and Decryption test failed:\n"
                         + " actual decrypted text: "
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/KeyStoreTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/KeyStoreTest.java
new file mode 100644
index 0000000..1c1762e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/KeyStoreTest.java
@@ -0,0 +1,275 @@
+package org.bouncycastle.pqc.jcajce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificate;
+import org.bouncycastle.asn1.x509.Time;
+import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.McElieceKeyGenParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
+
+public class KeyStoreTest
+    extends TestCase
+{
+    private static final long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+    private static final long TEN_YEARS_IN_MILLIS = 10l * 365 * ONE_DAY_IN_MILLIS;
+
+    private static Map algIds = new HashMap();
+
+    static
+    {
+        algIds.put("SHA512WITHSPHINCS256", new AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512));
+        algIds.put("SHA256WITHXMSSMT", new AlgorithmIdentifier(BCObjectIdentifiers.xmss_mt_SHA256ph));
+        algIds.put("SHA512WITHXMSSMT", new AlgorithmIdentifier(BCObjectIdentifiers.xmss_mt_SHA512ph));
+    }
+
+    public void setUp()
+    {
+        Security.addProvider(new BouncyCastleProvider());
+        Security.addProvider(new BouncyCastlePQCProvider());
+    }
+
+    public void testPKCS12()
+        throws Exception
+    {
+        tryKeyStore("PKCS12");
+        tryKeyStore("PKCS12-DEF");
+    }
+
+    public void testBKS()
+        throws Exception
+    {
+        tryKeyStore("BKS");
+        tryKeyStore("UBER");
+    }
+
+    public void testBCFKS()
+        throws Exception
+    {
+        tryKeyStore("BCFKS-DEF");
+    }
+
+    private void tryKeyStore(String format)
+        throws Exception
+    {
+        // Keystore to store certificates and private keys
+        KeyStore store = KeyStore.getInstance(format, "BC");
+
+        store.load(null, null);
+
+        String password = "qwertz";
+        // XMSS
+        X500NameBuilder nameBuilder = new X500NameBuilder();
+
+        nameBuilder.addRDN(BCStyle.CN, "Root CA");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+        // root CA
+        X509Certificate rootCA = createPQSelfSignedCert(nameBuilder.build(), "SHA256WITHXMSSMT", kp);
+        X509Certificate[] chain = new X509Certificate[1];
+        chain[0] = rootCA;
+        // store root private key
+        String alias1 = "xmssmt private";
+        store.setKeyEntry(alias1, kp.getPrivate(), password.toCharArray(), chain);
+        // store root certificate
+        store.setCertificateEntry("root ca", rootCA);
+
+        // McEliece
+        kpg = KeyPairGenerator.getInstance("McEliece", "BCPQC");
+
+        McElieceKeyGenParameterSpec params = new McElieceKeyGenParameterSpec(9, 33);
+        kpg.initialize(params);
+
+        KeyPair mcelieceKp = kpg.generateKeyPair();
+
+        ExtensionsGenerator extGenerator = new ExtensionsGenerator();
+        extGenerator.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
+        extGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.encipherOnly));
+
+        X509Certificate cert1 = createCert(nameBuilder.build(), kp.getPrivate(), new X500Name("CN=meceliece"), "SHA256WITHXMSSMT",
+            extGenerator.generate(), mcelieceKp.getPublic());
+
+        X509Certificate[] chain1 = new X509Certificate[2];
+        chain1[1] = rootCA;
+        chain1[0] = cert1;
+
+        // SPHINCS-256
+        kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
+
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256));
+
+        KeyPair sphincsKp = kpg.generateKeyPair();
+
+        extGenerator = new ExtensionsGenerator();
+        extGenerator.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
+        extGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature));
+
+        X509Certificate cert2 = createCert(nameBuilder.build(), sphincsKp.getPrivate(), new X500Name("CN=sphincs256"), "SHA512WITHSPHINCS256",
+            extGenerator.generate(), sphincsKp.getPublic());
+
+        X509Certificate[] chain2 = new X509Certificate[2];
+        chain2[1] = rootCA;
+        chain2[0] = cert2;
+
+        String alias2 = "private key 1";
+        String alias3 = "private key 2";
+
+        // store private keys
+        store.setKeyEntry(alias2, mcelieceKp.getPrivate(), password.toCharArray(), chain1);
+        store.setKeyEntry(alias3, sphincsKp.getPrivate(), password.toCharArray(), chain2);
+
+        // store certificates
+        store.setCertificateEntry("cert 1", cert1);
+        store.setCertificateEntry("cert 2", cert2);
+
+        // can't restore keys from keystore
+        Key k1 = store.getKey(alias1, password.toCharArray());
+
+        assertEquals(kp.getPrivate(), k1);
+
+        Key k2 = store.getKey(alias2, password.toCharArray());
+
+        assertEquals(mcelieceKp.getPrivate(), k2);
+
+        Key k3 = store.getKey(alias3, password.toCharArray());
+
+        assertEquals(sphincsKp.getPrivate(), k3);
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        store.store(bOut, "fred".toCharArray());
+
+        KeyStore bcStore = KeyStore.getInstance(format, "BC");
+
+        bcStore.load(new ByteArrayInputStream(bOut.toByteArray()), "fred".toCharArray());
+
+        k1 = store.getKey(alias1, password.toCharArray());
+
+        assertEquals(kp.getPrivate(), k1);
+
+        k2 = store.getKey(alias2, password.toCharArray());
+
+        assertEquals(mcelieceKp.getPrivate(), k2);
+
+        k3 = store.getKey(alias3, password.toCharArray());
+
+        assertEquals(sphincsKp.getPrivate(), k3);
+    }
+
+    private static X509Certificate createPQSelfSignedCert(X500Name dn, String sigName, KeyPair keyPair)
+        throws Exception
+    {
+        V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
+        long time = System.currentTimeMillis();
+        AtomicLong serialNumber = new AtomicLong(System.currentTimeMillis());
+        certGen.setSerialNumber(new ASN1Integer(serialNumber.getAndIncrement()));
+        certGen.setIssuer(dn);
+        certGen.setSubject(dn);
+        certGen.setStartDate(new Time(new Date(time - 5000)));
+        certGen.setEndDate(new Time(new Date(time + TEN_YEARS_IN_MILLIS)));
+        certGen.setSignature((AlgorithmIdentifier)algIds.get(sigName));
+        certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+
+        ExtensionsGenerator extGenerator = new ExtensionsGenerator();
+        extGenerator.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
+        extGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
+
+        certGen.setExtensions(extGenerator.generate());
+
+        Signature sig = Signature.getInstance(sigName, BouncyCastlePQCProvider.PROVIDER_NAME);
+
+        sig.initSign(keyPair.getPrivate());
+
+        sig.update(certGen.generateTBSCertificate().getEncoded(ASN1Encoding.DER));
+
+        TBSCertificate tbsCert = certGen.generateTBSCertificate();
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        // TBS
+        v.add(tbsCert);
+        // Algorithm Identifier
+        v.add((AlgorithmIdentifier)algIds.get(sigName));
+        // Signature
+        v.add(new DERBitString(sig.sign()));
+
+        return (X509Certificate)CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME)
+            .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
+    }
+
+    private static X509Certificate createCert(X500Name signerName, PrivateKey signerKey, X500Name dn, String sigName,
+                                             Extensions extensions, PublicKey pubKey)
+        throws Exception
+    {
+        V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
+
+        long time = System.currentTimeMillis();
+        AtomicLong serialNumber = new AtomicLong(System.currentTimeMillis());
+
+        certGen.setSerialNumber(new ASN1Integer(serialNumber.getAndIncrement()));
+        certGen.setIssuer(signerName);
+        certGen.setSubject(dn);
+        certGen.setStartDate(new Time(new Date(time - 5000)));
+        certGen.setEndDate(new Time(new Date(time + TEN_YEARS_IN_MILLIS)));
+        certGen.setSignature((AlgorithmIdentifier)algIds.get(sigName));
+        certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()));
+
+        certGen.setExtensions(extensions);
+
+        Signature sig = Signature.getInstance(sigName, BouncyCastlePQCProvider.PROVIDER_NAME);
+
+        sig.initSign(signerKey);
+
+        sig.update(certGen.generateTBSCertificate().getEncoded(ASN1Encoding.DER));
+
+        TBSCertificate tbsCert = certGen.generateTBSCertificate();
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(tbsCert);
+        v.add((AlgorithmIdentifier)algIds.get(sigName));
+        v.add(new DERBitString(sig.sign()));
+
+        return (X509Certificate)CertificateFactory.getInstance("X.509", "BC")
+            .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/McElieceKeyPairGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/McElieceKeyPairGeneratorTest.java
index a8326bc..6600a17 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/McElieceKeyPairGeneratorTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/McElieceKeyPairGeneratorTest.java
@@ -2,8 +2,10 @@
 
 import java.security.KeyFactory;
 import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
 
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.jcajce.spec.McElieceCCA2KeyGenParameterSpec;
 import org.bouncycastle.pqc.jcajce.spec.McElieceKeyGenParameterSpec;
 
 
@@ -32,6 +34,24 @@
         McElieceKeyGenParameterSpec params = new McElieceKeyGenParameterSpec(9, 33);
         kpg.initialize(params);
         performKeyPairEncodingTest(kpg.generateKeyPair());
+
+        kpg = KeyPairGenerator.getInstance("McEliece");
+        kpg.initialize(params, new SecureRandom());
+        performKeyPairEncodingTest(kpg.generateKeyPair());
     }
 
+    public void testKeyPairEncoding_CCA2()
+        throws Exception
+    {
+        kf = KeyFactory.getInstance("McEliece-CCA2");
+
+        kpg = KeyPairGenerator.getInstance("McEliece-CCA2");
+        McElieceCCA2KeyGenParameterSpec params = new McElieceCCA2KeyGenParameterSpec(9, 33);
+        kpg.initialize(params);
+        performKeyPairEncodingTest(kpg.generateKeyPair());
+
+        kpg = KeyPairGenerator.getInstance("McEliece-CCA2");
+        kpg.initialize(params, new SecureRandom());
+        performKeyPairEncodingTest(kpg.generateKeyPair());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeKeyPairGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeKeyPairGeneratorTest.java
index 07d07b7..c70450f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeKeyPairGeneratorTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeKeyPairGeneratorTest.java
@@ -7,6 +7,9 @@
 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
 
 
+/**
+ * KeyFactory/KeyPairGenerator tests for NewHope (NH) with the BCPQC provider.
+ */
 public class NewHopeKeyPairGeneratorTest
     extends KeyPairGeneratorTest
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeTest.java
index 3eeae4f..1d072b1 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/NewHopeTest.java
@@ -1,18 +1,28 @@
 package org.bouncycastle.pqc.jcajce.provider.test;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.SecureRandom;
 import java.security.Security;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 
 import javax.crypto.KeyAgreement;
 
-import junit.framework.Assert;
 import junit.framework.TestCase;
+import org.bouncycastle.pqc.jcajce.interfaces.NHKey;
 import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
 import org.bouncycastle.util.Arrays;
 
+/**
+ * Test cases for the use of NewHope (NH) with the BCPQC provider.
+ */
 public class NewHopeTest
     extends TestCase
 {
@@ -53,7 +63,61 @@
             // and recipient's public key.
             aliceAgree.doPhase(bobSend, true);
 
-            Assert.assertTrue("value mismatch", Arrays.areEqual(aliceAgree.generateSecret(), bobAgree.generateSecret()));
+            assertTrue("value mismatch", Arrays.areEqual(aliceAgree.generateSecret(), bobAgree.generateSecret()));
         }
     }
+
+    public void testPrivateKeyRecovery()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("NH", "BCPQC");
+
+        kpg.initialize(1024, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory kFact = KeyFactory.getInstance("NH", "BCPQC");
+
+        NHKey privKey = (NHKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(privKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        NHKey privKey2 = (NHKey)oIn.readObject();
+
+        assertEquals(privKey, privKey2);
+    }
+
+    public void testPublicKeyRecovery()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("NH", "BCPQC");
+
+        kpg.initialize(1024, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory kFact = KeyFactory.getInstance("NH", "BCPQC");
+
+        NHKey pubKey = (NHKey)kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(pubKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        NHKey pubKey2 = (NHKey)oIn.readObject();
+
+        assertEquals(pubKey, pubKey2);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLASecureRandomFactory.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLASecureRandomFactory.java
new file mode 100644
index 0000000..738c848
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLASecureRandomFactory.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.pqc.jcajce.provider.test;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.util.test.FixedSecureRandom;
+
+/**
+ * Factory for producing FixedSecureRandom objects for use with testsing
+ */
+class QTESLASecureRandomFactory
+{
+    private byte[] seed;
+    private byte[] personalization;
+    private byte[] key;
+    private byte[] v;
+    int reseed_counuter = 1;
+
+
+    /**
+     * Return a seeded FixedSecureRandom representing the result of processing a
+     * qTESLA test seed with the qTESLA RandomNumberGenerator.
+     *
+     * @param seed original qTESLA seed
+     * @param strength bit-strength of the RNG required.
+     * @return a FixedSecureRandom containing the correct amount of seed material for use with Java.
+     */
+    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)
+    {
+        QTESLASecureRandomFactory teslaRNG = new QTESLASecureRandomFactory(seed, personalization);
+        teslaRNG.init(strength);
+        byte[] burn = new byte[discard];
+        teslaRNG.nextBytes(burn);
+        if (discard != size)
+        {
+            burn = new byte[size];
+        }
+        teslaRNG.nextBytes(burn);
+        return new FixedSecureRandom(burn);
+    }
+
+
+    private QTESLASecureRandomFactory(byte[] seed, byte[] personalization)
+    {
+        this.seed = seed;
+        this.personalization = personalization;
+    }
+
+
+    private void init(int strength)
+    {
+        randombytes_init(seed, personalization, strength);
+        reseed_counuter = 1;
+    }
+
+    private 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)
+            {
+                System.arraycopy(block, 0, x, i, block.length);
+                i += 16;
+                xlen -= 16;
+            }
+            else
+            {
+                System.arraycopy(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
+        {
+            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
+
+            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
+
+            cipher.doFinal(ctr, 0, ctr.length, buffer, startPosition);
+        }
+        catch (Throwable ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+
+
+    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];
+            }
+        }
+
+        System.arraycopy(tmp, 0, key, 0, key.length);
+        System.arraycopy(tmp, 32, v, 0, v.length);
+
+
+    }
+
+
+    private void randombytes_init(byte[] entropyInput, byte[] personalization, int strength)
+    {
+        byte[] seedMaterial = new byte[48];
+
+        System.arraycopy(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;
+
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLATest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLATest.java
new file mode 100644
index 0000000..453df94
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/QTESLATest.java
@@ -0,0 +1,300 @@
+package org.bouncycastle.pqc.jcajce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+
+public class QTESLATest
+    extends TestCase
+{
+    static SecureRandom secureRandom = new SecureRandom();
+
+    public void setUp()
+    {
+        if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null)
+        {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+    }
+    
+    private void doTestSig(KeyPair kp)
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        Signature signer = Signature.getInstance(kp.getPublic().getAlgorithm(), "BCPQC");
+
+        signer.initSign(kp.getPrivate(), new FixedSecureRandom(seed));
+
+        signer.update(msg);
+
+        byte[] sig = signer.sign();
+
+        signer = Signature.getInstance("qTESLA", "BCPQC");
+
+        signer.initVerify(kp.getPublic());
+
+        signer.update(msg);
+
+        assertTrue(signer.verify(sig));
+    }
+
+    private void doTestKey(KeyPair kp)
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("qTESLA", "BCPQC");
+
+        PublicKey pubKey = keyFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+        assertEquals(kp.getPrivate(), privKey);
+        assertEquals(kp.getPublic().hashCode(), pubKey.hashCode());
+        assertEquals(kp.getPrivate().hashCode(), privKey.hashCode());
+        assertEquals(kp.getPublic(), serialiseDeserialise(kp.getPublic()));
+        assertEquals(kp.getPrivate(), serialiseDeserialise(kp.getPrivate()));
+    }
+
+    private Object serialiseDeserialise(Object o)
+        throws Exception
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(o);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        return oIn.readObject();
+    }
+
+    public void testGenerateKeyPairSigningVerifyingI()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpGen.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.HEURISTIC_I), secureRandom);
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        assertEquals(BCObjectIdentifiers.qTESLA_I, SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()).getAlgorithm().getAlgorithm());
+
+        doTestSig(kp);
+        doTestKey(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingIIISize()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpGen.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.HEURISTIC_III_SIZE), secureRandom);
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        assertEquals(BCObjectIdentifiers.qTESLA_III_size, SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()).getAlgorithm().getAlgorithm());
+
+        doTestSig(kp);
+        doTestKey(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingIIISpeed()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpGen.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.HEURISTIC_III_SPEED), secureRandom);
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        assertEquals(BCObjectIdentifiers.qTESLA_III_speed, SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()).getAlgorithm().getAlgorithm());
+
+        doTestSig(kp);
+        doTestKey(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingPI()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpGen.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.PROVABLY_SECURE_I), secureRandom);
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        assertEquals(BCObjectIdentifiers.qTESLA_p_I, SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()).getAlgorithm().getAlgorithm());
+
+        doTestSig(kp);
+        doTestKey(kp);
+    }
+
+    public void testGenerateKeyPairSigningVerifyingPIII()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpGen.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.PROVABLY_SECURE_III), secureRandom);
+
+        KeyPair kp = kpGen.generateKeyPair();
+
+        assertEquals(BCObjectIdentifiers.qTESLA_p_III, SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()).getAlgorithm().getAlgorithm());
+
+        doTestSig(kp);
+        doTestKey(kp);
+    }
+
+    private void doTestKAT(ASN1ObjectIdentifier alg, byte[] pubKey, byte[] privKey, byte[] seed, byte[] msg, byte[] expected)
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("qTESLA", "BCPQC");
+
+        PublicKey qPub  = keyFact.generatePublic(new X509EncodedKeySpec(new SubjectPublicKeyInfo(new AlgorithmIdentifier(alg), pubKey).getEncoded()));
+        PrivateKey qPriv = keyFact.generatePrivate(new PKCS8EncodedKeySpec(new PrivateKeyInfo(new AlgorithmIdentifier(alg), new DEROctetString(privKey)).getEncoded()));
+
+        Signature signer = Signature.getInstance("qTESLA", "BCPQC");
+
+        signer.initSign(qPriv, QTESLASecureRandomFactory.getFixed(seed,256));
+
+        signer.update(msg);
+
+        byte[] sig = signer.sign();
+
+        assertTrue(Arrays.areEqual(expected, Arrays.concatenate(sig, msg)));
+
+        signer = Signature.getInstance("qTESLA", "BCPQC");
+
+        signer.initVerify(qPub);
+
+        signer.update(msg);
+
+        assertTrue(signer.verify(sig));
+    }
+
+    /**
+     * # qTesla-I
+     */
+    public void testCatIVector0()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+
+        byte[] publicKey = Hex.decode("D2F21F7B398701A369A10AB5CA5752324D01D2E4E85D7ADE9C21F2F41CAFDF25B15F173504C315EA250E1EFC243557E90C23509E7D2AE486518448EF18BA837DDE8DD6A30271E793D50DC7999485AE30A649636257E71DE6D65C9803A8FBB384181C6C28604C2B201C938A5198C01704B0F56ACEFD0CF58BC98F81E20CD233C6B5523C8A00F5DB1C934D2B4FB3609C0CB37543A42C4145CAA283C269B49C8EF323CE941F5FCEDB172CA9B5DD116DD6B2B6284DE55C2CC033426C84BD91D74837C6140E12D0C6B05765FD269BD23200FBE110D61856F8C5CE55FDF7269D6BE7D9FC213C885D74F2311A46BD5E32C06308C7C2E7F26BDDBDD10DD1F6DA004B8D28990D9A62276C4FFA90BF6D734DEAF116000CE004EAA0BB25640F9342E9C2FD40774DE360FA1560D478985BE9639F1E5BDD5B3000B364E12AEF3AAFECA99E4DD24530BB223663F095E3254E42A9E0D88639A946388331A4123CCB71A4D8925DF3EF6B6EADD257788B92E8C5E6596AEB77E0FBB86BCD03D0EE68D9B5C08E0E4B67273CCAA8544871276C9375AA0D0CE5681EBA576EF02A617879E2C27E137C8EC106B544D727CEC5B93F67CD4CB2016640B8FCE631243EB08FAD4F1237F3039376061B1AC3FE724C939CD00000EFC693C6D94828428E4CF1103B6BA8139724322ACC8832119F16528056E7D8CCCCFFE683E91AD84BBDC4D492E0215C45B5B75D1C01114DF952C731DB41D3673D80C21E901D06E898E4E6CC2CF3686ADB6760DBD0076ACD31F610C16B291EA1FB595B912ECC5F3E57CDE588CC14046D1D5AF625B0D52D61075082EF052F5F00B66C429E2B2977665E8719F4E4BE4D0EDFF5E6A793DEE09AC0589A6FECF7732684226E03A9520A472DE129A7EE74956FBA661610804F22C235BF5EA9B83D05C634215473309AC5E0BBCC30929AF45F6669340216DD0DCA2E04FA1697546EC1C93BE569068ADC39790435DB82DB3F3C21F16015CF17D1BAB83DACE254A37BC6E4871ED6031B9BA44EF7A71C1E5A709255A9CD226C0DE6733F1615403820C4B3DB50AAB082C04D0D922550DF4A940C9F20710D7B8208E0E97648F02B330EF436F436E011338523718D9DCFA2C6459DB1920A38E978D57D67D8D163313093C58E5A55F96D0DD7A170F3F255CEC4CCAAFA5044D1B21B2B5D44EA76CFC8F5B1A03A99D5DFD04A0ACE897CF480A008819FDBB7216313829CD14A9A1C9596F95ABB48D6F88AFB2B852F2E2CE687B0623D52A81001589EC05D7F6E582B79F2D036030BC6D1573618A83860A8A77C144D7B2DB988F84B16AF61E0931C27478C99B9A1C15801D0A01464DA0611318334238B2745653F14690E37699BAF5A7576FE451F5EAD46DEF2694711E018E37B24EDA8EBB9364553C5EB976DF38D9E4D21C2D174153ACBB0E2644C9C1EC56C3DB51514AED57AE8653C8361262CC21EFD6CF410160F8070753C59439465E62EAF8A34C17E8853C9817F327E273C2D2911C77C1BF9090D30C4243A39F9865545A83D089C5CB23880450E18A4B0036531164B004072E2EDCDF1A015323256BADBE8C1548F2A4CC00241B18825187015D322CE160BA48C4FAF070969A6F6CA9495E6A1959F0923394E7E5E4820B7C6358C61F4471128C67ACA4900728452BD64F6598DB9A421C8D4D33CA8C077C468DDF8FE8F387E950ED979EC2A1AF8FC473529EA069C21A9F26AA5811D343B0E3373183DB3CA9F10E63BA3FC7F81F4999BD0FBD0CA5D6C5546E9B7ABE2AC4D7A5C0FDC8C33A257A8F09281DE4C38D1273B5B37C88F8611CACD58C4E7F9BB916169104CCFA6FB7087FDA4150D5B84F4837C0DB5C6DB321FC7DC79FD90F707456330B37E0109F0ADE25433D3637112E2D96901E22C734407BD988AD203CA805AE8F757EEA09F327DD49710B471CFC197724948A011E597F3A4564CE00FF701B9B240347F227F2BE01582E07680AC993699D2E1536A155B96AE4E461E3D019F0350CBC52EC12186069382FDBD19CE7D70734FD72F8E61361D6BF9EEDEFEA6D44B6B50E1612ADA4E42033329B098318DD9CF695A2921A332204044994F244C0944993B08009265B8004398CF119F95FCC217D38228F1D1F14BCFC5B7160986C339");
+        byte[] secretKey = Hex.decode("2074F000FEEE3B804206EEEB4FFE07FBEF2FC006FB839F4101076400BFFA09AC6F00FE2228C080FE0944CF7FFEE16BD0FEF80D20A0BFF912102080FBEDF7BF0000FE5F9FBD05CCBF2F3FFFDEAB3F00001F48E000032A648F8104F5EBBFBFFFF92FB0400DF78380C1FAF53FD07FFEDC27C03D00D3FB5F8002F153C080030FD88FFEFA139C5F410CFF5FF000081F38D07D01E137A08002120050FF08D4EF1F42010BBC6FFFFE0EE4AF4103D23F0042FE061C60C2F8F5BF8FFDF9111C604200270010BDF8025CB04107F387C07F06EC0770C201FC53003FF60B5070C1011BE0BF800518485000F9F8FF9F81061E6410BCFCE42F60BFFAF4DF5F80FA020450C0EEB0DBFFBDF9DFDBFF8100F42F00FF091068A04101F2DFCF41F90B28B07FF10FCC2F80FFE73BD03FF9F4F35F3F07FF6BC0C2031AECCF7D04F467E03FF2EDDBDFFF0C02804F3F0608D02F3F060C28407DFD2F54AFFFF905D02FBEFCFB8340FE040C885F8103EA1FA001F8F2F36FBCFF1FDC2F42012D1020C0FEF8DBAFC1061E2CB00103F7CF20BE032E5810420417AC1FBEF3F537F0FC05F807603E02FDFFBF7DF2EA13A07FFDF84F40FE070908A0C1FBE523A0810A04703080FEFEDFFF3D01FB4730020A033440BE0AF86F8F42F6E99BA0C1FFF5BF1001F9F15FC0FEFE06E4AF81FDFA4F90FEFC27B02FBFF702DCEF00F7FB83807F04F0CB3F80FDE09F8F3F01E553A0C006016CA0FFF438983F3E05F5D7DF3FF71A10F03E030BBC8F7FFB19C84F81FCFF03D0BEF9EEDBF0BE02E9C71F3E05201830FF03EC17C0800028741F0000D97B60C004F34F60FE0206788042FD04CCDF00081A8CAF800401708F7F10EE0B70C1F411D0AF00011CDCEF3EFA1654C081FCDC7F907FFB0104508208FDAFBFC0F815B0307FFED417B0C0FFF0471081FFD8ABFF8205134C90FD03EC4FA07FFF1434F08003FE9B507F01DD932F3FF6DC833F81FAF6EBCFC1FC0274F07C0201F4DF3EFB203850FEFF0BE4EF4008F71B20FF04E3FBDF7E030A608FC0FF1904E0BFF5FC0B50BFFF18AC3F3FFA08C8BFFF04EED31F41FD0B78CFBE030B38507F0302DC4FBE00E85B300000ECD78F410105C02FFFFAF393CF8102EF278040F20A288080FF0FD03F7FFCFCF78F8009104070FEFDDF2F80FFFD1DE4CFC0FEEE57D03EFA06E48F400103E89F010228DCFF7F07FC9F3F41FEF4235040F309A4E03F041D980FC1FFF85BD07E00C88BDF0008F5EB7FFEF7DFDB9FBFF7F43B307EF60B688042052B982F83040AE89FBEF2E51BF0C00621CC0FC40817BCF0FD00F3D3EF00FBE75B2001F8170C3041FBFEBFBFFEFDE487C0FEFD318C3FC0FEEE6B8F80FAF4834000F8FDF37FC0051A0C00C200FCB78F0000F2DB1F42F812649F3FF9266CD0C1FC0B8CB0C2F8E5B7CFC0F6062440FE0304F8EF4009145CE03FF5F73350BFF80C9480BEFAEAFB2F02FEF9CF6040FC0E6C403F09016040BE0109A0403F01FFDFCFFE072C48CF00F8FFBFCFBFF80AA04F0103E6A3FF40F706ACEFC0032AE09FBF0C0948CF3E012914303EFD1518904106C917907EF9F56BC000FF1650D0C0FF008CE07F050F28913D0126B44F4205FAC3AFC0F40134D07D0CE93BB03E04EC735FC202035C1F40FE0810A08103E8DF0FBDFD22D8AFFE080A5050C10007186000FE06DC5FBDFA0DC8EF020300EC6F3F071248B080FEF9D71F800B0D3C208008F9975F41F902BCAF01FDF06F9000FC17A0B0C1FA17A42F41F8FABBB03D090108707FFCF5BF2FC107ECB390FE05E25FD03DFF11706040FDEFE34F80F62F30E0FF060E3060FFFC993B08009265B8004398CF119F95FCC217D38228F1D1F14BCFC5B7160986C339F23EB15423271EF1CF476289657DBBB1460665D3944B78BEE92D15AA609768F9");
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] sm = Hex.decode("AF8718AB747CEFA75F4A5F26B5392113BDE8CAF1DDDF1D45B473CB64DFFC255B2E211588DAB5D5D2C5407EBBB684E7D8F0C6AB8EB3AB874CEEB87C9CE32568D0ADA6A3B9716EF9AB92BB9F02424D91DD5C3768E4598D2DA954D0664EA3D61070FA4304983B44D6098FB74B55FE40F156F07D0E39F11DBDE5E0549854ACBD65B2F6EFDBF3CEFB27DB44FA18EAF9BDA9F4409FE020B8FB0C7E9A1C17F5A1D59D6996FC1EC43AE246AAE1A8FC777EF5912EEC95A6F9A2B32A91C2EEBD733E0EAA43C3A33E989D1DEF0B1F412C6299628DF5AEEB599391C411942873FDCE0FA701C58FD2FB383A7FEF455AE12F415496BB0F6E75441BB0F670910F207CFC372B823A099EA84105E1B316CA33587FE990017BA0C52C1EA85C9E0F030B63168B093EE83D81C47EE70D5EEE3F5603E00C3FD306DF8D58C594B4E2047397FAE4BB83FCAC0D3A8BF7896770017860E9612BC4FE5E416AD961C277836C89E29B22C9C6A1F2F8A55FD84D57F4C3550EC14E9B30BFD18A3E479217DB48B65B9E3ACF731C08F8655D2D2F689EB6547C5241887CF017FF9F0EFC49D3448D623353C6846F2C547090873903DC281D2D327C1DD8B49BF87CDF28DDAB0BE1D48B8874D8693DACFEE07E51D5137B57F58D1EDFA374C5FC2A9C42FCA63393C8F24160BF54110D8721BADFA288F304E345FBDF179AC43FE45DFE55D98683A8B7E0A7B48BA4193B01294D6666688EA4099E842F78402874FB1CE5A52FB14B6C6BA3343941E0012556C1BC8184DB46B1E49161E5504815ADB45987E7EABC1C5657D5D8F23847B728F5AD51290995BB742EFB99528147D423FB56EE1118E4C170523E4CE2734219902FB54457BDC111F54497002C0D06D4EB11FED96683DD3F5E1A938F4D141DB638BEC5C8A5BBD5E7F4BDCFB8D03C4D003BC0DEA33119015FC6D2468C0A5635049A04055B542463B4AC17FF4AEB339E674A1E5EEE7C0FACB7E6CE7D2B990C4E5EAA29030F5CB76BD83CB59D215875439F68BD8731A4D5F54B1D27B905AC42D381AC8A1C6BF1F785CCAC2CCA0D08411A2845A920DBC51C82105205F1772DBB0914030BF06115CAC3596E0BE6F97ABBEEB6FC10A84819C84507348F2C887366A82B6FA5DB6B34111066DA4A41A709AA7EE8622A3B3AD5C854381440D08C49BBAA025E7F1D459A2606368285B12E50330DA0FDFB9B50D34CC3664B9F13A4B9E0E65F7F5390A4D8D25353CA2DDC5BFF4A1238321AFE33BBA3318D5984AE57799ACBAB7B5A0C52120CA6014B89B3BEE993183703BC717871A1D080E57ED5E4FB5C64AA4620B5F78DD4144C1005ED3D4D2F1E94FF01DBDF0107B52D5FD3B212A72AAE32BD32846E0B31AEB2064A30361E8F58A82937AAE4C3E74A61C9953406B946809B11379411C910A9E9B7E2384D0E9C09A78763CB2DFF356941B2D863C35FCF0D5735B478D62D1D90DC21E833C871A77E533EACDB08081105BA3B65E8953EFECB9C2096DEEEEDACAD630133A6D35AAADF07019EEBE7D1C9EA1CD25F55454D861C1A4A17BDE58FAEE5696FA4DD496A4C0AE443524BB33B98D8F254ACB9CCB852CA4561BB4E3830A24E5EDB9233A20B37F42B323DFA6DC1A891F0C306A121EA94AD8071D107C4B4CDB294FD524014339CBB821F60CE298EABABFD3D64F6F5237DBC14BA7867740F047325691DEC988E401E8CF1427CD040AFA8A33B8BFFC2AC080ECFD7BD4F22646CC7013210B80A99B1F7D6031FE4F50768531C604E7AE9349E87A0006566B61EFE18DF34C385B1B5E8D35946D8E53A75A7412DEC7E8A28A06E8DA2BA3B860AF01455359C01EEDBFB96D6881E5EEAEA7E1B2663A18410218B1588DE897E8E01DFCD72D08757FE1CAEAEDF71F7303CB91E91EE7ED7C7BC3BA6F72A3C8382FC3ED355C6F56CDC8630B2C3A06EA9DF197823448E70C01F349FB0E6D69958AF7347D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_I, publicKey, secretKey, seed, msg, sm);
+    }
+
+    /**
+     * # qTesla-I
+     */
+    public void testCatIVector1()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("64335BF29E5DE62842C941766BA129B0643B5E7121CA26CFC190EC7DC3543830557FDD5C03CF123A456D48EFEA43C868");
+
+        byte[] publicKey = Hex.decode("6B8495B1C393966CE20EE722515892480FDDA8F7848245573308D4F1C3C93C4F31B262002E32BCBFBCE58F58C95322F9158CB10F30ED2DE4E1F202DDE2C4F68143F354A52F2C27B12B8E434E14EABC9C61B421708B809CDB3908B75106A11B09AC4EFC19662DDC818D4D2A50827D6197B8405C3C9D1AED8A513D3BED08CC60BD04B028D50CD55D9ECD3640EF81047F505B3705AFBDE294DF42EE9E488D647E9C7D532724ECF6501B18680F4376EEE1E0ED1CB4DAF5861F19AFFCA2092C8A6845224008B437F352695B7827E55C7F0BE5A49B464B4654280FE67A137F69924F8529FD947C5B1E9953BC86808243FECE6E03682FE46396D5B174148059703694A74D9DD399B64714EFD666874AE771E1C8A7C2E73D03EBB82731DE05324BFDC8356D5809A40835393B78CF577778B6DA061B2E640A083E5748A6D88CA3A1B106BE566B86C3293886CAF38BA93E16267BE96A0E91E08FD8683406779C1528298FC98101B1ABE625200B1296C50170388B5A5432A020E2D6DD7B40524B52FB960B8FF3FDDE3526842D355AB860B78F9A4AAE588F548F7EA05F94ED711646D82D4BBF8A22244FD9C466DD71E4C314B98235D819321052039F5996C216DA9A1B0BC897A1DA04F2DB48944CAA0C0F5BDCDC10498FD1E3B6A76EC8158ADB9280F7C0F43E1E835C178B114C939F94DE20974301D9AFB3D5D4CD9D3C24152F891E8019BBDF5B74E7212B85E5C6C90614219F54E674334D1D3A57EE5889F2E6C334D2475FF899FC50DA49C0B106F05810868F850A95411E6B616F56397221E45A363A2A4066AFB5A56CCFCFC3419F99B5CF87C977B6AD926504062CCEAF1EFB578EE1A2EC6CD6D1244EB20604E94D37C283238105B0F4834FB2946B8523F308D0020B1C8557403EF85235CF9F547842D6D94A8DD380BB06732EFF51FD315CF111BE138A7D118643B5A8921FE03EF309169539D7779CBA09C6079BCBBDCF84C383939E55C9F042FE5130E4E31EA33E2412A9F3B308F781E8353F7261DE83B20A5DD66210F05B71B49BFB499D60D8C67E2CC72B67D25E6B550BAF745F0E11F29A185406752B0109EEE7CB503B6F785DBEBA42270E9AAE1991FA41B5758A9D0CD74C6823EF23D4D62ECE67370E0732C43A1D54DC61F54CF507C251758E6C78D6DAA5120E202CAFEBD58A25A933B4B48E1A78ECF36104467A7167231918E93AC356C4AE2FCDF284A3B0E9F19038F8985204BC35839804484D3D4194E1E72A7B22FBBFE03F00072A3C05689FBC6E11C71DE2748826A40261A6357904B76ABA722AB91DE9B3C9FC66034B2C06AE45625E8071552A80A807F20A3ABD1D094512C64B57212941D3190638CE3FE69E5CC8759FEBEDDFA46FCE02B822DD32F1751BC41F4EDCDA63BE023154FE075275A28548450494D9743AC52DAB26971C78371AAA3D87B8F000E95FD79EA6F17B5F614FD0626C236F87072C56D8F7F7EEC167323F79A26E32048BF7C43C00904697F1E4C93D018D7F0845EACC01D3989D3502E2891B776B11CB349AF9C462E83AB1039B30688E153D7C1AA62C46B8889E404936912BCCA2517C059B89EFFC1FE1488C2EE3473C27805715ADE619BD3645D540C84D187441A46C0D904C3C9E2A92A92FCDD6D86380395A934DB93979AEA13C428EAFD9259CB3E2478DB643D0D6913AC275ACD2387808669D2FC40FC6011A87AA1F03639D02692BB18322D6417808A902A403068A7A45DC62A5ADBEC18458AD4F91BEBF7FF60494AA7C0A02AA6E067A86430CAAA3536056EE44944CBA38B7AE9016D66621703A77BCD36BD104BC2A040A12577DBA2FBF4B89AE4F49D202C27681B05BAA8C4026E0A60D44140A2213CAF78BEE9047A10569ABE5C568FBEBEA276DFDCF905F8398432129A82AF45CBD10BE668D0C60F4B64327388B830B1EDFE4ADB67EC5472541AD29C16B732A5F2F6B8EB1DA671B6F1ACC4AB510694F2AAE8055127734591BCEDC1077B757C036ECA8E4A114A649EA4730EAC4309B07DF98A5D8EA838C84675A7D10EC93E1D36704B23BD04E27F635AC6DA31959BEA5A10CA37B7193664419926D965E7F1916EC44B52B4BA993C5FBE705800B2C070B09CF36E0908FB89F71E2F7AD9448");
+        byte[] secretKey = Hex.decode("E2FB4F00FFFF37F07F1016B87FFE00DAD38F3D08E9BFBF7F040634B0C2FF340490FF0205F87F8106F3075080FE0F105080FEE9679F0006FA4720C110F4EBFF7FFD13840080FFE60B80BC04003C307F00F827807F05EFD77F800217CC5FFDFF0AC0B0BE0500701F80F4D48B9FFEFF184C70FF00E9DF7F4110180810FEFFE1F31F3E03F79B3F3E00FAEBBF7E0508D0AF80F52BF0807EFFEC3F203EF61CC0F07E04EEE30FC1FF02E42FBF00E65300000A2674E03FFD1460607F0714F08F3E050E9CF07E03FB7B7F7EFBF4B76F3FFCFF6B303DFB0D981F0202E56F60C000E37BB08106FFFB1F80020808F0BFF6CD43D081032354A040F8CE63C081FC070820800401E8CF7DFA106480C000F5DB507DFD0C7400400515D81FC1F8E32B10BE02F06B9F3FFDE947908002F41F50FF07E44FD0FDFDDE2F91C2F9DB23F0BCFD020850BF05E4DFFFFE00F5B76F020DED573043FAEFC72F80FF0B4000BE04C8A77082FC22A45040F9E7570040FBF713D04000160050C0040E884F81FEDF9F0081FB12D02FFF0211980FBFFD1768F080F8E313803EFEEA6B2002FEF9B3BFBFF10B7820C001EA0FE00001F5D37FC1FA04A0EFFEFC3CE4EF7E02D9234080FC198070C100E417504203057860BF07F68360FFFFF82380C1FD1C90B0BF051328203F02F9F70F8107EC435080F9FDAB2F40FEF21B20C10302F02F4104F27BDFC0010258F0BFFB01986F7E02E7AFEF7F0418C4CF3FF4EE0F10C007CED36F4209FE5B10C0FB02F47FBEFEF867E07F00F9BB1F41F40540407D01ED6F0F41FEE48B8F01FFE43BC0820CEFA38040FB01AC2F40020F54E080F412D4CFBF0118A44F0305EE67A03F010B04A03F0819B80F8004F09350C1FC10544FBF00FA7F800107FC9BCFC00724F82FC10A2F74C0BFFB0AE05F8103C9D78F3F0601F02F7DFEF66F107F00E85F70FFFB1678CFC1FAFC338F7FF701E86FC1FFEA27C081FAE81FD03FFCEF87C07E080BE4AF81FDEE63208002F41BF00201F83BC0FFFA0BE4CFBE0416E8BF4004109C408008F5A72F7E00DE835FC0FEDE93B03CFFC7E7AF81FFF03B500101EC5FA03F01E0F33F3E0600B44FBF00F01F200001211C40FC010D8C200005F7DB9F7F09FBA3FFC002070C7080F80388AFBFFEF297C0BE0AF92F507EFBE3F79FC0F7071C5040FE00E0DF02FA1FB85FC0FE0F504001FE15846F8003036830C0F7DE6FB0FEF3FFAB4F40060FDC7F010DE80B7081F70D741080F813CCAF7FFEFFCF3F81FCEFEF2FBFFE07E0DF7CFE017C30C1F9DF673FFE02F11F900004203C7000F9E04B40C205F107B0BE0225EC9F41F8F9D77FBFFDFE13D0FEFEF5CFDF4000F433C0FEFDE3CB9FC2FBED4740FFF608E80F01FF022C8040FAEF13A07D02F4DFBF800331548FFEF9FD03903F0312B46FBFFE10C0EF010224F0BF7EF90834D0FFF620705000F4F55F10420021DC6F41000D2C5080FFF11BB07E021CA0CF42FE2FF08F8012FEB3807E07CE571080FFEF9F5F3EFEE30B80C00512CC7F41FAF96BE000FBFB13B07EFF0BA0DF8202E00B9041FDED4F30C001F2CB60BE01EC0B20800102E07FC2FE1710D0BF0B231C903F03FF1FE03F0533F0DFBFF807EC2F000917445040001A58EF0001FFF36F40FFE627B0BC030E3C907EF5F2AF4FFF08F2D72F40020E683F7DFD049C5F80FC13F42F7C0006949080013118207DF6FCE36FC0FD0D348FC002E4B7F040F9076490BFF7E56FB0BE0903B04FC1FEF623208408F06F303FFC128C4F7E01F967603E14F447908009D7E70F830BF1F30F7F07F9B31F42FDF57FF04004E3D3BF81FB1916EC44B52B4BA993C5FBE705800B2C070B09CF36E0908FB89F71E2F7AD944898B305452F9EFEA0237F6BACBE4022FC80E5DE2D66D398814A7C835419435744");
+        byte[] msg = Hex.decode("225D5CE2CEAC61930A07503FB59F7C2F936A3E075481DA3CA299A80F8C5DF9223A073E7B90E02EBF98CA2227EBA38C1AB2568209E46DBA961869C6F83983B17DCD49");
+        byte[] sm = Hex.decode("553082050F6A8BD54C40E296C67011C2765199D81304F22C3CE306AE9C05235FC8EE08D851E2397C2956795D02F1AF944E54E2D270ED89FD6F89FE9BEC269B5CBE7F0184B244F89455876E181F39E1221F962B34FAE1A8F6E4EEFBE882DCD5BE4941EAC3ADAE263EBFE5AA9E0BCE6D2AEF44A12710B4301F07545EE129974B27536C2DD6C38FE9D5F7B725DA66C8B7A92F439C6721AD2CFC389176BC6A39FF44483C06B9D1A554164F5FCEAB5BF317983E8525ABF7EF291DE0FA281791680F61012A06742836CC6E0CAA87BDEC0C7D451801979D3A993068DE6F601DA81615FA56CB0A4B315CA2CE06F21925D088DE4DF0CAAC3DD33E51FDBE8C0DD187BAA82216009DD2819D524BCFF70012A734FE9D936A413AED15B3D652258CF6C3B0FD5BF4F64B7EC9BDBC9251C01A34AFBA707B35BA4EB6C392DFD6F93EEA454DCD2BDFDD689B5A980C21EEF124843F5571E31A69AE097A25B3CB1F04ABE269E78E098B658201B8BECC456147F759B14C3A3CBC00D1D7E3344F05E433C1C82DBD4001C6AFA8D948E92D1686C64F57436F6DD9ADFA82F35CB3814DDAD7AD0912B62B6B412A40D70F49E05B305BB5DD319C5C89775F27D3DD1884B5936A54738F816AC81FE328AB344FB39CDA6EC0B64D47556733815D2D83818D88D65094933E6E9A9FEB7E433D3EFE0B4C98D7ACCB9EA851AC72134283116513EDE0802BF959ECD5A8AFA9407D8EBBBD8ECB48B24CD034C3FA763E83782D6B0B2115929F94645F9378411251692C8858BE725B9D8AB880054956E71A75D5DD3A88D1476C36064A3C62EFD861C4A25943037ABFCA2DA9363F34371E868ED76D48E068C3A078757FD8E8588B356530F098ED81E3C21652E1071A6DE3F8610B94BEF7EFFA5C5A955EA3987B0EEFD16E6996BBDB563BFAE6C769AAF95E4CC4E42634A0CA641297642C92A57AA8A704E35CD8A759366E1DFD2006C173D31BC853A1089284F043E1648DB047E944EE26AED13A07DFE7C6893998A43CDBF9520A9E077DE27B36F03FFF08FCF4C4F6F4076C54EE6CD7F93C7C1297A4917B3E430981B312C2E1ACD923A419175C735F80F3957418BBACFF5F09425F047564E2F9FE4A9445FDEA63657FD25D2177A9C5A2BA11E54B3D0C06B2323735622AA23A69C14B4AA78BC4310481067E68C0225CBAC0C40A21E8676B5D2E82DE06BB5BC95A3A4269747E9BD3895C0C29A4B25BF85AE0C46959B6AC8BCEBF6CC3039FA0AD7E4698CDCB73C44A10A3469520C5967C2B491777711DFF1F311FF16639A5C35F10FF7DE14EAB5A786E7CCF152BC45600CB3565BF69477EE7BFB508F3259C75EC87B7F57561ECBBC8178497F6B77D5C8D6C2D3DCADBD7B2C21B59333FAAED20C5152E35886B1C672F6AC4BE7FB0DC6D9F4478428141FD35E30691677A1E6B866CB91BB9962D4BD5462275D5932820FD104BF0E1D4E08F0CC2B7B1FF36D2A694B848F1B84475FB647A828CEE4DB1566CA35A908562DCD45AFD107453E4FD18B571B2F64B077DB9AE3BFA15F0C10BAEC410805EDBD34B6638138651F24F91F9D82D8E7CC07F1F5AD93B1017A375A4A9EACC85A96403442BF1D5D9BF18204011E666569B6F280B268BBF339B607D7C33BB59EC7E12E7F20B14BE770547F797E990E70A8F4F1B738B4EFC7CE124B919537CCDEB5F0CA8DC2EA9649640EEEED11489C0A1C110DEB64BDA50C756A3EAF34C4E1059560D3788FDACBC2E352F2B93BAE31DE89C0D19277BB8AAAF0A419C2C4C1B265BBA1B41A9719AE53A7DB5F051E3B08836E5D3087A94315C8B83E21B1217E1276FB90252F56E72CF9C406A9539F782B4BCF95CE4E55340A00899CEDA7A0C020AFBBA1DEF8C394CE2354E86B7752DC57D1CC94ED32A27F12A06AF8C72AD2BB0A4D619C7F1569EA066B8231BA0CDFFA761F54F1A574C99E1B24C53F33F3D1F34225D5CE2CEAC61930A07503FB59F7C2F936A3E075481DA3CA299A80F8C5DF9223A073E7B90E02EBF98CA2227EBA38C1AB2568209E46DBA961869C6F83983B17DCD49");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_I, publicKey, secretKey, seed, msg, sm);
+    }
+
+    /**
+     * # qTesla-III-size
+     */
+    public void testCatIIISizeVector0()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("2773A7AD9D114C05E70C654102216989125420019CD31541E8A191B142C2732FB3CEC5C9C070A830C50A960E4B07C8108AAD0C044AEA0CCCA544422AD17966A52704BEFF1F2BC92423D79C128B891392824FD4EACDBD5DEF0CC2733BDF9D0D0835CF08640BB61DF4006340013144DC913AA00D411FBBEC59D5A3D20E1A3656319718F82F683978FE820C2AE48349FF42B63F44E71DA1678C2ADDD8447E9B30AA7D083A9FAB6CDE4B44AA9668F1CC4F58432D55340041792C06F3ACE7515DC1BDAE4DAE329B49FB3C4F086A0CAA846AF3C52721C1DDBD2A88093563A6F9FA11695CAC38C838024008B10AE0C76DE6272E0198209E133FAE50410192782130B838E56EC611E707140595C9B9620FB0E547C10A757EDE0FA29606028E8200740307E9ED8870D9199E0976E30F7C4233646209684B2AAC28E4B343C28008B4A4FFB8E02AF2D01E61E20F9C5E02886DA498AAFAB60A702AA4C2E050A4691FA6DF8ECE316AB75A61B55E5381606877488C630D33CE27382BC5EE368C5579F2620673E79789B1C79C405B97A426E09E49090BCEA3FE40E01513EEDD9081129C0A0A3B5884FCE5CB3FE7E6B37BD08FF1324ED390E507CCB3271150B1D02F9F15ADC205ED0068E141D50DC078D37E4E7E283510115811AE1205CDD1A109BB114771C0D4FA9E8129556F1F505CDE6E0507B73BB32B0AB3EB84AD99586E3E3326A0B1BAD6C7A8E0053D18368AA8BAAD7D702618EAF33A3B5F17E5EA410803E552E4262256682CD27005C054103B41EC37EEE354CAF7AE0F19A75EA470616F94174C72F95ABB4B9523CC4F291FC787A990E8EF43D2B8C1A76AAEC10C095F1BB0111E1A4046F6D131E307B960523D1884ACF914CBE40FD3130272510A13133123AB70356430620A3EBB2728172BC56C0F55064C7031D6A461693C80495930C25A04F61E223DFECF29526CF8BF261FCD8AF44CD13AF9E8AB227A579F35B0955AD56EAF7C012265B0A839D951B93C0E6A2108942B82C2658FCF12E165ECB57B74DABC2D30CA246F9B4FBADF654C011D00FA144170C9E9EBE1A935D620C43EBDDD8D1E60C29EFDA04CC17407514887F8198CCF58092CC75C241CB34AC2F9477DC0675E8CDB2D6934EC7B3EC25EBB370CDFB79072300575597754E5609BBB94788FFC5E12E713222BA5D6A22BED445E13E170AAD9ECA0D26E22CD0B808E1090EF44D9BE210E426765B9CB2D75954316160A44C3D936EEB21DAAB7AB1E60A4F651F3F5003061D80E0E17C0530D4F15474C2B861B72B3FBA058CCBD14568236D40E6B3C359FAE089388639703057DD09806C9108EE6843F02F03492C6B70A421B01DB32C10DF88AEBAA3CB45D4CC0604479AC1CCF0C9CD76250AC3345BB60BA081462B1667A1A5C772B505B5D705223E8AC30FF1C423783E1977E62277D196517636CDA83A80A87AA54087CF099B1494DB8F63C166E183CD3E8528557038D9D826DE01963B471E3337093139CEC888E6C5B1249826D966355BDE17CF989254C8C35036714852B24D89BB666EA8AB5AA68506125ACD8A51848478A2C14C94F189578C0ADC8D04AC0E896F6357DF3AC17172B06B5B25BC4A41CE3AFB266E17F7990327CC174001F71ADAF8F5EC08404A3A74B80E4974D20B631C11477BCCC1164AB8F49F7D6F57AA4A77F724435C218108DDBA508285D3E412A8778035FED2E516DA7BF990AEA03FD7E91F05537FB5A962A9259A0750DEC465687304925CFE5BD8F044A7A1EE638D2C448235D2BC2C9958C21DBE152B8D9865E336B57911AF86F404723CC995AE595F888FB675C47C2BC311F60BB3E47B4125CCCE51AD3A231A9792DB6788A54FC2935E22313A744065387AE450F0168BC00F60BECE76FA41E5383B0B45F950B27D327379865A73C6239B481FFB4AE78757CF51F5ED4CE20A4C31E89E19FDA2959158578439EC56CF6D29B0B2F8C307D63A13E8073E5AA7CD2C0FBE55EC4348AB807E773D726754F72EDD526B788ED61D031B3C2F64803AD34CDDDD8DB86C0078AD3CDC728B0399401A0C28E3D322FBD576FD97CCA4EAED9146CB3F83D0139024C14E249ED6996FC6182E739C72B177149EA08FC991CAA73DC8D6729649F395658B33D49B21D221D65E0BC90A0AD85E0F714D06E2CFEBE84F163843B7854F9E83A0409C07CBD22646C1A841FAA6FDA61FECC495CB2C881CAEF7EF4D8CF54B52241DB36B68093D40880822E808D40C2DE6D0488F476FC79064A0C8E03A05C02C6DB20F3EBA8E0067C154F2CBC7C4849CF87F7F1C92B4EA8BA70177FD1A31F691168317A3D4CCF9B9EDE02DB044645622313807E49BEE1D481136EB26803CFA5756E177EDBE1AAE6B0C8541B4FC48CF01014E54B2C789241DE9C54ECEC921AE7AF8A427213689A4A2CAB6376719C25A896B45CC414CF762B91A9840210095A46F3281A6DA086B254C916663B7941E4CE5281E33D8E3712226B201DAEFF4ACAB7C504C727F16D2F11C14885A2ED080638B153A40B3B12457A6A3AEF950BEFF282A079185C64FB1CACF19B8182900F2C4EBE28B7E370A3235D45C6F484FE3F75449E499F07F2F0A4FB1AD6508ED8CEDA34EC98A4957EAEBF25FB8F43D16F6DF3E32705A82979E221CC11E63B0C883D1EDABD4CD5A0855812477BCC726D68F0B211420C068D60BEB2D2CE9DA32EA68657432CB2014904BE4248FD08DA1F9C5E455B32A1A1C9AC31C7E7A1ABD8BD363A38AD78734D1EBED954871C274A5D54C1B492F01149BC90848E04D026118E9DDD0B84C1FF22473C08D267F1581D566C5F62EAD72C79D75F565BEC6C3275ABE3638D2D003CBA666B5AE85AA6E33D807F72AC157F64390320F3D4203D0FE2B70EECA8BC174D98AE7461444CB33688FA8BB82CB81F84DE6085BA9B63A9098A07E78DBFE5A20161366ED941116C790C67D7BE28680D3372C19E7BB17433903ED4688D63D4CE54DBC0BABBEA4916FA49AC71377A7E2812BE6512D504C04F51A6B01E625E806DECB5D3F6E19C726EDE8428DCE04677B2D8A7083A8C5A9304B06302F01973A169CD4A0D04473F769BA8B08E069F96EB07AFA0A5A061723B5C537BB6B41AAEB03F736F1B00210CDB816109D148692D49854DDA515DF53E9F737E594EFAA8033D576401CAF82ACD6CC7E8E913BCFF0CECF25D6C8BE37CA3B0D05EFB405EC144E4BA8B43814814B3488C6C7844924F77D06ED8E9D4090F758C13226577265B20898D40EF82D8410AC9C807D6BD0DB7124D4F23468736555B584BAD6864130027C825F8F15AD4A6550A5BD9AA04C69D38D44BDE53B5DF9EB202347876643C250278D786F8D8C4A8C9EC33FA57D8803909FB0991D370AD2FE3B6BE7E0798D4282C05B27410DA4B1718F05C82269C3717392580BF0391F20BA083057D3AA392E29D5C81CADA50069F812E6B8EFF77C59CEF51D27098AAA219BC59D4E549767435766344B754AA70FD43877CCBBDCAE4D0D6FE8F30C94B87C82ADC3BF62DFC6683D910B13D4ABC4B6B68C47AE51317F1F51B16918E7D9E851C653330B01838ED10601494C8BD86328405C18FD20A62F384129DC5AC3EFC58A915A182369F1A1F088608F64B314FE1BA543325B72986612765D3A474CC9A6CE04E1ACFA49BBEF23DD0B0E4E40C320578C985E8C29A653AE7046016E067887E85C9C3CDCC456CF811A256FDD8F1E9E8660466372EB85BE369D3B496991A7D99198794DDCA7B64B4BA91BFD7A3C258B170213639302B4EB45A8C9CE7C2A79FE3E3FCB0C23B057025CC207A6D02FABA9E299FD95525B4DA20250A1B54FA196210681961A04B9F6862C5F87586BB90027C06E9B769D15CB06A7867F7329731AD18DB714980DB021C8A93D32A20858AB61A741FE8B6CEB89E6DCA0960B06286AE297A5FF8B99A42660B3B802C330B91F686E6F314B62B2C83E8512D93C5E29D689AA1AB43401D0A866586BC937F66A3D0711C20C9AC36426AE20041358AE8C3E1E6AF31E4DF0157EE582210B6C32F346481B7D888D34DA15238C62E1B43FA4D5C1C67F6EC4F25A62B810418D3512B81CB107D9E88711E64C4F224894CF80D09997744FD7AE52446A7DA4E10051D597833AE1B36C86A0CB69B51C27FEE1B0A126FD88AE54BC04291183C0919C4912DD5D4E9EEC9C40B65D58DC33CB0289B0FB60D11B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD");
+        byte[] sk = Hex.decode("F60AF7FEF8F7FE08FBF50300F8030CFEFFF9F5F60103FF0FF601FE00FBFA0E0003FC030007FE0201F401FE0404FFFEF6F9FC0209F705F7090E0002FDF1FA02FB08FA010602FA00020B06F804FDFFFD0601F60400070100FD02FBFE000B02090308F9FF080005FC08060103030801F90B03FA060704F9FA0FFB050CFAF412FB0114EC070BF4FBFC03F204F606FD0400FBF400020806FBFC0CFFEF010CFFF9FEFCFC01120909030CFDF303FE0200FC0600FCF4FE0504130D010CFC0208FFFC00090A01FDFBF111050911FCFE010BEE0302FCFE07050A04F616FDF8040D03F7FB03FD080503FAF90003F70A00FD0AFBFBFDFA040EFCFAFDFFF100F507080109FFF7FFFD01FF0506FE04060B01F7F9FCFBF305FF08060604FC0DFA01FCEDF5FA020707EF06F8F00D0800080EF0F6FA04F8FC01FE0102FCFCFEFE000306FD030601FF0308040104000302FFF5FBF60AF7F0010B00FF0009FE090002F402FD02040F0100FA0606030205F71200FBF70909FD07FFFBF107F703FFF5F70FFB0D01F90301FEF30802FAFDFE010608F80BF603F8FB0E04F905FF0CF9F40EFB0B04FF06F7FD08FF04020103FB03060208FEFA0CFC0A09FC070BF5FBF903F3FEFF0312FE09F9FC040A0EF5FF03FFFBF8FD010E03F708FC08FDFCFB0C0AF805F90EFE07F5FDFCFEFE0502010801010601EEFDF307FBFAFDF903060B0C07F800FDFFFF04FBFBFD06FEF8090A050A05FE0A0602F102FC03FDF711F70401FDFB00F4030DFF0808FC070405FC05FA0AFF0EF200F6FCFFF8F20E0AFFF8F7FCFF06F708090FFAF6FB04010207F30402FF05F8F2F0F8FFFF060206FAFD040107FC0AF305110E0502060B11FA0500FC0BFCF7FCFF09FFF510F802FD00F4F807F5F8FC0EF9FF0AFBFFFEF6F902010BFCFCFC04EBF30703F7F30204FFF10A0BF80D01F7FC0B0701FDFAF300F60204F60EF41103FAFA06F7F7F80304FEEDF205F9F9FF08EF05F90DF6FF0307FDF206FD07FEF7010307FF0600FAF7140300FDF70410F601F80C01FB06FF0BFAFB0209FC07F506FD040A040C0DFAFCFF08F5FDFAFF03000503FA07FF0601F8F30A020201F5F8F9F4F704F60203FE0505FC0A0C02EF020204F7F5F60405F7FDF702050202FA0B06FEFD0FF7FFF201FE08F806FB020100F904F302F503FFFB00FEFBF6FB08F9FCFE0407F60609FE01FE020105FEFFFBFAFA07EEFE0E02FA01FAFFF7FDFF0403000207FEFBF804020008FAEFF6FC0E0C0604FE04060DFE0D13F5EB06FE000A05FF0E02F7EFF700050107F5FF03FE01F209FD0301FCF00204040203060208070AFCFC00F2F40BF6FAFFFF02F90701FD030405F50606070600FCF6F609F5F504FD0D0A08FBFD0207F3F601F503FEF404F7090403F90101100E0000F303050104F4F20C04FC01FAFB01FF08FA02F304F3040A0BFE0CFB08F80B09FEFC0105FC0B03FCF1FB0904FAF30204FFF2F9FD0B0201FDF602EAF1F9F708010000FA06090301FEFCFBFEF4FCF9FFF8FCFB03020A06FB00F5060704FF08FEFDFBFE11FEFB010202FCF300F90C0105FC0604090402F8060A0A03FA07FF0C0F0C04FDFBF7F7FF0402F8FD050B02F801FBF9090C02FBFBF1F30B0901FA03FB070300FA010403FD01F303FDFF0F0209F7F506FFFD01EBFE06FD100F000EFDFDFC03FBFD03F8090104060A0900F508FD090D0105E9050305FBF500F707F70503F90706F10801FEF805030B02FBF701F60007FF03FD03FEFD000808FEF4FE04FF0CFAF500FB13F600F5050BF50CFF01ED09F6030305071B070C040C0206090205F103020508F207FB09070A00FB110502FF06000811FFFD0B0D060401FFFDF702FEFA001209FE09FFFE00FE0BFA01040308F6FAFFF6070309F5F408FFFC02FCFEF7F80B03FEF8FFFE01FB04030304FDFF02F90AFC0A070407FD09FEF5F9F3040002F2FE0D03EDFF0305FE010B08FFFA00FAF7020106010D0A061913FD00FA05FBF808F404F9FDFDF0FD0403FA020200FEFEFB0004EF03FD0B0301FF010A02F0FEFF06F6F400FEFFFC06050603021005FD0603FCFC050A04F9F804F40E060100FCF3F3030BFFFFFF04FA0705FC0E04F401F9FFECF9050C07FAFDF7040EFE04F9F6F900FD18F7F5FEF6F4040307FE02060302ECED02FFF902FFFB06FAFE0BF90100020BF8060008FFFB01F5F3FD0501F60D0B04090204FB0508000CFA02000AFC06F8060200020606FB03F80A0D0800F501FF01F90DFD0203F4FCFDF8FBFAF0050107FD0101FD07FBFE00FC080807F600FD0801F20200000CFEF5100802010206FEF006F9F8070B0712F6FF0600070309E8FCF7FC0BF8FC00FD030F0307010807FD04FCFA08F8F6FE06F8F6FCFCFF06FD09F5FCFFFC04010F01FDFE050E0D06FD0AFFFDFC03FD08FB00F7FFFD03F607F80707FBF8F90108FBE603040406F0F303FEFA01FEFEF303FFF202050203FB0407021104F5F7FEFB07FEFC070B05FBF9FE0CFEFB00010CF80C00FBFCF609FF06FFFCF9FC000201FF04FC03FB040004F8FB0B0301001001090003FD09F9070002FA06000D0D100508F2FCF4F10B090501F90509030104EFF2FAFDF903FEFCFE0906FD00070802FA0304050C050A05F90AF7FFF9F704FFEF100005070113050CFBFCF8F30212F8FDFE030505040C09020201F70BF3FFFF01020FF7FE0005F8FBFA0B04010801FD02FDF601F9FF09FD04FD02FBF7FCFAFDEF04F90801F4FE0FF7FD08FDFCFE05010306020EF70201FFFE02FE040705FB0905FFFB04F100FA0A13FBFBF60AF8050803FC07030AF9FE0D01F7090E0009F8F4FD020401FC060607FE0206FFFCF8FEFD0004FFFE0704FDF5FB00010403FA02FF0005FA02FE000AF207FA05FD05020504000BF70FFCF8FDFBFB00FFF5FAFD0309B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD394D1695059DFF40AE256C5D5EDABFB69F5F40F37A588F50532CA408A8168AB1");
+        int smlen = 2753;
+        byte[] sm = Hex.decode("FB07A3E1D8B107FCA922CAC88C668B69F9BD54330712E3788663FFB44310B4D5057F5F479039A87C3B0564A2474D1A97B944E852300044C68751C09A7A5B8394D4D388456DE8535EE57238F2364DAAB11BE3983AE492D5AD2C9508CFA2A138F4666871BFBD394BBFD69E577E812A13D973E20D8B3E63976021D946B0B3DD53D343C344306B85942927A75EF08D7DA4BC31DA2B0F727B997BA243BC1294B30CF5399F58D9A1EBF5F2D4E5D3D3FBC580FCF2F6EC2457EA4C713B5B0CC6FC565331E9DC92CA419CC73A4D618ECF6A5B2EE146B28A87458D332768D93C3E01A43DF628E1EA0B539040B6D5566189FE8260A22F701EF93907B061275637359DB9BB6361A680DF9FF38B4FBFB2BB31CE0D1891CBE07C6C2F8E0832038239E92FD4F70F5BCC28BCCA1E9F800266EDD0CE007FE86052F1506DC45A8A9405F13EE63C963019776D11311AD44A01E5B27CA4C51E051BEDFE0DBB5A3A7ABEE5B245689AAE0470D5803D1308D78D8A15167F31D87A89E186CA8B5F6566CFC7148209485AEF22B1735FD487138A261D35BDDA857BC0A966A8D407463D1BE9EEF2387F2F93B1A835AAB97A17F496AD92654F5F70DE317120365DFF755077168DD6C3E2320BC025E361A6EB98873DE8F9228A223BAE4138EE114B926C48494CBE6065C08E759109BD18C41FC740057ECA699369737E78C15FBECCD29D1C6406819511B4D115C238469E7D0722D0F17756F2F9AFDAC4343500AEE4C24D39F7C932270796E110FDC2739DE152A42B494C69E38A2909CDF0FEB6E4DC8EC028562EDA3109ADD188CE142B8189565802D648067BDF6497E61758F9C893100C2FCD678DD5CB17699ABBD670EFFDAEE6BDBC510727A48ACF0E4FAAE0E7D9A04A27BB375671048CCB3CB2C69AB232A9C31F617B7608ED88EE44696A2F75F8F35D1938430D482C67861E7E63FB71BC6925F66D793C9BC30D0F11DEB80BC23E1AC43E5D2F91F4DD216AC0FC0379A8F50F290789E72D322E9F75433857D99E5B25DA53F181B52C61E1ED59B99D7D86333C69D8E7989F37FF92D3B51125ACA0C477765D2710DCB52804C620E39E85EF9F01F9C49E5C123B898952CCFDBBCE985CE9D3E3D9D7A3F254FD10614899B694AD59520CDA87DAD998A7F0F4118E14EF50470F3B970C89702849EBD5FFE168E387AE970318CB5642D46D8582013779EA03C956B893BD2374DDB002A14BB0FC8E2098E1612E47703EFE0A75F35B751EBA9539E02915AF108821C3C908315F1C34F578D077A78F91EB6DA12CB4CBAE937CB7246A0C56A9F997D58A180653FE8B5DEFCEF1BC6BA41DEEDEBA6F3D6B695A91C270AAC086D9CEBE4D04D6D939EE0A0C1201F10C39AC3708264DDBD4B1A1B7D245304F8A6AA4229A0F7599837D9ED334246B719C102239C7BB93C0E9EE6EC9DDD57C59D7B88E43E1C19206F356BDC92873F8D7A687FEA9408507B32C2F4103936FDA2A694F486E05129D69F36563ACACDE2D60F9E114502751C2C5A5EB27D8DA8F497260A7BA003A858802F26A630B69A554CE006D78223D400E7E757AD8760D1EE7C8A6AC629E28C5CC0BA8242FE96203D970637578BBEE77466A6ADB19BBEE0B3C894EFC692D200106967F53AD26BE7ACFEF2D3ECE609B89F6A940FCB948D458E89798E5DBB2D5EA7C75957FD9F53F58050CDACA4CF5613C4460C4E3638663D4E2A4975B8271BFD26C48207ECAB05712E1651C352B0621C3376BD713CF3A81DB0D4AB2C3BDB1EF54775BC980BB04E3872C9119890037AD8BA2A984F7F090D8621D442383A2EBB4667F885772163978B346ABECA297D983919D80745B061154543E1C36271D9F5E82D929D2AA007025E8718324E260E992528D0213FD5A5659398FCBDA4080B34D6AE7C7C7DB595BC0A7E0887EA5FC7FCC76668214DB2BA457C2FECC69A37D39C7C2009BC52F7F4304C26A260994395BCADE9072351DAF81D484876ABCDD876FA87CE76E5F024CC8D098351B777D9B908FA5D903EDA4BD732173E974DD36BA039FC30BA6F5EC7C60C507E1EC4A7100CC3042EC30FEA3905998483CDB77D18118C547805C41A26DD0007F73BE9447590DC544EBF8D6880E7E61104DDCEEF39866DB0868CF9C8C0EA3BEC50B2E608540C3BA1AC7D4C9C945EC830A002C15AF68BD349E5E2704F6C244EBC927051B1F04072411AA7299FE88A38D1963FE778F5D86C34DAE4983BC2B6152E84A65E820AACE1FD7BDB5E22606AEB8590EFE2A7A40D4159EAE17D5F4239BA3D2ECC659DCDD7406D76493D4092F0D4FFFBE97553A26D94B4DB223A8B62A135AAA6C692BA1DAE42929B9D4988BB0D7B06B14A60607115AAB791A3AE54D5FC0437954B9917EF638F0EAFBDA8C4AD68C997E732C071A49E6AB0FC66238629C6E027C1EBAE1FEF45874CB28C12298734F0E0BBF838BE199BEC0CE52D206EBA2C4AC6230762B476A36042ACEDC9DECA3D36C92492D998230155D08522D8E11952A3BB06128D11EF142A15376EC172B66294A7006647DD1831ABC0034F0E0FEF507D988F0E60625AA76A535C2388F8E7F5DE8F59997883280BE1BA98B7CE54C797582933B688CD48460ADB2602A82912768A34F9E911F31BFD39094E7D8918F6DA0955F03CF44C0D4B37FB331727F1C36CD130A9CD0CFB88B1F274EB3D58E82E0852DD6C80E0F368C43BA2BEE67139ACA77ADD2633788AB994A29A565380118FE843A74178FDAD308BA5F6A570FA337A6B1E9BD5B6018AAD8BC8C993FFBFA7015D7337477E28EC09D6D9AB9AFA9CBB96DC46F9F9304B0AD829D54812B8FCBEDE58CECEB89E7A992AC0400E7BF0AE2F90768D6EE308C78DBC7F8D0BABB8AEE8BBAD5DF00B04C4C7D70F8B3F0B6A4BEAFAE07295C27EB2FDA2E15C1D0BB5B4318944117FEF36A0A94BEEE967D56A679116151FA916727B66D174824D13DC80024EF0FFDE2C4C24781F6D24ED091F4CFE61B2151E6BC6F5DC08AFB5DEE5E53C9897805D76363DCC57B42472C57C8DD5AD2D028AC0AC9640C14BFB18E8D6D4E6CC505F68AD65C49693C42043595098DA51B7AFFF60D7B26EEF522B7C5D1281B5962E3DE06EE6A09253897379DCE3285DA42DF236431EFD2E60E255DB71F380FC58DA34CA88E86A243B20486FE52B6774CC17C58BF1A2E3978A61FB046EB18014F9485752A9BBA5D9A434F51D0C0A2AAEFF67B801A996E1189BF68A0D3390B94595018FF6F925ABC9F4E12C90B8DEC1BC7D2720F658826CC53082397E19806DED90481E73F99578AD4B1D21FADF9E9D808BA8ED362B3F86E6EF491B13F48F064C22D278016DB5DDB264A4A0A18382913723E8E3FAA13057872D75C158A056176DBEBA30D7C62984E046E2188DFC3AF9C56149187BBA63CBD1D52798B4FC90B95081F5152E4686D3B9293F379F2ED37F365FDEA9E15024FB028C4C1F76A49EAB005FC92692A09BE757CEC084F4856D28A13C36637688FF369695A1FBE9991CF2CA3A10C6646B36B0ACC0CB00BE7BDE17682842690CA18C17E690D937E55098C82FEB02907B83BE2069B336425277E3BAB065614A44FCFA606CE390D504E72A8CB79745C82AE0A259713243BBE863ADAC3496E9254F2BA2AF5007EEA92413327FAC766600619DFBF7C82D4A8B53C50EFEDBC76A199A8CA003D5BBF56BAFC0DBCAA0BF43E4EE53A0DCBC4C10945CFFF2398377EB3DBA89710C0AC3C38E484645A629833B204E68E03D9C02322313E70F20B2E0655146FC8E4D94464DDAB904534FF4A53FB8D5FF9C5B6787AAF969275E817D66525C07ED7966FF96E906FF6F9A3F0B61788389C1F779DC4E6457DB0C2E1056F28F589021211450772BF97643DC8351809DED81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_III_size, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-III-speed
+    */
+    public void testCatIIISpeedVector0()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("624563CB6E4499327172AF7F15FE1893614871835EA6F3330DD94337204F88D911FFB91B06CF2F07240203F75353906FB7EA7BCD0A4362546D94E60D32833623CD73B1D055DD1C2533CE5B81DE05C02966275D289D25134EF8577C7A489B383D3FA769DD3C1C033325034B731F7F678E5E58426F372F0F24DCEE455A8C6B0E6A68796E7B8D36317B23641E7D4EDDFA1E4F4A5012F50E70222E36527A14A559F4132F025F2C20BD3C2C023AE2E249E94D23460C80B94006CB1E38769A510911672D2E48AA5E5DD54F38746C195CA954D6A909CE33759A222399F209FBCF26641029D08D4C0C3D51D2FD7DAB2A6A3779365B06720B686F052A1FC5A54C5B4A26962F224C194096FA7048372F037D5E9772315B956881615C72EB57A0B661E9851F5BB61A17755BBFB571CDD90FFE562C4F147846407266A43AD9D063CD7710256D69FF6F07164E7A41D13E66C60614EB7F69B621F83237633E36E7F16605DC0A89A774E425323BCA551F211D4AC6110C314F39B873E48062C00255EE6E7E5BCF75A94F6DAC0E182C44532468658A2A6A9A9B72BAD7715E0765A10B520CED236A4D3343FE2A347D726D6107046877FE583F09FC50F4804CAD603810A15C7BB655400533721E4D38E020EFA64E3DA8277DAA437465033C486FCAFB5235B44C76AB27ED5E5D7BFA6CB2EF6EC7AE008AB5320CF861DB605321406ECDB56837572048175831343A1A192E7844225B6A72E66B42C98C7A3C513D2B5A71BCCC329BCF20246C241C4B5EBD5F37ED144C1CFD173F6330F78D40C9A952192201126F79EB1E444BE27E3FF500FDFA0BC27706FF2A4381454634540D3BF44CF418613C087776C940F5EC1928E1190F710909EB43EA715FBAFA3E3C140396EB516C9664A87277AAFC139D343D512C65776056A1C31104645033E03EBEC24D99837D1A427CFB7B72DDF86054744ABBD62B57A8355C8D3994F84B2A4B2CC1B731529E495F811A73414C03FC1B66BB0C58105283F42AF9B2521A5B717D8A3E8DF411A85C68F706006E8B7D1114769E2103FB2B7601DB1BC28B0355D909AAAB3702904F96BA6FCA3152C4F042596A7854B13827222DE0925A47CE40F2AF3FAE9F19395D70DBAD28DA8E7AC34F5BD36E174CA352807F2C49645B05015B389B3666A679AF7A033C8C0659600F6761263ABB6B4BC2360F29267AC82D80905502B7557A295406D605D75828565046DAAE09B12080EEAE67179D53B05A54444B4CC77C1E2DAB2D298D785B841047B5665D3C36E3181CD7AB2DD49F78633661949F585E0A18530D7940046E140D684C033915EE5DBC3A07C34A4178516F43964F0E2E04647324D26107980F68FF990C7B8C0C47F12FC1AC384520485EF061DF373B78A12D80810834AE36E5BA37397D35AA5C4625B708C51066F5C54CB1C52E0A9D3CBB772819D44A875B78AC4A65D3EA7621BE271914770B8D363CA911B8874CA1C16FDE314725B409391933DBD96B5BB013C2723E307D7D9C1C41654049204837D4074FA12B46E25D39E1EB3F74194C08CA47453E2092AF3142D06950FE14087E1A80970ADE4E7C6F6D70294F3710743854AF2D0A1776DDE166895950DDF06B2ACD11E5A248D8CE52EFF276AF9414B0DE264B315D61413EB731154CEC10F0FC230801229311566E781A57F65EBDCA4EB0CA71C62F281AD139D105108FFD5345016734F37C039377F71B36B649661F425A2CF3132CD40FF6DD5668C25808441EB86415136A55F57A1DC5991463A00BB3C67CED334979BE64A192471EC95F9F591014D607BD23585E5F0E03B33AA6E11E70F3314E7305336F5BF5E25E25D33C985B2D86BA0C41980199C506CE420F12523269D62949904174B86B383E41255F64940029094C0E9BAF76636E6E37B6711DA70ED1510E6BE35DE7C34FF8613BF3AD2358161E0E7F58696A669FD50ABEA278872C58BC3B1387F93DA26245E1DF1A35D32BB51F372F1133975E5DA27B5E112B43508D5588EE21D7A2704A9F7B1E7C62454D6D4321171C3647841D4A7BBB3B25DF019F856B7DE1652F26132C55049BF85AAEF21FDDFA03BB873761A72B389B3217C238B1EF315D4778EF4643431E1327350F240040CCF90C324370843554E9C23F6075089FB94677E85377B74FDD6A174F3B628B3F30778A629F6A5D9D8274943110095C3B763E4BC9862DD3F1441DE30256F20FE5DF629DBA55AF182C373D03D1E707253360E69455388554FC8140E97377B38024DB1C1CFAD014949552BDE132F8BC1D066166B48566C4A672B9F07A3C32399CF351AC073665FE4D52D11077CC6E823516920843FDF62FAD5D7D081B17470F16122317C3C84A7BA54B988E77952D1ECD012F90BC3C1E4152FD577E927847A8F943F5B87170A933C7DD5C8DEB4D0D02615E7B66D5CD399D0613CD111784C27A27A71516174B0D9534CFB51B6EE67D77767B818D79444F4FF519397E4C7B87EF06B0826DAE7031CFA63B835D4E2FC5035F3118463977458C6F5EB97F9CC87D77F107C28402F9D616681C108FF14C432315A368071A6F19C18108833137A4DF25764754FCF509EB6B1B118029CD7425566F4569020F5EEA7C3F5A6B0F3B62E8B615333F5FA43E15484C4F71496420D331D7FD74445816273113BBDA6DD5434DA9537EAD8479F20C2B4AE7648B9B04EAFA3A36143F0AB364552A482B766ABCB33274C832F5B3687B4802E3E369804275A0F443C5B76113E7380ADF03BF7057BAA575521D75F1EB5F774A7353A94D9BEB1BE70D174E602435FE55B8863CF5F71782B83F1AB62C222D5DDC8E04B20D5EDE755CB89E61855A160CBA7F60D708238267FE727DBDF67B8F160280A363F04B5EA62F79ECA260A0DA31B0C016DCF509A32E80A2B91176534A36B345FB2018C17540D14D569CAD14644F1135E779D360078F1D3DD4790757E035A4466B1C097751C01FC0E32D6A0C24ECD16D027441A38D26A9125F0D13039F9B2650E64C31322DE2743FB5306D057C1AE9B2051D916C0E076439070328344C970E47651B2D9E826DF12C6724347FCB3E13C61E0B79AD1E67D747C1BD3A9FF274B5EF0143126EEE3F6A72E10F75A70A77EF46B40B1A2D5A39BC2A2933DC3222787C60AC73ABBD110E73698E6A00C8BD031B626583832490B67647A3499A9431F3446DB4710E24F32F3F321E6C87427ADE28DEE2287D6120C6D633E1036A1B72067B955C3F873026E01957491B5F8D60888C13A91136FAAC24DF284668B31602C24EA4EC4BB0AF4A444C3AD22620E0B046E0CE3B28186265BD34C2EF34A74A56CFF15F230F308C6547D7252D340412F37F279B033C068961CA220A6855624EB860BEE53E8D0C492EA668A81164A87F042D9749DF8B493E4F2564D437ED53183ECB06961A09A3E30C4CDF37013A7435316DD6877CF3E2556689320CBF6B504205CDCF0B5F600EEF300669F65123B60972CA14785E4B54514143193B889D17415769413927235A24BA7D3828A844798179A44914C9F94B47701E07233E5B4050B6044270930583E5381AAD35E1C906952C1CAB143777C26208F90AAFBB6586295F168758400655DFB852BD314E5F5935735D66090521395B56CC60029B752D5D0E2B69F70043DA3A620D6A089661060B04E7402D33B77E726A49AC7C4D8EF52819E704B8B41745811774611BA3F108CD3143BE976A4A310D13320FB3C92B41C353C3725DB54B091B1E2CD9BB474360159BB615AD614399D7635465761748078FFB19828B72A1B57001CE20289E0F02BD15BB5E109FDE3E92BA495C660332337B94EC05568E4AE9065D1B0664CD4D11CA9A0BECDC7AD4041E2C044456B77E4E3C2066A33AAC412D53E66E2AFB12B946079B330BF0852D033342F2914337AE04425A02F80E1CC55C60530C08692008AFC758749726B5340B29F74A3F8F4045436293870A91746FAC025EF07C7D1D065B9D120962F87255E21E6EC73277D66B353D741F2D3ED099670C560D929D5E352D7CB5BB2A529F35FE8E514E4D72779256CBAF0B382A2E082E08294E6CC83955B2FD0ED1C65AC2355674823854F807EE707F83F42120375D4AEA21D076503CEC7DD539645B527856973BC0F06597081B7DCC48D428130A9F3230065632201FFA404FEFBB7EAAD60FA6AE57CE312CF7A51703610B6B9D0BDAD64A09D402C8D670DBEC4146816D80723CDF8E4BC6DA4CD5AA540FF510626F12FCA9485DEF65045257B5B24E896C647FD61C5D12013B3277DAD75B21243AFDA11109826B7B7F5873503323EC665D8C11E5CB441C4065FE1763BF302B21DF7FFBD021763F6031AA36369B198D761FD1FF1925583733EB325F845D754426B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD");
+        byte[] sk = Hex.decode("1C002800F0A040010E0AE60380A0400001000406101850408100020FFA0F98AF9F7E000701F22F08C060BE81F9FFFFDB1730208002FFF60F0070903E018006F917F8B72F60417FF9F1E7BB3FC0FF8100060000CCCF8FFF7FFD001412B8EF6FE03EFF04090A0CB0EFDF0003FF063A10C82F7F8002040604C89FF03F8082FCE9EBFF0F50C0000304FC0104E81FC0BEFD07F8FB03C04FE0C002FCF611DCFF6F5F4081FBF90128F0FF3F00010609D41710F080C004030AFCF397105F400203FB03FC3F80BFFDFA06FC032CE0AF6041FD07F4FD3F40EF5F817EFBF8F733D00F1FFF7FFAF81F00E8EFE03E85F40A06F407C07FC17EFBF70BA0E71FA1810103FCF90FD05F6000FE03F8F707F02F60FE0403FFE3EB5700FF7E7DFE081C08282081C08008F92D2C2030A0FE8103FEE5236000C07FFEFC0EE60FC00FE080FDF712FC078090A03E7CFC0C1640A01F5F81FB0D00E01B809FBF7E7FFD0508F0B78F20417E00FE01C4A73FC1BF8100F901F0F74F1F800208000014803F1F427BFA101A2C10D06080FEFC111AC4DF7F40C07CFF01120C581020C1FDFC07FE03286060FFFDFBFE173038801F828202FA07F417307F3E7D05FC1B50E8AFDFFF8306F005DC172040808004ECE5E33FB080418304FFFF27889FDF0103FCF5FBF73F0021FF8300FD152020B0FF817D04FD0D3C90E0FFBF080008E03FE8CF60C07A00F9E30788D01F8280FB10F22B686FC0407A00FDFF4B40F09F817E000F16D8BF4F4041FF0602E0D70FD07FFF8301020204B03F20828203F721BC47801F7F01FFFEE7FBD7FF1FC0FCFA0BDAF75F30DF000406FDFBF3DFDF7F3C7FFCF31F886F5FBF8181F904043840F040817FFDF80DA4A76FDF00020A06F02BE0BF9F0202FB03040058D09F037E0100E0FFFFBF803F7E0006FAC3BF1FA182FDFA0E16C0EF0F014182F5071440B0EF9F4183FEF909D4EF5FE0FFFFFE06F81B1840E08002FB0E0814103000007FFB13F403F8DFDF3F00FFED13E89FAFDFC0FE010DE4378850600002FE12F6335810DF410203F5F9CBD78F7F0080FF00E023E0EFE080040206DAD34710BF3D010204ECE7DFCF9F80FD02FC170490DF20FE010107EC0BD86F9E417F03F2274038201F7E7EFF050CF4671FC0C08100FE0B14F8FFFFFF82F9001838881F213FFEFD05E4F3077060BF7E04F9FBEBEF0F1F827DFEFB0BD8C77FC0C07DF600F42F980F60FFFEFC00F257E02F21FFFDFD01E653F0CFBF80FFFBF703E86FF01FBF81FA04FCFFD79F2040FEF9000A0410406042FC0606F4DB67BF5F0284070A0218C8BFBF0201FCF90D3838E0DF41FFFFF2E5FBAFBF1F40800300FC0380D0FFC181011024F47F0F6000810102120CE86F8181810200E0C7D7BFFFBFFEFEF8F1F3CF6FE0FE00FD011634F87F40C17FFC0DEEFBC73FFF3F060A01000818C03F408302FCF90F60209F7F7F06FDE7FFE73F60C005030EF69B476F600082FF1202F4F7DF7F3F0402F22D0080007FC0FCFDE607280820018001FD051EB4B76F204000FEFC21E49F8F1F7F0105EE0FF82F70BF81040500FA3FB00F60FFFE010BE013F04FBF7F7D0210F26F287FC0817C071302E81F20603F030119CAFB4F1060C0FDF9FCF717F00FA081FFFDF601DC1FD0607E83FE12E6EB0FD0E13E82FE00041840E040FEFF00F0190410DF203FFAFBF6F903E00FE03F8403FCF52FC00FE0BF7F02FCFDF39F4F40C381FA020AE417409F810004F1EFFBCFFF7FC07EFB070010809FA0407CFC010624A01FE1FF00FD08EC0BE81F003F84030F02083040FFBF7DFFF81B0CD04F60817F06030C0490C01F408108FC0F10A8BF3F3F7A041200FC1700A081FA03FA21D43730E0BE7FFDFA0BF447B0BF7F7EFBF51138F83F21BFFA0706FE1FA83F40C1000001D433E80FE0C07C0502F2070020C00003FBFBDBDFB7DF60FE82FC010AE4DFCFC0FE800A02E4BB3FE0DF7D0400F4FB0F402000007A03FE21CC3F1FE0417F03E8ED2FC0706001030402D62B38F01F0100010D0C1828B0DFC08401FF073CF01F404102FBF40F1020A03FBE81FF0000FC87CF808002FDF8EB0340A05F407F08F11B10F83FBF3F010001F013084020FF800101FC4300705E807E01EAF51360FF3F3FFE090A0C28D03FA00000FD07F617F8BFDF7FFAF9FD051028B09EC07F01F7F11F2800BE01FA09F20334C0FF3F017E06F9F92FA84F1FBE84F600FE2BD87F5FC08208F72D2430A06040FD02F5F10B20A0FE7F7DF906F4F7B73FE07FFD00F00510F04F3F00FE040702BCA760BF41FCFF0004F00F6040BE8100FDE907686000C1FFFFF8D7FF176020C0FD0000F017981FA080FFFF0204FC4F805E0102FB03D6EB0710DF007FFCFD0704D8AF1F01FEFE0AEAF73FC0FF3F80FB0D100CE8BF1F0004FD0714C00F40A040010002F2EB1FE0BFBE8100F11F08C84FC03F7E02F50F1C50B0DF82FEFEF9F55718C040C07BF8FFE90BD04F7F417F0114FC0378F0DF80FA02EFE91FA07F9F3E7CFC0BF24F906FDF3D01FEF709ECB75001FF800601D2D7FF9F3F80FF09010AE8AF7F80C081010FEA0350F0C0007EF8FDF70BA86FDF0001FA0E162428D0A03EFFFE07FA1B28E0DF807C050A082848F03F407DFCFCE9F33760C0FEFF00FF0B18B81F000102FD05FE53C8DF5F007B00FD191CB03F01807FF5FB07B0D72F403D83FFF807A8D7DF807D8001FA0B10889FBFC1FCFA08F0FF2FC09FBEFFFF07F8D74FD0DF3D03F300E4D337B03F827F0AF41104D0D01F807F02F4E7F7D7EFFF7FFDFF12FA3BF0EF7F81FEFD0006ECAFAF207F06F907F82FE8AFDFC07EFE0612F83F301FBF7C010124F84F50A0018204F9EF0F0820007EFFFC07EE2BC07FA080000005FA3BE0CFBFBF7F0705FACF07A0E0C07CFDF4D93BC8BFDF3E0102EDE52758F0808082FF0C0CF4BFD0803E0204F7FFF7173000BFFDFA13163470CFFE81FAFB04DA1B70E01F7F8003140830D8AF1F417F03022CE4CF9F60BF01FCF9E5EB4F1000BF7A080BDC0FC8FF1FC0830BF7E7E3E7DF200084FEF301D0DF2F00828201F2072818C0803EFF0300F4D77FD0DF3E80050306E40F60A0C101FFFDDD0B809F603DFB04001EE0AFFFDFFD81FBF1110CE0CFA07E7F00F807E04F903F000104FC17E03F501F007F0BFDD7E73F60C0FEFDFF03E41F987FC0807FFB0414E41740C07EFFFE0516EC2F9000C1010703E6EB675F000202FE00ECC31FD01EC17EF60028C49700FE3F01FF001C0490901FBF7C00F40700D86FDFBF7DFAF80360D04F9FFF8003B60E7FB7708849FEDB54F41A68314805A5C0766ACC9F338A46B29EAAC00087AD394D1695059DFF40AE256C5D5EDABFB69F5F40F37A588F50532CA408A8168AB1");
+        int smlen = 2881;
+        byte[] sm = Hex.decode("820713D8B1371DF090139185CCD8653129882FDC98C3FFE2E84BC7F2D80EBAA1ED2EF0270809C60D2AFA9E4250A237AA3432E612FC95427E40740031C6B8A6AFAC01A4D4430A8BF6A54F262C57E0235FBC4981EAC6514DDD8A92C551597E2E3C700C85C26FF62DEE5B72CE035F2B7457CE02556A6ACFDA6E985733E6062CC0AC1175DB2EC9D3D37F8984B315954C79EDEAB5B0B1432F6F25EC553971CB4DF7E012F19B9FDC5451EFD313FF71E8E97AB9EDE423A0F7D700F212B7A703735592298ACC16CA62BE985301E3B9C12D076E3C168A14F6FD5901934BED20199687B5236772A36596EEC99EDAD3362538FAC2CB46E0D1D546D112C90082307D41EB91AFEE0012E389D09ADAFAB94BC6C270087E769E9F1EF51B6F37E26F0329C925C87CFC661C65CC0CDDCB8955438DEC61BF2F0A1E644F4E8072DBDA1D3E03CB45C79B14DFBF8DFC9C220482B88AE60C1161B6D5B55E8B1126AD84C05C021A69408EC230ED2E377671E3E8662F57ED83D6C295E45A35B19E494BD75D0C2BC0FDC517D40B066E78A5F1FF99D948644A590998B6DE51BDCECEA27E13A118B8A33AB276563B203366F8790FF8FF6C617957FEF2873C32FA658D8F5C7D25F4665B25C73F7D4EF3CE91077285EB031ED4668A0692C3926E16AC928C4A331D8C79C8E93C2B93A2571D171B383E339608B3213462B20E56B6DFB14A6642CE0E2244C770EFFB68AB4D039AB34C172CA497F57F272030C376950157A32708E343F1AC11207254FE6A8F7CB958AD8934A5195C8D07378ABA0FAE7242CE322848BFB9B70EF852943E92A4AA8D604909DD0C3FFE9688B323E9126BD1D9E1365AE7243A0458C90269B565C09692C1FF6E72A5E687ADF21555427B77296AD64872F9BE9AE6556BF8A6FD9EF87BBD4C1D9D211598ECC04915387E3613A5530A3866B0084C25F3093796B92AC377F817E09E9E84B65AEE54E7D4C2D4E166F18ADB33A4F3886198A140BD7129722CEF4AC8B5E506DBDC4FF140F71B7B87B02410A25E5DBF25E94F8CA8E6E5DF3687CA1622F144E23A1859E59E45897002697B672D11D614AFCD9691607804AF9C8D7D165078265D9F16FDFCA92FA9AC36E6102BB5E4FBD25D771BB877A2105B03591D5A0805345440B1742DC9AB8979C09847EF3A92E40ADC3C6EE87A003955D688F0B99906185B9F4D31C34094AE8DA9189E828A235A7768900FAB1B192EC3A127586FBB1FBE7BFC7CEAE5738CA5654244CC0AE90ADCA3525F336CD557B293065D9457C1D64B400D1EC567863872F917E7D35CA192FD56A2119D3C3970512AAA3F20DF198E9D10C3938312461E2DBC93A2917BDD427DD92EA49CA4CA4BD2BBC1766AFE446805010683151817B73B26E71D48D9D11E5C5358CF5B366D6BC2206081C1603BCC6DE2429D0DD55DBD8244237A465DAC87184C366553320CFD4E5D14DB498D16A9CDCCC7029AB3F93CEBD090958CA3FD2062DD780B21705F77F7A6DD2517B3C2E23A950BCFD568C0D7AADB35CAB9FBABBF876E48819E1FCC137849F02FC9355BC9DB13CDB6C2AA9CF9EECB82AAB2A36F4105205EE3E9F098706ED2B5A2D7EA1A9378849D1B0690DD8898455E51DB42BF52EAA1005DD84476033995AD13A86DA7A2E388A929E0318F37C53C2D502DC0A4C21C88313726056F33039EF835F5491A2B0118C4263ACE4FABC611B0BEF2C355ABCA5E7DA1EFBD6D9CB570261B4EC5CA9F9BAC1AF7FB25DBF12D77D5BAE9EAEE0266000002F3BF104799157918F6227082E39317C7B48F93A9DCD2D5F6DB8F0D958E9F5DD9EB46E195366AAA188429BD99D33DC15E27047583DBD6A131B2BF98EE022003F124D8D6C82266236DBB4DDC44113784C7F821D58C75511AD9BEB9DEEDECC3DA9521895CAEB33660D9B95F66FA8A2C4048E7C1202E0A959E0CDB4E1D6FC304F7AA48B605C099824E7890B841589B49E91B0223F5B51862E5315D96E50348A74949F9F11AEE6C49C077DD119990F2F46676102738497B79E5959981A625A3CD5D8EF1086C6B7FB95F44B071CD18FA64052CA5E29062813ABAFE51063B94C6DB6DC70DF9E9B9C82F41B2C8E03B6BAAD9F5E484BC219AAD819D58F65C13BAB464DDA67C07D20E2F23ADA7610786FB20D8B6520DFD2535427C53FCCB3B66924322897DE74A10CFF1112FADE0B16D0012FEE2F852B3C8AA734CC413DFDEA0838C2F4821DC7EDD31D16C23AD7CA6B4E8E09AB764810BD741E0A7BA4A86A9F72B17CA2194C6BF62E315312F8D94B0D3D0F6D293D64F9D146DF8E08640E23855429D3F23D3860F63CEEE1E3F1B0E726F0DFFC74B494C1EBE52EA725089CE4F9F7B4F939B936F0592E5507C45B30C75844C97FAD4E446361347B38ACB22334EB7F79A6827754395C6EBFFAE5FABF096647C69D251829A50657E9B2A0EB455DCDAB45B52964C6339837D28BB76ADD4C44684C9D7FC8B3A559EBEF40EE5A2CC4DCA67BFF35DA1B736DE23AB3C994EE458A25B246A60BD602CF6428CBFA300320C1D78FF6498E8D2A504AAC0DC07E5A0D077445A78875F20275242F1D5290EB3B98171C272D4D686948D33CA4F3626D35C3C9376670AD9ACA8C420E5C43156A2CAB9D870E1D06EDD85E652D962BE186D96B2F9CD84585F67424D74004407C291DC1F454644B96E003BD562658D9B9C07807FCDDBEBE761223500B43792EA77AF20525EA0C1897368FC97097DB6CA4554D1AB78126EF40D6AE4A6961F8E90C4D3B10741B6D2AA3C89F0BC24985EEBDF691779BF1CC69B27E076424AC71DAF713295B6D6A620DBC1D6B9C8BE196CAC12E97970FF14A139D5AE21F9D802C695EC4AD957CB340760612894FD76C1537B0E87EE28B56B52A3276063A7E96E48C3108ECDFB2FF37BBD3E1B8AAB2D7407C91DFD7BB6F8CDBDEFBAACCC8D74E1E75B0298F0820DBB02EB372EB42EA768BEEEE93C891F2B768133D90A32D60FCC59BB9E62848F7D3C145ADBADA260461D0FE0D080990CF5C1DE9EC5104CDA10D6F1410A4693B5EED91E4F8275E86E18C2604374D1675EE902A5F8790BB55656ED228640E9043CFB7BDBC6E38850680F69FE51C60F6DB689911505F398B6991C19E5D9A04C4853749E9B5D90F8F627B47B9A490EB657E0576C587EF1D7A1E3005738BCB5503BA34B55888AC6D0E037D26463F827F67705860FADC524A50411E22B544988B15884DEC6FC463526FEF1459A1A47DEDA883B36FEE3C0915C42609B8B719D1E7D0AB1137D101FCBF22EDDD6A148D9EDB29A47128E13AE50F11B8AF5946532E85FD1F6F24D3084AD1F672EE9EF4C33C71AC6C7C8144589984E31A56E48CDE12B51007F4559C2DBD0031448E9A648F1A31AE84B9C457C59000AFE43576AEEFD34A8900CA19121C5B18BB8475F88967DA7AC8F5CDDC8F46B9D1DF7EFF843E65845AA03D3EF3AF35F6223B973DCC2367BE4CDE98464C51FE4556412664E1C506C86D9F691A494E70263CB841F1F0736AAA306F0A06673B6526CDE65478D7DB0FD9E4F49E7436D910FBF3BBD72FFA34823B83B78A7AB8754E9C6E76D0CC5033EC64391906A1B023949384F72FCCD85B4BE279FE5F69D04A6108CBC1706A49EC180EB6B9A93049F517CFC0E9EE45749889CD86C76B3E371BA5D9AF10E9F04911F6A46D734980A9B1D63C05C2276C5756FBB39015526008C3196FAA5C698B41F953045D0E366CA96DA5D46072B5AC836A2F87A5BF577453103EA9DB0816C9A46874DA2699777D071B27315C972C99E6453ABA11DECA1866E4296E451AAD416F1139118E4F544DBAC1993834CD5BF4CE3A83DDBF2177C2FE66BE72A13C93140DEAD9F9EBAAC1178B128D0A2726A91D3C07C388C40D1F000D251370E5AB73D5F429565A5B1431E9B4C0714E61F58C2918E73D381A38AC42181D3DEE092948A761232386CE273DD7B1F8B7C002CB8D9AFDA95CFC116A3DBABCEC7FA991726BE4AE4FE5D547C0B7206E9D53EF329BCF40A84C339C8394C8998B69ED6B372A4672DD79E068FBD7782CB3873526D43D8CCF4A4D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_III_speed, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-p-I
+    */
+    public void testCatPIVector0()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Hex.decode("EA7347183E405433EFF49CB63A9E736B39A86CB67125110ADF35536A44940BAAFAFAB19FCA5B8F11CF72F7199E051A9A607D75D093FD1DD7F7038F4EBB172F9549747FBEDB2EDCF24BD007610C111E032C1852E4A92A8EA33057606200B17C785B8BB0B2A08DBE93185F3B931371B256CEF871167BD21876B1D62FEF325BE2D0A716A6ACC3D3CCF516DCB267093035DA9359E8A698C3946D88200E9033AFA1E87A30A7A626056F944010A5CFD972399AB7C52FC0E87D255A589EE0D3E14C4BABCD5CA50A561E71428C98B72D413B117F827F0FB557559FB16FF4EBC73C539C43040ACD34FD676C1868F59F84AA1398A61E99C090A8E1A7C5A19CC23CD21BE2B9CA6E23F6A7E05E3E69AD6516A085D2C58FFA0883EEC30C04B648414C3D4DF1C87AE2C5F06CCEE989ABE455ADCABB6C84FD17D8A9D1158E91C4A2598A153595B4C2F0921ED24A49F758CCF7FC170B3959B2F26791572ED54AA80368D5800025F6138B595C7D8B6B8AB5F340399AAE0C821C4B0A3D677B530227B33525D9A48AEE067368A81305FFCB2E8615035CD56203EBBED33B5B1F5B575F920A627A770F0644610094EDA55FDB7C7BB7A723DFD4662780ADA0BA214D91AAA9C832FC29DB0B59EC14F4681FF05B6E5D087E91F891416CF8150604DF06BC7901B6ED33A5967E91799BAF9E903EB7967D393772F7C170D4ACFA36AA333BBE00AA2A40FB3A8D1EBE9D88926207677BC27BFEA91ACF49E009725D1CC9CA4566FDFEB762F4DADA0ED3323A5D48FE9D19B0B60809DE4182EBAAD5113019C9FB4F9216DAF2574EDE87B41E14590AF2B6E71950881DF07B5303C13EDD4D516FB059C7B7F1DC5CE6BEF6D2ABB65BC6B2D5A0BD555914BCF21FAB87D417F66C87C0F007DFC6DD6FDF22DB646F887037227A20AA1CDACE683FB7F9E4D4959FD9D361C073D22DBD89C4BA32CA1B9BA803BB8EAAE6495269030C24C08C8844427CA72F321303F112EAD94E3823D354730EF3CB2F561728227BDC2321FAAD7ED43B54603F94CDC42A06CFED0F44CF34F357CE3019973988AE101746513DC8054EE0464A8FA8654A724277C511D0EC41EA009F837116E9B51C9491E0D6E7D4796BBB84E854C6097156F8A77CC8913F3D05E8A478667071AC757557FA30958314499E377262C79EE8EF891201A7E13710F4F6B252EF970B9B71A050681CFB0B2147D6108F7415A55FF1E70CEEF738272B8EAFAE18907E6D64F6729F5EDA8FE2E779554B41846521DC530C3CFD36A60EC145D0FDB6AA61800A534D9853A6B364F48F54558DE0132A39B4E5BC1ABC5BF376A5BC02439498B4C21FA9C64A72A9A55E51078724BD235E3D75D8AA12D4186C2D8E65CFF70E93211D996556B67A0231CF46E80B1F258C8FADD5662BB8D23B84C8C5EEB2612B0BC2A7B432B321AF2253AB16EBA0D24CCBEBC6D9DD83DF22DEB57EDDE1677CA943FAEC92E560E58FA0009F7DC4673722DF8FD38299FBB34FB61239611DC97A6D24B1ECB4CD6DF5EC4658304D34EEC218A55876A338BFF8BB2470B7F8275DE34863B27D5DA1AFAC7A24039DC53ECB99079E0D7DA4E3A5632D97AB065D11EF17BED267CC88EAC42A9A7C119D1211724D0E3A68A54A6220CE471EC3CF49FA21B4541FBD0CB8257B6396E4F2CFC8BD0756D4C0703F5B5CD656268E98C043CE4ECF34A377B8846817DEB391CACC4050E797873A1F88E974106471944D8A2A25E80FD54024C95A2B5E496FFA076F7930EA29AED839AA58954E5228F2EF55420C048E3AD1EA894EBBD4851AAF0A8C99CACA3057C486A0E0C2F1763B4F3440C87BAA44705F563EFA435FC68D165D0544902A3FB3B84FA8D74001F4C314F106D4AF37BFC54191598D2A28869F46D76B68D72D5FF91AC1F0E31F556F2030C816EBADFC9F72A55B9CB5F9D4C881DDF35448140025651B9D0EAD4C59845119C8D3FDBFEB219142C3C6820032BFA7644DF7E7D2783E27E8786053B02EC68EB4CE6DF29BF9FA8B1BA2E8A6015419D0A3D491E884C4059184B9C6E7BC35908EA60BAF531EFEC1D2379389291BC5E17A31F1ADBB5AC09EB3EB845F3044D31706543F063644F6D5E7D6DC686750134C9811B4BC230EB1EF7E8B214D82E444B0D3EC367D5D5D0912B5D6EF40F3D36302D726D9A988C208567067B5C5D9521EAD36A23C8E9C956443141FF78569A8E0D9EA2BD815A3A9746CF4F1CE603E0894529948D873A67C8F244DC10D9B706E127D508C0A814E9132E1AAD7DEC1BFFFCBB98EC65F1BDD6A8A03DD08256EE50869EA3509C2797184BACAD88798492F8958327D5F1071A90F0698EEA7676B4A86DA0ECDF4F3EB5553599514CA78773CBDFC9F091124E62181C637793A98E02847143ADC41CB3987334566846E4D201CE926D23AD382CA2E5E153342E7AE8CDE9FDD76148251F09C7290ABCCA43037BEE98947BD673B1FE28C0951298BDC45317AF1896D0E4B91BDC2AC886D18DB6008D8565FDE10D5D87322A5777C66A910FA2710F83BF61B396E9F82C2D68FF4B524616261D444F7AE05B57B4A86E23C6B759504812C305906ADFB5C900360E79EF73F0BF0233F5181608ADD756B40EA15DBA12B4AF06B1952F3ACE3908F6AE4625202DB122B5087E7808963426B23CE9B85260C924A677812EFD1A55E08CE2ED673BA2D6DAC7222A336CA6F0A05996DA63269EC3F5BA62E24593472AD7818C1800DB5A727D0C8A492EEEB22AB981CB688853751DA34F34A6723591909E2197D1A4D065737AE476831C19F92953CB7F889EBAC46A31D247A9108B3450A8F3B4437D0208B0D21558AF4E31DA942CEA458DD88A605DBCF1136A2E47897CE17030984952ABC600B8F30292DA4274593CC71497E9BCEB9CF84986E4C2047BE8821F406F1E000E18F050E3DD8E7F3FBC923D0830D1A84DB2B08F8E0623E3480A20A48E20A2F0F37E144CA58283A97A20E12DBE95327D69CAE4234381457CBA8FA237F88C466E8DAA72BF89E9517C28BBF4744D3214EE8B3263DD64C06C78AD9D0CC42517048DAF4AAD330BE626F23338FB6819329D11C10D928C11FDB96BC2A00C269D7839DEF20580F82F7D9B48CE896C45F568A6B38F123BE6B0690722DE463690EAF8C8336F829D4D48E001A7DB63470A39430E6929BEA765B3E8127C4213658C6938CE5D566546528D60C8B938196BE5B3378755580816C8120803A46084BA1980D9F129D47462874022454DA5E040456B2F406218E315149D1B79F7115C81B7F98E1A1A3F61C96138152C0A1B3E124E630902E6033742F7C74946B5D2B4D08DFDFD29FF7479C313FE889B1CCDA14904E0CADD9963F21E564EC09202A1B263440E9CD8EF3C25F2F5D58D278DF530F3940B79D7AAD270497DB9EAB1536C2F59549CFED6E5F4A9AC9907E586357C7CA305170B622CC3A571E45E1891690EF11C1BE641A2E4169C2D037EF6130C7C70104D4BF844CDBBB6C805A9B210B853417F6E2E4C85CF4D6053DDCC94122D5F2CEBAB51E0232E0DE9B19FC5CE0B78D569884F4F2E33BC4B1BF6A370B7F2E317670A136FADB81873EA13178798FA48E9E1123DC5B31D88301054F0D964C3939C0CF1201DBE5B43C1CA9E95698AD80AF3EE0633F973665468039EB73C63FAC0E70DAFD81F17492B022E98C61950A5D50846FB707CB7CDC7C51BA2EB7B2648C22BDF81C17A8EBEAA351C396E2BAA27D014F16388F215C5DD5CF53D077A4BECD1F57D0BCE1CC415C52052934179366443F80EC1D2266A80EF5A4CC09BA0E2301219CB9DCD88F30639B0A5363CEC3847CDC0207FE65B260EC1A4B5D40E3870EBFC1C02F2F4591A4D666E2754A523D9F1A5E79B69FE553B8405E338E4587EE2D03336EC2675A95AE9A35D9FA2D91FF934D30D3FFE4C7635C2DDD2FBF852859AA91EF913263070E238AF16D140A470A8A4A2A38720BD667EEB08E3436EFC4B74A9CC9725A7F0ED64B55B084E8EA7EBD1ABCB8A21E03CEB0F9CE5E3445F226B5797613C030F4ED5C445DFDF886AA6D7230962548B53CB551F3E6CB2FFF4799AE47E2293AD0FB30AD70417BB9F12CE5E7CBF9835B593F8659D98840D5F93FD67E0C48268F770E13726687DDDF8FD41D5B729B1B800892BC46D46154449A230139E80B59BC827B7638E1DDB20C366583892B8FC40E5B038772E4055BB625996969CFBCD95374F5966B5CCCDE8EC98CA3170A43417A941FD1CD00348E12465568A3DBD075A08D282D84401553B658FC64E233C5D3E5BC383606C737A3581CD38CC7BC362A7E79D46CBAAC8A76C2093879E2EAD94A9E9CA4703ED884E56FCA8B4EFF34BA4A002C31C2A15258879610551BCDDC393C0E1E4CDB543BF3273C4AA541D107D3EAE2DC643993779E15D214B7FC318970FCC976C344ED4D147BC226D1F193A7026E949778E67B395B111F943D44CA98E5A4EED33272B13986D48902692325FF749F546A9E6674FD6B8F67901725B8A0543C81AAB10228ACF3AB5F7EB2CA870DB85A69E4E63153DA7F5F8D8CBCB254E2CF003E04362F4E94C6EAB08F53200158B8F3CC8E8333E2EC6D5A4DBB607D6B180D45CBF05A7A9A60FE9ABD902A2EF248B9CA9C516D5871C88AF24C5376A4B3DA19B96E454DC969BD4DFA81170C627A2F6A0F0D3D9C65BB03DC24C470944CE27E108F3402062614F878088945A201EAAE842C20AC99D743E650DAAC82970709EC2D1A6CD5A49A36C434496ED6F5E1558C0580B61312F63C195C4FE5CBF43BD8C3B189FD5F0766041E8DCF336C550EB66C4C0F2B9AA420F260D1458448D4AA5CCBE22340058D60BB0C7B21231ECB2AEB51CFAADD4D10B683248F8ED6D2B61D4D5C66EDA259B30101F71F1C19A60C987019F236C35259E7C39DE6709806D24BCACA2897CEC81BAF76205A488E6920A3792A0B906BA71B9B5095C85F6FC368ED0214F026A6342359CF05C768EF34F1CA5E1F30E76D57ABEF961EA7097B82F8B81B9219D9D5EAA4143F23FB4812CC8E8AA5C089F35E697F1FAE5F45B725560892EA7A99F9C8AFAB2ED9033262780AA97DFD88B0E810EF4ED113494B8B4556C46C287BED57E7F60E0FE2A93938C25F910ECCAAE27293243956F9D8E14F08FC0D6CF1E1F43E5FE5FEA8A1156C94DC94EAC01D61A47966CB7819AC17E8A6C9CB361F4B13DD43A61156D378EC627B0E7414A29BA7CB0300FF1EFA20CDC4A2D2CE25C628B5A6109F4542C23B6E2D3E27C808AD2B706399C23E392D350BF6D7C02F0926D9E40B2CDFB98971AA06DBE751854D8DA832824A68CDD67ADF855541F9B1004B48E921E69FFA23271AB2AC3F2F170035553B8A8A00C8C248E2D8A763A4C95A709A38C56622FB8F4E3694D5B923A2CD8E538F113E8A08DCF19655C3E5818586C334D4E09BC4703573D24DC77804B24EABCF3841F372CECC9954A56ECFCEE60431CB015C84FC6B61C11E15B5899A81D07EA453DE77551070EA5B484FD937CF5C3BA7E8380D38B474379AD0211E35281A4FF6A2291188853439D472E476B2E93FA44F707FB7BE136CA62AB3E9F02D2B864EE25B357621AC7C08AFFBE1B7D6C184428E7A59C0D5365E21DEA5328B183D540DA779A38CDF2B55B710A3B8181A853D04482829AA656C77B94DDEE372B77CAC11D8781F3C554D54C3A74583E630C3FF32063672E903DD4382A85E577043B0ED269D9A5523E1DD32974640BE7AE82E94A13906B19DC3521F1950E1A5FF054C593D413446B3646E738636878C5D2FC64BD602F7D731F7CFCBC297A979A4B2922972EFC28BFF3EA51CA87B504AB011FDECF3344216DC8C99921BBED2E697203B4E994E4F88CCD491329A4E15BA8D757E6804C0F13C0D37D9064B1648C435229CBA60E53A764D9CD4047FED34209F16005BA94D07E0C12E1C27C38406181C347096472AB7145FE3B24069CE1BAF274408FA690391B73CFA1CA56ECD497D5770E01BBB2B8BD424873CFDD8B3D1A28AEDD7DC4172A5489194529A880F1115AFEFF020224FFC7F1B199D7408D888F765CA6F75F4F000508A9707BEEC60E144572D8E13FEE95956968628003148332ECF06136A9504E4DFDAC8BB6BD4A1267809478E43D693A6E8ED6C104A05908A37C75087BDB5954CC6D0FABAA841B86F5DB6473134015F75D28CEC95E0CB87B0A687D37A704B9B7EA7BB9DEB76BA63DEEB2F0ACD7C1140AD02A7FF1422B3FB8CED19962738FFE453F561E908519451F884A015003E65D98D81A61CD8FD7DA2AD14BB4EB2E7FA80E28BFF59DCD12D9065B9549641FE599F41E582E1821C2CCAC0C4DBA6159E0BE7F0BB2847F2299EE23010B7114925C4AC8C2BBA87EDF43694A1824B08745686EA845C02A7BCE2D98F973D49500E79F563B8F0DC054C5F1D30E4826093BB5944D7095E662CF008628278D85C869A69B634AEA2735D4AD59CE0DAB044D6D2F94004A2B9C1591F2FB1F7F88ACD7F5AB252B7E72922287E4473014D26CC43AA3D631B6A0E98444AA8E31186F8A7922C3A6F911A3AD2F03F4372F143F89AF8B758DF20FF26255C31E5E8C3DA86816318AEE2DAAD6655A36CC36A375EDBDFDA241BE8DE13080163D59E6221A90299AF4D070D6658D8EEAA88553DDC03778EF9872DCAF19114F8D6D8B0C13ACB647C49FB0102D79A388148C52BAE905C9D787015463125B336193B495F16C8E35F2146F496EBF780C859EDFD95773B6A19E9E5E582220D2DADD167379F632D8AAD683CC9B1EAA77F141DA66B6F002339C6DAF929F67A57B14191A2829CC55EC5A940716F09FC5A9B08C23881D956A36E2BADFC84E5427A866E2E76B476B2A0DE1B9CD3FB6AE156C74A7689B08D25DCA0AEC4191E33CC091B181F8C67BFE2A5DD82EDFC226D49A1AC7BE8936C27DE05D243209276003A63875B43E20BDBF79D8CA81F7AC56AE94EF46CB3FC9BE5473DC0C9715BF8DAC744D093350B4C75560CB1C099681F0E440FD682A387E7AA70B3F335546F026C5351440A5D61E3C83BF96F07A46DE986A21F7E5A2EB0A28AAC2087C3896E933B708B4A44234EA8AE48946C6A10213521A553A020D7C9991CC31526A4E94D210F97E4216BD88504779F3EA21D52C2A205FA47CCD2405D26F411A9312D561D4CBCA0763311A0EBA45FA48CCE15C80419FE5CA04BF416395D25265280E0382BFDCC868C719874DDC795329E07CBF5C8FE83A3CCC4DDA4B31B15702AEBF561F53634315EC8B1A5423470DAB46963403E10EEC58E9E1C5C4DCB3935B518D4623E6C8C68A4FB13B492B155E06EE3D16BCE6C3E79AC891742AF3B8D01767542EB6B7BC8EC71579136ADE2BC39FD07AFA207C3D3012229267DCDF6CC3E9DB25AD378F836763BC517DDA13CD264C90983D73E2C9E2703740DB4C38773B49625967933F41FF0220F40C976385367675005FE20AECB7FBC1EF5C71255BEFE4A39CE59ADA83F148C5E4383BC65F0440A1F5826537EE40CD55862E7239ABFAA1D458F64F7423668EFE8E1E979B34EE18A71F82BD1975E09EE1613D9F45870B98B9855D28F3C00DA8B9740F50E0C51E506DF1C7C804FBD2A1DD912DEE604B2080069AFC5095D23D726D94552EB7986C14C86ADB5A1306C6B074F5D3605A6C2189C4D22E1C8795FDB77803C60E3CC04B82A021BB89BE2E3C45AC5B914966D4A3DDCBC32A760ED7BA6291B6C728F13AC684A6B6F0ECBEFC06B54249FDB3633A786E0822B1A2E43C3F59011263DA540FE2B4B5D31F5FE2A0954E45E106E1255EEFF02A717BB9061BDB528CBBB7854956824311A755B68EC1C179213C418AE21B7DCFFA9F9DFD8CAE58FA1D0B88670E5A25934E7F07916844AC026CD5E1F768978FC0F3AF91922014D75E50F4253A2C2E3420DB1856C206CA20651703D4AE01AE22B8C3DAC60B8961586986F17A93258ED44C83DA6276E04A9D0E03B04E93939FBBDBCAD6833960F02337AB1211E72BC180EED3DC605052CF86F31F1960DD8DD46E848584E909800DCF13146B6A71A79AA92F2BF8D254132423794FC2416A412B001C47A9875FA4B866868A9A88487FE410F81BE5236B13BCFE9302BA9443F3C4523CD3ACE7DE93CBEF6D9E4E6090214494027F065AA834CF24F6CBA9293A02934E52F20C1C2BC9609B559846B643BF49CAE7FB8332BB91B719E1F1838D7F63F5C3A7D5107431E3B854E03EBA795DE9C717B0C9D692B566138CB338C3E5C6F5931DC33214B74F3A116BED19B91E27572A5EB875F14EE81A8B5F2CA2C98739C93C04611335D221EE922C62F02E304BBE507E82E329B4359807F595D11BB5CF761619FF685034004A5ABC89BA4DE82FFAAD693221002F399A5F1D11302B68291F85079D10AA2D1C4536C70C7739CD16293EAD510CE3A04D91AB80D5BD2D02A93C6CF5BEF703A2273E628EA272987167F8A3E2F8E53C3FBC907C2A5AB051653051266470D74F14B632AFBEE256AB24CFC2C7F6E035A0580EC3D1DC11DB98C7FE605183884CF3D7F24C4B3A3D05A697F95601C367EDE314FD3D7CDCD618DB63BB7599E4577D6ED62E891CFD85C9B0E43F0CA588CF282A5E5070AF8E9CBEA9F1AA1FEC53CF411C3EC301967130267B769177AF5508DAFB62F5AD7D8AC9445C62E59612A54F77B1606BD9EEA24998726ACA7EBBCE63F73C143A56435B3EEFD63310A6DF908979DE38D04D7CC75B5FAD9548071D7D1647EC6AAD9663DB156622FBA093B1C470C535BA155FF88EB90BB7CAC9BF4CD367F8CF68CBC5129A577C31BAEFDA834CABB52E0D02FF1A44FF9E85BC88F4BE69DD5C1EB9D4A547395DCDF8DD82C1F98252266BA21395B4272A50420000A40A50CCB22546CB50617F2D7FCE96DA0DAA8FD668D3DF70F3639B45D25910B8970A6F26ADB6001DBEC9D78E94D020307793CAC8A4E69865CD8B7565765C4C4686B0941F7945095A4ECF808A46930AC66F0B569908E97E9557400D265AD10D2A4CDD023D421922BAF3DFD93D7120E4F2C4D20900BB32AE8242295B3EC9D61252BDCE14AC6EF5B2F73895DC27E5CDBD878F24369A533B33DD67ED239C61C333C9FD879D267446F1DEE3837B66C410E3AB8B8BC4C782CBD8449C7B2CC89E81C0CD0DBBC2DD6FE3AE7F2537DC3BE3E7EBCE49EB63A76FEE5ED0209650BF6E06344FE0A46693855AF3D87FC61DFA621D5CDCF0A39C80F2715392DF2C12685F37390982BAF96FF6475CB436DBC6DBCE4716781BE983393D197902C1C757B831BA1564149711D3D8231E908C8458AEFE45C3147A71F87E046193C6BF35FCCAABF592E48724C7E0582077FF94EF1DAE47D911BDBCC43876978BA38C7D6372B23DF650AD5F27C95BDBBA2352D492B5873C3835ECD142C5D3007A4D16FA35DEFD4C21F929A8B02ACB71CFCFB9540B489F9F0C2609CC2E26C5E85847D06E82B7E53B37C698659482300A18B9C42E962E9CDA8968ACAB266CF1340298693C87CC18AF9A52832449EAB89E84FE63BCBD45AC3F9BDAE65E62EE8F97F1165E40D874E9EF75763972A9413C81D18A1E551AE0208053B6276841F8470C23BF48F493BE5A0AA94AC10CB69F0E60267BDAE69EEE405312F8056A54C47A80CD01DC81EEABEBD8336FFEDBE19E8D66C566658D0F8EBE7999BFDD8FBC9BCCC59480F98B5701C8CA3156A94794ABFCF3132445832DC9A582AFC83BA2E41B6D64C8A1560880B7DA9976FB61F1C33ED52506570AC3BC65E345629A00A611A442928BD908A93CDDACC7D8D997ADC9AD9EDC836A803FB392593D03A79440D4F2F812B4C76CBD8F01D3B7119E42B98C64865EB2AA1626EF1B4C1877F6E484BC4D776C6C47712E88E9AF422D676103A71EB6CB31F360FC0EE898A6C28950A448D77CC07D513CB6A58D6364CB1480892DEEA7585CC9774A42F6AE49468A65A7215D4112C5B38AE6516798CA6A1281042F435AA4D6872A2AD8FD600638F0297B9C732E3D08AF5EAA9E24334E56C57C931B3D5AD37C5E5A5255B3038311A89C600944C0946008FA7F433EFDF3C60B8CB0083DCE9A46247E7B8C790E9ECCD03CAA10379C13D171B2C912927324A818D3EB762ED56B1B1C6EF068453A75B2C3757AED2D04333A11E21F12060EA5FE58B4649970A194E223CEC7B9D2BE63D57742317E93AA66F422E0AD7689878224E921CC95B53511069CA6C5492779864E3657CAE50AA46CD8BAE0B29D8DDEA69F05D41EEF627749BA606F1A614166D143923E0A43A1822D726613B24F4B7C691205F6C6F569EDC542228BB244A6A133D6A97BA66018F9AC0D79884B2623442536F17D9A3EDCBD00975A5890A34FE87C37B718A67A085A2B3579822D6673A3041BB61EE24072B5B67961B01DE05F348A63E15BFCB48D3EE991BE1699E11A06E02255F522BDBB1F74A5E90BC7F46FAA51EC84D1473BD47A9DDF9B0C226DB8602EF74D9D6A54E2685D6631EF6AC744F51BE6609B30BAA0DFADABAC90F6322C4D6D0CCE589F09A659FD2B4FCD7F70874FC1E32C7602D4A70EE1F1A0424445A3A2015185C80838391C80637B369F43896BB990A00D55D0C44CE10A5BF10C853E7702B9B03AEC1CFEB983ECEB22C22F199776CC7EC01DE1782D27359DE58A4B177B138D59934A2308E937F4A6EDDE167113E87001C6D485974DC7ACD95BD3A0C79436EF4A9305EAF894889CE7AA3FB758C2C98800EE51C37F4204C11716384A997FDD4F9CFDF71723B37B8D4F4B15E457126BB98ED49099F0881E0386F4CCBFB86F246AEA4A2790D1B62B6430E35271025CE8C710A1413D293959B8264F6C41FEA8EB007FFAF1FE0B57A0672F9ADB6734C2F8FBE051DE3B3E80670F8169DCFB30CB6EA4A7A9A7E56ECAFD79B4A8C7E35796F94E89A1795D9B7E19FC40253E27967338BC4883E7205E2D70765C38D8256BB2504BAB38274CD9645F3F9C65CDA32A50D09C1B7F98F0467D9FFE3A7385B6473EC2012F265D06BCD1522BC9B82019163632C63049D60C8F79DAD64BD92EA183B3FEDC67B450B88FFB825E0F3C36443394D6FD02D61D42881823D8E224FB0D93F68D39FE0C8AF40C08C7E5A438B9B686F3280B67CF7998532F53DFC01299C7732A69DE3076F28D14769A324620C5535EED84289AA55B06FFBF2BB4D73FA75AC8EB66609C5486559FCB920A2F4217F7FC25184A27D75B197F6C2A479D6900D1D56CFADAB446F002615736C9D541981928E08B4C4A75B6D9E0ADFA6795E374D71640EB096BD017BC16B9A6E71EFECABECC9E85040D9803C88A5F11F2DAFD49B506CDA6AE0EA5BBA9A627C0E89FA6B0D35FA493E4C853D728B1AB214EFE14E39A999E4872857F039C67024E106454328641577DBC7B74F441B01A613576BF0A21950B079937BC9427464CF4428CA5EC21AA34F55A0F42005845D62355FEA077B9E6111DD2402C715A972F8FA43C317713C71255D834588DBDB63B8F8C1DC5D672B346135E138D835B36334A38B0B1514FDD87EBB6D8FB8DC41D13DA74BE01A1823E417D3F932083FAD5F2FAA0B099DD03A002B5BDA0F1997B65B62E2097B948B406968CF7759AA75936CAA33B3279A1A92882A9C9A7109090E0FACF49790B1AD1BD606D551A806B1EF434D491AE50FCE2FD7B17BD605236E4E20616F7B7E27CA1C0592F25F3AA967EA41A73D72F4FE4F241610D4F7806E603A1A188EED49D4C7EB6F34CEC63F7E8B82F33B16D7CCE26CAC7151F4A7674667406941291FB0B6315CFEDCA1039B1B28C0E3F29FD3AD285B044C3C24D317A4D83D47985A2EB0F661AF5589F647D92554407AB5664187871F4D22869948A11E31E59ABCD8A14E314CC46D88FD8E2F4679C473B02C3FA0B5704BD2ABB65BD4917C3E6ED2CDB400CC4C82F9A7B9BC71C02E0AC7A029C159E894A88426E60599BE62F97223D12E318037A16DF3C2AB4F90A76F6594297CD5580DFC287D080AC03B445A9278F51C61E8D693123262014A2802201D92241C1269ABB4AD0ECC02FFBAE5AF52D2BA5AB08DF8CEF1D5116C809497279E7D4D9754AA5BD616BF0B5EFFD40195FD0BD17A180BCFE0585F8ADB1741F52DC34D403993C71519314F103FC207AED3F205DC546CD7996E2A2CD2712E26008A74B292A75A1011BA15E8D567BFC19A97C4359F5209A93600B81CA32A0CB1333BB535B699C0CE95496EA897292410D8C89B2FB9157002F82CCE582D62ADC159C08C85357FCAA0F9A10D137F09C5CB1F427725732BF1AE5D12C95045DC4B8F04BADF4A3BD6DA9D99E30D0C79C3FC49763883F77397D1908A74D84FC9BE14BE0AE638B4CB261FAC04EE77AA2BAA5745BCB4282ACBFEB4945EC820CF507B834C70DB6ADAD6A3E5D0796B375B6A29B42D7564EF51E43943C708FA28C4F565D5867FD7E6260D44FCDBB491B4F1394120002271EAD1C1DEC08CE3E942EB8D681D837EF9387E960B33762B59459CC1812E8A847F7EAD81E0220F4F0A4896DAE94C2C6967EC578AA4D56D0D7CF72C53D17D57DD00079D10CF2E98282C2060B483735BFEF6197619182D592E3D9D7540929DEA260D6BEE7F38A21C267AD0B7194A1FD37082117529F2DBC64A39E170343ED8B8717796D802A57B57975A1232ECD48C79D2713EC1EF8F6FC731118B78F081B680CCB79A0A4232DE73D1EACA3455AF0331921195A8E70DA28D8BD04C178198D55D6DF1C5A8C1B641C26FCE25270A6D07D2C1B52A824A2C089988C35191A98A6085EF3A000D22452AD2C55DB0B87CF00C6BF2AB7E66E341C03BAEA55899AC814DC988391250A4D666A9F98F4A1CB16C80B23C4A97309AD0D42EA85A2776D3F097587648CE0564826CE4127DD1E7314EA9A9CF4910E17EBDCC40B4CEC74647B7271C3B735DC5AE8E958CC30617F01C3BD61B5515C91C75B98EFD8DF09D8BA8DBD513C5CCDBF7C5152C340500B09EDFBA42EBCA260DA419FE8856B4B1F904782815261B3924A5F9347673C02CA252B666D6498518A79682B321A8BCF2514CFE07E387DE3CCA6F92AFD4C31D2E83381A1FA741F5760845202EDFBB55582D3CFF2FF2B04A7D469825F1976CD2A8C388FF958F88B4E52C5916B30E6FE03BB9FF6D721ABAC2E7EC46CF6DAC3362929F00321FBB0BA622E1E87A279D5B01D43326D4AD95574CCB2B77E5C8F5E7E9670F4F1EAC11DC3716E070A8BA20E1FFB4FDBB0B75FDACEEC6E4C426C31CDCFA95DF4A1E4E6FCA2D6D674E641CD707281B9D5EDB5DEF9AC42A212A1DCC342DE5C1057E49763A791EE7635F8194533BB2541A690688CAC2293FF19F26B12E2CAAD572B616DCD9058918866155347FC3B7DCFC5A9FD84A9E020126E57CB6DBB5C3788D8B20C9D00EA68DCB344988BC7D92AC98E3E6A278CB536F0AD1A0D0DD8A60D2EE9775347EA0654566734F3687FC669D270A4FD2574770740E1E85D8453E4C7A8A97BEB84F229A630632B1CC90857FE2666E08FB105AAEC690B22B9035766D5A2ECDD63326AF9B48A3247D7F4007947B9BEC525611223B0957532B818B5A686A027FC0C5DB24A849EB573CCC55C204A2EA6927CB1E811F17E4E23D11399C3F88874B79B455D9470FB47833B10258B6739C120533FCC0A64E97B87DFFAABC3982A62DAD557F01F926B2D01A44DA5845BAB6C784D1E226539663F9AB7C59FAC757CF8C930D0BA7CC74CD98B7A60C755BF63B2362F854BD0D4327C6829E53421122BE8E46C54687958B142857039E565DABBC660F289F40E3036E5FCB332644C9BFE40F88DD932FAAF338188D4B7DE60C3D9C2153BD024ECC1BF3E7B653A3704A14B7EA6437B03FF83DBFB25413528AE9D4E8BACE91B93323BF6006A982D34981AD395D3E52BD33337D02A8370C088C2616B9CFA71CEA8B1AC23D3053F85C4B4B195B7F1F7B2BC11D0CD5FB5FA4FA64D95D14F0FBC26873F31703E15A74EA25CDACE3D8AD595A2EEFF6385406A2CD9F182C24B05D842DA26AD7CB762C76E5B255803BC6A281243700ED74093B6974B88EF179FE09D70483CDE0282E26EE377D526B088CAE8B72AF4D82DC178D71FD05BA2DDF7A912602EDB6153F45E3250571D1CA84D49E543B3717DE7E876D19C543F0A0EB8AF9774A6E2E7A17C679DCF2345D4775F292B150FD5789DEAD85DD1536E5FBD9D3445FD5A78FD7A9761A8D588A8CC97720523513DED7D6E95CDF640C7F7D0667B3A09134571A2E741DF8A007EA56765AE6EC046BEE94439E675BB49B6E32EA14F56AA3FAB2AA45FEAF65938EFA7D83934B5487896152F7C0409BA2572C18C7EE4EF61A5484963586E10F709A1396AA2565DCD9C0B4736B56D205851D2452CEC5E324FA4CB6B8698EAC2C9CE9074A17558F23FB7BC14868B8E73B424C8CABFADCD4C5A3EBB6903CEB7543FFCC5585B85BFFA94DD2AA324C505D7F08EDBCC0024DA4E5C1671B6CE012E605AA6B1BFD58D92E8353F05DA5893E3D3822D76338A4D8013297C5F643D648A53F62E8920674B58675C64D0C662FA08FBBC1E9FBC088FCA457E14608CB0D0272A4C77735BA3A7A26D1DB6DD2D0845178F4FE627EBA84ABD7679B2F86E3D9AF2171927FC74104A9F198733FDECEE23D2D51431641F657A85D63244E96D2636096315F291C019197E73C9E11B5C5A015C12A43D176F6D33E34D9C423C1050545E4CB89DAB3E6A58A8E2FF3415B4831A91E72718E5D09832C4970D3C3D600CAE926FF8A24ED9D14A5E5A623C7A777639C145D306F20A9A5B4DB24C99F824F40B5BF387A0E1B6B49468A62755CC437E787DF96748EA3FA5E537922307E7C0694699D227350BA020A5DECF680599AD95F00C0D73F50B13E8385C04BA03C0F52BEED8F4C1C1D9132A3B9608AA1039169A8FA850DDB4CF01966CF881C64EBFD758CFB245B486FE5689C20FDA833F67DDDAD4CFE4638BFC7F6D967F039786A330D8393EAC7A52150DD7D5D30F79A09CFA383A5B7E96E3B2EB6BBD66CB3D6D844B0BEE333E06D027E4379259465A38516686255550A6074B1814100A60F84BBC3CE560AAD08B21821CE740D90EF12452BD53516DC90E60A4480CA758401660FE1A6EE8A88B433C14698CCC86C4A270338DDEA2DE2A5ABBE1C5CDA9F2C222590AC705A0C86F927D830EE91FD1A3B9ADF35C4EA05915FAB0F988D17C522971B7FD2431088020CABA03543C1E76255045A198EF900099FF2A982D626A4DD09F0E3B573E0660E2B09A8C0E7000DDCFAD466E890A4746D92AC1877A53215E35B9D3EE718B92A14706FBBC87B18A42D91CD4017AF5C6D2F549A052A09B57CBF186D1E9C004624D4A3A7A8A03A89C47353EACFB90647B2C6B29F73C506835E320DB751A1EAD0538B74DFD39B383FD7042B51CD82CF10DCE0C984AE75C12DE1A5BD614045DC91989095D32250EB1137827EC5E0FF91D5AE8807389D238AC331185DDC332C90CC9EE4394FD9EB81CDF04DE8D22F6856C5BEE1B4E5739FFB19CC8A948D24D764947FCBC99218B4DA2C439DC410BE7262C062486D55AEF977C09A36D9262E2BAA088383422BAAA20F4D8F86C3A4D8422851B6343AA15C4A1DD5EF0ACDE63937BE7D7F7444286BF00621CB279F49B47835925D71E07FCFED911E94D378A73548539CEAA7D8D84EEF4BE67EADF7C7B57452CDC106033D2B22DBB25D05856C4617E144F4E23094ED4D2BCB7A5DD1DEDD661432CD2AA369EC0B32169D52314A8646699B49DA27FC6C233C449884F540068127850D6E383DA6CA607A0806561363928FBB259AB5D98ECCF00B697E1810CB5422AC9B2F12AF205CB3721B5FE1680BE7C8E9B89A6EEE126E75866EB44B48B3DB48CA63818E6EB691002B2130B6C591F2605152E382251153A042708781773146D57C7B16D62D5FCC2252CCDCFF10EFFA1BCF2D4305C34E4F19F07595102B0A23929CC5470B9E16060DFB7D0F28956492A5DCC8825FAB6133D490BE3BCA7141F049A52548BB792E8A84B0C6A2F7B13196BB8C27DBCDAE620019EC8E480E6521393C98866B04863B8736039F42C23CA001E3B83178E1A65FAC153CC2F32EAD32440FF6A8A4AC29762903B32140B49BB59E591B6480BACEDEC7CDC77D89C82F60E23B5B127B72B9A271996E2C9FE310346D739045EDC020668D69D7EFA248FD6F786FDC4A5199685D68084A1B4ECB0719A8781EA366B630F0C18D023F57CB302D92F8153FB99ACA4EC2E16C943FCB944650EF9733C97F66F986945B8531B65CA531798CA139D8A31A1CC2657515B0FF4235F0EA527E0EFF2E83B9F160857956C894E60AD59345C95D2319CA82ED49306307B75800036D0CFE6689BF9672F9D065678861F7E174489B789FAC5D6942A792ABAC3D85359C36D35286A51DB42C022B305D182E65A9D11602B6D2E88AEC5A556EDF3979E1660F162409FA90C3FC1377370C9389CDCB305772A225671345D30055A528F27AAA77819A7819F64AD7185202827EBE0D92A1E30B05BC17F45E8422E2A0DFCD3BF1347841339842F908E30502CEDFFBDBF01E2A1CA4C8F8BFD8489FB6A60F1EA4A2BEA49A9F986A9BFE1792F97D0842B74AFAD6E97902FA043A087F2DF276C5D30DBB274A824208EEC77AF7B84B0D4D18DB495E976EE802AD61672EACD514F7CBD355FC7F11673D9198447BD142B6AC1A6225851BA2D7CD3565CBD3D8A7341915B40F713165D6B4B949140B91AF9F89E46FD45CE4ECCB2C259BAC0DE91ACC11442A8C146623A0A64ABA992816FBCE99C835554B6ECA8C53A04CF70E81067BA2854FD2332E9A3813F9A3F1E039D4CE9F349FC1BF9327492DA648740E5ADB7E470B513B9375D439FADDB93A6B928440A31FF6F1184C33E1342CD3B29AD04C22AA2D68F4F3613450B7CDB2A3590CF60C03401949AEA3AF2060047A25F37B6B4D81783A6E3BEB0E6F3EDF523667F25D6093FFC23C0B38C99DFEAA9A6EECA7A7322ACA8417F80F819CE53A3090811733E71EA07B5D3773553335EA7BDED1128D48087FCC05E5860546186B657233B4A370B26969D7E179C49BE552B0D17DB34537645DA06F6B1E40A897FA632D1623303671A20F38971357C139602A03BD170783F6F061FA433986AE9747F3D235B662D29D1DB7F077004E34133EEBA14B2C271F3C160AB1FEA528C85404EF08A33A23256BB47A0E55AFF01ADB49845AF72057F2B9FCC6C9927EC5EFC0B510D65D81A123D082B3AA20CBAAA1301C425CA97CD8EBB2FC9D0DFA17C8F0010343BE3E4B2764A269C598B8E541E02295707B6D30968C75B5EF76A1E27B6C5190B818AB5D543E3E8525D007AE95B948B53E81E96D349041DD97AC579ABC59FE1AC5302691FA88F8453D90BA87796491EDE09F70C67422AE3BA6A5914219321E3EE34DD59379E412009869E6A611D4E0CFC7A56D645AF62E130779438BB89CA54BD04ECF012C94B59A800987B449BA086C859C91F1FF5D4C2C5A558342B2B836DD427D45C07D8789EBAD66C2445F5931EFCDD4EEC651EE22081953C30F7DFFBE1B98A8295A545F829C256060A36849FD80D4BEA694A404B4252D86496521EA0EF4BA1415BE3C3A4FE687E67E715D15C7DA44CD7FA18A010F46E8F9221466CE53503D3A9840FB77D7765B84D5D4CA19223CCB22FE81BE7215C8DD0E49C84D787FA921456A6943C5892539062313FE024A0C2E24D15D75A8097D0D393C422099BED9259878E4EA02ECF174CA3B8F5C80D01E998487123C10EC2E04043A4D87154BEB2087C7194F74201BDC8FAE3D8F202EE83A21DF9E5B3B0908C9321F4431CF108444CF5A93175B4563B253F84897E3F720E8EE87E6B268DCABA7017BB07C5DD29133B5878B6CBEF6DDE103E3E34D8BF6DF151A3A24C077D8529BCD02681DC9E5701F93939B5AAC45FACE46A5855E4B4C3AF82C303999937C48CF3D88734EAF75E3EAF171768F6F48FE2CD5FA9BF40328E642D2510E331BE5C3AD6632875A9BD51D6904B95BD5C17B24C43971D45055C7AC0299017249A936BA40E3BD17B63B510F4B7A3B4F236BC073EEF4F61CE561D893AB5B0A720D70AC138D47CCD5426C827E1AB13B2C140FD6E6903EEAE7B30A06851759FDAA28810A258306160452486199EDD0F2B44F32361E8EDD4A458A93012DDB8B4C08A565DC49EC6BAFCC759237AE69DA0DA41251D9E7E8509E4A09A8917418C6B2705CF7C463CFE64A67F88749CCB80B8B1B20A33B5D9DC3F1E74BAE981AC670EE2D86D7B4C8E891F3D5C98B3F5455423412C641C0315F27A263DE975D8D6DE1F17D6FA51262D8E6B3F32AFD9EFB72958460B5D58F4BB5CBF5AE1CFF2D2835FA294760854FF53522E2D52981FC276793EE5EDA989EDC921CCDC50D4B63A908BF47587E4FAAE3A8A1BCE3A21E331D9230B6B1C4CEC7743AEBB83EE003690D2CCB27301D88518A43B266A2E2DA78742230B2258E5D1855A990827B3A8B900362791AE617A3DC0BA2D12766A70316229028E09F96EAD7C773158A549AD9E500751D812D3F759468E22B86FF85DFB0C7C79148CE152A813D0B2D3979A3DBE3765102204ECDA68D24FA6330D599481516603607FA04439B9A0498BCB359F1EE20D65A5A84816AD43750AD1838EF4597876291C98E82E1F46A6963C7726C89470E778BD0A65CAD07E2A167AF6067CD058152E9C14979152103A7235DCD0755BDD793F9B23AE031133756EE3448CD1F93985B40EBE72F7F54124838E3787182147B84C3B3BF8CA901253E04BF808E3F3AEB84C05AB5CCA8D0C755D7B0CB02BF9E06F3113FFC098557DA3F439D397251D48D2FADEBC40286A17C66E0A8533328DB4DFCE9BE32F115A293567E4EA004157045D6996C7B73F27ED2F25536C2A381996257FD23EE4D962B663940C2A57442F5A7CB41B4E6615B223E91B55A7F98B416B2A6003D3A27B496F5E1A446DA9F0DED6A5AA0D7C283F34DEF97065049150B136B2D0F7A4207C904D836EB1E35BCA84EF2780068ACEF1942C2B31BF1F89CF218044A30CED4AF8743EF034D7F526989B99390D79D7454961C4606F6AD659487A803F4CF5FDA4F0A039664F3EA84AC4D7E0E805759A79B959906655FFE28609963BD8BEF5C82A50922A9836ADB18C9416358C092D69292C40FB3BA288D4029FC6B200E59C7C2FC9423360B11F157E5286D0A796A74A088E257F00954CA0789E3507E572EF3DBA68E3CDC7AE328F2072BEABC0E901D5F5DB1A1F41D14732F471F089FEA9D022660949C80D350B37B7F2F551DC22F665DE00FDA868F131E30C110CCB4C746663513E9DA22E143796EE2E241CDC47E2BBC148A230157C0378193F75248FE64858899B3B357CDC21CE8A6C79D2438569CDBE5A44015038C0CB8C260EA75698C272C9D5B169A4D2FDA21AAAB502DE3C4609707BA7C1B2E118F3F5B8A17DEE019465C6EA6B098139BCA2E6873D9945791F7D5488EDC2FD08E09973A55415ECC21556C422F2E0057387363A3D762FE41B054D3C5ED107C4628C15A409C0ECF77AA5AB911A7D896C9D106DB44720A42E0F89447B9E8D277859C941A5E49F4A577C0B6B0049972DBEA98A1E57991F6C0D239BA052CF369D94CEA0D2AC4A0F178D0652F9E028E12423D8D14D7C8ACE33F7E9FA9C2F8B987F89B1BC22190146FBF99B124D028A5AC57D4B21A8F2A2C9BF1F67841BCF3F47051EBD205822DD7F13AEF2D9EEC54D465D89C987851630D0F7930B4451914393C31468126AE0C607ADF304433E581A71C5A19356231A9C7D9810D282F3DBE109F6789BB997BB73E97650844F62FA1954D9BA9C227A23A65F500AB459AEC13D0D25DAC9DC885A01912C70005F440B0EB1A2C0FAD96C7D597DA59AD0199F7978D8DC871BCD4B8143420F5F9900600FEEF0D8C908CD259AAF1D446841B2F76827CE888E1B8A2C2C2FA63AA0C63764E173FF170526FA3D7973931E6C1317F9E7175BEC6F2D6D9FB36730A604879CE7B2FF5350D1A689A57C5D366EA0C219A8E839838A0FCF45A2ED7FD08AEFD2BEC15C47391D9D18E42C18F03B068DE6FC160C176499D06ADBBAD50F1773D036CE5AAA26247B4ED0B68415D4CD101C8B97567290CE4068F4CAE9A778F996A3831FFEF44CA682100850BB5B6290AD8CAA7C10878352C707C0E7D0D0D90462DD46C6317755E4151FAF1755106983B75B6F0A3FCB923D56BE32573D59062008A48AD6A4A87D6242FB13DCC2A7354D470633D292F3EC598A24AF75362BF3A4096C7400A1E97E31FFD716D75BDE945F8FE5A2A40C8C557A5181E7C4CF155F0E1F168A0F25F3FBD0D24C743977C12F1B6D0B4CA5042017194AC81EDE63A324CFE149482AEB04CE5209F0BD768C7CE90508DFBF5EA9A66A5A6FE038746852359919DDC76795A68A4B522C86D5CFDBA46BBE140DCC5B377F70FFD7B776AD3A7885C43A9A7B5BCE599415FCAEB0CA7EBC1D51573F95822CB3135B602ADF40AEDE049501F6BA26EFC18D4A5E9814EA2C2C4CF301AF331672896A9B383F7E582F3A09BAE7BBCB9AB088A0D7FE43827C393E5EC27F08BC5E85C4705EDDA2862BEC9A4DD0ED9BC56D592F6062F75719ADB6A9CF1C8C2526D6B79E4F53932AEF6031D30498587788B0569F6C0FB226105FE5CA0B8B5239C3D11C795674DFE2C8B46FE16DFE5C389888775E56BD14DAE9985960D0817E5354FAE5F20147B30001A9E8D89E5BDEC5567561A27A08A84E2DEF8182060C003A8CF5DD667AC830497A9D4BD1D003019630FCA28C1E272C8C2EADB1DBECEF8E2CAB9B60BAFDFBD6D75562294E0E169506FACDFDC559581B86559720FB517AD8A010D64D03ADDD2CF9CFE8D2FD244A4378DE585F2DC009B2ED6DAC2FEC87D6097A25CC720E7DC989332C5A341503B7D03CC57A875A1A26DBA88D840F24EF8C86DBBEF37905CBA382A8A5C9980504D0CD6CEBCF00F7335705BFAFDE3C3F82EB3225E5EC63D6D69EFD015C9C51AF71B23215E4BBA667AD525852CA6C29AE3AB303AFDE0279C45668D02A2AA96625FA7B41B268E783F829A3A2464F22436917940E6EB3B1D313F794BAD2514B35954718CB2D92CD7865EBA847D388F046CC7D5D3E3B57D12714E9BFD4A09A18A8A626CDDBB21FB599EBE030CED022E16D6C65085EFC6911F7D154B8C9787D3C4107110809219414377EB07AF5441DFAF02EED0B6C7A6BAB1D9902D7A3211F22536AEC7A0A365BAC29BF69021B46EC80143CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E");
+        byte[] sk = Hex.decode("FFEB02F6010609070CF80CFCFD01F7FBFBFE02010004FFF002020900F2020602FC090000FEFC12F6F6F9F601F6F904F9030101FE04FF020203010B0706FEFFF7040A040900F8050805FDF3FE070D060C0A00FE0CFE0D0D02FBFE0B04EFF90002F7FDFF030307F303FFF8F5F4F60505F9150304FD0AFFFA1500FBF409F801FEF909F3F3010201FCF90B17FDFD0101FC00F002F000FB08050EFEFD06F504F5F906EDFEFEF3FB07FB01FC0106FB0304FBF6080005FDF605FA00FB07FAFFFA07F8FAFF050810EC0001FDF107F9F2EC03F408ED06FFF70009F80C06F60102F6FBF4FB010207FB0A0304F9F6F90C02F80007F30D0C04F5FA0702FDFDFBFE0607FFF0FFF803F30D07FBFB010300FEF9FEFB0A13F007030A06FCFE03FD00FEFCFFFF070B1004F8FAFFFC0D0605FE0306F803FCF30506FD0211FD060507F8FCEF0CFE00FBF80DF20A0F02F8080011FD01EEFDECFDFBFDF5F8F4011210FF0403F8F3EB0D050212F3F80609FFF903F5F309F8FEFF0AFCF6000EF80BF6FEFA0C01F4F3F2F508FA02F8FF0A060009F9F70EFD04FB060005F3FD02FE01FC0DF30AF706090CFC0006FDF80AFD03FFF80BFA0CF30B070B0B07FC1003FB1404F5EC0709030EF701F705FB00FAF2F500000A0406FF08F5F202F8150F070BFFFEFA0603F9040303FC0206F8FCFDF6030507FF03F4070409080C1106F107F20AFAF3FEFF070CFCFFF4F5F10710FDFC070EF7F803FE060103FFF4F6F50D0B0202EFF30CFAF5F80C05FEFE070802F8FCFCFD02F81B100404080213F8F90C04FFFA080AFCFBFAF903F600FB00F60503FB0106F2FEE80003FBF9FFFD14FE04F50C03F70103F2FE00F5FAF600FDEF03F70AF70604FAECFBF70EFF01FAFE080803F20F0D1704FC08ED00F304F904FA020403F207FE0DFDFA040B1006F7F9EF06FCF909F903F7FD06F602080501FDF9FAF40106FA07FB0CF50C07FF12F702080A0900FDFFF8040A0BF20D04FD0F00F8F8FBFC00FAF0F91705F901EAFCFE0304FBF7F6FF0E0802FF0FFC020B02FC0801FE03F2FEFCFBF7FFEFFBF503F8F707000612FD0605040404F7F3F9F6040B01000009F5F901050202FCFCFA03FD1501F6F104000DFBF7000DFF0EFFFE0000030804F1FDF313F403FCFEF7090E0708F702FE0C040BFE0812FBFE0EFB0311F7FBFF00FC01F8F20405F502F40008F8F70FF705FA06E8FD09F8FBF2FB0704FCF4F3F5F9FE0AFFFF05020D06F4020413F500F703E7FFE30401FB04F9EFF407F9FF04000EFDFF0707EF0509FB03F506F9FCFC0400FF0900F3050AF50CEAF7030905EEF50C06FDF8FBFDFBF40400F902FC0300F6FFFDF7FCF1F014FB03FB09FE15FCFD02FAFF07FA05FE01070E0402FDF31102FE010802F902F4F90AF60408F9FBFCFBF503140FFB0DF80B0DFAF9FBFC0102FC06FA03FDFC04F90DFEFB0301F3010C0FFE0FF90207F80703FEEDFF0A000407F1F8000807F7051306FA090800FE0EF4F6F6080AF503F112F106FCFCFEFD0BFE020CF40DFEFFFE0F01F6FF08FE03F40B02F50406F401020301ED01080508F70907FA02FDFEFC030108FE08F708F6FBFD02FFFF0000FBF9F7030505F6060809000BF8FB00FC02FFEAFAFB030309F806020109070DFC030104FDF701F3030518F9FD0800F9F60DFCFF04F8F41108FF0202FE03F6FC02FCF706FCFDFE08FAFB04FA0B01FAFA080209FB0CF1050C0EFB03FE02FEF80CFFFD150803F7FDEB09F8EEF1F2EFFDFBFAFD0A08FEF7ED06FDF4FC03F7FAFBEF07FF06050A0307F710FB04F5FD0100E901FE09F0EBF6F203FBFCFBFA0008FE02FF0A00FA16FBFFF70BF80FEDFB0000EF01FEF900F9140C0E0901010606FDF2F4FC0FFF0EFF0EF8FF06F9080403FC04080BF7FA01F4040705FEFEFC0203010C090AF1FC01FC06FB01ECF3000B010E08EDF0FEF9FBF4FB0DF506F4F007F6FCF3030D0A04FBF9FDFA00000D010C00FBF0010100EAF7030DFFF608FCFEFB0207F601FCF80109020010FF100702F9EFF800F40109FCFE08F8FDF508F2F6FBF6010EFC02FE0101020D030BFCFBF2ED01F8010210080EFCFA000B120017FF051711FF03FAFBFB17F805FAF9FB0102FD09000CF90B1A0AF500FCF60EF7FCFC09F1050DFAF9FEFAFEF7F50CEBF5F8F3FAFCF6F6FAF308F6F505F6F9060A00EC05020AFAFDF9F50200FA0306F707F9FC150A000D0702070B03FAF5FE0EF7160D06FF050004F9FCFB05010107F2F50103F60305030610FF0B070A03F704F602EEF411FFF9F5FBF2F5F00101FE080BF303FB090402F30509031107020102FDF502F5030301F2F8FCFC07EE0A04F4FE030406040802F406F4060FF2020DFDF803FE01FC030AFF06F50A00FDFB12FB00F4EC04FB000B05FFFFF60EEE02FFFFF7F301FC0CFC08FB06EE06F8F9FAFCF5F30BEF01F4FE090400FDFF01F8F20BFCF40D0001F708F20304F906F20201FA0BFBF4FDF309060BF6FCFE1007ECF30202F907FEFA0AFBFB05FDF60BFEF5070CFCF60C00F5FB0AF909F1F40302F90D000503010705EDFFFE0103FC1004090C0007F80012010DEBF3ECFE09000807EFEF09010EF202FD0A04FFFBFFFD02F3010906F808030409F9FC0D010C0200060703040606F407FF0006FE05FBFA00FB0FF6FEF9F8091105EF09010300051304070DF80702FF06F30A01000102F7FF0EFD010500EEFBF802F8F6F9FC0602FE100105FBFFF7F8F00AFCF906FF0401FE02050A0205FB03140B040806FAF900F5F3F906F90FF9F604F602F705FFF8FE0A020C09FF08FE0FFDF6F3F6F2070002F8FEF1F90709000301F307EEFCFB06F9F7FBFEF9F50EFDFFFFFFF508FF00FEF6FEEDF7FC02F3150106F60803FCFD030FF9FF0CFD02EF0510F6020801F70AF7F8080502FFFCFC04F7FB090BF1FBFE0AFEEA0407F8F4FAFFFD0FF5EFFBFAF7EFF7FF000E00F906FBFD01F9F91A00EF0C0502FF050BFAFB09FE04FA0703FCFCEE0507010707EBF3130103FD04EFFE0DFB0503020C0D0AF7FB010000FDF409FDFFFDFB010BFBF404F0FC00F8F007030FF8F5FAFAFB00F700071205FEFF02FCFCFFF2FE01F00C020BFB04FB020206FA04021008FA00F609FD0C06FAF009F60FFDFA07FC090E0C090CFDF9F7FAFA07FB04F20BFFFC0500F8050DFF08FB09F30104FDFCFE02FE0AFC0CF9F70903FA06F6FBFF01F305F8000807F1F9040E07010A03010301010D11F502FDFFFEF2F9F904FE02FEFF09F4FF05E9110309F3F504F3F807F2FCFAFFFA0BF501FD040107FDF816FDFCFF040404F9F7060AEBFEFD07F50000050B00FFFDFD0108FFF806F5F60D09F909F610FB05FD09FF0C06080EFFFDFEFAF7F9F70B05FA01F4FE03FFFEFD00FFFCF8030D07040B00FE03F3EF03FE00F5F8FF00FAF9F80CF9FC0102FFFEFE0B0108FAF7F8FFF805FBFE02FF06FD0311F8FF0CFBFEFE060CFE00060200FDFFFA0C00F30EFC060608EFFEFDF2FA0405030707000108FE05FC050403FEF800060408F301EE03100CFFF700F9F2FEFC040800F8F8070104FA07FCFE01EDFE020A09F00EFA0C04F70CFFF002070BF908F50201FDF8F80AF90CF205020BF41103100205FB0104FDF50502FB02F7F907EDF3FBFC0E0E09FF0C0505050E020700FE00090609F60C03040FFCF8FB090AEDFEF4070AF3050D02EDFF05FC0503040A0AED0803FF09F50201150F0CFFFE0604FAFF0EF801F90CFCFBF9FD04FFFC06FF06F6EF01F811F807FFF9060407FEFF00FF060B030100FF02F902FD08F905FCF50700FC0D09FA0302F901040AEE0602F401FDF6021703F50406F803FEEAF10AFB01070916F0FD11000E060204F707F60802040302F60F0B09F802030001040114FC09FCFC0A0B05F5FEF80F04FDF8FFFD01F8F3F6FBFB00070704FF0FF70A06F3050D06F3FEFB040001F103100F06FA03FD04F4FD02FE05FA06FBF40FFC10F11407F8FB0AF6FEFB0303FBF7FCF4FFFFFFFD00FD05050900F20600FC0601F40D070403FFFA0505FB06F806FF01F4FC030910F70A03F20809FEF0FF04FFEC06FA090404FE01EB010106FFFBFDF9FAF4F91703F7FCFC07FD0DFCFF07000AFD07FBFB050CFA0702FCF60E04F9010603FB0501070AF806F6000BF6F4F9100BF4F90904FA0EFE0203FEFE09FAF3FD03F908FCFFFB05FC06FA0DFC0001FEF803F60200FEF80D10F80E0B030503FEF103140A02FB12FD0301FFFAF8F905000408FD070C03FEECFC01FFFA000F0C050203F9090404FA0710FE0DFE0FF4F8080808EEF404030909FE07F805F800F7FFF9F4EFFA0307FE06070005FA0FF5F3050A0400F8FCFDF001F2F312FB0D030E000C00FDF4050904010602F705F20207F708FA050E0B0206EF04F8FC000410F0F30DF7FFF508F7EAFFF9FDFFFA0504FFFE09F5FEFE1607FCFF0AFEF40206FC03F401FBF7FAF7F50402F3F8F9FCF8000903FEFAF801090504060307FEFE070E0AF6F501030FF501F40407031007F90206FF14F7F9F300FCF008FC0C060AFE04F905FEFC080704F6FDFE01F8FBF0FE080A0509FC05F803FF0E02FE0E05FA02FF1000FE050814FFFB03FBFBFF060CECFD01F901060705FC030B0BFE08F5F70300FDEE08F1F5F70204FDFAF4F6FF0705F50AF5030610F90F0C040DFE050001FBF9FCF60A05FE05060107FE0304F11104E3FCFD03FCFAF7FF0805FF0FFDF8FFFF050101FEFCFAF30C08F2F30107FBFEFF10F0080403F40309FF030B00FB04F50DFC09ECFBF506F711FDFEF401FF06ECFA0600020108F70CFC000DF9FBFD0111FA0002FA01F90008FF0B09000AEEFDEF01F506FFFEF909F80CF806FF0503100206F0F700F602F901F202F9FA1402F705F8FEF80A07FF0CFF000DF808FA0FF7110701FDFE0CFE100500FCFE03F1080EF60B080603FA06FB0802F9F30D03FB03FB020903FD080700FAF8FAF8F9F2FC0707FAFF0208F4FFF7FC140001FD090108F80D0AF1EC0AF909FDEE0500F1060507FAFDF9F6F8EE0204F3ED0308FE0B0101FE0C02F303F5FD0BF9F5F00C040102FAFF05FE0C010408FC06FD03F5FEFA030700F9090109FF0A000108FA0901F5FF00EDFEFD070705FB0600FEF2000E09FDFDF1020500EE0503F2F8FFFEFAF8F803050B0602FDFCFA0C00F708F40602EFED04FF0B04FB0115FEFEF6FF0AF7FB190006EF01FA0BFF02FEFE05FB010107FE02F7FD00F10EF4FEFE01010302FEF4030700030BFDF5F903F503F10D0904000A0A09000106FD0DFF02FA03F701FA02FD0C080F0D04FDF603F6F7F900F8FD0609EE0BF702EEF5F4F4F70105FBFA14F708EEFDF1FBF901F7FDFCF3F40204F9030401FA0203F103F90EF4110704FD00FBF001F60905FA04FD11F305FB0A0C0406F7FB04F6030A03020AFB02FD02FEFF0DFDFB04FDF9FC060B0905FBFBFB0BFF01EDF50200FBFD110D070803FDED0D010C0B0006EE0302040108FFFFFE0304F6070102EA0EF90701FD0711FCFDFBFF08FB06FCFD09EFFB0DFDFF01FFFA04FEFB0DF90A060D0407F8060D02FEFF05F90AF60DFEFEFC0F08FA0102FC01F6F6020501F407F90DFBF500030800FDFC02FC020AFE08F909F0F8FEFFFB03FD10F5F3FD04FEF80BF4F5FFF6FCF5FE03F8030107F500FDFF0DF2F2010AF90007010B07F2FFFB05FA080EFB07E9080614F6FE0E020206FA01F4F5FEF4FFFB03F6F2FD0000F60007090103EB0211F1040203F5F90901050D0F05E5F307F909FAF8FEFD00FEFEF9030C0BF2070605FF0806FC0011040309FF02FDFCFF01F404F80AF9F807FB00F5FF04FC0DF3F504F90DFFF900FD05FA0908F90303F50AFE0CFC0DFB0FFD120906FAFA0813FB02F7030107F700FC05FDFBFBFB020D030BFAFCFE1000F9FEFE050303FFFB10FB0104040B0B09FE0102FCF7FCFC02F6F9FFF90BFFF90F01FFFEFEF9F6F2EFFC0309FBFDF701FA12F605F7EE06FCFE04FFFB03EF0814FA05EFFCFBF0FF0BFB18FEF7090BF3F50011020C060604FDF015F706F30EF30206FE0A0403010507F5FB00F902FC0C03F901F718FD02000703FAFCF70B00FCF3040602FDF8090106F800FBFAFF0BF9F902FB0615F9FF00F401F9030709FF0305FCFB08FFFFF505FB00FBFA00F611F9FC0A0A04FE11FF021207F7FBF702FA010305FE01ED07FD030E11F900FE000101FBF608FD080F0D0DFC0BFDF900080F020FFCFBFC030513FDFFFC0EF7FF07FE0C09F4140602F70201030501FB07F30C040D0603FFFE010603F2F90CF70203FBFCFFF2FE0801F507F8FB0405F8FD07000C0102F4010009FDF5FFF2FE08FE01FF07070908F7FD0E0601120205FB010606F8EEF9FDFBFD04F80304150AFFF9FC09FA0BFFFB03F80B07FDFE02080DFC020CFA01FDFA0B0702FDFBFAFF05EB04FC03F1F90C03F9EE0F030B1202FA12060505FFFEFDFBFC01FE02FEFE1400070AF8FD0204F7F80DF10714020805FE02040106050C0315F60704FC1201080A0307FE0004100A05F407FDF60AFEFFFCF40400F9FFFCF9F810F00203FBFFFF07030A00010E04F5F106FDFA0EF80D12FAFAF20EFB05F20307FEFFF2F809FE05070800020009F0F6FEF7EC0A0601F2FEFA0AF508FE060004F606FBFBFC05FDFBFE02F9FCF7011100F6FAECFAF7EF030EF9FD0DFC000202FA0B0103FAFD03FCFAF8000F0703FBFDF4FD08FDED09F70B07FE0006F5F508F202FDFD0BF2020807FB0FFAFFFD04010408FA0803FFFDF7F00001FEF4FD01FDFB0E05F4F8F9F6F3F40406F709FA0EF805F7FE010B0309FBFBFE0706FE0B03000D0701FCF30610F3FEFAFFF6F80311FF09F60804EFFD0F080706F805F104FFFF0605FCFD05F9FB040BF614F5FC0502FA0705F4F6FCF407F4FAFE03F6130600FF06FE05FD0208FEF90AF7FCFFFEFDFC0EF6011101DC12FDF70407F609080D0CFD02030AF4000305FAFAFBF10C05F6F0F9FCFE0009F7000100FC08010DFDF805FDFDF5F7F7EC11011103F9060505F4FE07FE03FC0308040E04FEFB0305F9F5040AEFF9F50601FF010AFE0C00FEF6F9FE0BF80303FE0303FEF40A070D080308FE05FFFCFC11FFF501F9FAFAFBFAF9F701F9F5070703FAF7FCFFFA0C02FE0D07FEF20FFDF8F80705FAFF02FE0308FEFC01010C00F70813061103FFFC01F90CF3050C010F05F50101FB1202F805080D0701F7F302ED0AF40B0CFCFD0005F707F813F9FE02FC0101100401F90CF70B08FE0108010000170004FD06FEFCFB010106F9FFF80702150D02F902F8F1FA10FE03F9FB09F1F6FEFF0906FEEB10FB06FE04FBF5FBF806CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E4F5C254B6292FB5C3DB9561B8793D8AE3E1611423AC0A9F8CFC13E1C85FEC6B5");
+        int smlen = 2881;
+        byte[] sm = Hex.decode("498A996E2B6C14E9C84617F4D63149C4676855D15CDC54A85F1BA00CD5FE550C6120227A77605DE227878113DF3137E13ACEC54F42856C0AECCEB53E1E96760ED6DE0E3F353ED4E4BCDB6B1AAA5CAD3CE1DD882C6BB12D3CF9D1C9888624A54D3E024783DD2C39DD54EA4FFEB280B0E546008CA0C779C8917E8FBBC7A79BEEA32D5E6652FE93D8B4737D47D8DD0C1600D5DB9135B74C0ED6E94D9FF02D06F951FB680902009EB68C34028EEE5176E1D64EF869E14C0AB577197444149CC6EFC60871899B1AC9DA86F8373A1273A97BCE318F40BC6A2AF35CEB52A5EA853BA0588531B87B0E1170EA6F464948A55A84920C97AA9D3960B9A099E947608A1E393C634DBE5DCBC6F0BD55E853470F6AB286CDD126B52A29BE7465542D477E3386821901595597E4D6583B6E16326D4AE71A2967FE20BB755A12CB7037614D3600A2B02D4C8DBEE17670CE7B4B144C3D2BEEE9FF6049B94DE4011DC401687E83211AA37E7A9D043A0582A9A7DFDB0B5A3A4AC8CC58F9634DAD9730311F2CDDEE205FDFBF160DA631FB11D0D0DA9FB42223194B18A5850E80C3537F8148A965EEF7B9A2A81C69DDA4797B28A0FBF4FDB31559F940AB7F47021CCD5881934487A6CB751845BBABCF3D213BAB2C61D21F06927DFAD28581E2F8377E2A36319240DD8380BD85DD1A5BF4828AA4F8F5E11534F4ADFCD1B881DD8A094890B20C4F67F5C1545B4065B84F0B28A64630E332ED2426CE04754BD23E52A90299FF399B240D04CD6677E782409D7AE46F5CF718BA1227CE673C42B1CC0E759429F6486AEEF5CF9BE96087ADAC32606B3D2FA53254C62B9617FE0C8D0490C7C7A77C9D54DFF076D44DE2864E3AF3037511C05D2603354C2AC19D94B6CCB6C6362D57B85C6D3E40E5960F7D3DD00ACE24B07AC41014CDF58B106A6D61B0909DDABE19AB78FA29C5987E7CDE6D5D084C278417645995976C74CB7D9A7D6597A83CE1654209325B788F056220F672CCB16051EC94B34F89B6DD33859F6961432294DAA66805488E894BBCE31BD4BF29BBEFA3B987857A4EA2F87AE9F6B5EECB8BC3DF90547833F3240071AD5A5464CBC9F42DC0E9E96DBBFE1AB2681CE2DC100F07CFF7480DE919AB27A2F51204BBF779380B337A550371960757D3BEFEE45AC13792F9F885AC22AAB4A5263364078AD1474B206BC54795B69380B0056F9CE8325B7B780B6AF7DB44F3AFEC558CD93FA268A6AB9AB6714ADD72C93B5BB011D974F8D2D778BC476C5F41ECF7C7D48E632716EA393973BD67F7496078F937AA45FB33E8ADA4CE5192DFB244A1D8290BBD29F239CBCADAC9E1D260D22781B7B41E8A42ABE21FD0D3E3517AB941AD09013EDE52EB9081F731A7935B14E1A9132FF2EEB2C53ADDBCDD3A9656F5E22B0F6470DC961BC0C1B38BACE2F16649536D28271D7BB9C76BE5B63ED2663C5842335A783C524FCEE69D6964874D4BB56418EDB945EFB097001506561A1ACFCD03B15B42A009C9B150CD1FF57104737E7978185F980B333523FCD89F522A59055515A4895E68C07161C47320CF1E542F03EEA4CEBC76266E2C85916638CA4C054F0F085BDCF2A84B5666932A23FF8A40EA90F6CE2888731E88FB7D90CC24D66934B2FF378E662CC57D516072698CAF09EE3759AD305A30C0C222992D3A22DA664A25B74043D338DCFC6A70C786853AABF31B6B5C49D2BE0BFB0CF214467B511643106E729F4756B147467530F9477503BFB9393D36D4A9F964956DCC0C6ADA0A4CDA17D794F7C6A32C7D8E146C6769B810F97F9FF47BC187C57158684280AAED7807D2FF6B9AC3F4E11CEB29F112C30903983FBF38F5D118F084A5ED22E020F066BD458716B491DB9A9A26AFA5C187FEC28BB67C878F82EFAC22675C0D2B6D62DC1804D7933D68441A9F66419314D66540F30BC7B1A8D1F10F5677BC3C6E236F6D5827F70E3F471686F640A8253582F0E5FF146C3BCA53CAEF22BE026BE891BDB96697549DB993B9A4A1FB4B8034FA9EBE42F7A953155A050CE106EAA94C885E225F2FE61E254BECD43E34DE41385DEBB456CFB70FF8C0E46E19224D5FDB7A38F4D2BFA314DA933D8A93CC8ADB99344641DED33E8AAFE639FB38AA9026DBFA1F0C754192A57B8933C99181DF62C9F8101B86A743BF7D07924EBD6C14EF54CC8ED397D73AE2EAE7E2FB4D6456EE7C68FE6DC0912DF23414FC15575E7B84C161A5BF8CE69738211BA13614B1A40E7507701DF29D3B50C26C547C8E49537D10AACE95424520340A57F1B5B35CE62DC693694A2A68A95409D52DD0A0F167F4212826811993D4F245E56CC36A87C404C3218388947BA63814D3CD5DB8666EC2E2551E8E130E1A1C998F301005A645BFE2EF1D208412F3937246A5A41AA4F3D3A5FA2833B4A30637EA569EE3B2E6B72D90BCB4D79A7022824BD22FCE98905D2DBED895C069031D08B69E0B710B4472BE7BE2DB52898BFFAD6F65D73FD28C8BE578A152E53C9D2F04E206F9BD0714CDFC5FE8E17AE4CDFF12B9CB2EA5CD8CE7FE81B0033C9E1E91A88D3F475012BF6882F3D5A25C1E5BC92B89229D38CAF7314DCBA05636232E8409CEE11E7EA607FEA2FB2E2FDF07EA3994A3129DA426D64DE74186A1F8B432E106B786BBD302FE40889DF80A5B1CCFD129EE0B0DBA5C1D506A8CC69106D6BD5006D2BAF26D51DC9BE072E4BF5EE4D1DF9D7B4235CBC7DEA9813244122DFA753479CB66128753E5C4352D0CAACFC8CC577E2D689028025D72EB6A6BBEFD22F2CB01D5013D86A5F92A1AD6AF48F5B7C9F6AA8BDABC760CA25C0ED93986183F00CF3B9F37385DE183EB614D267DFD6E48C7AE1CFFBF6F977512C756103FBC5AA332F8647CE46D12CCD597A0D982C0CB30D7EC954515793CDB3CEB143DFEE2552ED21EA089A486CA6137CBE87DA4F27008B3758668DE776C46794303E316A3480ECDDC2CD5A97421EF9369CAF27E6AC5A56B7C9D42C64096EE13A1F8795042F24B92566021CAB8A988EA5E5BE324A19CF96BCBE0E5B1E32E932C7F45470D99D785BBEC8F9DA12EF268B330D43238637A16D699EC842CEA24E9BA3F2C58D642C9DC7C482FA73644B5E48B127573237D1F1821206081B7C34809F9839569EE2D2C650347B0EB48B76F1387CB79AC0C6306B9206A65E9C7B1DA55C153FC9D509F5B56AF7DFF6D3909F94E1F7FB57BBAF68555D2203B33F459B5C449113B559EB0272EA9C0D73469B72256C3D730E2752CF64D1AB0B47196FD8C07EC32D2E337242874397A0D5A37ECD4BFBC88C96B1655460FDD63B265D822A13DD20B8837F727BC1ACD58AC33603C73A214E90A59133A72B126240FDBB4BC450FCC19ED6E9FB9DBDA4CA3860A2EAB2CEE7D8A4534D2786695D6DF754F5EF52ADBF642C5F3A46C9A3CCF949A6E6B025856C07F801DEB544BD70C46FE86D4824246E56F171A81CDFD44730D55F89821A0BBFA449B9D0FBA039731AE81CF0612CD4316B0BAA743CB7E9DB42708D40C60A95973DC9CED8C5FEBCA232B093D7A5D6342325233187B9B2593288F6ECC0F163315598AE526861DEB3FE0A1DB1BD649D9550BFFFF90AE242DA3E2D67DB09C248C74C60EA83BBD56490E539C5D486EDD707CCF6C310C3E3A713A09183AD749D6FDCA2959AAF0B9FE73C2FF4373EEF3D4085951EB92ED015733D35DA818F48946648140A01951FA411FDE51792036B04A4A4CD3E5166DCAF152346FEEEE3F585F24532582CFE064DBADC33FF102A39BB4BC05247A3C78820B2639CE455866A344B557ED08172184BE286B0F8611236B68A9A97969795A6DF73F55D9D740B21C7D4832972EB79E033BB48F39B4560B0CA338D85577F76F38BD69626CFCF6969D40102291DA41BD67E7BCECA8BEE6D3AD58681630DDF7EEAE5A50F19AA0D1C353F36377D0628E0EB299F0464063AB8B1CB89CF512001949E1E66C68A750871AE0A3A5FA2108AB4E2920A08892D68719E092B48C3043325E5944F62D4098452B4A8CC63CC0792E256448F42C8DF5AB191A13DD00033600D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_p_I, pk, sk, seed, msg, sm);
+    }
+
+    /*
+    # qTesla-p-III
+    */
+    public void testCatPIIIVector0()
+        throws Exception
+    {
+        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
+        int mlen = 33;
+        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+        byte[] pk = Arrays.concatenate(Hex.decode("14BE5C398A69D45B0E7A63672036E007981E846337FEE6A01789E7CAFDCD43B59471207B844CCA4240A56CF1DC37941B8669BB2E93B7A84687411425B04ADFDD81011B8DF309F9BC8069B5A0F53292F6F6D1A6B9191CFA8FE8DE932A1403296B3D6492B457849EF361481655174882C2A899482AC9236901E094BE6BE9113E9EB7B18E0018C46CAB21F14502C63553D383BDC53CA1064CE6BC6704EA05161F0C9071428340DD0342F66F0422AAB29950ED8A95363404759A8E08677223A08806814EBC824D40D89F7560175631533BCA8DF81DCED3828EA539BE87328C14183C91A6C24E496227C6F6A98197A8ECE46D5912D99F72D17651F5850901CF4C060B4D1E5C860C12FA479342652B54D5E5F58AC2449C6248483B900E8F7A2654DF3ECD76453450BC4162FD1AA9A36B6468C0E37A18F54A572E9E35C2047513466BE99BCBCAA678E0FBE657488CC110A27393BEC4841A4E7B784DAC3F840DA102438041AEC6BEC4AC261FE118994D38CC1461927D3C08AA4B3A22E2393FC49B5A5C408182FF943C1F59B143BF0F11420C78FC29162F9E2EB81277FD7DD8779F3FCC9FDB0E6065B1C41272709A716DC204C51B47533CE1B3BAE15EB050073E878F70696B92E6C20870E8F3C290CE4812E00C1341D4E36A9ED8BB974EF13A8CECD32F4D36A1E912CBC1A65458454BF58A874245EEEBB41F7FE6D30CAF412010D91F1D455F904459FC9E259C812D584EAAAA200395D23868E838DC7F78C1CFB23BAC26788C10A0DEC2AE358DF79980543B274C096D194D1750CCB74C0DCBE3C30B50B74C8FF33EC1C0C93B65747A01A85D56B17FC2F0CF091E7D98979028FC0151E3E3A3BAE05D0FA5BB9FB9B62F031E8DD9D597E5758DC1632B6040D72B0D65B1C566AB9F846C547C8809E678D86EAE55280F2E365C0B6539D3108763D7D6A3821992B9F0052152D8A4469CFA02B332DA1A41A340452D481660DDD8B2107FF8968113DAE0C4BDAEDB087B9868AC0E3DA8E8B7F54D5FA4EC26C94B18F1919E4E5181079A8C4DD2C2F6FB416D302FC298D943EB43A6B588EF9C9498036A0D59C85FB37FC4EA9C8084DA4C25193330359483EF8D9603B38E9A7107478762557A590D1DC1D07FCEFC9EC969F6811A92B69B184F8E605A489D0872232702464AC8616BF9EBD7ED82657541C3D96C3922B7E2C31A7722CA36B4DDA4CB775071AED78E2829241C9E20096BD64AA69FBB47571BAF11EF42918BC0606486AD5838EA8794B2519A5289C48517AABA1E51217C49718DE8040875D1E1049B0372A2C307E2EF6E43503314540D3C0D054ABFDC85FC8BB4E5B48FF071F0FCFE3BAD96E02108302EFECD21380FE59E1038D7E7D79EBFBC4DEB14CFD1114367EA1E64DD950A84B11E9C1815CA4AD3971EB20752EEF63E2F2A188277A93CA022C1AC3E0CDBE94110240F3D7FA1264385CA9B997BF24C086897D8B23E585D3BFA4F8B611C578F789BE382502A2A14D2CFD899842FC88B300D5E929DCF9388E9F60782DE92F344598901225B94B48C7CDDA04C5141A7296C3046173161A8B902FA9C830FAFD04CA53F62B11403E1D96906ECDDD190C77DDA4350F2FC8A33AF7C5C9BE9DB0C73F726F2730D27A32B85845032A20BFC5454E8C8E3A4501C7A04B5AE82140A65511CF68CBBA3A82F18448B84C0E576BDCC94C2482C08B95E03A66ED9D0206DAB74C32E160A16BCCE9F699AC3E9872BE87774168891C148314C1500B506B6272C87AFA7034899BA202E66DC224A60F824E9FF51C846D651C5FF5215F828093458D60BA1488FE84494A2D26F5BE5A484AE6B57CA2E5502C101A1C4C828A70AC1E7AD3D71CF8E5A4847A56F3D110D18FD501A0A60AF0950518ADD4008540799CC1D1D66DF821D92877B80A6C58780EC34B1C97CBE9ADDF79F923ACD142FBBDF39293A0ECB17F2424C2AEB5714710D2749DBC02351656F54B5268E114B549D1111CE4BA441974AC989AD24B5E1B07C2A74802F1C09014C752A898E0EE9D51485E370644988FC3DC0A531EB6609962AE8C583179833700E944BC002394A5A93F11B69D9F90E5EA56C139873241A8EA824C8ADA018A5A4247D8C46BCE6768FC310C2C4C3E6D3A91158A2F21B37AAE306860C60B890B4EA1F2A3ACBA3BA2D256031E0BC8683A41F454DD5E4929C6201C639A9C6BC26628ABF45B1540BB2F94D0D5D7515B8A553A0CBE3504F76EB5B5A3EA8FC6E115807303199A5ED93E1170F021CD9F48D5E7C260348754E4079926C69D6B911FCB5BA642A68D81477C4631A089037CF95C623C52BB59D697300E0919307E21CF9B0116C009FB38ED08380E2E3C26320A29BC2A01379E202BA115CEB0D61C299F71DD8E153AF3EBDEC1D0187836F6771FB71923CE6E1ED3265492E4728CCD62267B1D49F84200BC094769860C3409F5B07C55CA5A046BA1089CF0685B6BA6804335E8F196FBDCD01174EED8B35338C44CA3B666A5E7620C822E1D14449763FEBBA08840D446987A9308C02F307BF645F00A63888101535C3E256147BFE3A1B93992FA132B95CF3DA5CA76EBC1B1118D4469274628C047C647E76E69A21DA235B837F0EC5DB7A684E28FA38229704E38C012B82F5FC2329B7B8C32643D606C923D4D1CF945361A4E86F637107DD98DDE0767BE55991E42F422D7136CB12989434C2E2D73F9D6089AEAD7B002DA0C823877F102DAF2D827488BC5047F932828EC1598277A68C0A043EE333C25E8BA09B513B71053136C43AC637DE604A801B5D031053957F2AAFABE3B322466393C38B3E79FD3883D4B26952579C7FBF27BA3161BE47EC05C36274C486B28069B85F3A1CCC0729B67D4065CFCAF822314FE481A97410EECB6E629196CA2B4936FDABE460F9323C541220F6B81097DE9F2243598577EE8A931E02A7A4C99019C95A8D981B0DFD84E14FFD36651748B9B39358809587AA66ACE6F700727B16649545E3EF19A88C5840BD1993ABF60DAA5E75F3A8479EEEE400DACE2B9B56A010F5B704A45A9CF67123F3B72E00E3218B08D01EC3E62CA297B95777993D30E7ECEBB84E2C89F3008AABC787E2F463A0E8AD93708B441791389B447999044F2C297F300A09955F7FDE9312696728C4D57C41CD0D6DD171E1DC8ED13C71FEBDCC6E11FAF5661A4762AF1E481D290A99B714A5A9943210F686DDC15F52135DA1C324FE13AB500EF8990A7DA916D36704C0A2770172706F76559377B6B698235A2CA0950603E3664C2B18139E964F8A65EEDF920766960D7AF2C454EA459C8C8A8A0BAC144026EF3E07655E0468FDB0A070D02766ECE413F0CC6B0E8AB6141DCEC4B7176F747CA888BDE34072025FC5903954033E8C2E238C863DF874AE2352E69D6481D3EBA028CC82DAEED636307E25019613B69BFB3C3E789889F12120D6F82E6F147199D426448FB133DA20E2EA35916831652017E7D549BDCF3B2847457021F10F9EECAFE9D8F6B603AAB7C6D66204555BBA86C30F6411720BA1387C7420E39FAE827F454B851B1B6D7FF850338B9F6621F1C00D0160A61B1A5B2C1F416C6B3BF455A4C3E675E5C2CDFCE355C08513A06691497DD1A317DA84C14CC5FCBC8FF1F49DCD820117CD4E11FB545D5503BA4BD4B479A76FC62A30D3202E22643C4151874AD5A4394277246F72BD4F5D09C98112BAE11B037B00125E1BD1E20BA234DD5400A72A63342CDB726EC5AF987CA91330D77F3BD4CEAC2FFAD7579E8112C81758D9140FFD0AFAE0B6BB60CB78F40FDAD784F9085E2134F778FFA2FF71121D9D07E48DBF28864052D851831DF4EB89BD661882C4207E3F5685068D89D0DFD3F73C8072C946125A64B6EBF957C94D4A04409EF9B17FB5A1A7916C4610CBFED3474E563387B69793B3528650FD68C741E260A2CC5D2A229FB00CF4DADC7ABF168EF983F613F2B508A8C465171035D8FF0616BC319BD2B98C88C5728EF92086E4AD316247A199F6A123AFBAC048ACB1E0DAF62A0E17730674F61A801EFC2E6505E9E6029C862C29E79FBEDDEE69F8FCAB716723D6A99B8B091216259185A65C5A9461413EF8616B9102F0BB6FE68E014C62290345C2C89E77511D9746052A188A41AB3E1E141684B38020051EF0E4EE77C979A7B7EE08B91338ED742E30E91250741BE8D1EA6B4B105F428528506B646A027E82711C56D7503049021809C050E2FF3FF1001AEA49FAD40AF255713D431DD7BF6BB62B15B3F22EE53B4FECC7E865B3881A15488C18AC052C1DD90F21637D1E06034649DC8CED834F4A95C511AC19815C86BCE452CE0A9C15BA0B7E98E92280C9F8C7F2260CCE46687B46702F2EC81926B3A4BC9BF7C6E28247B5E248C231B84CCD95131D63DFCD33652D2C1B89025EC954F6C9987412F707846201BA2A07D80AEF7612E2CEA5B473980380736076583D140C71BAFF5C46023D03AFBEDF45132E16D02FA0152A0505DD9E20A4624CFCDB09737E08DD5FD1CF20C1896B7660163B564FCE0CC7B1339D74138550CCED7C7B950CAE55706EDAD6BFCF3C9447AD3D4D2CDE6B952702C0C1E1C3E3272959EC3F82233562959608B8DD7501D1BE022326331AC674D71C01883A216D8801880D14D2934A2BFF3087D3B4E28851C86180F474A4937DCCD901F3BADB1148946EB6FD8E50CAFA588749AA6987B2DB6EC838EA3712D09DF128C6D6810079C2EF26CF4F2082CC539A7B569455AD124E42347195C641B4291C6AE909119DA25E8C99B966B2EB3B009208971C159E4B03DEFD15052E53C398952988548F8D2470371D4142E80D074877E115735135478DA878947939BFB19D4B8410B7AE39B6EC7A7AB040BE4113848B806834DA4DFE8FBCA104D061D884C491DDBE44EF645DF53D89E468884C8692C8F5AFE3CAC08ABE231E721D93F76234604207D1B243FE4C9E69739D49E69ED4E68BB249EA4AE384A811B8005823BB893E8504DB7F0257A13F6DA14DFED71AC59FDB02603D11968F8631F4BC4F0548F51807F22CEB49A932AA154A9CB31318CB2C86413589177E97FB308092EFA08AAF8C4CF0316ABCC8BBB55DA9410721B5B90DFDE4682496CB040240B900A00BDF7D3E19796FF6378333D95D609EADF839E7E9BB05636C5B68DAB394E059844FDBB26DB9013F34FFAA96C88799A3B3CC8F192442A03FEA9F69E5E000D0B2E1EFD05B4D3953C63C0B71E7EED50A97F725542DB6B0D451C64971A9A600CDD5AA8EC6252E2AE8F73C16CADC0786CD970262780642FA8C320896980D596887B76476307A41820A008FBFDA15EE8126027B188092C4AEA08A734558DC1F680D8CE12C25F315421B7CB353C7D5363F06CCCB2806045E2663716188F439A9F654F8736D06BD0F98E1B53F5A5B1444F0CF43F1672DDCCA7AEB76ED2061124B5870C62C4AA66C03EE94DE1615627747D1D930A8DF250E9351FC8CB88E451FEAB0550DC351111F3DFBCA5B58F71A635F68F101F9FC5208084E75C4FD5E7C5664452A9733E481FEC874D17E85DF09FEC044B0ED303930F20600BCE3A9715654A13BC9194822E1008256B8482C6FDDA2E5A342D8CD120638152D48D7F3E25AA7589599B82E4AD175C5C4B09261337EA014AFF273F026F27019E2BC917CFDE737179BF8B8D9F563D4275F9BAA21D17AB16A5B8B93505F6C95BE36CD7277401EBAD6EFA0303678432227E125E6759270593C4903A7C8ABF8F7246E9A070A01C10BAE9168E261C1FCF8D8AE558A3050358F1D389445181A6F1BAE1685B66D1EA41B294CEDED9458325FEC7C0291762DD7CE6219B1D1615082C472CAEB7D616BFE1756622F212AFDAB6A0A597F03A7779ACAA95A4E385E36D4288A72123ED083BD8D9B939F6CA155E29451C81698B8236BAB70127029A258A6B0D6434F02CCB26A846457440990A72EC9EF332851197B6325E2D8499B8C70057F0C8BA6763404EACB43489E2F301B233FD66FE7D47B199A93CC355E1CFC350754D09104938A17F287C88E916AB3E45CE0A44987385E0E56B923BAC0D80EFE37779E570C20470B9B019A60C471D425437F8665B15D3287D02D4E93964778225A1428B579F4C68DA0B3BDE33C2A5014B34D93DA567BF52D11F530E032B0388F8964BBF09AA58FA6B64F4B463E2C3735DA10519312E98D5A55A01A326E6DCBA3F036AEC91206B089E3A1C19785C582263EF808860C79311A10E6E3F77C70FE96A0FE25C85C08A1CD43FF0D9A5447CC7601EBBB429D030AD2E28110258DAF68B9F90C92D4ADEBD0065CC9D1B090F57C93F8C773B913F8C150C6337FB6B32E4216105C91F13188A65E870DC3CCF8EBE18C19865D98645CC4CA866B04E55D8CCB25F6C3AB52DB299BF304D34EBD68C184D552C899DC57A03C783A46E01A8F82AAA35296930A687E6A99250641DC62E9A02D7CA5C96174198753BBE09C1A2A36F09B1A7CA42A787C16D2FB9BF739CCDEFED80CE40BE9EADB69DDEF02B6860A240E4A216A4D50A917D523C9CE43C26B56E4F87B7EAA8960283117D5B396989719476036E7C9DCCB3277192A502177F2FD5A24403C747D70E26B7D824988369517631223EC3B6D7233001020A54C2414C0C4FE3CF37057C59A4B2268197E17D097E24945D95508F23B10794DC904684E33203A6B074A466AFFB3AFA48A830EE6F1AE1DF35C703D5910AC3E71EC4805E402C2743D3BC9D824B02D0760525B5F738196A7E575D921331A61D2F5BD838A3A8A287E2718E538A22D57525AF21854A1B1F0C8044C756618ACAE9B294D5E36D84F720303600C8241CA2C117726CF6E5550DE534892A14115F7920491B9A54A84BE3C2A0E168D3E9ECF1BE3E7E7CFEC206CB22D97273A830514FAE42FF5B2D7FC1589E097217C16D658FFC1C08FA3FB37445B75A3AAEB0CFE91D234037DF9C824EFCB05034C1C2C89949032E90C760D038DDD894205EA12B1E6033F185AE335E0FFA129C19C435490DF53EC30C1B6B7261D1FB89E5427B8D0103BC8D8370ABC42A9B0CDC301D4B9827D27D2FFD361C93976234BC699108DB4B540EF89329CAC0933C1274487C80E4E8F128F54E5EDEC60A102585F408E890B52E590393318359EBCB710586261EDC1E66646B514660D006A2C126CBEFC21EC431D1E99D706BEAC7EC79D06A909D02AD7A694C672F13A16FEB9D51C3F11465A51512F14D23413CD10D7F9D47FAC45B751086E084CB1CBC39E64994C9696B0A3337B9A6E07151E85D91B46AB81E716798604FAEA1830C8EA55082C7F6206FC50ED18FA013934665F21233485597FB9767663E548E60A07ED62AB3A704D7950612032ACF4F811157B292F6FEA408F56CD1EFE1A6D3B8E616EBFE756F5055817F857073E69709B538CD7E863020FD0139FBA6AB3343B88E8A2C25EF716091E41F7B6022F3C06C148F2E819106CA03FA29A6B50E18BD16C347F7C3BBB4A331B98A4DDE7FFD3E6B7A70933844EF37A4F576B14FED215FA4747145CD5008CF90A996CCB7CCABE698566B97A4EF00B605792EF99E260CB9A5D86A0B153E6991A8720457A003003DB44D39EBD567FE9CCF84CCD6C59A765C272A15C683082282D3B103D556FC6160F4126FBC57FED0C183515D5C423229F1805BD2868E3AD8B901455CF8C7A1D2DD6225277C613ABCB6B6F3105C423D502669F729FFED2B857C7979C21CB74403024F3141534294AAF0C5B9C9E4ECD542BF31860009C24E4746D0A3ADB396B91F362F4C3FB5DEB7AEDE2B470487F2EACB3649264AF0C8154A3DC085497EB95DD60D4E6D8F82321BC84945FEE29CC232005E4506127A26FF094F11AB5E63FE9A9F328E817490907FB60DEC2CA22CB435F5562BE22A376A75C0965FF209C7CFEF14F3483D890FE0B659CAD716497C5377EFEE0BC2018581FE10A580A76BE0430A0015F4172DD42E1DB4684ECD7E43C6AB7F18F999591050F46FBC6E92E11A59167423B52D0F820267D604AD671732ABB111CC0AE8C4ED2899B070C7D3DC7D68DFA2154D6906964901414788D29C7F3501B61133E0DA145FD88B0487DA2B9466C988E30BFA9634D35646A81071D3FC599CFB0DFDAAC0D1EC9605BBFE4158E0F63D542728CE577AE346DED77D6C3202808E653E1D4F11461E764DE863E4D2FD87F715630B39C8DD867580572D8C4970AC640022B0CB82897D3779156ED9A10734CCA402EF088DAD77A12242F003453512399794C688E9347CE657424738B335C49D59E7AA2B422B327849484418CE24C45621E6C07D1D5E816D3A6D6A9A65DDB4D1BB04EAA72F37C7B4DB39EA71B99192D7587A5BBF51328412797E1902999C4579903A302F9831EDBEBB3795DD1D09B288CC1A257C4349EAF7E334E9B966C34699B6685BC80DFC1AA1A5DE83306195562AC896840516973777AF437EAADF751714892D84B63517E187731C34A11402204438CF587B8CC19DCF05C826049C0E3304C5A0E3029FC16D2F8B3DA03C2B169A6F6D6A44ED0F35826EBCE85BE7F4DBEBF1302CB042E0AFC3A349FFF98B50C8993FC24131B3DA02B5DC1155DF5FA3CF0B884D9F0892E26B1FD140BA03C4F877BB9C58A99FEB3322F26D5272CA6D000A83BADFE9585385078CAC90E7301F2AFB393147327793BD0A4DC2578846A4E7D69C0F5419D272F1F022DAAE4CF662E9CF59B972AE8A20FA21845A3A5009C23402EA65C2E3DE16209260909A6CAC11FC5A05F61EB20D2C888E3091E0044B80FEB0E3B96292E6AD661FA9B6F7ED9915538D42D44F182CF103956C59C3C6140606DC18F93BE449DC1E2F20986BE14D67EE1BD0003BB18181CB441D070D74EB4E26F9BAC32E64C1EEA97A26C88BD264FDF1A12EC31FD0553E66478425E453B82E0C13DA9DEDA7328C28C278765D1D3B022B2B5B3FB7CEE42C254811E85105D39ACE8E2B64F0BCCEF03571039B7FD9CC2886CDD057EEB9C8C5A47122FBC9B192843B861433113DCAED72885B1F8644A68D21444E59A4ED3E474B1753B4CE034E9E6DBDB4F41CA00BF45A8D1F0B229642082F87D266EA02A972C0D314276236CA5F2013E15092DE373C6F144BE76002843C2C0EE081EBA4A4C59A20FE826F4BD0FCA63C9C06B3DFAB222221D1BD4CBB603AEA2DBFBD6BC722CF479709788D3F518A6D0B542772208E0478F19D00276057D4D7A3C7C391D10C715333011096D1CB7A3646AE5168E3085FC987A0729FCE0D35808667C4F76C7FCB2410BDD80126E56A3C5C72522489D2593F95A862BD5803A0CB0A4843AFFF2400825D41F421B5E7E29D68737273A5645C2142CDA30AD09F0ACB3B720B9C28732C9291442F9502861197CABE46753797A57ED183983F4D940C80043702EB0068F74C514A3A9DEE904EC3854E91E6C33C5BE6770F57ED0540AED19DE64D3304B53EF5F0120E96A4DB66CB4FFC9B3FA79B8D29966AA2CD4522E84DB0DA115EEB79C1457341729C4E91768E92F0FDB0B7E2DC0725E6A98409A73C6400BA4180B4F00C8701090D34017B3C8CB3A6308BB44E843416FD6ECE1773FFDFCA953835945B67BFCCD563A47044F238F00E1CC82095B649BE6176F7A67F5FE8032EB8F155428C7C6635EEDE69176847AB40D2E60BAEB3018395A60BC3AE9AE32B06153A8AAB22FC2C944D8887E0B8A6BE6E3DD39CCE2E0C85C38B8EFD0A08B06F941A86E17800A88CC036021F9D023014C80DC9CA772079A2CB23EAC1F8D78B284D15CEDE7E5367546B1D71CC0332F09ED4BE7C9F0E62F5A80FF462D477AC30D282269A433E36508C6495BEBE3115ED517A24270F048216508D8C20887CF524AC40980E3766E028D553EB34AB7DEC4CB96A940DD15218AA17B8444B4934DE09F0C87D89923B7227F5AEBAE3AA6146B3BFA1A790B3C98CB8F8F711D349E5A524EA9DC5128862AB31F7AF61BE71CE9893044B0770877258685260D5E0AF6B69FF034212B72134B17F4BF2948BD12BB087E82416595A4E92AEFDC505956A449239927BA368220336CE8CE8928A1B997BEB69A42817C44C13CA0DE4FF65FCD100197338877DCA198D7C4DF1484AEECB4107833407751904A83BF8F39269A8EDC405E285E2854B84DA2B200371E126CEDA464B0600EBC9A89BA0A36EAA063F5F442440E669A48A76CC5F9A63105D06FB801B3968CB3BCF5C4C7263088A1712E1777870C5A3240A85500E3ED0B6BA588096C082D07049053DBC16DAAE9828F1B0576E5B003927AF8FD04CA0E38E7BD50FF1CFDB59E794F46520AE42270189176750DE56BC996AF0D47581A18CDFB469717E8861BE49807F38AE1ACCE1036B695D8F67AD2C7963951646EA6D792723894B70ED65368141A4C09CEC5A43812359E0ED2AF1FCB2198FB0C129D2E60EB7F3384460BF79A0F94FA20C97D602008D1408100D37B1563437DB470037C57E2E21D0C011A618E350C203CF459A3F8FE514236183A200BC90658AFEE37F0B94AB329A00361827B7C08D2F2A3B7153C9568426DC1D44D1E07EBF2BC07BCEFC31B5519690C4FD6758DC0AC5FE0E5CFBAF3641267503E0DFB689C4463567996A285FADB4D58F437A66455D5D38A648083FE6E5E0568A03274306D5BC413F1AF7DAAB5C283A22F0AE3498211EE4AD2680A3DE270F89F9F40A53329F91A6E0947244C6181C155A4E2E74C770A684B3B3F8C6E456F2E43F4EB2D0D3C5B07390D9E011E8D2D20E0842A84912185298022B627E87D7D04154960486150C9A721A750A035F601C701C7984296B71ED3680FEE28E5A47D4A78AF3142D4E034D26C431377C81D2201B87D52E57075A1D908A2E0200F9D16B602300A2316A19A1F9996A441D46BF4D1701335F742A8785CE099A948E1A8A12B867F8358BDB7FE440DCCC234A1A81C23D6CAD1D8129D815CD4B4E6073BAC8A78F6CCBE8CD0ABE28D65F309103DEB98F3FBAE877AFBB549D44E2399B8A07C4544985D9C88EA99C679F6177091ED64C374CBB54940771A59E4FB36D241F0764756F29428600961A0FB52E1E6B86012E74654B04337616067BA23B8BBB76C922C7B224614179F3199C007A7FCF534051B40A25DB8B20921C8DF46993D1D0BCD2D175DDB5F04905E9EA93C175754821DD2E07A38F06072A43ADA69A441EFE77AAC95327B869570970E8260F36401C4C7401077ACF0C2F7D3BC23B846BCE0D61F50911CDCFE1A11707565E4D52BCA7B78C61D96296D29B30E483E6C1EEA2534A783439E2D23740730092B170215427A78E079453565EFFA6EBD79EF6D03A39ABA3DC4C6165FA01DCC81E354FB986A09E543488C61F746F6BD7449666DA0D2A267C70C22B52E79E8A5270045EBDBC36A06E6F7AEB85635E91998F2DEC2D8F9FCDD5C955EE34CF4D4E039D59B9171AF084A7E1537C42C8A82B6FEBF3A528165DC465259E1CF7668F33A8D3E03927983AC29B1D052D4E12918A9FFB56C803A10ACDA79F00EE70967776D285CB7A1CC4D99F96B54B2B33429EAE0BF84FC914C182FFE66796DE6F06320B68B398DD92D757B80B4292141D6ABAD1A62394E0E5B71DE09588778741559E37A4FFC2780FCA13284C4BA15E0CA6CBBE2925C448D9D0C8C07699F30CDECC811707B4314C94CC6C2EB1A80346CB4338906CC013B532249260119E518227EE8149A7EB04C349CF4BADA1FE48D38B33B52648B59DB2A85F88C46B61BC37A3245C2B87566231A5AE26D645124993E8CB3FE1604C8CFE35737A654A248DAA21A20760DA210FFCC343B316FEA913658ADD411AE53EA837DA8B2E79DC92CAC6B4986543F5255206B807B9FBE07B56B3E211E337D71EBC1EF53A5C5E51EF044384995C87E1DEEB1DB610EAAF1A1EA866BC8B2E9560C2CCF522333E699E91C92CB4C9A615B967D2D4E992B1A8170428151AD7847849D713B41AF5D44802BECFA50F7DBCA626C5F60C5E9BAAAC67109570175944384E65AA6B624A73034F1C4459624A3D826E97848A0339417AC185B4DF9144E9EF9B691080E20B9BE8452D4633D106E65146EA35F7B12A9105A986FB8B68E31B301F8DB3ABB804244DC01C76E52C4C6972DFACDA999D48AF3561E849B10F00D06916172F27A490BFEF1E8D0FF3CB21F013409A7C1F2CE475B62CC5A86E527BBF369FBCF605BA881401DF460E0EDE1347FB49BBCCD01A0E12D7D97C771F30768E8AEDA387E3AE6E01615EB00F34734AC6DD320C8D97DDBC8F71D64438213D17147B9A0E188BF1C09D1686809F383384BB7101DCA45137C07632D5BF488013EA2D7FBEC4B2538B491081F90C47DA522D919EBCB87D83D941D06832A51C3D51F3439ABD64DB94EF540B86F627AFAB8779E0A52DA8ADC5285CD427965E223F2ECFBEEF8E41540381DA43D8A754B68F78D644A020BBFD295836E854B785CBC0B781CEA596D12ED21B0423368DF51A4252B0E2C4E73BA50841E36B8712A31166C935A4E41F6BBDDC2FD215496F1778C57C233C45B721069A5316D152F98689AE2A9383C9E7453B463F22E21BC78940944D9DF3301136FD829EA11E33A17B22505A2E0A34833E88B28650F67332D244E99587D8A528F891B1AA55A0A54FBE434E8815BB466D0CD0E2663892A0571775D1E513785CDC9938913309B15088C4D90A94C670CA65264791909EA3ACD5EEB929012DF6961400A53E0893EDBE432B0C595FB9CDC288523E70271AF36696A1D2E78DB6D7D3887A17F7C02D277185E7DA1521B7A45BA29AFB235048A403D86DAA6F9289F1612C440263A239254C219E695C402C56011D8AA75C163493B9BD37264A7F452208422988220A1D4A075AEF58E86A778149CB17D094BBDD10A61F94362295EB0D543411DCC6E5FDEED7F400CDD5EBC1BF8F7058B9FDFA71200157A10051D7E1753F92C14C0AC15198D82D7CAAC166B684F719E5F99730CC881A62AAE7351D0DA0C0052EE2D4BCD6D1AE038D0EF496F9F453DE414DBD5207EC64B5591291373D401E13A6AA04C4FF71D46E6657A6E888B9F480B340B160477CE7637816174BB060AF9039409DCC68950C74143080CA9B3BCCECE0DF5FD9A2F42FFD515C82606B324C997F0DB44653A83084A7C4EDB0A2E9D7C8630F5ECEE75313CC5FFB722A9F4FC45FCBEF0CA06384C22C1BD468A5CE5469369A50882254F79A63600B6D17360A318BD243050255A1B05921192D6A69211C26C68AFBEB94FB8C9CF9BE2068A5B0C9507BD2EFF6285473FC6A81AB96002E75792270E39EBC1C0258A63863E7CC9E58D8BF24D1E45B76E2C311F0F7420AB3156C4E7D5326045D861C3C2A25E344E41E11C61254462436A2C1D352990A3DA220E41B23017ABD92FD0C414C08C7C0F9754476D771EF6D542D09A61D025C34540BB18F143E80FCD41EA4A2B21E626E11AC84D1DFFCFA69DF901711FE916EEC4F8F0D131B7762F037BCCCB5DD818CF3292A17EBB0A83B02E8BE29827CB97C68025B6E36767762A854A334D07D46684A530332240BE0BA2B44F48FBDE026D1FD33D22650A2DA2331CAF89E4604A6ED8E262E31E23955D6660DDD5C028E841599E56210008F93D18EA746A8B0964037958B3F4CAB95CAE5201F664A68D2FBBFF221000AAFA138D8758FADD641ADD4931C261E218C7947BD7E79E7365E056BEF562A66299DFD2217C68F22568E8E64BBC228601E8D9915EEE3272E31DDB00F156539CC2C8964600608EC3D87CA3248EBB2CB5AE59C05C1294C22E5E2A22282BD7BC71AAEC0DE44D9FBF542A5B15429A95B6A1FA6EC76E171449EB682795F89D326227B05201652B89162C3035A368BF32DBEC955CD854DEED3E74B313ACA18508EBF16C8562A9E6CA624E1134589C3849B2E35AE1C569E736D7B6F11FF136AA6E61534D6EB96866139E7967102A095D4D9FA0B89817A156DAE43671818759A159BA1D3D894A7C9640091BF18B15B3419B0410A00ECBF11E70015AF7A3E0284547091BCFD92275FB2878FF78ABA5952741FE5EF76DCB5986B403945E9BBE3D681415F6FB106B684A88BBBE0E8311C1DBE95236846427F20583CE4F50C1E40750E074036EA7883184A4B1BF1C480141A082F62C112771B3C33517F2CD20B5E869F1D996BD7F62013F29BF8FD8C6A0324E1ED064D9B207D6A8434B104F4335806092B80EDF84D4A39B4173183A86317B77B0D1D13927251185A91896CC313775D466CCDF52250A6D36EDFB97419225F544BF0F0F179FD0E0EAACBF5DD87B76A31603BE31627EB12EF466EACAD5B3BDE7385F6AE1C4390D6248EB03A689DF78D4D63B53BCEE6117139ADF8AE3AD2249FD181F6554003999BC98F571B0B39930BAD526C1C0F1AF3F358B4F1228FF03FA69A8262C4AEA024B3E4984FC3DF5CA639410A70947216B0A82476E8BF4ADF0A52F18EBC0F79000B0854479349D5ADF2276C3FFDDB86B907D9A3E6F202CAE8178E20B656EA1B5FD4B686D6D4F9A5F106341A0CFB85FBFF11C1F285CE0C9C7C33322B28103406469689CA2AF8CF68D1F6226C08EA72F6813BD8D8A8B3FEED6D1CEE51CE8807781A4CA7125B8857A520FD126B663081642EA95E8CA9D76D1D2052CB60AB54C6CB9DA1C0AC094A46E1ECABD8AC28B4C93E76DFDA534975F36529BCB1B1827AEEC5F55C82CA9645AD172298FC2C1BFC38AA1066B2EE6AB32132058F34C5F17F66D0CE4863FE636EA2EF032C9B77696E37F1261C80E3AC7C7633393C68B29E809000A2C266A51FCF085B24DC4F55ED477A8878904D080E55471AB3CFA53E5730EE3AE85E52EEC2235BDA6267A18684FC1309F11C5E60FFCCD739107A649F2D760A4496E289CE4BE12A5AF1C16F53B4F88388447E13EE2B789E31F1DE44572A01A187F0D8A1D9FB3F4EB2A0938CFEEED42618AB5FC161AF9A89E54C476D24E30D6046864A3BE2829BDE851DDC11CCD255D5D961F3DDD1B8887D7D5642D2C787E9343B494A691E9474E86BA7D14A1B77DEBD1F6871960F48C42691789E718D69A58C94503ADE097B7C35AD8A44B1ECC51F60D4BC5FB08D5955C83B34FB8D90A2E2306791F7EE9FE820F204005746A79F6B34F33BB0D01AF35EDAB77BC3CAB56B3612F34574CCD94A9AFAFC6F12EFFD2B4BB5C3A540A052ABAFB5A58EB4A061BB575345A8546E1EB2E94AEA8569883A674206C6E3F370400D40E6A2582785FC2A62865FB96CC9F5588452D6210B0286F3C5B80DD613D8EBB934A53910F90AC21091B340243CBA168E42C481BC0E8A72414C9DE279581AFF1CE44BA05E7C04A8F0EBC9B1AC4539D2FBE519186173B0B8CAA9CFB39E75A9C3E172961CBD68D2ACAA37FD9FB238947CF34E220C06BCA10EABA0718B4EA77AC24E94985E7941A787108134F2AEB8596482671540E3C03A51F68F0CA1757E64F2F8159B285061C9BA19CF22B6D0C6E455D146824F3100B69E1914E4448200C330C05930D1F5457A34CE4EB08D0BCD5F1445455E259337E2E30920342009AF94E63B6ED85D399524EF3149C10F5C5A086F9FC26F2A9B273FC5915E73CF4A628BFD4513C7983024410EFF8A30C3380F2E50AAC7821F11DE55F9A3B2C81BD0F2CBBAD36E2B9C45FB28260CB8663A953C4E766918F1AFF791DBFA682E2786BF33F4CB170922E43200274A45D57E4F1FE8A2FC2ACD5591C903D4E0C8874700984F0B8D196568133E60BA5A3C4B1A4C688CF7184ED4E9C078E2B14A576842933F12B5779D606ECC64FCE28262AF818E70DD813CB32D9CFECAE0E4E62696E5C300459D1D71B3F39500097EA25A81FD840CB6BE80EB5AA72C8F04164221E77A12DDFCB980489E73D82D6F1C2024FD39AA14FF870E4CD3C8F2CEF2D1DB745AAC14E80B1DF21D7640872706511A39F300BDE1C215C3C4C040E00F579E852E3AD6D79A7CEDD8DF5C9F11FC0C459A3CDD0206EF21459767D98B558783F9880932FAB544B89BC844EABEE7C94308DE1334DCA8A2964812B76EFF74C44172B0665AD790CB8EF74A8A080E9C728BE030115CB39ACE40E44FEC1A95025C46937C7BB67013E4FD84B569EDEB29BF4028C7685C181229E5ADC18E72C2AF605043B42425CBB0A546E92B4F347115DDBF28BEDA859D46550B4F8531C873F960DA53B590AC354FAC61B124EA0880E4D728EA1DC61A7360DDB38EA0D08AC7A0063FF988065A66A709E03110812F8040B713AF9656D4C5B34460C2181772B1D7A57E188DD40ED6598E741B14B70B9EB14B6160DDF3C7B00A57B5B092405A4BC98C99EF51D22D06CAED110C7E8F09329793CCCC913CECA91E73EB605C08066D5E05FC5692B291BCB3B700C6310F2374DC515125E6B884D5167CD87F1A244C37CCF7BFB8F4BA23A516284B396EAB7E533E19F77FFF8C62684906637BB770B2001E786B2001CE07241A3A079319AAFB635120A7C029D3C70109B6DB7F9AB61E00DA79EE8EF06EF056F3628317E571C254F99078E09305854D6721E406926DA57994BB0FA93EB23D00D0EB1860FD33550DD43FC7F967850621691B313C799B1C196CDF53D5A65EF497133F589C3C96322843EE1404EFDF86609B764B9DD34064F6D2EECAE7697E75B004BD11079577D614A89B16E1AD58E010672BF8C2BC227AD9F1FC3F2B05243F0DB236F08804A42F63F663404772F85FC695591E69AA6019CD6B31024CC1C4264391E0C9D08EC4E896B59F6FA181FF6F477911E4C10EC6353C7131744E09842B0B9401DD2C5B89655339303A79C425C8C13547B9A29441119F682801DD11FA26AFDA02858A6F6421472C3200894060C9D671C4B7034A149E8C873E79B0800CE347B83E93EB5A42CF433488B16416819CBEBA38919B80A91968FCD32A00859D50D96F8F63651444BF0600E368C6B1870BAFFC845D8E4E59729A60995FF74805CE47D7A1EA847C174C1C130F20B18290C9C4CE8764D66DABF4FFA83D2F979ED313474B5934D22125E847147F65A76D106CE89225AD2975582CBC4C1DB1108EC59964CE839560FB924D7722D5F177AD6C37A3F116520127C88905F2994F70DB17A5EFF068F45CC3362F467191FFE4A42129A6404FDF61C73278DF985388FA20265B01EC5890405CFE6AED7748FB68226FB6247B3E989028DA71690B1F7211EAD7C1A5B5EE2915121D145CF2B3F2132D49547FAF752A157434C3DB8F69386B55F4354743C093EB7B996C4EC3B4E046158D7C937321178864499FFF8C890C286239E51F480DA10F0DD0F38CA84D4DFBCE2394F981AF92C93ECF3AAEE468E0E4D3A0EA1A33DF4EB95112A9482FF323204A9C230D9DAE43E18A6D0F290FC174E93EBCA3D3DE2CDC14DE43001106CC04783BEF86A45BD596C7138686E4C09233B78E19F7193F3135F49BC89E43386F2C1DB34215FB6918C9DDA989A760101BBD2E010DFBD4AC20FE04F676F46CF4255236F52108812D97D3CC64AC48B4DEB7BE327F61AAA4D52C6AC2BCCFB2ED8A894F7523C7F2F84887D0A310C9A57824831B6E09A31037F093081E97A4401FA23221B415F7B2853832A861AC5692B2921BD4A829D8C46176CB0D9AFA4E2A613B6ABEF0C421562D74AB6B7EC530C4CAF36F4AC70D4A6BC48C998D830E6AA167AA9E92A8975968FADEE4A8431556FEFA5CD8216005A225605B24DC095568166BB728401AD5418C062F81F83B6EEA6AD9DA335DC07B84CAA1C75525108640A1CF2A27DE5DCACC1741E7A627AD640A18B8783CE7E371C23968A8205AC7EFC4A6844DFED2A8FE126266A41E67494887EABAF60C3FC744C2FCFE09239FCFE91A6C034CC1FA5240858BB0174736387BE896EDFC6606FA39ADA03E886E5F660F9BA3688C07ECEB02D0944910DEB1B5ED6C65699E8DF4C973F2E0131D28AC2B9DCA39D10A7DB6050FC4C2111C41EF1967382EEFAE507E88553E2DB6AB50358600AD0FE8A0A4B00C81117CEFA33F93AC918DB4F758BD8B59F05265CE533B9FC67316C739922DB96783745F531A663DF80C703F5CE7C34AD57165583E219693291058AFD0C1EBC180E89F7A47E5B15CE41E1A70D6D43D2F6B60DD7605EB50C7E3AFF67CA9BD348D9790291F5C4D30D0DA8DE07FE303C576A5B39A39E55C615F83348DACA47264CDF4136037569D2CF0484E52C461AC8D22E7F91250B9B306ACE71DED5637263955538D04AC316123807C9286ABBD14538223CF2A446ED8110615280F015EB0330C3E9AA77DD7C11938F434CE346943E06913AE4CE6CE112B4F6DD95124F4233BD27E897C05C441A6F36DB3B565212FC1A8E9771C968E826CC1B9E6AA4AF5F3ACCBF6518B38FF4026FD247158505CC109F60CC4D4DFEDB0E9B885309B5DB3F48CA00128EEB4CCBB8AFA45758CAE49122DC109DFB171F4A85D40460BCFCBA5A9487196420DC858EDA59E30C52F2883A5DA570099CF4C225DA6500D869AE955BE49FA22C62AE7C710974F1236F59AC43222114F9AD6E6E9945309FEC819D23E146D8F643C61BA50291C6701920DE883970502641CED2F342D13E059A5061F1DD117961AA5ADBB190AD4BD5781D2AF9992CF3A09E52FD2E867FB180665C0A059637328B9FE2F4560216034104E1BB00E5C82C46B274291C0EA50320214BC228398C27AD8F8C82D746083A04F934E4DC2C0B7070710803C1F9B9084CA91ACDF2DC0A34B53EF39143E88835A104487CEAF380A7C00DACE48A3728D2048438FD3EC255D68A566DA4180490A3CCD5788603CDD559606EC5EFB8861CC1CDC5BA49301BD5163498B37DA0CFD8A8E585380F7237A319AD7F1EA1C32BD49C561451A17EDFA47485BE0BE077725C9560A0C262A8B534A9716483123BA790CDD9094BEFCEAEB7C093E571198796C4A21517523B2D091E2FCF42AD241ACA64D980CA44FDA91098E0F688E3A081706D43D460CE9403236AED42BB55DF1D5672DE8871CCE42464920E98D316F7F4FDBF930A1F0D828127B1931965B499257A8E8533C6CA30CCF312EC468E0A70004A45B073A163A3B943159552D6DEC090C822BC8093A467C7831818D6FB5CACA20347AF117E79F555348EAA278F1493AD684C973AE1B5B1DD1BEB92A771890E43E078F08300BE9B3394B18B1CAA976A034830C43679C3D831C416DF4C3334A9EA6253630131451AC729609BB45AF9AD6FF27E570EA438E33787F29040AFD12A7B1C770FC39A9274B3605419B4A641FB61AD1442B223C8A7C58980BAF33864C87B1C41E25200D880B4D26CAB5BCB3B5460A6940A222986672CF947B473CAF496386F510EBC49532C8993F2856DF67FF8205E3C0F9104D997906D24CEC47FF9339760C4AD1191D192F145188E4683F8B41C7E7A43BB118B44384622F2281C6C0CF74B849A673357A768D524C0D5C57681230B8B681FEB2C027DCCDA83ED450E7B6726436BE85BB645B16FD8DE7D67FE20ECC380AC5459F878AA0E8D418527882EA23F6431941367CF8BA6F9E763CFFDD811BFC4F7C5D4FE2CC8F7E31A172B1503F0B1DFC4271CEB480983E6B1E42F36539D1E6AFCF646890AC4924063565EAA55ACFFD9ED231A68BC0CC1E198C05C4B3A98E8612DA8740E09F92D488EC9201D4EE6DB698437A8AD8D393645AC72B20270FCE1C139984E4D790387BC7AB8F8960ACD6A6A79D07102DEE1488124CB28B382AC997FCACE34785E95768BA9273D7B8F4A1BFD64B76049464656D7C3467CA9441F99AF5E8DAA0B0DD66F2C8EB2BE46FDA405EC84C9B013F7063C38920083B1B54A183A313B3DD1AFB62DA777F180EDED072209104C0238C12FCB0A0FCB45CC7179FCC7B9516FC787BD0A8B235BA225C7EB8DF92BD14D2E1880AFD08C68FA9E43462F2E8465E8299C25F0ED4C9457E0C42BEA4F13465A0922A8C3BF445CA8DEBC442F6FF8142AC2D2F192152F97B0E79C44A4A42B57C23152693A31EF631286FAA44E51729A28D63784CC425DD30B5FA97781D575FA125C16DB0CAAC298C6E9DC57F38DF5BE47C26102D326A64CAB4C0932F9B909A3C3EF473CBEA1FAEA6BF97DC7EC0F1AAB05F855E8DC4CADD0B9473416BA30D0D52CBA5D45778DEC65172CC0782FA328FE58007529EAA28AD270F69117C062CE48636DD7F8B56012E626033FC0928BD4E016E5905D22E34ED7B0FB5622B1B0889F1033F3939A18B15F79FFF981A8AC831AB36C70685A641F759B6663B1F1E7A25119B6BFA2696B5109D0F82E2C685F51148CF94D6C993BE07872BC40E0538C74F7C0DC9C32CF6F9D887DBFF48168DF7EEC618484894850734BE482D7AE8AC92E5989943489523E1AAF9A8055D1509CDB3409EC497A472E51DCC8417818768A8CB2C2AB08334FB2BD14A8AB091BA153AFE1EDD671C14ADC068A31980D1D66097C2AFF335C68097EAF985892CBB52B9CE86C9A09A0D839F2904BD9FECCB2C7AF44041634B014CE0013520E84E63EEDA9BACD8D562031B9696C285CB5E9B644263F1A0F5553B0BEBAF708577ED5ADCA02C235B1E1D1168249D14AB0C9744DEAF03A4E15880837C13BB9D5344EC101434835A21AB034529FC496A4838FCCDE9D48501DBD9A3BC1868B80207EDEE6B67A883324A243B9314480C1F5F9096A5B147B87C4672914C2FF585DE510A194F730B33308BDB063DCD924FD4207083FF23C19739332632F899E0818F68960712EC0B41DDD220706F9A16B0757E6B6C759BCF54A9F42A3C212AEB60FA3881E6D26CCD92912F2F877C5FACE9640A6EF1DF1BB453CA111965B75E479CEA035D963EC59062B871834F8448593AA5B0951BFC927B495CFA796F11079C0047F3DE6823DA6E2C01414E0B55C3E949734678E6116A91F0C9B181251D18B02F515D07AAF37709A6308AEC77829D0411020C3BC10278499E947F8EC8EEA659CF1A5618208B01C2DE8ACF88C6AE4F45669FF6A541CE73132D8F87DA60A4C99AC33189CB9532E77A0B0027D11F2C3D709F972FB82E552F1C70B1CDD683BE63746DBDE3E82E27A91940FA80528CFE358741896361D783D92B110936397085CE25198E4C0EDDAEF80DD80FBC2B4D45F77C763BEFADB2FDD37E6258C2ADDB563D716F8CAE3D7352C863AC96AF178642E3C0115FC515D8397212BC4ACA838A01ED3448AB7524A611937437A73863B8407DE0C925D291F227C3ECD98169FE7A7A00A89A8477B083E0BA548B5D81AF84EF7A0F58E571C305D26A0D1A9B175AA9A0BC00C6DEA795A42E3046BDF4B26C89B3BA4402E983B6FACE5B0067460EA8E12494EAE0A34C5642902695815AC8DA4A5DD7D4C023CE3F32231B6B743CA9DFFB34E5814B99350A5028C0E012754FFA7F8EC955201091C41268E88860A26075138CFB8398F540746ECB74FB8F45233A7BA9398B36B372C7B5E4C60D5006B21A0BEF8D443C66CE4398F31A2150FA53ACD8B91E2A5B1A71C3108B849F4EBA1B2018DCCB7A041CAD0492A5B2B7D31509C411FA6D424CC248B80412423EDB3BE697C016D42C2500CAC819A4003B2E588020EF0EF8E8F7B0A432060881C81055FA2F573F1DD0A3030327869F704FC1795C6B9B4F9038AAF1771A0F83962D6BF12340B8DB0E49B8086A6051BB1EE112C0534229043ABCA1262877BC858AD98936D71D1004EB193244201DB0D014947141D0C42A1696283B02E5D23722696B1D092220874543405356BAC809B34E50EF1614202F48A3F78A82D8A885792CAA4B77F9CF09BF3715D0A25C1C604CC5266BBB1405EFDBEB4F21D80E9F2716B652763A2E87E854E174BEBC2D52C610B8B707D8419E01211440B86A9F2625E6C0FEB16D2ECF45843867132BDA4C3433F8BD6269FAAEFD21E5B101472700FA6DBB5D28E9B0F7936BDD220C036F04159DD3888C4BC8F017FF020183B8DB45822F3303E3DE3188DC6351D6BAF425AD15D47CC3095ACC6B747015A821AAF5DD10407DEB3337CCB403C0E94133994E989F785A642CB90794E91B8F36BC520FD74BC727582649AA698BE7F640982F17FE1C64CF07E51D1E307B16C8765D9596DBEFE0A1E3EC39234BADFA6D58214A91FA01900DE41BA8E22BB49456E61700D0A38D97C4E7C4A23A574866D481A9C606FC83A2D117932B73B2B541A1527DB1DDCD52B6ABEB81A8BC52C4D9CAC3081804264CCF66F775E0258510DC4045CB802CC8A111FD18EBDC79F661B929E33E347AD48562FA5579BB35D694C759EE500645842718DC91A2B9FEB4D72F4624BDB4D5EB5E02529F3E03F51917503FE008B585D03DB4BC1BF29F6590DC37DA1F445A282E44A3C3836708E3014D5516852E1276A2815895639D31DC3F95C22969FCCD643182F83EA2607BDA84B2E84252B7004839C1C2B5AF1803319A2CF87458726E748AB78B35D12C15FBC0BC84ECF20EB85098508307210AF120B4E1B4ECA176FC605FC13FDD73486D00A9FC6996DCDE6139C10DDF4DC868E79215D588B35100D03044AF5838538B093A6E78B5070EFEA4038397708AEB21DE1B81872947196E24B9280B4387D1F03DE22E21309C58C3E108A4E5E4D408DE170494304325388721724E8C5251C233869F80F6E7A9692720DF0C3C5C8B8C21D5CD177E9F47F722D96819C98785B1CAFB87EA6B0269F6B3641209523E643CD045F31A980DD2323BADA84E14FE9289F48617B95D0ACBAE4C6083EC9F5C092013D32658DD482039ACE8CBE64557C9BC742D29E0531F6FAC091738772836DB264852FDF89788513F2F5D6378D5CFAA62BA68F7A119981BE1C014886C0644DD2F28E469E3395DCAE1D4563122A4D2B2B0ED14A35C66873970A1DEEEFECA97BE750C08C514D05520DD7C89F5C588068FBF21229A6F86ADBC0AC79272E102C4055EEF9590CA0FC907D44C2466F3E075DED383772A1E894C244E2AEAED44766B9C910EBB858B169C8C8B70621156D0EC12833566E0C8E7441FA68918D6C91A0471A0C8D6E99299DDC6CE636ED2A0698898AC65DE14C04D505EE658FCCB3AD4346B1CEEED208940EF0EE2F3A1719BD6F0C8DF1F91756C47FA5843CA3351DD12509670605CE2394EDC2A19071C89CE827991DDEC83631422487B3AFA34876F1C0424B34753CEC2E82B76D02CF73828014DA48885CD6098707728C75B1C3FE23EB47633CB724C098D937176C07FBAF6C4909945BDDD62D068D70D54B2DE5C2474923042C7B4C32C8FC63A2BA2D41453154"), Hex.decode("CA09DF7F89B9B339C8D506B4E0E73FF145F5F87FE26354532E7EED21C6FE3355A737BFA0264CF661B9CE14128695D598254BAF3EC5C44273B375B70FE7E6457358C2459C1C19D589DFCD9895498E28A94D7BCEB9EB4C1CD113012AA3719165B7CD0507DCEB55F284CE537E82F60C0F83834833D02B6D825DA2AF1BCC6007B13F4F3666F02248A83843DE6ABAC5CC4A68608C261AE7CF5D8809956C9EA249AB005D0571D7399F7E51E5C730A15AB556E87C08067EC122998E0B4EA0C7D51B681FC82A8640333401C37179007B3DF4648D221CF2601F0B4223619F7474682A2607AF01FC5E6D40BD78E10982EF070C16706093A2B408583277D42D76DCE0D2607695A48254E8533E48DDBEFA39AA25060FF3BDB9B234964DC82B662DEA346505375A42A1405A78F294D4402ECA92C725F243E2B9D0C2974C05D45BCA30B97072CED73B30837F13EC7FC0F80000D26DD73D5E90F707C60157A7CBEC0749DA50CA136D517D1445FCFE770711F172854881BA302EC27A960F2CAB2BDE5B0631D2CA040CCB2850E85A868E0217384A46F266141EB354413F08D1EECD054632846123B83C9C15A03D83D9446A6127273F943538D60B8D7E98C9A9703CA87605F3BAA03D8BE3049F05394059DE0141541F3F08C298D05B0C3030CC6984077D5CDF9AE9D42DB1FB342CBEBC411A53A51438984B3B4CE07B272FB6D2FF5680E0BB6325CB137A3F26A6C86E5715B3C3E41A84DE438A94754DB0A19A1D18CF587680FFA2C435454C148040DE321E13E31929B348810F95852E0B6855F8E31E227C71C1062C8C61EC404A1F4F2CF73EBF86E92050368794E2388F5C58D171251BE631CCE534FF0437675B630A2473AA239F1ECA4E641369E9C67382D41A5111BDE590202A2336CA8FDB8132767C0D59E3081586C665802899275448463FF284AC49205500ADE4E93F7040725399509BB692C8EA04A092311030594C51730517D3C96D79EF3014AC404595D70E764F83D5B871C8678C0819239D3D8D63B1E29515E4AC731CCD93421EE6FC3AE78F520F40DBEB6B74ABFBD4355923B9F66E2ECDC39832420D7C575B89C82F3679EED6460D6249AB4CE037E8C835F18406ACF8CAB6DA80333B1B8201E0AAD1B3C8DFD828D1CF50B60FA5672F7091ABA26A21996B27A0ADB819F1B03785C83D004F8E000D8FA81956A378DBBC03154F766D536B1EFE75BB1C3BE660209E5D1E4ED205DE0B856138B57451DFEB1592ABC9625D8352738D50A1D1BA0ECE4CB4854CC187C7F35467879294D66B95F022C1ACCE296CF76D780770EC9CEEC099111BEC60E6BCB031D45318156BD59AD2F05A0264282C167326E07ABBD40D924D8A3C314D586B5F4423C081EA3EB8F9D4F85240106C55074BF1B38D335FDAC2BBEDFC6F61F02DD6F5472DDAB4CB5F84C09E804C35E35AC9637428D7BBFC889D32E78E2E0174F792D66C2940FFF090108D26E2FAAE166A92538B761C1C8FBDF4882102C2F607ADF275C20943D16527BD748A83B04BB8FD5018C5C5DF0A985644CEEBE3C200741EE8A39BD6247C662D7C1A24B500F08BF11E8E460203BCA47406D48D82502A41DEF1DC7FC9295E193FAE94057244B0F786F7A24E454699432CFE2F3F7472C63FC21131C7B9F6DF8BCE2D930CE65FCA9014A732D7BDED658D1A6CC154D7D995A9B1CBCA84DF26A2B65B080E462D1799E85575413D61A38C260E25B69E9C3A039BEE3395FC1DAE10B2C158CDDF4319C347074D89B27848AA6B05A13ACD6C51187313FDA50882E6B83B30CFB2319B4F3B3F5AC3A95B0E8A5C9B85E60E79AB7483DAA8B4769C14B75E28923839BC9A50AC8B2D424624A9F3A1B531F0921330B7B9D97C839AD64B1D2F9DA93B3A8E1A2C9A72F0D647C8D2E5004164B18130C39F54678E73E22F716CFA467918E440A159A8514A40ED2F0DA6873B9952F5D25414D2B2F4C21FFF321EEC20AB81E24B106AC33C648E96AFF7F293B7A8D676E21CFFF481EEF83F61674398AA01669004B5F7F86E54A7AFA6301392BABACECB2C18E0D14039514F982FFA26E024B5927E6DDCCCED9FEDB07A3FABA8C6445D0D63099832B133089E79388D62EF391D6CC089A742662A5898D7314C901D71004ED6C6D85B4E4561AFB8AC3CA25B1179474CA69D0E7AD185D9F1A3D8D066F8399BECBA4D430906446C3622F920826437E642CEFD375783C7D5A2A31326C0634F2A88422C456065D8BB515D818FF1ECD30FFD6A377103E933C086BAA9A12C068302E281739709008E2CFE66186A0471501082D1417EED5D233301683D2939B3D4C8A92FD5C4C3B28182FED67CFDA6A0D61269E2E123457BF8E436F57224C7F8BF7B63718DAA975C0681DB31508AC84634FFD75E55ECF9EBF27A59BD3D06BB909077D16C1A8C82F6340301FBECA553183CD09D72C320B97F1EF43C96F7DCFE0076DDAFC532C6057EE06C16120AC0145CD02DFD64106AC7AFF4F555E0ECC0CB244A21919E8EC955C18D320BE0B336FCA69D3FCFEBE59CD11D23D048102CB13879DF6C1A325B97534C5C4720411D490458ED1C5C04342E25CA0C94C2ADD754F672F80FD2E3EE424F7891AB751F27259EA65889B92FFA7C04B24D4076166ABA495F969944E6C41A3815AFA860B1465BAAF4A0FB2E139D366980B47E9F72D723A00D4D26AF88248DA3494B939D70352131D3D994E2E988E25CFA3DA83A53CA9B4215CF894AE103770BD04A4C75CBB423829D82E959D29BF84E83C5280DA95E27B5EBB45767C5CBE425A6B431FBD9EE12D694AC45268E81C26716255162288A20622ECFAE30724329A136D714DEF2B8AF842831F3472BD071414EAF694A8524369E97FAE312ACA6498DF1834CE9D8F96D1265AA59EA68124C32F75CB8C1259DED4D7FD0F5212DA0B2828B74FFF17D4FE475D81A9BF87C5F539A934025C6E07D853C2DC58A4AF75461D94BB5C32FD4A98D37F1DC90418F3F1EA9220CF6807B505A503F628A8969751161254226A93F20E1D042748E873296C1492D1ED5FC9AB7A8EECAFF71F114A49C8C192971AAFDE226C93EA168065FB0F03703D3B24247DCC7293AD97C1710CCAEA9FBA6FAC896A8ED9449AA16A76FB53B20C0C80EBC53F109F6E37582ACB14C916068B288052CA8389F456401D84FB3117552969A693669526E62D48ADCF24B2BC60381BDC3B138B25E2832409800FF7AD3009DECA801F408C8B251EC39BB4DCE6179119563F6FB451E43F93A83DA917C81E5C77809243F212865AE369996D62660F3AB34BCEE7F6FD8D1A23B0A8318211D16F4AEBD258B70F3C7E2D8DC69F2B52CCA0B7EE70B10B6C2D910A62BDB02160582C0641043A84F9AE710DB30E718245111CE2DCB00B82B1DA1D29F5D059BCF062DBFCB5DA4674E14947E8138B127D2DD201C4859035E4E92FE7B7717E971E3801827B7436E7FBC790791A2010A8232BA0BF3129B9C54BE3DE5DE4DA1D4CF6347D8287671C65E91E5BD86C5E69AC636C25511F6226DB92A5B6E92272E3342D3B605E2BAEA289121B14B19A4DF74D4941B24DBA840330BFFEF87F906BEC30A48AE50C9721CABDD5F12ADAA1D33A0391D7FE225BD111FC4A85447248C6033D9B531CB387FA3B29C70A4314F0C88DFD8070529BA8A52033B4379ADA6E37263D6301CADD83A9900ED804A628D98772577DCD8D8BFCFA64814983F96A90F9536E68BC05869EF1A1F99FC90EAE48E55066AEEEF9CB156142007037BADEF631DF535340188F5F2E7A415EB79889E6283256F4223FCC58D484D75F987EE3BF546C6070053C66D779C0F210925E30A152E80B19ED8FBBFDDE55801CA44E435481E27C5E04F27C57DC147E026290510BC09331B0EDFFFDBCD2A66AD13898E223818F4806196DA24F9B91AFC4E9B571581E0002DD6BF91331F3A640952ECE5DD244FE1474838595D8308B22DB80D560F4E37D3954859C074456446851D91E9E99E53FE2C3D9D0BF95A36C882092C40060AD0884D17295F7D454397B515FEBAB538C3BDA2BC4C1F93A0CCAF2C77E400347A6A31A1DBCEB24F008318957CE9D399E4ADD2A5844D2FCCDEA92D5CD3789F624AB34F2A387D600BACD5443AA18DEBF5096228F6F249C6632676ABD554875E7C3E8DCCC89F00B16343E164BF643825FC40DCBFD909A188B93985951D3D281441202D3D75C27CBAB4D20A5CCA82403FD5A4AD92ECE876177CD2C7B140A479BD55E9066BE4BF1AC453AD389C3311DC40311C590241B6B612BCEDF7987B8CFD11D65D3F249C9D7C638445C6092243099681F502303AA7766C7D1314F575BDDB3F388AEBDE83A6C1514F4C881C3698AC89BDA978BB9B04637BE90F1867F7126CF5EB458F681016556BC551514BC8B90ECF78DCB2914BBC40A925D2401A07AA6D1AE6085D710106B6433A1CD1A34C4CB1A752CBA1536E5C30279E7775DCA27ABC86EF7A26067C801631AD6BFD286AECCFBAB22532F51EF33ACA53FF8F80AB55CE2C0502F798FE4E727BCCD8B99364DA5417E24836F97B98EF05878C9CAD1CCB42A1443023173FD11EA32C5570F031686AC51C0FB9259336892E863EFBD7AE5691C69046CA1A6B11D7D16D528A89864258131F85F0C0A9F51F07DC3A8FC3ADE3B817D2DB1769745E4E59FD5AA1DC7F1C6422C7A51C23846DD21429AC07DB6769923B4650F9DCD06D25DC524912DA42567BFCFB1A9A0EA98BA0526C6BE7FCBC86CED7CBCAC9AB6CFC261675187DE67B3950861F58E47E0742D8F5CF35CCEE41622D0584F89F0C829F8191A2D33D68A64F518E2F50D44BD616D721DEC74E6597AAB4002FC87DBD9679014F9A887715CB2515559FD80946B41A4D27490AB25560E4A1FADA6834CF2D543EB9DE5378D07842BB8450104D39342E21B522E3A0FAAFC9D0771AC27B1706AF93B13C8AEBCC8D46EF5392760967A3B31E4900D5A4CB080B793558A56C1F1C4CDC41FEB54E85FA8444C7DCD7C364D8411EA0ABBCAEC8FC06EA482B59987226B025B6B455C31A1FDA8F6BCC1F00E5D2E9B605B330EA0A407B58AC110DCF1E7476A9DF332752EC63D50DAC7B4380D1ED52F080B060F79F2F145D318E9A09BA567B9863C23D943D58B3EE7B2A07D80402D11F1FB0B4CFEA9170AB3430AF016E08DA6A1AE4DE84E0BEF97C43FA2FDAA6EF9336E8F7D18D7DD0B953D832BE2C4FC05498BD8EFF0BDA01B065A0D0CE54E6C580031A79F19C1536BB261E43028AD8A0A4D4AADA0D5E7C498960289F462672AE9AE4C2A11973D0DC5A66F628BCCBC580D0D507874D01A68BC2720F82847FAD98E5BC13444663B246F5C269146F64A0F4FD6FC1F4AB9105C19A92566F8F341410C7788164A97D8ABF531C2860DB9004950BE0A92EE7E5D7B88D20183479419B5B2520AEE1EDDC951A4E6D4290BA2387A003609E5C826C470EE476F9D0109826F16415B0B746D87D093A006022A6F38E4EFC21548DFE5030C7BB45248067F5F9904A8FB874725D5BB4137D2E427C170845CC2C8DB4F20FB0A6BBF368AA9C4F206FEC9A4A567E65060DC133DCEE08DFC67656EB917BBBF1CFDBD1F315D71A75E8754A4AA9AB6D35BC618CFFB2DF03E796CC00D687CA807BB974CB1B298F1D31ED2FFE0D273460BF10BC99FDFF602C619E6A42EC55817C0CE483598EBFD917DD06451E96466DFA089D67EE1157F2C9D7C409EE5B50C048DE8BD31F0F297774B226661645719A8B8045EB2912820780A49A7C5EC24CAB39A218FF6C74576C5810B1788417CA180552BDDD82F2ADC38CF91192E2F268EF78827DA8A5754A6AC6D2F860EAA20A863E54694DB6C27D2DB37CAB532319F64804A5CA5C311A26BE6699830AE8B4B2DD67980CE9B51D828F6B1887B81681014C9874C50E043F2E0A8D7D36B914A6EAC8E0DD581384CD66E87A43760C838896AF808C33122356AB7C0680249EAD3A291C90883EDD4BA914DEE43DF13E1A70844C08326882A818CA5BD068C58AC34BA919D25EDCE6211866772FCEC1B985D651BD472DDAE6B03A6A9E0BA00DDB3FA2D62B07B82E05A3A7A7933CF0FB8EC22399B3EC77849B8D53BD7781971352EC1359951E8D4FD06244BBC2C6EA645BC37C491DE1C2EFCB1A88D903960EFA4C461CA21B4059164012537260B060652BD9ACC36D0223D575866530A9FD7DE11DCF18FE44BB84C0C6205D77196F349DC914736AB83CB87506989F83A627F3DE150523C1BEEFD9528489017C023B6738516692CE73CB32E13AA3B902DD2D0A7908760D12D6B170E3D04048ADCD8B8DF55FD18082F911F87E93113E1A66611F176AA53DC6EA272D6B76115AA6E210B9F1C23EB8E06EE521A3806917D584ED58CAA992C340660725EABA1DEA69C5AB8F05395B7B0BA8F414BC63E6FD42AF6AAF2A5CBC0AB15D57E7211E02A8344056E53A665F20EB760237A5CFF3547F84F4098936C4C05BAAAA6B6D7D64F07F9A047CE77E5B2E6234ACD3279386C6FDD5233141F8A799D62DA14D03869C6DCEF12883DA4A846FA286C8D5B09522619FA037D88EC559101C42A3B4379142EA7520513A51ACC3EB818C00450CB7F485E4FF855A9224FA37A89177C09DE1246FE245C366F4A96513C2E8A1CD77C4DE2F6422FE7204C4E501EF7273A1192AD5B2E6D974C472562BE666BF1261CB15CE9F9E24709AEB55A96FD838FD041F1274554046222BAE29F29666D3FEB06145BD3481A1B82B65D3FEF880491F39C21EBB2C03C70648ABFA376749E9FB3BC3997CF4A7F94932F37D35BEE18597C85A223A45D1A3D3A426D0D7060CC412483495EC11A0535DF8D8A6B92DAE50FB18E37848364E6D97E2D4B7CC63BBF3CFA62AB2E32321016A48B8193E82A2D3984ED573CADFFB90CB8357D85F6220E99EB60063A84B3E3841A34FF102075F90672A3AB1055D016DB70557AD7FD0D02DAC7D6794625010E685FA19F2334AF6F660473D066E280359DD5D2AC4080CE28D228066BA663A8040FDF8E72AD16ACB7661C993813E5AA016131BFAAA80437E04167E6759F93C453D64A0EF4DDA10057FB60D522F059A994E679027037BEC7F60DB81281007CFA16176569A663413265B7C308E04192E9D87E48D8C15259BC288672969A94AA2954E2D210AAE043E02C030A11E96C9D0983B2ECF3BEDECE727F834615731C26D5F187AE0423F5B34408288876254129112778DC998B713490346C955AC0E0CC86D52423A8C552E815902E686E28FA466190B2FE042A23835A45B3045CF300AB66DF4A0615C93164EBA72FF460EBC6A728A7B304E6719C719FB18410D3DEDAA751C02FF06FE18E8C26485580ABE81784D3C73CE72F734ED688EDAE3A06D8529FCF7900C029FB0AE926A601FF3739FE6EC7C5891D32F91DBF47985004A766B3C93611DCCC47D3E6F435EE0D21BBC0281D4D273172B3952617E24AC292037D6047982A6BDD0BCED4653CF5E07F7662A250107AD9EDFF0396913048030F87ECAF23310B9A83E01A46EC9CDD028CAEBB3DA078553C3FB3C61F7D41ADB0AA0B890447A0C07BF9FEB04DBC5C7162A036007D5C8E3199B21AC6498DDCB5760292783B0C8FF3D88431ADF9ADB23209197B5A1460F7BE09826EF91D1B6A6F011BF52CAAEF9AD34488C0594A3A4EB34CF41527EF56DCA2EAB729518984A1E30FA5296759F08EC6DEBDBF5D1ADC02C6DAF13B2D69020ABE3419DFB38FA3A9A8F7782A30F980B09BB1242DF82413B2FFF6640126088E213C9F8C3C2A6EC4DB005907AB48D1DF60382C61A14683A5030D2F81A89CBA572EECCCF26976EF2D61BED698F127392E71C5CA93C0C61217E92ED22FB1870E425100E46EB34B06819DFA694AECBD922A0187853C8BF33BC07EC68CCE254D4F6B5918A0297F6DF27C62313B4C67878C91A0410AA1B6142C0BB263AF02FF80BF4154BF14CC29C5ADF9BEC2A205031148B634B8D65E688D3C4431529C03B91784C7EAD5ECF020842A47F327142AACBB901586BC571BA17F2BDEE218C53A9E58333033182A667273AEC10D1CF900858DCC504694459D0840528C2F52FF2D0C080DE481B0B169696E3B36866E50CDDEB398B63989382DC2734E032179F2084FEDDE0CDAF2837E8C11444C1470DF1E054CDA96DE7971AFFB6A1E8B34D84F4408BDC7E932F5A2C63214891E0C16CBF8C20C145D18371B1DC6A339FD19116EB884AEDA668D8EE2CB85F68CD0B9488E24C06C75F53A6D9BFDBC71C8BB17E1202EEB10B7D3AA7F9AAF31457DD4981426D21E91DD76CEB24EA982E68A12F77850297B0592C9287996834958C2560BAEA0F3BB8E76BBB1224FDED7AF1D4A6ED880BD95F128F32BD392B3FD343D58E8C5BCC9B1424BBDBEAE26A2120AD759136DA7F28381B9F84CC902C4BA639294255F6344483CE34B37FDEEC50544A1D26184E76D05A6B0FCE9606870FD32E0E2619C7D6E280DED92194A60D5E7CCD7487E0602BABB62FE3A4EF15846B7683E7F0D262AC233C735ED78DE58194D288435700A78661B753ED26054DF4FEEE0923EEE786E5F6FA1A6666697D33436A4E9A3054CC82ACF0BD2FCF903464A2389C87A67D669538696E658D1CE6DDCA9AEE1131AFCA2DB21376D43E382A4B9810C454E1B613ECB972B9BD60FF35AE698B653864DFBE219E9205FFABA6700B514F41936440D926B3D1DD2AD696E8E86107AF6663A43FB4F199E5F271ED3F6759C54FAF4843B9EA3F0814C7301C77995A43DE6793E15027DF52FD7EAF2AF3811667430E52451F56DDF85BF290F8811D586A3184836217DFFBD78CE485E72D209AE267BB4952DF8D301A55A6BB1368FC522A398D35B59FECCA5F89C0635D83E6730990983D47AC6AB7E7DA85F57599C3C69F85DFF280BCCB38C2F515AA60957ECBB2F2010958C234ABE4CC7D755A82B017BDC2BAC5FCAFA7C9D392CD32769641DBC5D8C8CC3E376C66991A0CCB4B440015B3084665163C4876F488224AB6E8E0B2C215C541084A4A15CD774FD71F110121570E8C05A06064F8FDD1442BC841A78D6FD42B3D398EF2470231BBC717619E01144E51952F8F1EC605DF00F6D98D5F18C1D26E0271BD9D51CB27C09F99CB749286A46DA3DA77F3645D181DD9E24D3DCD01FC0BE2347E588A4042A49526BF89ECDB3B6A1DB48760C03B6362F42852FD49AA903840F557EA0FF6920C678F1AD06BD5C8E7BBC1343673FBA164218E09720F453E54273DD42BE2B20DE1870C7BB6312F833F76F2C5F2250414A5F3554AD28548F29C4273AE67830B361ED8ACAD7A4E13C7C832DA0958A9178BAFB4A40017CEB4E9697F8B0C055904D1DAFB539D48DE75A3D2BDA6B7E00510968CD9C5F4B290E25C79E46BB9562C5999B4762B053660FCE627FC6E92CB6288C529A72938EF591413371E263D8D71949F64C3001BC323F8F03F2834E34FE4CDD96E61305ED478CF278D3598E07960B6B749C34C22F5875FC0F5BB93EBB3A157EE54200C2360F08A03196FEFCD8471360384F32314E2F90CBBF887BD63C444A097A04F9679E373E9A6A0FE0FC730E471483485891261A1579A54B76BC8165E243E4CB7377DF346237CB311D6E1758F4BFA3FE6A0FF05D3B82BA44BDCD5E8C9E418DF41F81DC989967D329C3D53454D6A4D58D41B8AFA036DE038ED2D8E9132E9357740B33B918C8CF243DB3460092FB6E5E264A1924AC4F249B9A774493C4C3D2AC7B672B1E998540A2609D4049D8F5F63CDB1ACA366B7278CF90579926B3C55230C64246A65FDCBDEE57C8BDB34E36508999A9856B1CF748DFC999827D58558B77B322E0091E1A630C1C486997FF7B56E9CAA0621E569D11E0C3325CE6076F1A30F812A0692BACB338BA1F55B82FF39D7498BEAABA06AFEEC76FEDD62F97BA9027666DE9F49FAB64B54107B51988801325D9B51119E4AC32EFFBE13D1FE28ABFABA759A04781FE98E6881F29C994AA1BC5E9D0DD9EFEB18484D717176903A74D3EE1B27D54CB0E33CCD93887828523E41D115F7B51E7E0D5D43DC484F1BAE8111100C3F30E72968DFF0462780AAE01168088FB3EFCC30F329667489018AB35A9691FB76A6AACE8B02B872437127962AE12BFD8C82DBEF2DD89EFBA3DB2B63C2801C0EE6BD331D21105C1431FB867FCDC51C02DB248861348C543012A8878A6056C8D047270461D51EA4D0A85FEDF1AAA416029B523FABD44E3E25D9F555A1DDBD2C030F3527F758A0EAC9F69428D3F74BE6D8427AF8B0C407E0E7818764AB42004829C8F102AC9822F7766AE94A40A8F8F74F0C0FC1D601702319530BA0AA86326A6C555845D1C85DC723A07FDFD4C6F85C2F5C4D912CDEA186D12EE418839CE1F9456A42C603605D42BDD9C41DC40DD0720BF99AA3FB0E2A807EA3A406894C81570A323DD8DC67F445A53FE0DCB0C0835293EDBD4F850116D2B3896D1ED631455D9C9C5255BFB20453A2DF76E9EE3460A09C24D08CAAC998A080677D755283BCF4E4EBED728A81A6713B1A988164586DA4A96E666F5ED8AB824D7931C4E5EC3B6AF1D623A3ADDA1EAB6E7ACE419CD63E81CD93E81EA4D2A64D25A59DA51999707157A512B4033F52C89ABA4DEAC79217F725F8D881E3ECA42FD0A7C08520E273C9E2EE15D3E704AE06312116D3668E85E41E94DAD2FB0F4A506FD40431251AB643E4A866B361CC42963DFA57E9249904EFCFB430F30346A02EC19997F96A3FE2AEB5574C9D065B68804109F32ED27CC6DDFA24D86583C58494960A78AA820D143542694708F53973150FFA3C3EB73B04592CBE8EC365EFF2EBB491298E16E11125D37B6072D67C21E87EBC8B410943AEB7D8E23C17E2C45C05F46AA6F852759A78B07CD5463CB30395A5B407765D9CD99B00D40176D050D0AB736D663BFDA7110520639D1536022A0F99C9A4995DE1D6AAFC7F8C33423ADE5376E95744DBC797742F7F24258FB3AC93249B5BB7112A23E2D1F8316803D2FA940C5B5D3D66D13819370DE2FDC4A90494EB5CCC69A1421D0DF68424A0CCB0E53A7BD18682556A540436A8B43B0213F27958CCB5D5A474CBA693567663521140F3A4BBF5848CB4D77932A1050299404ADFA629566CDB21F91358CD918AA65109C971335E041A0661BEB972AC835A451A66B6AF503148DB5587540D4137A770B16412C9709AA6500364A38A24CB9C826EA367A62D330403E90A0BE83BB4E3980487F0B67A046B2876032524B5F185A6E4A9410CA7D9231DB109AB0EE09B25024F570C2B57192AE98AB0466A20D634F266413FE8F3E702DA112B6DA139191CD5D398ED9D1445A83E237954E9921C55308DCB34EE24B7068B69DC4A7E096A18FD968C2C3E23FE75249341C79366F4806436EA5EA1E7CBAEE87EE61E50A08EA13666C241A2F35B04FA20EA1D52C809F0C30D9D80A8BE6903C93CC448608FA51ED25A02A9D3DF93024BF9D262EB032A16DAA0F16D60BBD333E7E211059947CA4B794D581C56B8DB4E46921D2928C6A9AB5F58BDD0C8D9660CB9304A5FA488F46B54D65161AB28B2354B341A694D0D0BE2C6847BD19A532D5E34129D917CC1534B1EF2A3B69317DA0A5740262DA7626252A11B4F49D6C95E215762B642384373C4931EB8B879EF1FF2355702DDB6A367C9D82DF33539F9C15B38C764A1AD42C627A691423702064EF10C105B54BC05EB2CEF02BBC15F05A6E0DD6208E61AAD7D95422BA752DF35B71EAF8CCF4710B7908C7BDD42E52DE294BA2844CD5F801242BA10BE8A9839C5F4E51F9994D0C147C392FFEED44D66FE8AA8E5DF1FFF1FD88D7A2ECD4780D6586B128F6406E5299C7B5C903654B0D098D6D0C605009FF29249D6FA05015A3D8FCA3476F09097832ABBF8C5709DC14F167F4B256DB7B29199F481060BBDD60C3BA82C1051A5D4D8B6D6C81CCF9149720FA74C54DD17C3729E63D1AF55CF4F29A854D1339746C20B6BFA5C9A6408DDEA5C76EF52051BB31B2EC2145C9CE79F7E8377BA78043CB3E2B64463950FE6CF5865844520276378A55C34D2E861072D6A405FB28AAB1C32E6702FC93A3AB9A45FB51A0287E5286B94525DA50F0E95CDE3D82F6D8B65B13ABDF66B018D760F7C51C78EC19EF0BE026D237170AE0FB000EDC54790357C4015DC70BF1478627A3D021483D6ED3907FEC1094F10E03E30C3EC0A1AFF76120EE48D042E12BC6F27BDB7910CAD39D562B74B056E6E87132B46105AC0739CF50176FC6232AE0E673AF496C6201C95CC46254164498735CC12198C50C925E9259F041445ED012D31E10631782C8D69143A0FA5B0DFF7324D33D0C419FAD821F41392823D7E00BC923554002D0F88179BEE302EBFF417120DA81CA9797D29A92AD4DA741B0E12E81E09CC209C90095BC99EB7C172E1221A208E771B293CFD706660260635DA6AAFAFCEAF97CD119D6279C0EDB5841E7C9135C563D9A6152D6E548A408BC8F82AC2D9F1827653864872440C86B35DD2F3621F23E59D5B005641735AEB8F51296D5D5083DDD16846AF1F871752A3CD23B8F132BC97769B7684AB075F4E2F216249F68B0C9961A63A836684A439AC01EF6A93D49B09184EB0695D38908F25A734C782982803EB09042B15BC7E7CE2FB717A10771C76207E066B945A03A97A9857C44F1312C5C6BB57C8BE69609B1EA9C241F4A2E6063A92746B5E6FAC9AEA6941E78468A9D464473206CAEECAE33BFCB807CC8C548CD557AEE0E63CBF87971005E435A0E0EF7CB7ED136E8850DFDE05C5A1E5909EBE4DBA52E38038785458FB6E2E6582988D56EF366FA168586F2C3A24B4EE89631D7EF231A46CB78F50FBA33F05AE10F484CBA51DACF02FDA944191C074097A527249D485498B3356CFA2436ACAA4E0E31DEFDA70E2A4936040BFA3443A8A09771DE9180AC6AA017E952B8D2D0023C3AA2C70C89C78B7691C36853013FF77155D6427BFBB4FDF338B4F245607B5B2A1D0A6A1E1774C640D760948DE243AE11CFD829BDA12D117898686162EBAD1B41D6E7B2743FD6D3134313445E409B4732C1FD7FF5E03E797E7C758ED22A1A660A56BB8C53D11FB1F47944C281BA6805642E1FD4D46057C1A86E4D300F64FE88FF0939F154DAD8F31224A790F72A83C83E2F23FC93D3855C24A3027C531DC4C92797A33554468CAE2B88073ACE64A8B506260606540F5024BFE03627AF72FBA4ABF713CC7DD6F90FDAB5490544F936BF2F44E1D501DD010415783C04E735A620FF4195B5D911CCB470161E2B88D42A3D84392A814C668FE08754A97009366DCA99038B81E30E5FC53F4A09F2A10E4B8C40E687687FD93B931C2329EB26742EC6907ABA5600C9415D7B9341896DF4508B9C6FAC0D41E92B72604B0307AFA7EB9BB7B32DED98C62713971BAE10BD258B435AA24CFD91956BC7F96128981EDB49ED70E6A20FD233282C23DBD99AB4787E12FAC6EB42A289A5DF4737A9DAC91079D85F4E14F28387C7C2D0237625E5AE3642A8071AA15740A2B11E49C152687A4E346B243529DE6EB9C9780E9DA5093CEC7A0D74BF5581C1B934827023AC4A618BCFF934B65CEFF21DFAEB2151C7E0C4D64F91AC082801078E97BF131838A3D585B661E691F31AE0B7AB306C91AA6269DA4940352BD7E70CB55AE2483B37FFA8D4F6282935834BD2A171D798A924A78424E56F9881F0C98C2D154B32412B0927A00DEC8F42FC3AB694D6B3503EBD5908385BFF6AAF8FB92DC01D9C67A204A4403F89255A9B06AEA4F8F781383DF3E6BA5ABA36DB31DFAC3F8AAD0F1089DBC8148BB1886D27F4AD398A8C3C4D3BB6077C63ECEC218AA3F8D9CD6E0264B6465BF67869A9CC723500EB937028FC93EE67071DCD10FD50AC8F8D706239BAB3987499A2F0AA916C5C98DF02EF68149F28F2C01111A0AF50996D4026AE1A93CE35C32B23FADBF9010609261BD25C8661BBE64F1CB2F1926027E80F4A3466F04F94B34CB6C3E08771210E306CA8090A32B9861B4B4BB9859548C0A9C3D3D39C4BCB5E55854006037C026904829F30752EC29644F089A0CBEAC2A2E8873A081EA9E5B00FD97E77AF722A15809918033B48080F2C8B9FEC4EDE29C636E013FB4B17738CECAD690E2E8160FE8B299E912CA5E109FB53BD663CED0406D28F1D39AD70201906FA36DB09CD5EA20A9D6411811B169A0C81FDE92EA8F436507DEC21086320CF297070932DBE117427FE13BB4BECC713310609CF0D0077743D618AD60761580D40E8169698DE22934613B13CF86243589709AE6D717F78197AD045EEA1978AA11B0DDE84D347670E4F8CEBD5CF44719D1C2E7D2271446852DEBD3BC14B96F9C82F81B1D460C2637C9D435A544C556785A000A00F8A7033174746581AA4925C923BCE4C0CED41402AA1D107A5D7AAA667B19582DDD31864146D75DD3844042C1E994D6A2E2690A53DA29659B09854674863837B013803961074A6CEABBE1A4954B606FFE63C10F5B716409B876B8F4525A338802B34CA2668FDF2E978EBF82984205E2019BBFD865249A560817DB872B03D000174B7EE0452BA00E20840A1338DAF31679233BFED0C4CC538462062079BC8005DE87199B0DD680E9ECC5037287EB18C08BF1691987637AF6D08736D6E0D8905E2E1ED8CED21CC15FF3F61465CE2C808BB7DAF1F21B08C614762E4D3F42191767D41CB62E32F049DDC6D2FC51F680E4D89CDD6C48A9D2492D1696A05C1470EB680C8F90884B604E25C169F53C8A39FBC6D5BA02C5A474712BA56741236BADC59497F3A7A251CC52A82AFCCBE8B5E729115AF56324EC1F12A59F143ED014BA5F252E2A8CE75B449462002A49977F7C888FD4291E24EA1D0ABDFD8BE24494045EDF43264DADBDE2310A51923CF474C1FD9AF84E877F7C745A2887501EC70C394490ADF817B9134F84DF5B1C0FA17942E374D854B73841E464C2AB9F258252EDA66921804375862E6578EAAEB419417BA21488397E9BB68C99F4127491F5F894089F16A4303D3BB862C17BF225DEBB6258810B317632DEB21F9A9ECB11C5FBEA95AAEAB286377E5B12A71C11CC44B2D656873E6903C729402D2912F37387028655C975D7139248E06726DD12667C8074391789B11E0C68EC2011A7BBAB648FB754C20CA7F3C13D710C4611587D9A1B9D404802F8186963400D6A163D306697C072E638AF78B0E852E917170869E30C81F09C5DBB216367C009F6824D596AEA12F54F1074128ABC68E2F02A5ADBA25377BF1F2D3363619D55999C6C00DC8EC2A3053E28E3FA31BD2C9FD52E5A8A34BEE7F6BFB3A6AADECCE36DA5B77B77D7F5B12449CFB45C3971ED679ACD1427C5BFACAE6A03C3C8EB83CB6A73BB6DE3C5028110EEB4754CCEF4644C1E43809F22EA8EA5B2FD49E3783762494FA7D6FF65496115B993581A0283E686E66A5EC6F6B30CE8CC4258CE23CDA81B19839A5DD7A957515960F2875802388F58B7E5439CD06E56445A708E4F31899DA1DDA68FF0EE90100C9516364A1CE8045D7862B094BA4859DE7378B03A30495405BA7D2316EBCDB5C5292AFECA096A273EE3BE5EDAC093B3675E6AB19EC3C0BC7B1D88DE28CEE9377F9AAAF31769AD4432131A73218B3A11FCE061C9F297581A84E1B277A9FC4EC25D9627574F749D3F843ED73A46C8F0390F6599D335B3A7F591E0F9AC9DAAFBD43E9D056E568C3A2DD3C169052A33D94692CA33E784219C7240A537559AF9FBCC0089453129069F62F27D1F70CB3EB0BE9C555731C47EE166EF9500891513BE587683DA88A9AE9C34C1DF208417625B792997E1AED962BD2719541378C78205DE1F0B38813D2208121F46D36C000047E35098410132DCD171FD2168ADA258EA16641272A19A044D9142C7A861161818F8AC23CD3B07DBFA21AF9663C465340272A7E6932F1EF702DED1ADA76E01BC8054A906B8A11E7128950C05E777773E172831B4143B1C55D34679A6F2167DB63012B480E94956E46BFA5A664AD836ADEDEA8E02FCC1D01129854B3107025726862511339BF37D8BFA97D014B43A5F5AE93621CC7E21B362478725C092A534320B4A0C7902F686578CFAC97845964728AAE31EFD4875B2E0EE4738950693A4221AB31FCFFDCDB6EBE4EE727EF0D60E968DA6CB8B503E7CC293D59FA38170329B67F3D4C059BD0C09321F40719DFC839C00584E86C3B7C52FC88332B3AAE13132A6556566F56FFA0CF65F4311B98A29114A89EE48166DA8A7DEA2AAB7586A0338B3E5ECAE3F8A284B4E1F5B4F13BA7B1C5043FB4AA22F226F3242C7AF2AEF552885081FFE62278268C1FF5A3F24A1BE8F5863A867F136F2646FFB231A8C0CC65CBBA7FD0437BA1A80BD29A1FF18F08FC74BA1875BABE3361BAE394AE78851E4161CC354D16579C1F3DA19B5E8C4DBFBC6F625D75527A595969CE1F59764080B0D7882C99884F6F3A8BCB3AB63616804CB6F7F728D0110D827D8995DDF29731D4AC6F2E91BBE034A85F6434E4A6E93AA30FFC349C3ED2F7021A4421842348C90D606096D10E4392F8210BD5A78EC80C9CAC88005F381152322B40F3031AD300A1F7B0A118F83BF17F05E568F468FE0CE9ABDA646125CA920A10FF9020B384A2CF8767601C2A6CEC71553CA54ADA7954784D74B43E6237C8A3BE4CDAD7342F6603E159F4882F13261D43D41F8ED6B392603FEF00498ACAD5DEC2DA528D6460D2281628F8A76946895A18D318184A40A81CD50A90060DA062C8BBF15E4907821E986191DDB63AAC26E8CCD870C0CF1B7BE5C33ABAE6BAEF55A9FD89E59E3417DA25020A08AF5604179A2232F45A712691C3740F2817A71C5B69E3642BF57E35CDBA612BA5B5A0BF05F2D7C65F51785F90ED2C17898D4D2F62E76999C653768A03763E6D85C643FBACB833DBB1716C9F9E9E86E7A9986B375A2D40727146627A3B5B93CDA240E592942556A42834F76388485C1ED23B0752ED21836EEB731909783A19AE2CB4E487A157CA2C1AF6ED99B75885065FE273005435E85EC66962922C4C8C246160A6AE31CE13D04E48A27DA835948B3288E762C687FE16B160F27C54E1DDAF2E2901E28C1690A90118B7A04B4FA87ECBA45494B3E77E183646F8087F3821266E66127C5A0729BCA1D874C8521526966F3CE1094437287A88234D956B85000C54F654756304C23C4E1E979F985E134C4E4057D89710D3CA2DBE0CCC8B96DD989B0B5DE5ED2E85D9BD9AC48CA0826D1B00024C4D788361DB80F995DDBC23DDF0350F04A67F35AD819397B01210C6AB63D98AC308CF8EF0790E5DEE9883D11D9E0AB8C097ADFA2B2ED761C52B86FBE81DCC1C89A73B76D98E3C694099540C2A2941AD89B713F5EF4CCA5477ACBB646F8AB74B2384EA7DCD7A5D20D5B29DBC49B2646732AE6EF051C879C8AB11E16EF80E2241E7852DA941B8042D08EEE22DF4ABB708B2DEEECE28A9AEA56BB7A1654DE8A77D2AE487099EDE575A5316558E3D099A0E6649D3BF37DB4077D807E7D1C82D9D3BBDF162CD486EFCEB1975696B9B21FDA1A60FFF76304A07841C05ECBADDFFA6443902AD94163464E51DC489A1295FA0F29E5323C6CDC7F85930EA3B12B89CA5A808150430B1E6933975FF37993E65658B212167C8F0A359A33470986C8985B82F15FA9E0839837146605D392FD8ADCBEA9BBB8500B792BDC5B0633E98DDCB5A7B50C798B09C2EA5045694E6E1A1ECE77681C278C68A014EE030CDA031377D2F7EEE3B4558151A03CF2C9AFF390589F0CB3F772454395F63E1FC6B151BB3AE6218FB7F3B6A5AAED22A0944CD916023EAC65FB7A47C919B53AFCC0D1EA295B09777A4191D647B95153E70596D8D3671EEAF863FC6A0D956D0EB38DC91A72C6F68960731A17BC99EA47FA484D041A2AD6A861E0CD9C0EE4CF618989A3B146158088907584F0C2B1350E9163465CA8168B45E5288E8FEFCAD919D1CC54AEF5C82973A7EC74C23E3A64D0D2C94D845C360E43A9C38D8E26BCC3BE901DC89B98CA50C71453110E0D587D1BFF8B72D3066C8079C08FC8C179E09CB9F02FC3677A07689F2212B0793FB9303F9414152E80C24EFF0C5D7AEC991BC42BA622D309D2EA4716E31A50739DCC97A6269D3E66B7D3959AB40B8ED047B4FB20D58C7E067D28520BB2F2DA582D9EEAB21BBC66B6B3BBB6EA1F6C5BF1E4B3707C3D00A53F636A40AE882F101916397A32B8502DF50494A304688A189A4BE736F56227A73E4A5BCC68FBD548CCB46EF5E52C65CC5B3CC52295C77777B323BE13C5F0FF5B9EB1DDEA64B4A5DD7E84BB1870AB5DB087056AA2C8570BD444656545C4E41BE2D0AD22D1658372AC4E100524392CABBB49305B21FC643C1090A065224A74118C9D2F90E8CA3B40A21E631FBDC87B8C0A585DCB7D841F4996B15146C709C728BA7682E1CCE7F6D29E387DC918F51E3DC2FFD19186538B7D7D14F5BB021322A88B9BD145C2152424076C513561C1B3EAD8CE7BFD850D1957C5A2546542F332E320A525BED6BC94DC6FAC1511B3810200DCDAD2A22800819350E93FE7A967E0DE29BDFBDFF40E851774B73D93B0F62D07FFA18827E0565A043EA9CA41EF5C5D8578303AC0F3EF83A19F592A216F1E8ECDE9B8E2FD466DE77A1D2951B4FEB21C121EB8F032A2196383499DA9838C802322B72D8D64A837B831FFA32305CECDE8860BEE3AB03EE2159C2D8C083A889D7670E14643FC9620101281382359546F190E0EE29D546D546A0B45F42E4590265AAE23B9A877AC0A38321AE6FC787BC7868804E431D4290805475699860B827981F75386E7CA6A90A1A18C6D9533A72320649368E6577B1D9BAD80DA0F673E0669A4D3545AFB7F0810A03B218BB02F6E69B63DE570FE001F3FEF952FBBF30F85A0F0A72479ADB8BD4ED5D840335DA8D8656D1D39CA0C216DDBD0D276BA8D80DE10975C9EF520E8E1BF01796111FCDC33533AE0A338CAEAC146E218AE2B49F9CB021BFCA75B619D74A5BBAF305C213F4D7D2306B24D00E18A57BF95C8079AF0C31A6B979AEE3690E43CA70AB4E8798020FE173A49E3C855FBA4048E860E6647F5B9F10A05457459B4C07B58817E6C2CF4A230564F2020CF6D2B0DBAED99805E5BFEA9434DD7B45C892472D12E2E43D6BD13DD3A81C045BD6D8927295CD5D32F06180ECB43CC76CA0A8CEB5B0296B5E80E35FCFA4599B2DA64A3A2DD63ED64F680076111908444B94E124457654C1EA646638B8018344B4B82D1A6F59022C0E8817FF891195663F8A2AD043F6F59199CB17C5CAA9EBCA040BE65E617B7FD40BD0E97B0B66CA53E8F7D4943822313623F3748F61D4EC04396B9C01D2033CB097FFA4C076D81E82264336B1AA73F55433114D52B534A11203F97CAA7028383EA61807B4E88F85B491562423094C517044786A09EA4B808CF67748E34A13BDCF805BCB3A2A0F4326A8ED902ACA98A3456B3CEAC037DB88A0488E9973016B28060CE70378A86C51523071E3F4B91B08E8D97C0C9DCCAF6D3A143ADC3220DFD5C75301032D9A10D01E4A2AAF88E8D9B39ED25A5B2E9E336271E7C3949DA8F7D70C7501ED7DE7A400C2902D242BE5A78DE6140D7B4D1B625462D5254E9D83130CB7B5CEE943926B3F33B84F0FBD65A8D03C309B4F6F136DE8DB9E6653909EA5D65D6A5065514AD5B2F84509D456925B2C9F938D9D217F2962855D09AE1C836E54AA3C125FDFA020CA7DCD50CE2BD712EAD879A24533F8AFBB1E0AF0C54D60935E7F87BD9DD2CA3F49554CED5BE81897AA398301F7C0FF2894680A0042B52C88D8324E4DE5816C81407920E2A2E70196C0C00028C02B4315C00FD38BDAE9A72852923A77BC03FE28C07CA3CCF8087A22924B0FC18C9BBF774F55B8A5E30FF5478DAC9B28D390C7F82EB327F8EAFEF4AC0E9E25C9316F72E0D2D985C84381F3EAA924500A86CA4995E68A361A2D7CCE7DD05891007FD44A9E43776963DE127679D07C1E6D3BA9D4F2AD943244C18B4E869D273240366958A6E588A6CBA3E6F8D4F8E1C021D0BDAAE3B40E92A7B63AC343094957F2F4517AAAC843EA56159AB85948FA98DCD6BE81664C2DD2C882DBB91C9BCD29C1157C2C47883197D64179F12E3CB4BD0B778EC63DE092DA4FEC408045780D7F15C2712ACE9701802D3B50449550915701FF00F32CEA32565299F5137E6BFFDCF2B1540467712CDAA4C87CEBB42D7088AF4EA726B583449223E01BB125AB9C821B4F824F24791BBD01BF252BBCEC50060A69EE1F977BA62CCE1BF92C2181963DA86F7790A34C367A293F459C2EC4D488C9E574D1704D8FB112196F160C046E4C4AC0F6EF271AA947D88F7DC0EC7D7819780F3243A74BFAA6B66C2A9322D959219F465D8294EAC9A12285B4E2896A87F2E0BC51295C553E323F6D4E133FEA0E5E545FFDB971D1FF36CA1B45B6E44E1213DE0461C5CB01272039605361F620D431CEB205AA6A1F07F9FF9A4B2F0963522B476600A619C4F40319CEE9DEEF8E6635BF4F84056E97102329ECC6DFB80DA921D3B57122B8E5D9CA835BF4CE7431DD5F659AE22D3130F1C6290AEBE862EA4AB896C5BCA7935345D605A12E9EB45C21CEC26C2BBFA5E961F6CA943724F384319D252DBC66EB9210E8CA0032F5A54EAAF856AB7FA10F417310D09EFE3B44D9E55918141388427E5B4883B1F06035EE69E2A04D48D258C730FF178CC73B40415C9324723B59DBF07FE376A1255405CACD341EE9754024403E9215A73C33D9199DC35D3F9788C40E8CB6443F2040D216A522C1EE1612127D04454154255B501FCE244C5FFCD92FC7FF846E34CEDA2BC1F4A2132158C06A0999DB4B97A8E7A91751B750391F5AE003859A13AEA355CED13C8DA4E7AE3755AE942DB818F8AA1FD0B485131A53E823B81C316FD0D1185A452EE57FA09514B6464E64BCB92642F0FA43B71FA30C6918111053F957A91B41A478C7663D7A389D7E206843A83CE3E05218564ACA2BB6E1A283F3CE0533483A4D14AC54740528394F6158B12B0EE1D28CB26AE56231BA4EF9F3890AC41CE4D86792660E67AF491B46A6324954149AF79E12E452F5353F75E0A8DADF3C0779C3E63435E1438C320986236626000CCFC182896DC07CB36478B2A8453536277EEABA63E2E0515816D03C6DB95A05A32B984D50B0633535B2BD64AEF002B7F60C9370F844074715FBA08D34FD800F570F80FD269895ECF007BC37988AF09D14DF303C367160B6D09AC1E5640FBD1990272D363BD8EAAC07C03AEC98C49A80697B2D09338692734DE4B928107545AFC9CB62E081EF19ACD2688D6F29A3A8E6179C835A5118E129604BF94A75D08F96EF9591FF82DB016F1B8562F4DF888B170D175E50683AA19F4E94D8B730EBC75F50F3345DE17F98F73D17CED558BDEA0C4C689C3B4E3A46F3001A28E62B2CF337B5C28332EA5C046213643EE8F45D416447E84CE62AE4F6F853F068FBC70B821578B8BA41C3C5E0BD6B110A3B6CB0EA698D0010A45B8A7594F8077BE609138EDA8903AB3E7BD5AD6DE2D237AC92C7300076C3180A89BF8526EB6047B91114456FC813C20169E4E1BDEEB08A3FD15C51FD010890EA56856C42494C6F725031E0189F06ED80967707C22C9762C98A43BC07BA4CD13F911D6F342D3D663D5F60F9FAE435942E0DD7B64D197A082F080500291D79EF923A9CFACDDE43F059312A0DE76E7978ED69C70B4962ED8555DDC11452D33B163A508DC1DFC7940BCE7974B549A906F8B6AAF455131200E29EDC113CEA11D9F82CE643DD4511BCA5FA39DEE315E483601CDEBFC9196D92208FBAB0155FCD2A09C903F0018AA9B3913E6CFDCC307DC2B6A25FC7F21154821BADFD3A0252ADA968824AE2B88498A0948AE26D421AA6347C28901B5A437C1988FD8F6558A2A2176DCC41F6D42C2BCDA8374A0F3428B4E2B04E630E2E53E791028479C1132C37AA04E35A83F74C0BE0B71DC67B7E846E4B654972863C9D011737670C54A94E6B87E0196961B831D8BC89BE14CED0D69172CD5897AB87B2C1443B8FC46A7D8D91686BBBB1C1768B4D2439E9455271A4205C6F5EE26C45C627CD835B4CE0ADA396DA301641C7880E28A3145C841939B59B08F02B8C22C9D8EB02F4AED5E1A575DAADC17040B93B9069C10E985AC22DDF080A44458904D87A1AA106DE4B93F1648D36ABC89C80353C5C261A96C976AB63607FA22095C088C62C46550D7D15571076BB1F55A45D70D55A146EC430A31763255D290A0A5E8E7B5E366FBC616D528D022A39F6611723D43AC3E212B880F970E2196E692F8DDC9A09C122C5A57E6ED8A5EF4FB1F3ABE1FEE93D8FFF5140E0E096F38F3B94A53ED8071F544C3EE5B91CBB4EE9DBE7757C6736D83B6857D8ED8FC4220912E0495384C99542FB2D4A397EEAAE1D7161DEFFB35ACA0FA6FACFBC3F0A49018AE3A0A3D2907783D3186F0FF415E853E6C2887E01B033183210B8B0D910069CDA654AA6473F83C8CB217C6858369F73FAF6FBAC0052F5905839EAB7F55A9AF05E4CFD311DD2A212AC971791E6F89E621CF9FB193341207A20901597CD39EF0EE43D182B9AA11BFD18FA894D08522C038762027C49F37930A6D4B442D8450101A265FDDDAFA9138352E15C376068270769413110DE83A559EA8FCD0749B760818174207E713A80F54860CC4248A33721F57E531B579829D97408EB3666E47BC3407608EB076586801C686576440DF0FD0814B1704FAED76F01E8A3DA8664A1B3B070A3BB214C006AD0ED54B7C1724B28CA86008D961A094037EEEC0CF3330995AFF8FD9E3CACD820256F0D5E21B788ABCAA5282D74B3442581D18F4FB843F238AAD72B25A5E64376C2394ACE2308710B1799B0586040A135F5248DC0C9F0A6914C51C9FE1C9A4AC6C4C4D2A71E7535BB9643D87B5184B2774E1224269CDB787C403A9CB9C71B764965AD754D58601F29E1D403181EDC29A57F172EAD40723DF6DBEE0975337A0ED3CA20740359A892758062E97AAC98A6B9422BE031F119D910083FE7D780F50D55DE53B502C7BB623A8EDAD819AFA8FE82BFFA63289697795479C88396AC59157D6A5604350ADAF6975C45BB564486C46EA20E61F94CF3E0168D3CBCF3588B406AA1AADE22CF3129E91C998E62EAD3211A851ACCD13FD8232AD344C4147CDD1C338308130ECFEFDA8746ED0CD790596F142887F3A1B4F09C34B4F0327DCE6E46BEE1265D6C749622A1A0619D363FEF1390B5BADEC4D204B278820386BC20F8459730512D658EB11F150C58F2E01F21F262E3609577C80C580D0A896F309DB48EB998EA9CDD84535483DE96A46CA9738D91E00899436C24A32B6373616530EC33BDC39404ECA1682DE771BD6CAEB399CA3AB0851C4A62E6325DF72AC6EF548FA6E30584F52FA82123A72558C0E2FC632934924421E808FC306556BAF84E1B0218620FA3CAC343982615A5CB180B61D659EF1D26F6DE302EB71A3B4F562C22024018F2BEBA680C07957FDEF2EA4CBD813D786D1665A41F4723D2BAD5220178355361F0C2459390F2A8DF121AEAB228FEACE5702BE8A24A2824F5F710DD6E5E52301B152EA673778F0BE3EFA55314F43C577002ACF31E0FA904EA130712268270D867EC9A40E5671510FF23D0F81F70FED27B8EFFE44EFCCAC0B37BE7BDC146FD4A46D8B6F50ACF08CC21FBF17C44FBC0E2382D3551FB7B91105315BFC032004F968F31400B7D418778F52903149E143A0452DD731614DBBA337444417357DB8E6372BB5447715841952D7F72BE2E8C7FF56B5B1C017A4E83084519676B1F8345670B5E7773CDF61905CF1173CE0DC3703045214E6BBC32FB7397C6E0C6669001ADDAC54B7E29D06486FBB650F06ECA51385BFF5EA4CF84E3CA90D1F2646182F6977A93D4BB41BDCB4AAC7EAEB82EB80343910EBAD848C6DFBB70CB021296F47EA773F31C03ECB6920DF8C904D00640F8AF6A20E3194D5F42CAD2EB5DA0436478F09398FEB609F07C930CB756005A30EBBE192025614C409D84CBEC1821D7477814C687F4A8A7670C54161A222EFB9C09385720AD26F23EA7A1BE11842AD6CB7AB0C931475AE33618CB7F695AAF3E9DBF5C0689D13BF78DC4F7D0DD50AF00BB2328A160B26C5C8171C9AE61271321A6A54A5A4AC45A9682D4E699E9876BF7CF9A289EA0F6CE572FB2DF712C54C78735DAEE5AA413F808B933E82240B2BD937298A6196B88A70EE8CF4FD8C5710F898F61C186476D4BB77457FA71FC3278D7CC93100E124F597322570192D14EE1FCB65B56879A2CB8F7BF784AE8D1A66330BD9257443BED893F38044024A4D264251FB9214DE3B4378D4EC413644D751E1C96B3202DC79643164DF41E2591110F85C1F825E47185F43919F3391730E120187E4AE521CC75320970CB99CDC60E0C6FD9B04D33978683AAD36ED35B8873420D2C0297F91C4174F0D6B448EFF31286FB6ACD30E71C6B40C33084B5B9BC1EDFA241294E7841A7C03116922A2DC332D41284C0A0C2D85D89358061E560D4F634B0A9145358BBC9059D1B51E91F959E25815AB851F676A2C868D8E1A4C52CB6250348965DCE3DD13ECA9BCE89ACFC748559879AD070ABE3489024FA8EC9009232C7C858A22D559FA421F19FBDED910DCB6209E45062D1454B51DDBFF1F078A4532539C43019C7866ABBD5DFBBFF8DE61140F9E59A46ED3B91F83E1F2A20AFEF525E9BBA573A2D8B8944E9F350635232426A902329F805E590A49A44B011FBD34364378A123D086989F69639E77B968EFCADEBAAA00E59790681F624F8E32D7D31070430C5B0775BD9CC1D2CD4C0FEB8961B0D3E6465F3AF9EC89802B56C748BD4971CAC0AB14889B952AB25047215AC93DDABAE2DDA4276CC9EC74F732381608A302CD1AD95BC47384DFC13592DD5795544B8FA092A176C70D1148F176C8B1780159D4B1E2F9B264731C74BE745B3499DFB3D8980588E66C548136513CDDC14731FF59256DCFB696772A1480063BFB31B158B17862D0C913F0949C0848A84AAD08E456C71B4A9912242991168BEFC1B788E1CFAC0D29402750CE40895B536ACE81BA85AE7490FE43D2EDBD1D0F3261463612096992D5D8304F5F50BACB94447CDB617E62588916365C1123323F52C53D9DB7A8A1DA4A21F90A9BA345838285A43BBD1AB43472DF326437B8F619CAA6CAA6698FA223B1777F64FAC391FA387B4807A23AF218309F952E93C6F60108781003EEBA89F522FE0ADB82C1128EC57DA4E652A462FA7745A483A3195B8EEF2ACDB16B123418FEB3587490104861F0AF6E1C8A66A9FE3FB002EA26C3BF4D4A6577F651A50D15F53870305FDC40C610282DB9741A5E062D91F7E70034B4D5285D9EABD5426FF6DA4F4CE4F50772A0A61D98309A6F2CADCB76985FC00A570DAA0770207AA76D0B9988816C1FF5E1AC4B8C4169042916EEB683DE186DD390C49946C5A02CF5810888A369FF41FB1EAB5D28D1DC1A2AC0314D81EB5363C97D2FB6267D26D34A3658E66517CDB1F0BA512338D0C6374473C004FFF464A882810D97FCEE3669B6407F8CFEC4947237AF003CADB48E1981D3884E2C5907054F2D32B9E797F2C1BD271DDD030DF634E45D777510399D91EA105523B30C917407220F67B210C4D8EAF1CAFCE19AAAD17B8C803D9FA03A0241CD392083C0B517F3B5C17A1ACF7FC570985C303A562722AA0AF0800C633559D5801A0A45AFD4C4AFB1D93237A3870916FEA1BC05C34678A42F00BEB1BA03727C9BE887E7AAD67DBD192A31E6FD204E86922CFB3A27926955B96AF2AD03F781CB29A483C143E36A9ACA2C9F8354CAA89E96E9F03958E5BBB905E037A1513B0C90F7B9EE55E0CEDAC0D3A078AD5BE57EDC90FD10C2164D2EB5F0A8D92E7EA3217930E6648B0C48C23BDE188E584F48024DE64BF7A3498528EF0F105C6601A9810D4887D98FB940B27458A0A3D10C7808C778680401BF9B3CA4D40FB00582E6701311DC3B3FE34C5D28C5E27FA0581D8372246D253618B37602ED05B360DDDB03B1F29E30AC7D35BA507B75B1CE915D6C3A8567B01885C394CE083C213E977D06F8C0E34B5EBDB3CCD0D30849F3FC3EF00316AFF02DE794366FD1E51BE8C68142C8E673F8EF7E7C3357C042CA017E97EFCE31759AF3431CFBB9E5618383B259FF449A577B18C1475D22184BE92171A3D3B479B2279269856D6308E11341FFAC0294875F4A25A6616002AEA328BD9E17970EC589C7EB5BBEC2E57F94962B6455407727B59187126D2C84A17706429693885EFB98302680E2C4B391E6C5FBF14B0E1626E1914E3DAA24F365179A8B41AAE02C44A03883EE6F2F615286B47F0282B82764EEAC83CE825B704079B4A4F608DF52D5BC5BE0EDA7CD030402E4485569B8B4C6AF9EE25C7A6FB78A28985F0AA2964828FE648C60A1C44EBFEC99BF514A75CF170CF5CBA2153199E124B8CAA5091616CA3084E01AF0FF18C3309FBF9C0C52E76B5C368B5767FBEB09E872ABC9420D10ACCCD679C02DCF86726EF0D348FEB9B4BCACA463E61DE6B20D7AC1C3ED649704D87A56018ABDCE091A714B88F88C3F4E8F18020AC56F1357C0535615C3C94B8D912E81ED5D77F2CE121366B96F5290BDE36B7FD8FF01A4BC0A0005EE4163828B9CE06542DC11DC0108C5EAF4598CD07D6F05157A04FA7A79488EB6D429CF1BF6A45218C93A1DFDFA016900C2EAC81840906C512EDFD1828C7146D14E9993EB14A4EB3F6350D27B502D4B0D65547A28DA8C0BB46D947954CF8DEAC0AC65FC2175EEF882A4344559E4EB3A22FF081BFC52CD8110B8A2C0C9AEF20A7D89386888F6C061CA6B5F546B8F2D84FC42335BD6B0BDE86E6517FE1EBFCD5E84E6226770C41A903BF094D1F32A1C48973B0E257C281B07C2850B85B20491EEA77483EE11A357A182D9581888BC358834D349D1B77BC33C9D8F5993CDED3F3A44195B6CF18E3244E18C6AA9284EA677FB25B1363848D2545A7DE701A7DF13E59FC857B973B24469643855A0B4CF1104F9CA9A51DBD1596069E2CDD50E1536E0086441958BF43C3CDB51C236D36C9A1C64AD34DA5CC4AF4DC562CDDDB34100398F4BFD5E46E1A163ECB2B3905D28B4F2A32367AF138BE4C2AADDD341A4D1313958DD26E0FDF136EF84A60314C1530D079B453750CEA10E26D0696457CD5E7B601160C55DC0598CDA15EC094CD07A986E04D69C4464AFC7154049F620428F63088B0ED525242CDE9F6D116A7D150E6A04A51BAA36AC8EB33124849B255FB3AEFF612494AD0652208C2490F0A42FA55DD3CDEA6566A8074DD783226E7308A15350B80288104250A46F8351F3BF5ED1649DBC1F6CEAA9548BC0CAA19AA09B43562DBFCD4258F72B37643CCD8F9766C994D6A15966DF8BC281C942BDD8F94DC038C30F639A77FC2CA67D723A82ED02C38A3ADBC13CFBC403465698C90952CFCD8D0911FE18807FEABE9FC07D21D8C4C0A15B2A13061A282C64EB91945132D0DB70966273E20A0110768717BEC389E8921864FED429326935B960262880C0759478FCE67B3A6B3AA09839863102EF31D72A4840141632B75401C152FEC0C9B7C87088D67F70B339384970618F584325A238AA52034A11D4E2185182B5E4F778E02BC16279CC56BFBA0B93DC30ECAC64C5F121D4EFDDA6F36491F4E8BBFDE0AE82007BEC87AA960F0F4CC51C645CCF35DE6640F8B18A9BEC0497948BE1B3AAFC8B5F9DC6138297CC9B47DAD38DB0379AD9B5F4E289C20F1D34010CBEB012BFBF4CCA7322A20D340FC9B96C1C50F381D5565FD680A2F91169ACCC136B243E9F100BA3B2AD484D3CDEB999B3E686194FA19EC6A4BB71883BDC965E6C94ABCB2AD4EE1F3302C2968D7794424DF8ED0862AD4F59C99B2F9678A623FF2023FE2424250E9519C3C095FC83B6206CF44AA8207407AB5A2364ED9BB0904AD53A3B12F467C0C61319C8B85A7BC10D6E2C30C10F271A7C254FDC0729018B16B734039B7C9353692416DB1FDEAE1F6BD6116021BC884B10C1E8F4B1783451D8218AB372BEB89762D127372F8E276344119B24F35B04648A755B0AB4DCB2B920F6CA7E2C4D4C9D985F79ACA6D7A09A154FCB4393D4ACC949E86D21DC8A5B60A1E7F2AF3B5984D3CF8238EBB4C099321B0AE61BFF564CA411C2324A64491B88442916D6BC5E5F92AA5E445BBC9F379DFAAD01CABB1CD16D3FA9A0DAE3360817B3306FE07399985C0184E2EB6985177433BCFBDC42E08C90E8EC11C5033A1D9294EBB0B8D34E6668E1422046E6A3413EEA78344FA0DE3E929C739B5EA75DE9ADC43E5AC5C75DEC03679FDC85434A38B981C8BCF1CD53006D69416CADFB1C0E89AE314291E50740A289A0D035850FB0CFE2AD082FF6C45A572F549CEA96142C2D06855DC18CA64FA80FE0BFB9C323CEA8EEB8887652E2926732BCC85D1874A58886C6955322966F1B5655648309E3B2A26105CC819A33ED2477CAFF9B83271E50221073828070A4F3E33CF919B64469E0B8A893D24DAB1762EEEF8CA43C64027FC5E32D701C9736C9CC6CAB1269493D17B2875E4AEC2CAA5ADB052D341F5820E31E6940C0983560A5CDD816C2907004D2FFFEAEAA5B1E5F08A615FE1FCEEAFC136051E40E360D15C10B01D3781CD684D5EE9BC642D07CE4361C7F588DAC40CC6B80485632E9C1A46BCCCC4D2CFD39AA00B34E293429831BBD56C2430EE5614B96A5C036329451001396757D59204DC2EAFB5DE7BBFA09B4574E09D96F8E4CC4387E97075B436B7B1A125AEFB483ECD818596FC393AE6C8E9319D8CA5A6B55C64BB4351A8462E3F7050A2068AB58BA7D101D25822E70D3FA4FC4EBB5EE373874E400313531DF74A84C8239C464C53C334C359B34025CFDC4743FF229AF5391A6CE1D928BBBFB7FA1245902A2D4024B04736D838310254786D33BC97D42317743DE29E15971DA694059280C8EF92D80AF930A67B529DF55799CC0CAFAFF440DB8EE20B05B62E92FF447786F393416ADE380F2F7448C26CB5245DC51D3D2301AA12BBA23DDFCA473030F0EFA1CEA7764BBB5DB7212C4BAF1ED33501D6260CFEDE17A65291C1A87950E0279690E26472321AF5A9EFC5420AB4B860475C9DA1DE8F981CDFC79AFA684908D00F83D3E3CD1F07C9DB029C842796085D0663F4CDB373FF6A08002588EB1F77FFF4B9CC1A010289C9FDAF2A4DFE46F873562D26377F8B38104496698C6BF788F5A209A991B1ED84655462E3C8D29B83A99081C59FB3FADE0E9C75089D64E0DC93E8A58E95E4338544A89EC42AE145A7D95F9F590C8250F5D3F96ECC242B1870DB664A26BDA04E1DEEEC8C1BC44802F1D5C7434BC24759991C272836CFD0552B422AACEDFFC38C1A0F605E900903950136F97EAB802E392BE90451F25D04806B835549B113F9D6CCC88CF63E9F0872AA74FFE918D79EE2A2FAC79214E59023CEAA90E5EE5A34EDB93B9829DD672D1E3CA3BB159265BE00282183F28231309AAD2066FD6B32F0FC759980C905C50E2CB70546CFD876810490A40C29301C29A2C4DB2494D2DD5E3FC23E6AA8DFBDD820C41B1EF24C815A140A0B5950E22DB2BD7D74018A7C62C72150C95BD1AF7A5E88D244D5292102D6BF9719F9A6ED97B007DAAAC0B957110A4857B0FEAB94A0AC86A1A1978D4FA78E093238CD2C1C703FF256741F6F88151AC4B0DFCE627FD95D296019254147E5171BB3E59E6820D03F1B00B8116A295E7791B418D459DD57A21CC86500252DC8AB5B54DEB4CC7EB095FEF08AF51164708C54246BB912DA8EC424A6F853D0219D41C281C75C80A5258809DFEEBA702392F79E22100345046B86A17877B25E6DC2F8ABC1C53518247C0DA07B9D62C6C8308D61102C07E9D8B6CF3500A2830E186768D5385BD412BD0E3813026673FAA80DF47DEA18A8428FC1C266F30E93BBC2EAA32984189D249EBFB1312BC033530156DFF0C245B224E8F9A23A16AB089F8CAC35740665A2CE53847C559721D2903E9985D4093C6BE582054551422099DDC250581ECCBCDF9A89A062A1763214ABDA2A15E61B822E6BD7EB38998AC120E158233C34D2123A304111BB015A45C33D8F0C5DA622E35F955C0CECCBF9518421932870B2DDA10E6D2B3521D43260D8A19478C7DD0FFF96A51466B2E2701AD14E6D93667E61E78F1B89B3CC06515DADC48D46786742A71F9EB99B32A0FD8191EFAB70BEDBA9A07A8BD95FA35E162F5E95BB52666455C7214DF84BBE5EBD6285EC904D7F1F8B5B43EB7137FFFFA484CE26EF63DAA836137EB886B873514B84CA4708C7AE3F2973D6F2AF0246B99C930110CA9D456E99CDF275981575BBC2CCEE86956D971FF8DE8099BECE101DDF2772C266C2C5F2FB010528B21566B6CF0E8160DD495D6104CFA36448FD077124724C9C084A737093ED6ADEDD846F2B640F426C5D10E30598BF08D70CD114B8C8CCDFF36EED37599E6DD630C6AB7A28B23C55613AC4484A1C53D5B911A570F6E2F45122840929CA2E1BF0654ED678009B835F3425110E3EF2B5D746DB38024A0422BF2468F8F995C02DB9464E7E7D3CF71D677ACC109C5CF90DDBB58D2A096D30487B61E8659675E97C959CA822C92D872102CC6B90608CEE167D9B132D23429BEBCCA3A46301DD7415474A90F274406BA79343AA36A1DE8340C9402D4E519441D861478F0C5C3AE06C2A0CFB50436C473E2C2E2DB86A5828CC32884022D13E8CD42C09FCE8AE291228224FB5052F28F5A70EE2419E4A596E15236F10C36FFC09ACAA7E497BDB1C1293F2C7C3225D07969761B60198CB4C662A8E50EF8F5D8B9A03E7F93DF84C727A3A005D82ABB842124C87B9DD25800525B5E6748D5CC3932E8EDD092466201C6121A55F3090FCC3F81DFA48341BFF0A3AF4A3B38E73413E979C14AE201640E7E5EF7191037900B2331710A9AF82C5FD70CF9AA49168004531033593684FABD0190A64CFD2B458A9FD4B07E54BA1CE053F093841C84736BC28C5C4E050AFD58521FF9EA090C040B3ABE2DD4AAAD0C197921D03C3543889B5D1C7AA9850236A3B4DA07EC2FBDC309E058035CF5241D239BF090491028C1983A3560A93B028017D1E9538B688B487D0ADA40D0B71B6BED780A9EEFAB9402DB6B21644FEEE7000664F1AC6BE81900873C92345F50651C15901B41CD0A5BF5C742ED5D96F651582A2898A350198525AA32197A3C232A9B383DA9DD8C81567AAAADE83C66487DD6F24819178CD280252CC1AB5CBE515D2FC757AA9BABAB3A4F45AB0CB8682E86D3DBFA6084A5B77E78D17810ACBB55ACCD7EE74FBE624EA81E8B1804BEF83E426B76E004B228CA84DF4106DC33629D5CD45D4E345ED466FD3EB9E35E0F20D96FD6652DC6BEB8AE21BF46A1873BB6512FE102CB825FA4DA430A641045AD787B770006256DC8E845B33EB29FB82AB6D96D988FB428A25C61639272C261F2EC32AB60DE093C66E71D73CA586BA027DAE901839AF404EF7E01E4D2A30148DA9802C2C91DA78A02B41423BBBB011C29790ADC46FD4A488D23B8D473A21876B204ECEE0A50AC8EB5403973888265090F4A6F6F6ACB489CFB2173154928B19C849596F815B06EA270318CE585BF748BD8E1218090107B9887F83AF018B6A4E791247042C208D71A49EEE71620BACC8831398C302C752F95E4C79BAB3709323C04036068797532393509B6ACF47ACFD5F2DE0684C13C7824D6FC8984F25400A0E437AAA3F3556909B093709F5618C3301DAA5F52F2F3202F34083C248031CB88E54E138427A863959135C7F61E95A8BB0937B646F27B471772B78200E2BDB77CA6F5D373C2A7BA08EFAD330AC8A08C4810A1AD52E6033C738B15E3B471A9C550E26061A6947B2B1B36945F85A205C20B25CF4730064A1B05C5CF1D4453498271D560E7CA4F3868BD279CE2743464B58F38480F1B9728220630D4F09E09C32BFD7AACF46F0C730DCDDACE8FF2C781A929A59C0AF4E237050F418637353666B90B574F901686C18088990E2BCC1424327800C630641C315D89E2DD2FB6ABC7385077E1BBDCAB92C20FB27B8D3D1A34E25284C8EBAC0C00CA0791A9F443C53BC13FCC8C3C1E1F40E12BAFA69E1661E17F58B4F05362511955DD23ACD62A3ACCDBF800CB1CCD8785EE66CA60514DB48D6EC5CB9E3E64ADBBA43FEE5AB077DCDE6D295796330BCF3FA9A0D58A437561CC9C00D6B3230418CA4D5E82231C07624495EA8AA899DC8F42E5E99CA542ECCFAFBBF2E8149C824E466E40B35141F07332600C98DE345C3DED7EC0BB3437ABB9E14428CDD5E3DA0CB3FFA21007921CBF1F32B04E7372D4AE4F83D557887D2678FD6229150F64E1DC21B34EBA49A2BA7C3F02D586E53645828E1AE7E42FC9931D1FC61D391ECDCEB3EA0A48ACD2A6BD6CC69481D76D024BD139041E16AF196BF3095A59C79ECA55DA7EDEB74C99AE836BC4CCD81F632C6258006CDE3BE0AFAC92386BC841C2291C7915BFE630E1D3124F4EB90CA4FE87CE8507BE49394C7AFF19E33E0CCEA628477B461641224D3B05931128C4D34C57E65F2B8D78E050DE20CF506B1EA44E345250A5267A0A61576484A8E92E43B4306AB2F3E85646BB386019516AAE9927EDADEF34E29C0390AC4203EBA0488B3624A34FD9927B31B5C606170EB94E83A91BB284E1C8CDCB0F4B360200AF4B659943D388653F85C9352AE19488DB79A2FE1305E2BB8FD331A397E1985743E789E4C63912E06B14B66C82285AE4357FACEA39E7F82083EA60194CDB07A7C7700804527712F4708BFB430C38C102BA6BF437C608A61AECFEC2C5AFE516E0C9941884DBBD1BCC3165121209025A39429A312A03C1DCD1A69B0DAFFA9246FA3F869B62975FC8601162B240F677CF56C2158BD5A9D835EB8E07D1A8DA24A7EA24723223B56543DDB4B42BAC5CCD94AE87333058CE939E590599DF0F85A3BF02B02B67EB303C59FB86195034D560E225E70F5E55B4F43386004D9CE6AE430C3AD91D76C1CB4315680CF71C7F2B4B86068E225CE232342E9FC1AD173721A3CB94FB6C34F5E9E00A68DB4D3B597F092020E51BF4F5BA6C434450D896274AC2A021C675DF5E43868169573B0BCABC9B7C79C781F0AC0C108B18773B1EDA20E8583E59048E5D4A603310C94687BAB4FFED461E045A4BC3A3B15744A5188337A98555C70E11F018DC6CC6DD4A3EBB5BF00CA901B7A207E6C021C66DB8926D09F9151E800878979D15A2ED571750222E095A4A1546BF319561639E988981F5513410CCC60EF95C5C6768D83F980423433E0A4322458E6B007F43AB7342969B99790F094BEFDC2DE15FA1871E494B503802BAC8CB842563182321621C210975CEC766A0FFC308D808E5BD8D56666192144621A208AA516A71D3F9C2CEF6F02511900229B46DF8BC708C44E3D8A298F708196BC66F75F91732E5AF062775A9ACA36CE2DA64BC"));
+        byte[] sk = Hex.decode("07FEFF0702FB0106EFFCF40401F9FA0B03FF04FDF2FA0F0201F40810FC02FB0D140204050CF617FF00FD0805FDFB060007F2F2F6FA08FB05080504FF0801F306EEF5F8F607070202FDF9F3F6FAF5FF0703FCF1FBFD05FBFE03FBFD0509EFFF090BFFF903F20002F309FE0302F7F3050906FE110207F5F1060DEDFAF909FBFC1502FD010707FBFEF3FD03000A0301FCFAFAF50500FE06F50D030008F1FA130006F804030AFEFF08F5050800F603F60700FB08F80D0AFC06FF0FFF01FB00F7FE05FEF9F60301FBFE0A04F9FD05F402FC00FF09F70EFDFEF7F10808FB01FD030203F3FE0EF8F5080207FFF30D0201080A03EEF60104FC02FEF9FA0A02021000040705FEFF03FD06020009FD05FC06050204020A0D08FAF9F1050DFB07080A0405FC09FD1003090E0C000703060204FBF806000AFDF7F4FE0A0104FF04FBF6FA02F7EDFDEF050710EC0B0209F90600FF0703070509FA0CFCF9FC0304F6FFFA03F3FE0202FBFF0812FD1409FE02FBF5F80B08FF07FDFF06050209F609F90BF6FC0013EF020E0F0B04F4FA050DFF0BF8F5EF0AFCFEFBF70512EF10F505FAF7FFF9FCF7FAFDFC0808FE0A030CF8F50C02FD03F108040612F80214F6FC030C00F30B0EFA00FDF80C000603F809FF0304040AFEF807F60B05F5FAF6FBFA0A0A12FE0007FE0AFC06FE040606FC08F9FFF6FE0AF6FB04F6FB060C00FAFCFDFE0800FBFB05F7F20004FDF6FD02FC0210FB040DF604FC06EFFCFAFBF7F6F9F003FCF9FB050801F900FE0A02FF09FA0203F1F900FA06F501FA10FAF806FCFA03000908FC020AFCF70102F9FE05F202F2FF0900F702F4F1FE06FAFBFD08040705FFE9FCF90907FDFAF00F010B010B02F4FFF90FFB0401FF05FE08FF02F3FDF3FFFF10FB000FFF07030B0205020D0B01040BFBFFF9F71AF00306FEF704F3020AFE03F0FE1801000AFBFAF7FEF7EB03FB08060A020109FC0E0107FB020BF80506FA0CFD0116F603070E0808040AF60808FB04F903FEF1020204FB060FEBFD04FF07F8040708F807FEFCF9020A0A00F6F80A0D01FB02F5FEF40908F401FF02F8F50B060205FC11FA0404FBFF0305FF01FD0303FC0DFF030A0AFD0009FD06FB03010BFE0B0A0D0003F3FA07EEFF070008F7090206EDFAF9040709F50D07FEF50EF9F7FFF8040208FB03F6FDFEEB06F5EF03FCF506110200F6000D030603F3FCFB0E0EFFFA02F504030B090B02F90806FE0D04FC06F6FE04F90607170106FEFCF6FA05FBF0F6030600F4EF03FAF802F30004F30703F5F8FD05FEF0ECF9FC0209F906F5FA05070CF60FF709FEFAFB0D0305FF0102F807F703F0F40D0204FD02FDF108F80D0CF7FAFA070503FB0307FDFAFEFE08FA02F80D0B0AF7F710FC03FBFDEF07F9FD010104F1FEF1F4090507FA1101ED0B050212F80E02FE000708F5FFF202F500F7040DF303F3FEF4FC0D050105FFFF0A01F4F6FFFCF6FA0001F908FFF3F3F8060AFD150107FBF3FFFB05FA08F6000DFD11EEF9FEFF000CF600FA020204F70F0909FFF3FBFD0906F7F5060512020803FC0001FF0C05F8FEFBFBFEFE05F90307FEF7EA081105070102FAF706FFF41407FEF901F806FCF105030F0100FDFA01010807FFFB07F50DF4FBF70D01F805FAFCFF000AE6FE0D00F80AFB0007000009F8F8EEF3FA13FBFCFD05F9F503040EF608050CFC0E060304FBF80E00080901F7050C0007F9F009FCFD0001F9FB180EFFF9EFF400FA0206FE05FE06F6FEFE00F207F0F9030A0AFF00F6FE01FE0C090404F7F0FF04060F0B0806FDFDF6FBFBF2FFF4FAF80CF9FFFAFFFE000703060BF900F80A01FEFFFF0201FA00FE0BEC03F7FA00F8F60C040300F6FC03FC000014FD09F80203F7FC0A0D0007F806EFFF0409FF0007FF06ED0A0A02F3F1EF0403F8070405EF0102EA06FA0206000308FA0205010FFFFCF6EF0C16FAF7FA010DFD0503150EF903060803040004FD01FE010607FC15020808020406FDF5F304F7F8F6060AF7F7030CFC01050404FB01F8F8080905F70607060AF3F303F50501031200F502001205FE01030503FF0400F2F2080AF7F9F8020DFFF5FF03F8070806FB06020704EF02FD0C0108F90808FF0109F70BFC0BFFF30801130A09080202040EFFF504F5010704050A01EDFB0DFB1109FA0605FB030CFD01FC1601FAF9FD09FF0CFFFDFCFCF5F8F50606F9F8060102FBFFFBFB080CF913FB0409F9F90C0EFCF8F9FDFEF602F4FCFDFC040902050AF8FCF9FEF40C0A04100701F60E0901FC0200FF08FAFFFA05F70803FC0E0707F9010A0600FB010CFA06FD010EFEF7F7030011F202FFF5FFF20706F7FD03F50205F70500FAFFF705EEF7080C0A03020C0001000702FE02000404FC0105F0F7FFF0F7F6FB020DFEFBFDF0FF07FD090202FE080201000B0409070AF807020EF402FE1003FE02061202F8FF02F200EBF90BF8FDFBFF04000704FF0306F7F3FA04FD04FE0CFDF80BF804020501F3000403FAF8F704FEF80FFEFF06FCFA0109FE0AF200FC05F3FEF90701F40E00F1F8FD020BFA0B09ECF2FC090800FD05F109FAF708EB0C0D01F30AFD0B06F70004F9FF0902F604FAFC02FFFB02F5F50900FFF811FCFFFF09041402F5F604FC07FE01F4FD02F0FAFC0005FCF8FD06F80003FDF9FD0EF7FDF907F5FF07030B03FDFE00FE000F000A0D0303031508FAFA07FD0314FF0CFA0B0206FAFCFA02FE01F7FB03F70004F7F409FB04FFFE0601FF0AFA05F90401F51005020A08FF0A0CF805F5FF02FF0501F503F8FCFD000403F5FDF1F805EDFEF9FD0B04000008FF07FB0C14F8F9FB08F8FFFA05EFFBFF0002FDFAF2F802F8FB1A0202FC0010FBFBFEF9F705FDFCFA07FE14FA010D070B0105000008FCEF04F6FD0B04F9FFF00DFC0A050608F9FAFAFE00F7030BF50BFD10F5FA0F0100080C10FD0600FBF30A0A0CF4FF01F20A0E0D0CF4150000FC00FC00FF0E07FCFAF90CFDFDFCFDFCFEFCFEEE0A03FC07F80205F902F60A0208F3F7FEF90202FD00030004F2FAF903FBF80103060FFFF9EFFF01F412EC0A0105F901FF0E0A03080202F6FF09FBF209FA0DE805F4FBF506060110FC00050FF5EFF50A0BFEFF04FE14F3FF020EF802020508F5F9FEF90806FCFD050E0711070D01FA060205FF0204EFFEFDFB07FEFD00F90B030508F6EF02FE16F400FDFB060304F701F3F4F90501FB03F8F404E5FF02FB0A00F8FE0C0B000AFBFCFBFF00FAF2F5FFEB05F0FA05F5FFFA0408FB00F90204FF01FEF00C12FF01F8F6070402000502FCFC0B04010E021103FF0BEF09EAFDFB06FDF9FA080CFF11F90AFCFAFFFE0301030001EE0EF3FF0000F40FFEFB08EA00000F08FBFD10F307F1F5F200050105F9FB0AFD031302F4FFEEFE00F709080BF7FD06FE0EF5EB010DFD0607F707FA08EE08FB0007FF08010102FA00FB00000803FD04F806020500F8060CF4F9060EF9FE020A05FB0CFC0006070202F6F20003FD0DF106FA0206F10304FDF80F00FDF6EEFFFDFE0FEBFC0D1BFB05FDFF02FA0806F7FCF1FAFD0403FF06F80E01FBFBFC0C02F203F90B0B02FA0CF815FFFE080201FB16FA02040802F50503FDFFF8F7FBFD020202F8060D0601FC04F1030202F30FFEFCF50BF60901F306FE0605F9F8F502020A0406FAF80208FDF8F9FCFFFD0EF30B04FFF5FD040001F8040105010100FE0E00FDFB0606FA04F505F9F811040607FD080500FA07F300FFFBFEF0F5FD0209F802FF04FFF9FCF9FF0AFBF800F80AF9FC09F9F6FB0EF80805130B06FFFF0104F40603F10001FAFD06EDE5F8F90C05FF1202FEF000EA0006F801FFFAF1130CFDF9FCF806F913FFFE120609FAFB06000CFBF804F40BF4FCEC00FEFD01FB0307111206F9F1FF06FE020CFDF802FC0003F9FEFE06FEFBFBFB0BF8FB0C04030BFB00FC0405F8FD16FBFF0207EA08FEF5FA0F07F50506FE00F3F20BFC0A07F702080FF509060203FD0505FD0BF6F905021206FAFDFF03FFFC01F20204FEF6010009FF0BFDFC0C120402FF0602FC0106F40601FDF6010B0611FFFFFFFFF8F5F7FEFCF5FB02F805F50105FEFF0202FF09EC0408FC03EDFA00F306FCF8FD0301FBFA08F8FD0007FCFFF6061011F80E090D07050506FAF00AF8FFF40304FE020AFCFF0CFFFB0BFD0E0C05F50C02F60EFEFDEDFF090802FE09000BFF06F208FAF9FF07F500050901F508F1EC0BF9F6F30FF8F006F6F50007F107EBFFF80406FA090401FBF812F608F4FB09FB0FF8F0F1EEFBFE050011FF0AF7FDF201070C050EFEF5EFFD01FF08F500F3090BFCF201FEF90BFA0DEE01FDF8F5040109FA03FE0601F30D02F904FEF804FE070700FB12FAFDF9FA13030A02FAFFF60407FA02FCFEF3010E04FC04FAFCF4F7F802090307070F03F808F911F4FBF004FCF704FB0B13F8030B01F307FBF405FFF30105FAFC07040605FB0006F7031001FE120600FB02FD08FFF60C0709050108FC0CEEF41AF40DFD02FAF50AF6FEF10000F2FEFDF0F6FBF9F30BEE0D0E0105F6FF01F105070E06FEFF0713FBEBF8FF06F50100F504FDFBFD080AF9F6EDF9EBF60B09FE0C00FDFDFE00F51EF9050E0A030101F70A01FF02071BF2FEFCFF040309FA03FBFEFC14FCFDF6FC01FB0AFF020206F6FA0D11F7090BF9FCFD07FC080709FE06F4FDF5FDFBFF01FA0F0607FC02080601FF0506F7010808030006FCF8080F0200F2EF0AFF03EDFB03EEFA02F00AFFF80DFA11FDFA0FF801010BEE0103FA02F5FE03010704FCF6FEFF07FC0901FEFFFDFAFDFCF002EF02080506091208FA0B0AFB0116FD00FE0416FE01F70B0DF60110FF0608F60507F701F6FF06F3FFFE0BFCFCFBFBF80204FDFF11F307010301F6FAF003FE050704FD02FFFAF503000103F0FDFF1006060EF1FAFE00F8FE0A08FC02F8FA051406F508F8080306FEF8FE0F0108001305030DEE07060608FF07FE1107FDF80105F20A10FA03010C02FAFEEDF3FBFCFE060104FCFF01FF06F6F5FDF4FD0904F702FB0EF6FEFE01F8FAFB0003FBFB01F915F407FDFDFA01F9FC0A000807F5F8F4FDEFFE09050B0BFCF9FA030103F2FDF907FEFAF70502000FF50605FCFDFE0A05FDF5000201FE0EFAF613F9FB000404EF00FE0D04F901FBF60CF7FFFD0203F8FDF70301F2EB020405FFFD03FB09020405F5FD06FD10FD06F5F5F808090404FFFDFE08F9EFFF05040CF5F412FF03F4EE0308001007EF0BEE05FAF80FFA02080807F90001F8020906F4F2FAF901F501060800FC0306081601FAFD07010EF606FD05F9F305FDF504FDFF060F0606110A0002FA030007F80E040BFFFF070100F6050008FB05F8EEF70005FC0409FE0007FFFA1000FE08FD030B0104030D0106F8F9F4FA0901F8F7FEFE000D0FEB08FF020B0605010B010EFDF500F6FB02F6F8070000030AF6FC0702FAF90DFDF80009F901060B07FEFDF002F206F109000DFDF1FCFCFBEF07F7F307F608FD00F803F809F9010408FCFFF807FD00FC0DFDEDF90706FFF7FF0DFA02FA00F60000100205050B00F602FFFBF70BF60001000A09060A0A0A05F7FD01EE0EF8020BF5F006070AFD040B01F2040EF0F60AF90BFC03F9F102FDF50000F3FB090FFC0705F209090304FFFA07FDFA02060507F705EFFF0BFA01F2FBFDF503FEF5050E01F908F406030005FDFC1603F601FE00FE03F407F50308FEFF0403F90204F509F1FDFD0503FB05090802070D07F9F8EE050E0107FB00FB03FA07FBEE0012F310E4FF04FE000C01FDFF0206FBFDFEFD0802F6FC030702FB0203F7FE00FAF6F8ECF5010DFE00040BF3FF05FD0807F2FFF9FEF9FB08F7F7F6FBFE010708EEFD04FEFFED0209F703EF07FB0AEC020BFF060AFC040308FE030D05F109120CF80001F4F708FDFDFCF80B00F203F8F6040203020010030BF9FAF9FFF404F6F9FBFDF6FEF8F7FC01F2FCF8F3FFFFFCF7F20B0F0A00FBFCFD0408F50DFEFF08F504ECFD13FD10FD08FD05FF06F70407000C1005FAF7FBFBF304050DEC0101FF0CF1060010FDF9FF0705F404EF0307F9000106F801FB06F802F8FF080C0CECFE07F5F4FD06FCFC0401F7FE050EF3F9FE03FCF10302FF08040907000104F90303FE11F80602F5F70201FDFA060FFD03F40A1205F205070AFDFC0A0002FE01F902FFECF9000D00060EF0FB0D06EBF50C02F614F4080402FB000704FBF706020814FF0A01FC0A0E00F0F8F607F8F608F1050C04060213030BF60606F9040C03FBFFF3120403FDE70003F4010EEB090001F40903F8F9F5EF06110D08FD03FF0408FDF50D0407F9070201F9FEF4140F04000A07FA08FEE907030A05F6F8F4F7FC09F502FAF808F6F6FF06FA0100FF0B0D03ECF50AF7F0FAFD0A040901FD0FEFF70BFDF104F600FDF5F1F80DED0D070009F907EDEDEFF201F90B03F90D0301F5F706F402F5F7FFFF0AFBFFFAFF08F9FEF5120405FEF808FF05010A0AF9FD0DFDFAFAFBFD01EAF6FE0BF3F7080306F60D000403FEF90AFB03F70D06F907FDF811FDF806F707ED03FE0900F508FF0C07F704130805F6FA0300FCF900F804FCFB010309FAF9FE04F505F501FEFB00F4FA0A0500FF020A03F90EFFF80305F8FCFC03F20408F5FE00FA0009FAF80EFD0A0311FBFF0D00F9060505F60504FFFEFEFE0AEE0A010EFCF701FFF80302F0F9FCFDF5011209F8F80500F4EC090CFD0601F612FEFB0F0201FCF31007F7FCFFFD0F09F01013010707FB020307020402F9FF00FB00000E050600F7FAFB02FAFE0CFFEE050E020D07010304FFFCF9FB1101060907F606FCFAF9FFF90408FAFE080DF8F7040C07EFFCF7FBFE0CFCF40DF908F0FFF80803FD13FE000208FC0EF6040AF1FD01F2FDFF0800F80FFD0FF807FFF9FB01FAF8F9FCF8F7030CF7FC0305FE04010003080AFC0300F5FE04060AFAF114FFFDF5FFFFF70AF7170416FE0AF70207FB0AF5FDFA06F3FC10FD01FB040A03F9030707F708070507F308FB0AFA04020404F8EBFA06FCF403FDF7F7FC0D070306F5FA000AF0FDF300010EF8F9FEFEF904F801FBFDFEFE04F2F0FAF8F2F005020FFEF7F9F4040AFF000BFB03FD02F9FDFCF6FD11F815F701F2FF000CF7FF000AFB0F06FF011400FD04FE050209FC03FAF807FBF8FD060201FD07F607FF010E110AF2FBFBFD17F11508FC0202FA06F90B0A0602EF02FCF0F203F4F8FFFDEEFBFC0512FF01FB0103FFFCF5FCFB00F703FC0A09FD02F8FDF4FA07FFF404FBFB0CFDFFF1030610F3F6F5010017F501F505F4F603F1FF0A04060209FC010608EEFF05F60604FAF90202FB030502F4FD05F70312FAFF0007FCFB0B020C07090AF9F2F6F8F203F1FBFF000207030411FA01FFFE0303EFFE0606FEF6FD000901F900FD07FD0B00FC050004070DFD050CF50B0D03000104FC0002FCF6FCFFF008050D0002F00CFC0506FA030DF604EEFDF90609E902F5060AF7FA0A0309F90BFE02010E0B0405FFF6FAF80307FD09FFFDFFEE06E8FF090805F5FDF6F90C07F40C04FB0BFA04FBE604040DFFFEEF05ED020108FA00F804FEF00304FBFDF702FB02FAFA06E208040402F2F4080501FDFA00070DFAEF11030F10FDFB08FE0B08090101F1FEF7F5FF02FDF8FA02F8FEF8FAFDF4F5FF0809010108F80106FC0701FA0C0B0DFD050301EFEFF7FE04070A050C0BF6F308FC06090207FF0AF4FF09000A020304F5FE0204FC01000101E9FBFFFF090303F5EB1802011204090EF80004F9FEFFECF2FCFCF303F8F505FF07F80700040D08FE08F00006040006F9FAF7000C09F804FFFC00F910EAF8F80700FCF5F6FF0213F8FEFAF8150208FBF80803EE06FAF504EEF50F0800FAFCFFFA02030211F50CF6F8F004FBFC01FD0BF20CF7F60100F7FA05FFEE0507030807F8FCF6FB01FA010600FC05010308F61402F605FCF9FFF40601FCF9F605FF05FE0011F80AF9FC03FD00010B11F8F90802F9FC0600F50A02120501FA11FD03010AFC030C040405FB02FB04F0FAFCFBF700FC10FCF6FC01F301FCF803090DFA18FB0701030DFD02F204080806F106FEE9FDF507FF1505F8F8F8010C030909FD050306F40F08FE120A0C050102F7FBFB000604F8FDF8FEF8FE080AFDFD08FF00F007FC0003000401FDFFFAFC030401F2F6020CFDF80401F7F807FB07020C000C0101FEF6FEFDF60811140E08011407F905FA04F504F60AEDFB0210EFE405FBFFF9F0FB04F9F311F7FAF104F0F6000009F8F5F8F7F303FD0201041A04FC0509050301FE03F109F1F7F9F10311FFF3FB01F8F9F6F6F8F80AFBFDF20AFCFF0CFF01FBF8F4080DF502F201F0F7FC0AEC0713F6F90206F7FEF502FD05F9EB110001F8020706FA02F80105F404FD13F8F5FAF104FB0E0301FCFD04FBFF02F5FDEF03FEFBFD010D0708FFF4F6FEFFFEFD0CFEFD01F8FA1207090001050AF20909F50513FA06F3FBF4F7F40301010307FBF60103FB0B0AEDFC0D08FE09060A03120CFCFC1800F5FA030AF901F2E8F903F201F7050DF8F9FDED06FEF8F0F5060507F5031001FD05F3F008FE0BF9FE03F5000600FA0103FD06F8FDF9090CFDF60CFE050A05FFF8F206FD0904FC0200000106EF07F301FD08F701EDFD0300070F1404F30BF506F4FC09FAFFECF8F7FCFDFAFB07F60AFD0AFD03EE090900F80207F0FE04FE060A0B0603FDF9FC0000FBF3080B07F90904F0F808FC00F9FEFA04FFFE05FDF3F206F8FDFB03FB07FA010CF60DF7010FFEFFF40407F302FEF0F80D011306FE06F905F312FCF10107F3060310FFFDF507FF02F3FEF1FBF700FB060305050803F90703FA0405020BFC02FE03F315FF12F80806F80201FA020E00F9F9F90309FF080D0501FBF512050201FD08010AFBF606F00908FD09FBF30810FBF7FD130109FCEDFBFEF901FC01FC0FFC0403F51A1603F20812EB0404F9FDF700FB08F5EDFF01F108FE030CFCFDE9FF040703FC061100020C04FDEDF9F8FC0A0603FC02FE0AF806FA04050AFB0AF2F4F6FBF4090E060504FF0703FB010401040606F9010C00FC0B080400FBFD0B0C0505FBF3FCFCFEF610040D0808FEF4F001FB000EFF070806FFFCFEF406F9F4F8EFFFFC0605F80805EB0403F91313FF0303FC010206FE06F4F7FF000AFDFBF707FAFE04EE090A08EE0AF8F9FCF7F50C0308020C02F3FB030701FF0D08050BFBFCF9FE1200030704FFFCF9F3F00CF7FDF805F501050302F7F9F3F8F608FEF105FE05EFF6F7FBFF05FE0713FCF80D0B0303FC05020B13FEFEF9F1FD15FFFE01FAF508FAF703FDF81208FFF9F90700F4FDFFFEF502FDF90B0C05070701FE0AFAF9FD0706100007FEFA0DFEFC0BF8FDF6FD0104FB0309F706FC0606F101F9FEFCFB1302FCF4FFFCFA03FCFF07FFFD1509EF13030001EE040105F901F908EF09F7FE05FEF1F60CFEF7F50704EE05F801F70208FD01FEF812FEFAF50102F4EE0102F10906F902FA1316F3F60406EDFAFBF7F5F808FD04F807060701FF09FAF4F604F60002EEFCF2FC13FEFA02FDFA0814030207F6150004F6F501F10708FA0FF7F9FA0108F2F705020101F30E0906F50F0BFAF7EF0907FA0003F4F8040A08FC03FFFAF4F8060605FCF807FB0DFFF6FDFBF7FEF8060F05FFF700F1090907F403EC03F705FB10030E0AFAF7F7FD030AF6F205F603F3FB0AFAFF07F40BFF03FC08FE04FAFA0001F60903F602060DF6FBFF0801EF02F7FE000E0BFEFAFDF506F503FAEAFDF5FEF50C09FFFD00F9F6F809F9FA090102F5FF0000050A0400FD0DFD0705ECEFFFF7030200F2EBFAFA02040E0CFCFBFD00F90600FBEF0609F0FE03FCF9FDFC05FB08F3FB130DFF0F0208FB1006FDFA02F902FB1108FDFFF8FEFD0311FFFA0502FBF905FD0AFEF50C08FC06F1FBF7F40003FD07FF060809FBFEF30A0500F6080BF60D0CFC030707F5F413F903FE040006040A0C0403F612090002050E0AF610FB0401FDECFBF7020DF9030405F609F0F0ED0103050FFD0B070506F808FBF6FB0519020703F808000609F402FF0907F3F9FC0704F7FCF7FBFB0D070AFA0BFCFC130AF804F2FF040105F0F1020408FDFCF9F407F7FBFDF300FBFC050A0E12F4031504FAFD0214FDF103F2FEFC0801FEFEFEF91502F10700FF08FCFB01FD060C01F6FC01FD070107FFEEF010FD0B0208FA00F7040103FF0BF904FEFF02FC07FEFE0007F80305030204FA03F9E8000D02FFF4F4F60408040105F2FC0DFD0504110102FE0F00E706F301F7FF0108F90305FA0202F3FCF612F101FEFBF80E0C09FDFD000013FEF6FEF6FE0BFF03170001EC0501FBF4FDF0F80BFEFD111C02FFFC06F801F8FDF9010BFE09F6050602020BFB0C05F90401ED0FF6060E02E9FFFFF700FE0E0304F2F5F6FBFAFCFD03FCF5040417FDF506FB05000BF8FFF1FE0405F102FEFCFE08040C0F06ECF6F6F9F4FFF9F9FBF8F1FE00FFF40CF4FD07FB0503010B15F3FEF90CEEF80AFF0BFE08FFFAF3FB0AF301FEFDFD01020706FEF5F714F601FD040A0A0D0A0BFF07FA0700EFFDFDFBFC06FDFE00F804F3FF00FF0AFAF9F801050F07FC0310F7FA08FAFF0203FEF6EEEBFF000009F906FA0A0CEEFFF1F7F4FEF3F5F4FFFC10FB080608FFF6F906FCF6FE0D00FF040B02FC0BFF00060D01060A0AF600F50CFFFCFBFFFBF9F8FE070EF5FEF8FBF606FB000DF706EE04FBF0030BF000F3F8FDF002FA070A0603FB02FC01FEFA0407FA00F2FFFDF2F80BF607F70402FC07F211FDF8FF0101FDEDF601F4FBEB04F0FB04FCFD050101080A0AF50302FC0803FE1200FEFDFDF3EE090E050DF9F803EEF1F4F802FCFD11FB02F4FE02040A0C03FF1506F806FEF9FCFE01FCF4FD0104FA02FCF9EC0911FAF2FD06FD03030A05F50A04F4FEF7FF0B050001FB07F80601FEFE0CF9F7EF0C1209FE06FF07FEFA0600FF02080207FFFAF5F407F5FAF500F90303F81106F90BFC050FF900000D07FD02F9090306F90107F407F3F7000007FDFC090003FEFA00F307FDEEF0F9FFF70CFF0BFAF9EB0B040605F70504FA00020CFEFF09FD0CFD03FB00FF0CFE0205FC02FB08FE06FCFF06FD070108FC0DF60E070EF7040D00FF00F1FE0108FBFD090008F7F812F006F7F2F60916F0FD0F02030404010502FEF6F0FBF10BFDFCF3FD0F0FFE08FF00FD02FD0602FE00040CF100010AFCFFF90818F800FC010F0AF60A0403FAFBFF030803080DFDF9FEFC070E0AF30200FBFCFBF913FBF3F9F9F1F7EB0C06FCFF040D01F0FF080009FA0C0AFF0505040008FDF6FE01FDF8FAFD01F9F90708010908FEFAFEFC010C000312040804F6F8FD0406020D0004000405F8F8070EF8F4FC070706F80A05F4F90803FEF3FC00F6F206FA0CFAEB00FC00000AF2FBF705FF0AFFFF0604FF040709040401FE1505F1000EFA030F0BFFFE0602FD03FEF5FF03FBF0000F07FB030000EF01FDFA09F80A0606F804F5EB10F502080606FE0A0703FFFC0308F40806FEFFF9F6FDFBFFF1FD090501FAFB050E0BF9FEF8FFFE00F701FFFAFE04FF0204FE0CFCFF00FCEB0F0D0006070606FB0802FB07FB060306F907EFFFFFF8FA0AFE02FEF70F06F8010100041211020608F505030407F400FEEE09F9F3040D0B00F703FDFD01FA0004050E01FCF801F6010806060308FC0AFE070BF813F9FE0F0EF8F700F708FBFCF2FDFC0DFEF4F7F70BF9F4FEFCFCFC0BFFFE0AFFFFF6FDFAFEF7FD0C1005040AFBFC10F9F907FE0AFB090808050100FCF901FB190FF20C00F8090106000C050AFC0A0201FA0902FE0503F2FAFF06F0FAF6FB050204F905F604F6FCF90005FDFB00FC0B05F1FDFEFB00FEF703F705F8080A12FCFEFA08120001ECFEF7F40FFC05060008FBFFF607FAFDF403FAF616F8F402F9ED1204090A05EB00EEFC10FD03F602FA0FFD010807060504FCFFFFFE0C0BFEF7F7F4010C0AFDFA040EFE0415F702F3F3F905FC0A0102010B05F4FFF7FBFBF701F80EF400FA03F8FEFC08F9F91BFC05FEECF90B06F907F9FBF409FA02F70605F5F6FC09F5FC0BFFFEF007FC0B080001FAEFFA0DFBFEFD0504FD0CFA0205F6F908ECEEF8EFFFFA02FC03020BFFFAFA0E03FE08F400EF0102F8F1FD0002FDEB060AF9060808060712030401F603F5EB01F6F604FBF8F0FDF201F50AFD000606FB03FF010E01F9F707FD000C04FD020F0600020AF403FE0707F902FA0CF8000503FFFA09041019FF0CFDF8F0F5FCEC0BF7FEF80610FF04000504F5F907F204ED0009F70002FF03010208FE08070404F606FE11F4020C0505FD05FDFF00FAFDFD03FDF10A0103FCF801FEF4FD0604FAFDF2020004FB0A021DF90308FB03030109F008FF01FF05090007F107FFF70607070600FBF602FC09FEEE00F7ECF201FCFB00FE0000FA0803FAFCF8FB03FAF8FAFC0B0903FF0906041003FBF901F900FE05FCFEF5FB020203FB00FFFB050B0503050003FEF8FB050105F9FE00F9070802FC0BF6FC0FEF08F406FE0313070404FFF8FBF9FA090AFBF9FD01F4020504FE020FF8F3FA00080308000C0FF6FD010BE603F305FBFFFB0111FCF5FC09F5FC0D111200FD0306FF1101030608FF04FF07FFF6F40301F303FEFD0903FE13F201FF03FFF5F703FD0A10FAFB0105FDFD0D040FFEFEF8F60CFA10F2FE00F404F7000DF304FCFCF40F0209FB020300F7F1FDFEF5FCF8F9F7FE0DFA04F20D02FF0B0EF80AFB0AF90A01FBF303FDF2FAF706F802F806F60209FB010702F5F502F9FAFCFCFEF9FCFDFAF80108FDF609FB00F6F1F7F607FB0C0101FB0BF00BFFF71A0104F004FEF3FD01F90DFB04F507010B09FDFBFC18FFFAFC040EFE09040004060708F8FDFFFD01050703FDEDFA08F5F4F0F2090811030701140AF9FC03050306FF0A0D0109F5F1FF0107FC0200030DF801F5ED0613FFFEEE0C0A030705FB0A0E00F8F5FC0A0C02FCFEF9070F0208030E01EFFB08F5090A05F8FEF7FDFD1001FEFB0209F70F0202FA02F900FCFE0208FCFFF4FAEBF7F6FE010801F606EB04FC0BFC04FC00F8F805F10FFA050DF30407FD04F0FBFA01F602F8FD04FD060EFC080F070F0014FB050F0106FEF00503F4F5FEFAF600F0FDFA0FF604FA0F09020010EFF5FEF904FC090904FE050900FFFA05FE0BFB00F9FAF4FDF4FEFA0CFC0D040701060204080E06040402FAFB00000006FE04F0FB1200FCFDFBFE020C0A0BFA010C0CF2FB030404F90D0208FD0900F7F9FC030CF3EE0416FE08FF0DFEF0F3F4070DF904F907EF07FFFA04060406F0000D00F3070406F6F5F808F4F8FB0B05F90EFD0705FEF0F9FB0803090C02FBFEF6FCF1F3F9020405FD07F30EDE000108FB06FCFEFEFDF3FDFBF4FC0302010204F2FBF50309FFFCFFF500FE0C0A0A0E05E9FEF90609F80405EEF8FD020303FE0AFE16FEFFFDFEF6FB07ECF308FFFF07FB0A03F6FD0004080FF902FEFEF70A09030A0910F80E000E11060DF505FDFF0402090C0FF708F6F2FDF505F5010706F90606040704FDFAFF00FBF7FAFF0D0A0110010201FAFAFB1706FE0203FD03F6F5FCF8F20D021205FD050BF6F1F10205040BFEF3FCFBFEF9FEF1FD0500F9F9000106EF08000908F4F8F0F4F8F90602FF0601F3ECFD04F6FE0C010202F4030001F900080704FB030108F9FDFEF8030602FE04070BF8F50713F8FAF102FB0DE6FC07FB04F707FC0E01EFF80B00FC05FAFEFDFCFDF703FF0706F8EE0308FAFDF501FF02FCFDFFF2F5020B0B10010D000A0A01FEFDFD02F60205FAFFF109FA0804FB040F02050012F603FEFC06000FFF01F8000A0613FD0BFAF6FDF806ECFD09F20000F2F8FFFEFD14FFFCFDFE16050C03FC030D0811FD16FE12FFFF140CFEF2FA0806F205FD08EBF2020905F9F2F9090601060300FFF9FA14FA0B05F4FEFD08FE0D0A0003FE07F7FBF8F7FCF502F70204110C1101F601FDF1080D02FF06000403FAF90701040AF0FB0103F5F903F4F0F510F5FEEF0A0107F505FF0401FCFD05FD030B060FF9F602FCF8EBFE06010DF70DFDFA06FDFDFDFA04FD00FB08FAFF090108FB05F00C0BF700FB04FD05F6FEF2F80608FC03FEF7F7020B050FF60EEF0600F7F50A0C0305F7F6FBF3FD06FB06FAFF0012FB0602FF0705F700FB06FF09FD040206F202F401FF07030105F8F305000E04FFF7F8F804FA04F70006FA0BFEFBF8FDFFEF08FBFE02ED03020BF506F7FC03070400020E0608FE04E9FB0106FCFEECF902FE041700F6F605F6FF05FE00030404000D0500F8050200FBEF0AFF01FEFCF40604FEFD0A080008EFFAF7FC0607F9FE0106060108F4F7F3F004FAEDF7FD06F8EC03FD030E031108090A090301FEF60603FDF501060A06F9F9FBFDEE0D0805040902F403FBF901F8FA0602F602FAFEFC02050C0902030A030D0D08F30609F80CF70507FBF6FD08FEF7FEF909F6F90808F8FEF904000BF809FC01FC0C0DFB0AF20404F912FFF501F60A05F2030B0614FF030000FC0008F60C05F200FA070104FE0FFB090C0E0806F0FEFEF8010000FA0305090507F6FFFAFD0D020BF80511FB01160A03FA04F309F9FB0D0D0302FFFAFE05FEF901FD09F51201FF0F05130503F8F7FD02FAFAF903070D0BFD0D0C0605EB07060909FE0D0CF500FCFEFD1306010502FBF4FCFA100DF0FEF9030009020009040901F607F8FBFFF70CFD04020708FEF506FCFEFEFAF702FDF705030902F9FA01FF0110FCFB0702F9FA0BF70C0A02020B0705FB0000FB11FFF2F80BFEFA14F7F6030108080001F111030715FDED0A0B05FAF5F7FBF605FCFD0401F3FEE7F20007060E020503090608060306FA0DF4F202F4F2FD05010B0005F7F7FE05FBF50BFFFAF80707F706EDF8FDFEFAF3FF010905FE0B000909FB0AFFF801FA0D0C05F7E1090200030B00090BFA06FAFBFA0A03051201FB0600F4010F03F80B0AF1FCFFFEF101F704F4F201030214FBE0FCFFFE0CFEF2F5FB0B02FBF8FD02F5F2FA0405FEF60BF705FD00F909FEFFF301000902F510F000010001FBFBFDF0FFFD0E05FFFAF1F91304FE04FF0201FD0A06090305FA03F0FE01F6F802FBFFFFFB01F4FB0AEB080308F9F201FFF403FC0C0AFDEF0F010203EDF3FF09030606FFFCFE040101FB02FEF3FD06F6EF02F6FDFFFD0100FB12FDFEFFF604020F07050B0F1AFD0406F7FAFEF2F70F0A090BF108FFFA0402F2FEF7FDF9F5F500F9F5FF0F06F50A0204FBF6F70A06F707FD06FA03080306FDFB0905FEFA08F00B0FFF11010403040600FFFA02070202F30A03FBF1040C0F0008FBF904F70E02060A08FDFEF5F3F6FBF80807090AF6FBF7F601FCF41700F806FA0201FBFC0B0BFBFC08F1F401FAFE0D0406000407FA090505090BFD0B0507F4FD0A09F90408090AF8FCF40CFA0B0218FBFD0411FEFE1004FDF201F80213000509FF110CFA01FA0201F602060811F30900FEFBFFFCF10106080A081107F1FF0C0303F506F6F703030AFCFBFA0EFD02F4020102E805F90501030FF4FA0409020109F2080BFE01F91302FCF61803000CF4050BFBF5FA11FE03FC031402F8F0F802F9F6FA0D1508060605070AF406FDFBFA000C0A04F90003FBFA0E0007FF0000060600FCFF01FD0009FDFFFDFFF50D00FCF5F5F3061308F1FEFD060903FF03FCF7F2FEFF06010904F214F703FE0104000B0405FA06FBFDFE03F50202110EFEEF041205EA070607FB00F9FE03F8F7FCF60F0512FCF70200FDF804FD0702FF030804160803FD0C0906FE03F7FF070E0219F5FAFF05F20401F904FDF2FA050C00FDF102F4F60BF90CF307F9F6FCF5010BFA0601FFEE07FBFEFE15FEFA0CFFF5010DFE06F60700F6FB05F708ED1201FCEB0AFD01ECF7FDF5FC010300FE02FDFEF7F50803F4FC05110400E90211020901F705F606F802FC09FF0B0AF9F9F7FEFFFB0CFFFDF309FCFD03FD0F0FFD09FB02010CF30001FD0103FEF1FF08F8FDF3100AF908EA07070201FD050C03FA0D1104FBF7FB08F20EF9FF01FCFF080313F70001F8FD0B05FB11F7FD0109FE000DFCF603F8F80A02F905FFF80CF9050607F80204FE100000FF020B04FDFB0109FB1001F7FFF8EBF4F408FD0F0BF9030EFCF4FCF600FE030807FD04F8F3F8FB0A08EE050304020606FF03F80AF006F1FFF806170305FCF507F704F80A10F7FC01F900F602FCF8FB0009F10704FA0404FE0FF70400F000FFF701FAFD09030203070901FCECF206090109000B0FFE0A040203F7FA0BFBFF02F800F9F8F9FCFC0C0504F9010FF9FF08030504FE050104F8F813FBFE0B00FFFA050603100505070003FF0D020FFE01FB05EC12FDFEF009F4F4050BF6FDF20802FAFC020C02ECFE010602FE01FBEFFFFE06FC100EFEF6F6070AF9FC05FB05F701040909FD03F1FAF400FB07F908000A03EF01031005F511F9FEFBFC06050812EBFCF90103060516F8FBFAFEF9FD020AFE0A0C01FEFEFF05F40303EB07FD0106F9F601051206FE0E00FD03FD0E050C05F3FCF400F110F804FEFC0902080807F80CFAEF0A0F01EFF40D0305FBFF02020B040000F50706FD04FBFCFDFAFEF604FEF905FB0B08F6000C0E0700FFFB040209FD04F5F202030309FF02030D0A060B0404F0140201F7FEFE06F3030F06FD06F2FC04EFF6FDFA060407FCF30508070703EB03F9F70A010C0C020EFDF8FEFEFD0B0C0BF0FFF90714FB01F90FF60AFEFAF407FC0BEF1205F001FF0508FBF0F5FCFFFD04FBF9FE00FA0A090E0CFAF7F3FF0200020905F8FF050CFFFCF8FBFE110200FB060AEEFE15FD0BFB09070FFEFC12EEF80D06FD0108FB0805FA02FA110A0607FFECFA00F904F3FF06FB0C08050A000AF212FFFEF5F50204120305F803F5010508FCF80202F90F02F81705000007FB07FB120300FF07F7FA01F9FD0A0212F8FAFCF7FBF40705FB0B070204FAFFFEFC04020D08F5F70C04100FF9FCFDFBF7FFF3000A0800ED02000A0304FFFB00F7F80A0706070115FFFA01FAFAFBFD01F8FBFD02020204010AFDF703FDF2F10801FBF8F9FCFDF7FEFB12FC06FD0AEBF6F600FA0BFBFE06FFFEF9F9F3FD0701F3FF080CED10F500FC01F4090E050407010903040807060404F8F5FB05FFF1090CF0080BFDF30903F9F804FBEC00F60201F50302F6FF07060807F4EC05F8FE060DF8E208F300FDFCFDFEEB08F4090104F50106040E0B0D031608FDFF0E09F80DFE05F603FD10F705F9FB0609FCFA02030C03F806F806060506030807FE0107060005FA0EF81108F6EF0C06FC04F707FC04F202F7F60B0307F3030306080EFF0B0BF8F9FF0106FF07060104FE0A00F00204F9FE100708FCFCFDF002F4FAF6FE02FF07F30B08FE0E0CFAFA09FEFE0508100CFB0C030F0306FDF9010FFA03F6FDFA08F605FA08FFFDF407FCFF0600F416FB07FA0206FAFA03FA0507F3070E03100BFA01F602000C090004FCF6F30FFAF2FAF804F8FD00FE06060601030307060002000105FD04F608FF0101F90AF6100900FA0500F601F70909FDF1070C09FC02F4FC0805090DF10BF9010F05FBFB130DF20106F104F202FF06FCF1130CF8BC708C44E3D8A298F708196BC66F75F91732E5AF062775A9ACA36CE2DA64BCF62CAA4F63293C7A8F894856E9F263EB9CA4A0648141B4B0EA3A2D3364C36A83");
+        int smlen = 6209;
+        byte[] sm = Hex.decode("3FB2759E1BCA07730566D4FBBF3FC19A39C68F190095789EC555884EE97E9ACDB09109C5A9CA96658603D63A5A23C9D8DB7D8C54D07738E958BDA1E631ADED4329AE16B183374E1C3976DF1EC94BC79B773E8569973EDA418AC903B534A83B8B2F7CF7856ECED63F2626530EBBE3D22A734B457F2B1F168225753A0F3B030251CE7F414740D707D10A65BA6F7464E0291749AB9CC694A81CF54AB8AA21F3687D197FD3AD1EB37EAB1A19A67B1C396BC480B2CA19D650FCD9335FF245863787275540D7D1F5FCB278C558AD6CF15C154073A846B51754D45A2439FC08382E93CB657E1CFD3B3DEE5394C5F81513041A356D2EB938CF196B1B4BF96186ED75697745A3304BFA7ED23F77DFEBEC60A2C2441EE6B695CD17E2166956CACA25B09DE505187CBA23F62D86D0871BD6B8FD2D116743110DA3386A7401DAEF39506C9091B5D32675660A7380C5E837FB7206604C1A4931B8D6D452C5F4095EC1B7EF0CD11710DCB6A18749B788079AB1902135B62280D928167559696A3A2EAEFADE345F91D8CE49EBB109CC926C28077AB5B1B9BA946D929C2BB969061829A6E58A0FA5B168F5A1A7722CDF9A23CD75946CFAD9D624F4DB6A47C78EDA05EDBFE164F6FCB4C32BED70FC7D04BEAD5392F26ACF330B99A9A4140B5ACD0F215A16CC463F6BC4B7BD5D81B51F98C586E58C68BE97ADA48CED599933F2CDD994B9B52B3D93C44D9ACA191FDC0FD894C8AFD58B05D73432D6A90991FA87089B68D7CD7D1B136F7E3BF0FD266F82D13B1E456FBB6E29DF3D330E2D9440AC0AB5805C524463EDCA6D27ACB13B1B4F0881FF3A5A19E2D512A84192FDEA959FB87A555610DD44497EF3AAE91D075EECF7945FC2B8FC531B53FCD236687D05457999E1B3510D92FA433205E4E42E5B474E2FE7EAFD88BCD3375DAEDA0FAE4D40A63E09F2C99CD9620279BD1C34A8AD5B014CE8898B17AB33C1BD311CBF3CA6EBE0C9217AE3D09D978C0A8A41FC558A8D31BF37FEF59D1F46575CCEEA07F500E1CD85CD00FC20B449892E9C463C60342F526CA6F25F6291DC72330B5DA6C24CBC0CC75D8146CCFBD1FA47D36250EE5113BD9A54846E40FCDE4CF33E38328B48BCA3953C99495AB14B5AE2ADAE893181B1AD49EFF63D3A432DAA67572006A4001179363EAAA1701FF939D69FAA27EBD7BF71DA1029C84B4980251ED9C19AE67591AC719D2038D39BBF95B29B217A95CE10DDADF65CCD90A6BE492CFE0345D5AB3BA26F44846E96E330E853D68132B6F5B1B8913C02997551E55EF4A2C48B0EED24060FD549087E85ABDFADFB35B13D9A44EC677818EC64CF1446EB9F012160E5998272993377D8B751F87F8F370D904642EE9AEAEECF498ABEFD14FDFF1DB90C41401FC4F6DF93429A944ED1F90D834D7C5FBE5651FE291488605872926ED853C0B23E958DF376C75AE1909BCE043EA6AC3B035ED0350C07DE7749F7181E2DB5C5A4CFF983634AC34A4D416CBECAB4BC40C96EE2AB20A513E865B792F2BFCCDE4F9107CB9887F3FA77F4FA9D625EB32FB856036768F6E89424345BEC0CE938D71C37DE7C0CC130957C15D06E764D819AEB5E05328C50BBF7B01EA9F2952D5861705FE66A8EF3F22295C408259901ADF5E675A9D72125467B18C75A0CA5C84BE77E8A678FCB1A98EFD90496527B509CEE1DF01B1C535273B109E5ED1F8C6012EB79C7F5F5B9412A4F7DE6FFDE45F10F79BF1135A370C953BEF71EECBA75718A56441049B8C306E371B02250B179ADC42B9DC0963C9D6F991F66AFAF05B7ADFE8FFA71E8FF3BF14B08550A8499F543A8FA45C4BEF6D4635EAF889652E4EC4E24B7343C48F32C61C661291747B6FF99057C797B7FE93A58FC219D44C3973E1C11973F8CC00A29EB43235E0B9DE326F134FA55CFEF1B7D1AA00FFFCD8A3F9AFA1127EDD97C5D20D642203F65D5C5151AE773B3683ECE96726CC61C13997CC8C8F291D77779686E33BC09EEA6B8739122D2B90B78022F368B01833D94ECB2091BB45A45AA7784603502B1C54657F8A7F6E122C7B46393A466933BD806FB4411288DCABEA5E2788AB0C088AC9A45C587C087CA9FD4C131969BC9F2025DB8FA4484CBEF0C6C7D887FC22599C28593DC8D586ED81D38D35249D7E01FF6DB898CEE78B6214EF2C215B76E55FB0A65CA3EBBB5586F4255AA2BD802C5E2B60A6051B5C4BFDB48C4520290F2FB1BD5A6D156CA375C1E700765F9923ACA126D41CDBA2F2DF339D143870CDC2964B20483154EB8E8D59651C17FA7898176073DD353B629D327BA130F2E56A9594D177A1EE82847BA8F8AFDC669F7BE160F1EA6C6EFB747D9BAB0AC58DCDC8D33B5F7BAABE5274DD7D4B5AA439C713615D4B255814A5D2AF44D5382264F3F9F48872A38D36D1F15C90FB768C9B690EC989FF28CC135D29330D84EA33EB173271DDF8F5DF885076251370F59AC0E4DEEF640D5C04ECE8778C1D8F839462C5504916CBF555167836076C88A848903B3779E007745F3CCC8282CC526306DD63492F0A004A99821D1BBC065A255F4FDCF426BF97CF9933DA6A79B577919F7605446A25CB549A3057785522EF5A56E3288D42D714163F16A36EBC6607779691C7A1E5C420E24A64299AA03930DE872575A18A0D004CE7CCBE85BE4227F2146E176F0B0689BF51988ACAC366444E2281B5A81FA505E01223818185BE05074AF0C94C4B99F1AE6CB4033487DB1EA46B3A59A1B840845570F468F6E8BE1CEC813148FC421A4A62BEC0D8CE9DBE73AC17D44FE36C93DDCAB402EDB129BAA93795AE54608169F58BD8D115EC6392DBC2BEF8BBFB820827E76B266F480D4E7229B4119C35AE0AE1F222DAD22DD9CB3DBC76D80722F74E6BA87C7918BF33811850F3C9300E0FC880B889CD3EF2C3E2D63B21AB7A05867B460D7B96050BA8F812B79B29851CE5EA92202BB553972EB1ED8CB82289987EF0B314D2E787DDE3CAD96238D7BD182CC404F8B19F3FD4AA55C75DFEAFBF6228A37B72CF57152B4CF7A65872ECAACA21FAC42849BD2D1A5812196AC6A52EB51709710AE1BE2345465DD98D42135BDF67C1A022A338F4E60686746B62B2624FCA52A46DBE515714A2C8E9CF76B14D2A7FA3A734D27657DE99C8A0A5883A1A9ACD6568A0F5926E93AF2017CCF8623C7363CB52390A93FD1143C575AD3B35EEC387321EC87B93C16FF30DC8848DAD229B0CC561F75F64D948A0CD82AC62DDE0AB9DD13973E9B6A19DEC429620A6BD52F0203820F3D2DB2369D121A093E64071E926919BB8B7BB0C3E58EDD9A69F5ED8A1C7210290BFADCE31284BB69C5F02B5E8D13F19E159FF334DFC5AC94798E1C87230424AD32FAC495299F357F74D914FBA9349F0D26491444C5A7B2F0388ED098A182122BA1C1A8FB751C52376E4385449B06F92C3005EF39BDB758AB542F7B3BA43A9D6D3B1B88977712E0A8F08C898AF5DEA1DD4E83BFB763DCDDFD34EF33E208F98AF8F90AC173ADDAC9E701B3F014560A1D029598D91C6A5715A4102F2162B8FA44A4B9F002E0E1C8D2C5906745708648A690B8CA89E6ECDF1BA155EF1D256305D94D790A856C79E39A22470824AD09A52F96D15CA3B0C09D19CE23C7880D774AC00D59C3009183163FC98FF74F99E03A550283A8D300E1C618678E2F8EC2D69B15E822571ED7A5F35D6FCBE5AEEDB56FFB6352D60434CCA2C5DBA90E82AD9298FB1369A06A1FE93098C86AAEDBF846F59A3390845B8EDD2FB8D08622AF52DFF43F1918EFCE00D5184BC19D4C782027248DCD875EC1B9BDBFDC7DE08A92E4838AE7871B897D9F5BD23256B9686CB001B5B2DBE7D79B2094E2B26C103C38E81F51A75DB8871A6AD28AB5AADF94BECF43D928B155FD4900A3F0F2332B992C0091B8B21D8609BFE9A1EA84313ACB83ADBC8478320BEBEDA9AEF0BC1A9B3FC4BCEEC226D13AE4A5163DEA4D019B148EF98285717AC45A935CB71B103FFFD9B6739CF1916AF5F3D61AC37CD9584DB4A2F9A24D38F61B83232DD33680DC30C32CFB9DBA27A6BA2BB8B2159F3EDA7F37402212755225CD99CC1A37A99F2413A4C57D1A73D9BA1DD02E310DAD355A14FD01A3EED8F73008607775AF0A5BE621EC16F2111A5148F977E9F79460450CC21AC5FCD8EB04BBA536385563D288BF592D3ED7BFA3A12CD9F914CD0C35770A4A928705D25E13AB4A05C437E009595B14381A5E215870EB468B06607F30FD88CACD1C11478BAB4372B9438AE4DB6C606941BCE98322E0FDCF59009DFC5E7A4C483B00DDF2386A236272DBA0D8064F27B7C1BE90DA8B592CEA17EB33ABD5A7E19DD69A805A6AEB6773D0E8CF6450C3C2FE55E0B7180CDD8863106D75431482BDF9C182977B91330B0F0ABF796D5348CD84FE0036E52860F7801ECD8EA9E69784994D425CD5ED09A63251835A233ECA4CA9B9AF6CF154AE4465EBF089F19D32024AA2C144DA004F84A7A6F22C3D71600245AFF5F1E5A1D9D5CF24FCFC0228A534E9E6447CE7DDEBD8DCCAB3BC0933DB333A39031EEA2EF6B81A9BB34A7F67554FDFCA8048FCD97E76CC082A93FC4F98D32910B895682D1FB3F5E97E463131F56364283ECF2AE029149B9CB73817AEECA497360C23ADCE8111D86E2C7005ADD2C8C0503A99DC91D30F9D20866C17E296649B161416C181F136608A05434074D567CDE728F7B31B60B97C04055698E108E6A2A65A77EDBFBB2F05AEABABF166B6DF25DC3A70062050C779CBBA550A62F021CD6222165082EC37858901D0A46C815579AA998EEBF4B3387B6BD71B0F16EF5B68CBC6A47E1A3DE015331953F3D3510385F1E048ECB84E7EF929142225DF16234EBD672B686D297E5A9168C6F97EF6C97501D4E086E9D73D1994D3C38C912691D8F0E00E0E2F0CD95B42F3F70194E3183BF9537188D1105D44839F7F3EDAB0E10232DAA9FC673D6071F3589095B6C392A766F0DC9D9B8DD563B1EB82076CBBB7C5636C516D3F200A3B43D384E79042E936E51C3D1A4D3F621CF738EE51D6D29F22D658E2370410E841A5CA7C3122AC26BF26730AEC0B23D8CDB46B6F3E0C4EF15410B15D83BCDF67BF2AD9EDBBC3890F70670FBFF86A4645A58F1EB5C49C8F6973CFEE2E1E38879F59D1740172A52744BCD839CACAC94BC3DBDA37A97A2FBDAD6AE0CCDF080B57CDE454092D763A1020E176515075DCC37148AEDAC1C7BA090B12DBA260F08F04C689661EB926716ADF5FB7159822E226BD3DE2EBEE1F98685CBD99CCFB09A6C9FBFBE377A122FDC01B9B7FD82F759129A6D0B0FCC9E8AD566A19FA04C12B95C4154E35215F5F11255493AFD5E66B73D4E0572F01454D1848A13BC0A9374BDC90994B0004D9D2EAC8FB506E7CD764A1B2A9016BEDDBFB987741FB68265E5B05CDFA46A4A7A1BC25ABFEF37118CE8118BCF92F32C433E5252D8DFC195D4CE5699A2CDC1612EAA171423E6299F86C2A976DB4C1E421E140BFEE8406DC649C57FE27A77CAF3481B8687DB07F9921790AB5D502CED457C3538E688F143A69175A535101E1DEE82C5E39A579DA178CE233105BA5275E27E79441548D9E0F70CBEC5C7E179D6FD8E167F90C3A79D964CC23CFE18D6BC300753961BAEF0EC6AEDDDCFB1760B6DB3D0B667B8E120CD3F867017507783F223106CA7753D94BC60E65538589FA87807F7AB8791C1957C9510A9FBB91D513DE0ED7166155E911A5622A6A49DCF1DE3DEA4072E5DA75583B6EFE781FBFE31F7AE50F4FB8897586B8078144CAE9842CEDA34EB9E0B996E3CA46F11BCCA46ECB81DFB191236046294BB0120ADFDA2EC0DBB7D4CC6353BE9E64FD668710B1CCD305349C0B7877566E3DDC4E21985FA6000229004DFEA9F39421476A5E33C9163CDAE4C7676B1097E48C70DCBD4B39CE4AAC3ACFE73CE9C0D826B8A61CCA0DD2340C860E57496A1915BF065AA6557E26A151603D31CA94410E6C3E6D5AC80FF6F71DAE371D020CEBFB58EC73017C8D2D6D6221598678D752DBBA95113CDF71848D2E05D22613451627456A96D15BD277EE04884F3F296B0DC5314EBA22356CE09D4C0BB7246419D8674E4D10EF64B3F057D95E70BF1086ADDB100453D81E19939BB18AAC1343FABE39E308284A8D6A38BE238B6E0503A504727AC20B48A2C26C130E7A9678BEF07A1828ADDAB90740526F99C11D3217E001172CDD33D6D6E949A8ECB963C0A5283189AE8C98B21933FF691931758E842977A4F3F337252105CF22AD836F91E4FE5CE97FE4CE59EBCC4DA2A56D9413BD356BF65EF6FB2CA2201EB31BA56D717D2F9C038BFA76A52610AEA57D45500A1408AA8876AFF585A2FF309C3EC852B9568AE1CF9D97635759932E61D61DD6E67402912EC538BD0DF9D0BF25C862C91E301AC499E64C061FC14E89EC361A10FA0C85132BCEE7AF028C663BE3B0E12EAC52F691FB66E58E34EF2AF4C5A9DB0A0AB01DA372E39BB824F9D6AF847DF6A08F680B4F99466D6E6F404F0B92210D641375ECBC0C10A38DD1CF449A75BF2420A294EECD649013E160D793A247D1649E03A535292ED3D88EF7901DC845CD9703BF0948DB61C08EA89C33A3DA436874AD22ABEC43A5F2896833E15B3102A66E2A7D69D272FFFD4CC8979B9733718B95B605166DBE16ED1FD1C5CB2571E7D9FEAB2BAF693448C1E9613E8FBB25E05E2838390F3D9D2CA88D4B1739AEF4039A168E908D245ED38CA96F98569FFEA949946B76BF6F1DCA0B866CF0AE157314A28B76654EFECBABBA1C58ACB48E61587B42DE0F5BF8D5A3DB6C2E1D1632894D940CF10F71390B1DE07A4DEC38EA93A67439361E71FD8149D26DD8A21E9B98DA1FCC014052D0FF54DA3B2A335D0122FAA1332B193043A9119BAB7BC8F3209812B5280625C44ADBCB92907B6C387E0BDB1B9EDC0377C03E305C7333897588C2EFDDDC82E95ACAA329839EAE514F8E8A46A9126847933E73820922CF62528DB950EAF940F72A7383A486F5E210120987173A75A7D1B12097287F9146735A7BA110E22BE8B2FE720B2DA6CD8D59C8A32E5B43BCB9B42B78103BA2C25C13693AD4F2CF49A93D4B543AD7E4BE7E7304074E906D114E77C9448A8318E71F67C3C5A3B3379B40D44D9D1740C8D5003B03AA614099CB77B97A1B121DE9F75A69CCE3B7F1A2AF7B2B7702919F45544F778D5DFB9B7DB1C5AD98B36ECF231300178EF1154F4B93CE197815F6C5D2C3A3EC2A3E6F9FCC360B47D5A362BF4A41982ADE9C1377EE1162A9ADA02B966F502931045537D5CFA700C5EE0B818C8B280CDC07B1C34046A908D31CAE7681AB7A59AC371326211C170E9240B4B00222F628ED734ED39D5D0A4D1D4937202F82A531B94084FFB7033595460B41C5DC021D955C8EBDD8787140F60FFA21BFE680234BF5EE2F3F998FD9662A657BABDF4FE4A3191D7456B928BA2C0F08596015B803CE938522A88B44292223DA54BB1A0318ED439C5E0B1513AF667D1AEB8A0228AACDADDB02F689CD43FD15B8B10B12359EB83936F9191BD4671A3EF7F5998B70EBE29E551CBC4EEB214DD776AB62B20D1DB533CB2621AC0D00D88BE1E21243DCEAA1494EBD581992F6F9171B9FEBE919DA5B61A99D2D5C3A23EB447EAD4857F217CE80CC5765E014B851CDB56518160B69C56F4A1FB9542792390FF4DF407E1BE94408A2C856DAF3F95EF4E1D3CDFCD5ADC8F9BFC95F5D7A9AF8835DF3205FD81BCBB06DC10A72E57855DF16794F3B0F0EC83B36922267755777517875390D7E5520D46610B5008ECE82C08E20888C4785A1CADA07E929E1324EF710D48212B739EB5276A4C895C46BE8A405332DF93E93BED6C2075261F90DB4ABA03B4F7884B663268AE18C11479098611FCB7B0F9645E315819995DF3F3A45221A6DB71874A0585A197A01E67CBC85DB02A4B3ED2BEEE43031E7065BEB838C601BF2B161633BC9C04B506564E5DF517F1445140029665EFB724F5F5D88FF654AB18A6D65AB04E3F36720A26E4D9676148020D634FF0B74DCC828394F9AFCE7C8443097A0091FE8255F03D5A9B5AA00FE9E6056B11BB772AA6361C51938447B57F3195081483AA1DF6B5A71D050AAD3C855A770A1636019DB9EE75875D542848A285140B1B7FE054B4FFC719CAAA3B21FC5BE4B92F45D7A99254733303DDDA18AB271CCB2DACA27B600CB5CA50E857070CDB657FE559B21CFB3C596B24727501707B64A657E3B46FF1DA18842E0227C02261F88EBA6F9502856752D5D5B6208FE660645860E42A32786C3E54E8AF6DCD3E9ADB76BC0200A4A17B1CD89815301AC45EA3070EA99FEFA310B7FDBCFBE817EF28A04F44A76B38015A27985E5D9D706997C1DF67CCAC869D4FC9972AF47FE2908C3B1349728796903DA78F67562D693418345E041FC6F9E3A991299EC387C53CA9DDC0428001216637F92AA19BED77CAEBBC2BD3972AF5B07FADF92F54E82210ADFD3623939BDEB5D5BDB019D89A1EBDDD98C1B1D6BDD854EBB594516183B32C24F4C8DF07943CE77857AA0F10D27D3FF0DEDE7026F924E34153F9741CB2D7394401B20BCC53965ABDD26881FD3CF2AC83825E1DC1D1402F679BA8AC57737EDF7FE7860580424559DFD9042C88DFDE010943F3C3BFF61283143C46BA1E85CDE3284AE526CD5DEA7A401F07AF0A88586C599FF640B4B9C4C601E650D7E9E4092A940F9E4771084112F8D8A7FA8E5DC43DF8214242AB81E8FB5A097F80E01C9B436C125DD616FF680DBB16A24F90F45A42CC1A05111E062348D14C1EFFAD81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+        doTestKAT(PQCObjectIdentifiers.qTESLA_p_III, pk, sk, seed, msg, sm);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowSignatureTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowSignatureTest.java
index e8a2a33..6290a84 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowSignatureTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowSignatureTest.java
@@ -2,12 +2,9 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -16,7 +13,6 @@
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.Signature;
-import java.security.SignatureException;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
@@ -27,6 +23,9 @@
 import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec;
 import org.bouncycastle.util.encoders.Hex;
 
+/**
+ * Test cases for the use of Rainbow with the BCPQC provider.
+ */
 public class RainbowSignatureTest
     extends TestCase
 {
@@ -144,10 +143,6 @@
                                                int numPassesSigVer, int keySize)
         throws Exception
     {
-
-        System.out.println("=== TEST ===");
-        System.out.println(numPassesKPG + " Tests");
-        System.out.println("KeySize: " + keySize + "");
         for (int j = 0; j < numPassesKPG; j++)
         {
             // generate key pair
@@ -189,58 +184,6 @@
 
     }
 
-    protected final void performSignVerifyTest(int numPassesSigVer, PublicKey pubKey, PrivateKey privKey)
-        throws Exception
-    {
-        // initialize signature instances
-        sig.initSign(privKey);
-        sigVerify.initVerify(pubKey);
-
-        for (int k = 1; k <= numPassesSigVer; k++)
-        {
-            // generate random message
-            final int messageSize = 100;
-            mBytes = new byte[messageSize];
-            rand.nextBytes(mBytes);
-
-            // sign
-            sig.update(mBytes);
-            sigBytes = sig.sign();
-
-            // verify
-            sigVerify.update(mBytes);
-            valid = sigVerify.verify(sigBytes);
-
-
-            // compare
-            assertTrue(
-                "Signature generation and verification test failed.\n"
-                    + "Message: \""
-                    + new String(Hex.encode(mBytes)) + "\"\n"
-                    + privKey + "\n" + pubKey, valid);
-        }
-    }
-
-    protected final void performVerifyTest(PublicKey pk, byte[] signature, byte[] message)
-    {
-        try
-        {
-            sig.initVerify(pk);
-            sig.update(message);
-            valid = sig.verify(signature);
-            assertTrue("Signature generation and verification test failed.\n" + "Message: \"" + new String(Hex.encode(mBytes)) + "\"\n" + privKey + "\n" + pubKey, valid);
-        }
-        catch (InvalidKeyException e)
-        {
-            e.printStackTrace();
-        }
-        catch (SignatureException e)
-        {
-            e.printStackTrace();
-        }
-    }
-
-
     /**
      * Using ParameterSpecs to initialize the key pair generator without initialization.
      */
@@ -318,22 +261,6 @@
         assertEquals(privKey.hashCode(), privKeyKF.hashCode());
     }
 
-    public PrivateKey getPrivateKey(String file)
-        throws Exception
-    {
-        byte[] privKeyBytes = getBytesFromFile(new File(file));
-        PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privKeyBytes);
-        return kf.generatePrivate(privKeySpec);
-    }
-
-    public void writeToFile(String filename, String data)
-        throws IOException
-    {
-        FileOutputStream fos = new FileOutputStream(filename);
-        fos.write(data.getBytes());
-        fos.close();
-    }
-
     public void testSignVerifyWithRandomParams()
         throws Exception
     {
@@ -386,16 +313,6 @@
         performSignVerifyTest(15, 100, new RainbowParameterSpec());
     }
 
-
-    public void writeKey(String file, Key key)
-        throws IOException
-    {
-        byte[] privKeyBytes = key.getEncoded();
-        FileOutputStream fos = new FileOutputStream(file);
-        fos.write(privKeyBytes);
-        fos.close();
-    }
-
     public PublicKey getPublicKey(String file)
         throws Exception
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256KeyPairGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256KeyPairGeneratorTest.java
index 7524dbc..428a533 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256KeyPairGeneratorTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256KeyPairGeneratorTest.java
@@ -8,6 +8,9 @@
 import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec;
 
 
+/**
+ * KeyFactory/KeyPairGenerator tests for SPHINCS-256 with the BCPQC provider.
+ */
 public class Sphincs256KeyPairGeneratorTest
     extends KeyPairGeneratorTest
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256Test.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256Test.java
index bd31ea3..084bb0c 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/Sphincs256Test.java
@@ -1,5 +1,10 @@
 package org.bouncycastle.pqc.jcajce.provider.test;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidKeyException;
 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -21,6 +26,9 @@
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 
+/**
+ * Test cases for the use of SPHINCS-256 with the BCPQC provider.
+ */
 public class Sphincs256Test
     extends TestCase
 {
@@ -1039,6 +1047,60 @@
         assertTrue(Arrays.areEqual(expSha2Priv, priv2.getKeyData()));
     }
 
+    public void testPrivateKeyRecovery()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
+
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(), new RiggedRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory kFact = KeyFactory.getInstance("SPHINCS256", "BCPQC");
+
+        SPHINCSKey privKey = (SPHINCSKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(privKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        SPHINCSKey privKey2 = (SPHINCSKey)oIn.readObject();
+
+        assertEquals(privKey, privKey2);
+    }
+
+    public void testPublicKeyRecovery()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
+
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(), new RiggedRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory kFact = KeyFactory.getInstance("SPHINCS256", "BCPQC");
+
+        SPHINCSKey pubKey = (SPHINCSKey)kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(pubKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        SPHINCSKey pubKey2 = (SPHINCSKey)oIn.readObject();
+
+        assertEquals(pubKey, pubKey2);
+    }
+
     public void testSphincsDefaultSha2KeyGen()
         throws Exception
     {
@@ -1128,7 +1190,7 @@
     {
         KeyPairGenerator kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
 
-        kpg.initialize(new SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), new RiggedRandom());
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA3_256), new RiggedRandom());
 
         KeyPair kp = kpg.generateKeyPair();
 
@@ -1143,6 +1205,102 @@
         assertTrue(Arrays.areEqual(expSha3Sig, s));
     }
 
+    public void testSphincsRandomSigSHA3()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
+
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA3_256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA3-512withSPHINCS256", "BCPQC");
+
+        // random should be ignored...
+        sig.initSign(kp.getPrivate(), new SecureRandom());
+
+        sig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        sig = Signature.getInstance("SHA3-512withSPHINCS256", "BCPQC");
+
+        sig.initVerify(kp.getPublic());
+
+        sig.update(msg, 0, msg.length);
+
+        assertTrue(sig.verify(s));
+
+        sig = Signature.getInstance("SHA512withSPHINCS256", "BCPQC");
+        try
+        {
+            sig.initVerify(kp.getPublic());
+            fail("no message");
+        }
+        catch (InvalidKeyException e)
+        {
+            assertEquals("SPHINCS-256 signature for tree digest: 2.16.840.1.101.3.4.2.8", e.getMessage());
+        }
+
+        try
+        {
+            sig.initSign(kp.getPrivate());
+            fail("no message");
+        }
+        catch (InvalidKeyException e)
+        {
+            assertEquals("SPHINCS-256 signature for tree digest: 2.16.840.1.101.3.4.2.8", e.getMessage());
+        }
+    }
+
+    public void testSphincsRandomSigSHA2()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("SPHINCS256", "BCPQC");
+
+        kpg.initialize(new SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA512withSPHINCS256", "BCPQC");
+
+        // random should be ignored...
+        sig.initSign(kp.getPrivate(), new SecureRandom());
+
+        sig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        sig = Signature.getInstance("SHA512withSPHINCS256", "BCPQC");
+
+        sig.initVerify(kp.getPublic());
+
+        sig.update(msg, 0, msg.length);
+
+        assertTrue(sig.verify(s));
+
+        sig = Signature.getInstance("SHA3-512withSPHINCS256", "BCPQC");
+        try
+        {
+            sig.initVerify(kp.getPublic());
+            fail("no message");
+        }
+        catch (InvalidKeyException e)
+        {
+            assertEquals("SPHINCS-256 signature for tree digest: 2.16.840.1.101.3.4.2.6", e.getMessage());
+        }
+
+        try
+        {
+            sig.initSign(kp.getPrivate());
+            fail("no message");
+        }
+        catch (InvalidKeyException e)
+        {
+            assertEquals("SPHINCS-256 signature for tree digest: 2.16.840.1.101.3.4.2.6", e.getMessage());
+        }
+    }
+
     private static class RiggedRandom
         extends SecureRandom
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSMTTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSMTTest.java
new file mode 100644
index 0000000..eec4c90
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSMTTest.java
@@ -0,0 +1,627 @@
+package org.bouncycastle.pqc.jcajce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.jcajce.interfaces.StateAwareSignature;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSMTKey;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSMTPrivateKey;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * Test cases for the use of XMSS^MT with the BCPQC provider.
+ */
+public class XMSSMTTest
+    extends TestCase
+{
+    private static final byte[] msg = Strings.toByteArray("Cthulhu Fthagn --What a wonderful phrase!Cthulhu Fthagn --Say it and you're crazed!");
+
+    private static byte[] testPrivKey = Base64.decode(
+        "MIIHuAIBADAkBgorBgEEAYGwGgIDMBYCAQACAQoCAQIwCwYJYIZIAWUDBAIBBIIHizCCB4cCAQAwgYsCAQAEILF57l4FB6N/vvGoIQ" +
+            "TjZ5gaZRgFQUPBjH7y6mfZgdvaBCBvDUjbkmb9GoHYbyKHxGlJ/dmHAkXahPNNfRR9AZCOlwQgBfd9vy9CNN4k4NIYjRvtz7QgMjjb" +
+            "kt5WAdQej5KzNM0EIPTPrmKVwjXe4F8QlmZOUZP28jDG/ZJpxR5712m2e4ywoIIG8gSCBu6s7QAFc3IALG9yZy5ib3VuY3ljYXN0bG" +
+            "UucHFjLmNyeXB0by54bXNzLkJEU1N0YXRlTWFwz+vLa6D+CbwCAAFMAAhiZHNTdGF0ZXQAD0xqYXZhL3V0aWwvTWFwO3hwc3IAEWph" +
+            "dmEudXRpbC5UcmVlTWFwDMH2Pi0lauYDAAFMAApjb21wYXJhdG9ydAAWTGphdmEvdXRpbC9Db21wYXJhdG9yO3hwcHcEAAAAAXNyAB" +
+            "FqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IA" +
+            "JG9yZy5ib3VuY3ljYXN0bGUucHFjLmNyeXB0by54bXNzLkJEUwAAAAAAAAABAgAKSQAFaW5kZXhJAAFrSQAKdHJlZUhlaWdodFoABH" +
+            "VzZWRMABJhdXRoZW50aWNhdGlvblBhdGh0ABBMamF2YS91dGlsL0xpc3Q7TAAEa2VlcHEAfgABTAAGcmV0YWlucQB+AAFMAARyb290" +
+            "dAArTG9yZy9ib3VuY3ljYXN0bGUvcHFjL2NyeXB0by94bXNzL1hNU1NOb2RlO0wABXN0YWNrdAARTGphdmEvdXRpbC9TdGFjaztMAB" +
+            "F0cmVlSGFzaEluc3RhbmNlc3EAfgAKeHAAAAAAAAAAAwAAAAUAc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNp" +
+            "emV4cAAAAAV3BAAAAAVzcgApb3JnLmJvdW5jeWNhc3RsZS5wcWMuY3J5cHRvLnhtc3MuWE1TU05vZGUAAAAAAAAAAQIAAkkABmhlaW" +
+            "dodFsABXZhbHVldAACW0J4cAAAAAB1cgACW0Ks8xf4BghU4AIAAHhwAAAAIKblKPny5XBcLTom61U/VvUCJ+/xEX/qJaRXitEAu89F" +
+            "c3EAfgAQAAAAAXVxAH4AEwAAACDLWNO9lh3R8LdD5dVoQ5r85BH+XbLY3a/Bbf2ABa7AEXNxAH4AEAAAAAJ1cQB+ABMAAAAgv7gBYE" +
+            "q+h3U9GsU5dqmQp/p2ap7tr5wv6X8mYVgNJPhzcQB+ABAAAAADdXEAfgATAAAAIDLtl68/OsguE7QTZ2UzFfcjGv3fGoiBomQNlyEs" +
+            "VWT1c3EAfgAQAAAABHVxAH4AEwAAACC2CKhUAp92/hJwuyEIJXxBcHsTg/vgBg3FfHaFJh85cXhzcQB+AANwdwQAAAAAeHNxAH4AA3" +
+            "B3BAAAAAJzcQB+AAYAAAACc3IAFGphdmEudXRpbC5MaW5rZWRMaXN0DClTXUpgiCIDAAB4cHcEAAAAA3NxAH4AEAAAAAJ1cQB+ABMA" +
+            "AAAgl/DnFFIHZ6u8yNQSOIh47zRoRZLfkj8/CzUHM54wKQtzcQB+ABAAAAACdXEAfgATAAAAIPx12RSLQNhXo5DWenzn18i5c11MQ8" +
+            "E21a3fKBI1c1xTc3EAfgAQAAAAAnVxAH4AEwAAACAUw9Wnqw/IS+TLVVj5zAOe0lMvf+x3x61nHfjYAXY5BnhzcQB+AAYAAAADc3EA" +
+            "fgAgdwQAAAABc3EAfgAQAAAAA3VxAH4AEwAAACC4x1ONSAJrJ0+2gqZxhi6MJ7jY69JS2b425N3ZUAwiKnh4c3EAfgAQAAAABXVxAH" +
+            "4AEwAAACD0z65ilcI13uBfEJZmTlGT9vIwxv2SacUee9dptnuMsHNyAA9qYXZhLnV0aWwuU3RhY2sQ/irCuwmGHQIAAHhyABBqYXZh" +
+            "LnV0aWwuVmVjdG9y2Zd9W4A7rwEDAANJABFjYXBhY2l0eUluY3JlbWVudEkADGVsZW1lbnRDb3VudFsAC2VsZW1lbnREYXRhdAATW0" +
+            "xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAAAAAAAB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAKcHBwcHBwcHBw" +
+            "cHhzcQB+AA4AAAACdwQAAAACc3IALG9yZy5ib3VuY3ljYXN0bGUucHFjLmNyeXB0by54bXNzLkJEU1RyZWVIYXNoAAAAAAAAAAECAA" +
+            "ZaAAhmaW5pc2hlZEkABmhlaWdodEkADWluaXRpYWxIZWlnaHRaAAtpbml0aWFsaXplZEkACW5leHRJbmRleEwACHRhaWxOb2RlcQB+" +
+            "AAt4cAEAAAAAAAAAAAAAAAAAc3EAfgAQAAAAAHVxAH4AEwAAACBIFJAzhXYHQfeDbwNePGtSxwbQECJRTd1ut5zN8RA3yXNxAH4ANQ" +
+            "EAAAABAAAAAQAAAAAAc3EAfgAQAAAAAXVxAH4AEwAAACCugtHVqJDME59RRNQ0b2Podg5KdFxCIEOqJbBvwDzxCXh4");
+
+    private static byte[] testPublicKey = Base64.decode(
+        "MHIwJAYKKwYBBAGBsBoCAzAWAgEAAgEEAgECMAsGCWCGSAFlAwQCCwNKADBHAgEABCDIZh5Q96JIc0h+AmYHd3UP1ldE5buCIeHXsN" +
+            "xBgGEtbAQgxENVtn9cR2bPbe3IZcmy6JmI6fvHt5yMkJ1lgQZFw6A=");
+
+    public void setUp()
+    {
+        if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null)
+        {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+    }
+
+    public void testPrivateKeyRecovery()
+        throws Exception
+    {
+        KeyFactory kFact = KeyFactory.getInstance("XMSSMT", "BCPQC");
+
+        XMSSMTKey privKey = (XMSSMTKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(testPrivKey));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(privKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        XMSSMTKey privKey2 = (XMSSMTKey)oIn.readObject();
+
+        assertEquals(privKey, privKey2);
+    }
+
+    public void testPublicKeyRecovery()
+        throws Exception
+    {
+        KeyFactory kFact = KeyFactory.getInstance("XMSSMT", "BCPQC");
+
+        XMSSMTKey pubKey = (XMSSMTKey)kFact.generatePublic(new X509EncodedKeySpec(testPublicKey));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(pubKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        XMSSMTKey pubKey2 = (XMSSMTKey)oIn.readObject();
+
+        assertEquals(pubKey, pubKey2);
+    }
+
+    public void testKeyExtraction()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSSMT-SHA256", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        assertTrue(xmssSig.isSigningCapable());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        PrivateKey nKey = xmssSig.getUpdatedPrivateKey();
+
+        assertFalse(kp.getPrivate().equals(nKey));
+        assertFalse(xmssSig.isSigningCapable());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        try
+        {
+            sig.sign();
+            fail("no exception after key extraction");
+        }
+        catch (SignatureException e)
+        {
+            assertEquals("signing key no longer usable", e.getMessage());
+        }
+
+        try
+        {
+            xmssSig.getUpdatedPrivateKey();
+            fail("no exception after key extraction");
+        }
+        catch (IllegalStateException e)
+        {
+            assertEquals("signature object not in a signing state", e.getMessage());
+        }
+
+        xmssSig.initSign(nKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSMTSha256SignatureMultiple()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        StateAwareSignature sig1 = (StateAwareSignature)Signature.getInstance("SHA256withXMSSMT-SHA256", "BCPQC");
+
+        StateAwareSignature sig2 = (StateAwareSignature)Signature.getInstance("SHA256withXMSSMT-SHA256", "BCPQC");
+
+        StateAwareSignature sig3 = (StateAwareSignature)Signature.getInstance("SHA256withXMSSMT-SHA256", "BCPQC");
+
+        sig1.initSign(kp.getPrivate());
+
+        sig2.initSign(sig1.getUpdatedPrivateKey());
+
+        sig3.initSign(sig2.getUpdatedPrivateKey());
+
+        sig1.update(msg, 0, msg.length);
+
+        byte[] s1 = sig1.sign();
+
+        sig2.update(msg, 0, msg.length);
+
+        byte[] s2 = sig2.sign();
+
+        sig3.update(msg, 0, msg.length);
+
+        byte[] s3 = sig3.sign();
+
+        sig1.initVerify(kp.getPublic());
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s1));
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s2));
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s3));
+    }
+
+    public void testXMSSMTSha512KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA512), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSSMT", "BCPQC");
+
+        XMSSMTKey privKey = (XMSSMTKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        XMSSMTKey pubKey = (XMSSMTKey)keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(20, privKey.getHeight());
+        assertEquals(10, privKey.getLayers());
+        assertEquals(XMSSParameterSpec.SHA512, privKey.getTreeDigest());
+
+        assertEquals(20, pubKey.getHeight());
+        assertEquals(10, pubKey.getLayers());
+        assertEquals(XMSSParameterSpec.SHA512, pubKey.getTreeDigest());
+    }
+
+    public void testXMSSMTSha256Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(10, 5, XMSSMTParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSSMT", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSMTSha512Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(10, 5, XMSSMTParameterSpec.SHA512), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSSMT", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSMTShake128Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(4, 2, XMSSMTParameterSpec.SHAKE128), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHAKE128withXMSSMT-SHAKE128", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSMTShake256Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(4, 2, XMSSMTParameterSpec.SHAKE256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHAKE256withXMSSMT-SHAKE256", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testKeyRebuild()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(6, 3, XMSSMTParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSSMT", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        for (int i = 0; i != 5; i++)
+        {
+            xmssSig.update(msg, 0, msg.length);
+
+            xmssSig.sign();
+        }
+
+        PrivateKey pKey = xmssSig.getUpdatedPrivateKey();
+
+        PrivateKeyInfo pKeyInfo = PrivateKeyInfo.getInstance(pKey.getEncoded());
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSSMT", "BCPQC");
+
+        ASN1Sequence seq = ASN1Sequence.getInstance(pKeyInfo.parsePrivateKey());
+
+        // create a new PrivateKeyInfo containing a key with no BDS state.
+        pKeyInfo = new PrivateKeyInfo(pKeyInfo.getPrivateKeyAlgorithm(),
+            new DERSequence(new ASN1Encodable[] { seq.getObjectAt(0), seq.getObjectAt(1) }));
+
+        XMSSMTKey privKey = (XMSSMTKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pKeyInfo.getEncoded()));
+
+        xmssSig.initSign(pKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] sig1 = xmssSig.sign();
+
+        xmssSig.initSign((PrivateKey)privKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] sig2 = xmssSig.sign();
+
+        // make sure we get the same signature as the two keys should now
+        // be in the same state.
+        assertTrue(Arrays.areEqual(sig1, sig2));
+    }
+
+    public void testXMSSMTSha256KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(10, 2, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSSMT", "BCPQC");
+
+        XMSSMTKey privKey = (XMSSMTKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(10, privKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHA256, privKey.getTreeDigest());
+
+        testSig("SHA256withXMSSMT", pubKey, (PrivateKey)privKey);
+    }
+
+    private void testSig(String algorithm, PublicKey pubKey, PrivateKey privKey)
+        throws Exception
+    {
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        Signature s1 = Signature.getInstance(algorithm, "BCPQC");
+        Signature s2 = Signature.getInstance(algorithm, "BCPQC");
+
+        s1.initSign(privKey);
+
+        for (int i = 0; i != 100; i++)
+        {
+            s1.update(message, 0, message.length);
+
+            byte[] sig = s1.sign();
+
+            s2.initVerify(pubKey);
+
+            s2.update(message, 0, message.length);
+
+            assertTrue(s2.verify(sig));
+        }
+    }
+
+    public void testPrehashWithWithout()
+        throws Exception
+    {
+        testPrehashAndWithoutPrehash("XMSSMT-SHA256", "SHA256", new SHA256Digest());
+        testPrehashAndWithoutPrehash("XMSSMT-SHAKE128", "SHAKE128", new SHAKEDigest(128));
+        testPrehashAndWithoutPrehash("XMSSMT-SHA512", "SHA512", new SHA512Digest());
+        testPrehashAndWithoutPrehash("XMSSMT-SHAKE256", "SHAKE256", new SHAKEDigest(256));
+
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_mt_SHA256ph, BCObjectIdentifiers.xmss_mt_SHA256, "SHA256", new SHA256Digest());
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_mt_SHAKE128ph, BCObjectIdentifiers.xmss_mt_SHAKE128, "SHAKE128", new SHAKEDigest(128));
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_mt_SHA512ph, BCObjectIdentifiers.xmss_mt_SHA512, "SHA512", new SHA512Digest());
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_mt_SHAKE256ph, BCObjectIdentifiers.xmss_mt_SHAKE256, "SHAKE256", new SHAKEDigest(256));
+    }
+
+    public void testExhaustion()
+        throws Exception
+    {
+        StateAwareSignature s1 = (StateAwareSignature)Signature.getInstance(BCObjectIdentifiers.xmss_mt_SHA256.getId(), "BCPQC");
+        Signature s2 = Signature.getInstance(BCObjectIdentifiers.xmss_mt_SHA256.getId(), "BCPQC");
+
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(4, 2,"SHA256"), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        XMSSMTPrivateKey privKey = (XMSSMTPrivateKey)kp.getPrivate();
+
+        assertEquals(16, privKey.getUsagesRemaining());
+
+        s1.initSign(privKey);
+
+        do
+        {
+            s1.update(message, 0, message.length);
+
+            byte[] sig = s1.sign();
+
+            s2.initVerify(kp.getPublic());
+
+            s2.update(message, 0, message.length);
+
+            assertTrue(s2.verify(sig));
+
+            privKey = (XMSSMTPrivateKey)s1.getUpdatedPrivateKey();
+
+            s1.initSign(privKey);
+        }
+        while (s1.isSigningCapable());
+
+        assertEquals(0, privKey.getUsagesRemaining());
+    }
+
+    private void testPrehashAndWithoutPrehash(String baseAlgorithm, String digestName, Digest digest)
+        throws Exception
+    {
+        Signature s1 = Signature.getInstance(digestName + "with" + baseAlgorithm, "BCPQC");
+        Signature s2 = Signature.getInstance(baseAlgorithm, "BCPQC");
+
+        doTestPrehashAndWithoutPrehash(digestName, digest, s1, s2);
+    }
+
+    private void testPrehashAndWithoutPrehash(ASN1ObjectIdentifier oid1, ASN1ObjectIdentifier oid2, String digestName, Digest digest)
+        throws Exception
+    {
+        Signature s1 = Signature.getInstance(oid1.getId(), "BCPQC");
+        Signature s2 = Signature.getInstance(oid2.getId(), "BCPQC");
+
+        doTestPrehashAndWithoutPrehash(digestName, digest, s1, s2);
+    }
+
+    private void doTestPrehashAndWithoutPrehash(String digestName, Digest digest, Signature s1, Signature s2)
+        throws Exception
+    {
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(4, 2, digestName), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        s1.initSign(kp.getPrivate());
+
+        s1.update(message, 0, message.length);
+
+        byte[] sig = s1.sign();
+
+        s2.initVerify(kp.getPublic());
+
+        digest.update(message, 0, message.length);
+
+        byte[] dig = new byte[(digest instanceof Xof) ? digest.getDigestSize() * 2 : digest.getDigestSize()];
+
+        if (digest instanceof Xof)
+        {
+            ((Xof)digest).doFinal(dig, 0, dig.length);
+        }
+        else
+        {
+            digest.doFinal(dig, 0);
+        }
+        s2.update(dig);
+
+        assertTrue(s2.verify(sig));
+    }
+
+    public void testReserialization()
+        throws Exception
+    {
+        String digest = "SHA512";
+        String sigAlg = digest+"withXMSSMT";
+        byte[] payload = Strings.toByteArray("Hello, world!");
+        
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+        kpg.initialize(new XMSSMTParameterSpec(4, 2, digest));
+        KeyPair keyPair = kpg.generateKeyPair();
+
+        PrivateKey privateKey = keyPair.getPrivate();
+        PublicKey publicKey = keyPair.getPublic();
+
+        for (int i = 0; i != 10; i++)
+        {
+            StateAwareSignature signer = (StateAwareSignature)Signature.getInstance(sigAlg, "BCPQC");
+            signer.initSign(privateKey);
+            signer.update(payload);
+
+            byte[] signature = signer.sign();
+
+            // serialise private key
+            byte[] enc = signer.getUpdatedPrivateKey().getEncoded();
+            privateKey = KeyFactory.getInstance("XMSSMT").generatePrivate(new PKCS8EncodedKeySpec(enc));
+
+            Signature verifier = Signature.getInstance(sigAlg, "BCPQC");
+            verifier.initVerify(publicKey);
+            verifier.update(payload);
+            assertTrue(verifier.verify(signature));
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSTest.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSTest.java
new file mode 100644
index 0000000..8c7bffb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/test/XMSSTest.java
@@ -0,0 +1,678 @@
+package org.bouncycastle.pqc.jcajce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import junit.framework.TestCase;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.jcajce.interfaces.StateAwareSignature;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSKey;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSPrivateKey;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * Test cases for the use of XMSS with the BCPQC provider.
+ */
+public class XMSSTest
+    extends TestCase
+{
+    private static byte[] msg = Strings.toByteArray("Cthulhu Fthagn --What a wonderful phrase!Cthulhu Fthagn --Say it and you're crazed!");
+
+    private static byte[] testPrivKey = Base64.decode(
+        "MIIJUQIBADAhBgorBgEEAYGwGgICMBMCAQACAQowCwYJYIZIAWUDBAIBBIIJJzCCCSMCAQAwgYsCAQAEIJz4Lh9eEhuxG4dgjfRXOw" +
+            "K7Um5YmC6Xf4lkXvtPgsdnBCDNR477ikIt1sOIr3+ElyurEY2gvVYydvk+LZm+OY/pagQgwCnFSoMAerORUDoJHb9tXqrCzIp52yYz" +
+            "gr3TOIKhzcAEIOCommSN0UszkpJUMLzJxe856LQbH7hl73xPpFnCwVJtoIIIjgSCCIqs7QAFc3IAJG9yZy5ib3VuY3ljYXN0bGUucH" +
+            "FjLmNyeXB0by54bXNzLkJEUwAAAAAAAAABAgAKSQAFaW5kZXhJAAFrSQAKdHJlZUhlaWdodFoABHVzZWRMABJhdXRoZW50aWNhdGlv" +
+            "blBhdGh0ABBMamF2YS91dGlsL0xpc3Q7TAAEa2VlcHQAD0xqYXZhL3V0aWwvTWFwO0wABnJldGFpbnEAfgACTAAEcm9vdHQAK0xvcm" +
+            "cvYm91bmN5Y2FzdGxlL3BxYy9jcnlwdG8veG1zcy9YTVNTTm9kZTtMAAVzdGFja3QAEUxqYXZhL3V0aWwvU3RhY2s7TAARdHJlZUhh" +
+            "c2hJbnN0YW5jZXNxAH4AAXhwAAAAAAAAAAIAAAAKAHNyABNqYXZhLnV0aWwuQXJyYXlMaXN0eIHSHZnHYZ0DAAFJAARzaXpleHAAAA" +
+            "AKdwQAAAAKc3IAKW9yZy5ib3VuY3ljYXN0bGUucHFjLmNyeXB0by54bXNzLlhNU1NOb2RlAAAAAAAAAAECAAJJAAZoZWlnaHRbAAV2" +
+            "YWx1ZXQAAltCeHAAAAAAdXIAAltCrPMX+AYIVOACAAB4cAAAACAGQv71vuxiZWXV4/Ju/9iKZCWJJH/tGib2csoUJOc8eHNxAH4ACA" +
+            "AAAAF1cQB+AAsAAAAgsqaSHpyaapwnlBv57C8sKLYUAp3Oe8jY2EZ8hSA7VQVzcQB+AAgAAAACdXEAfgALAAAAIL5Eb9aOASc8bJNt" +
+            "AwbO7pmTD7rMl74XiufBHOqgjXR+c3EAfgAIAAAAA3VxAH4ACwAAACDDX+WyjGU4eUb5OvHYbjVsjUAPHSSGRCfhC8BmTMD8gXNxAH" +
+            "4ACAAAAAR1cQB+AAsAAAAgxdz9x1wcJzZuwWSubFsFwD6IICfG+nj2kRbZtGP0LvlzcQB+AAgAAAAFdXEAfgALAAAAINrZJ2N7sn7i" +
+            "mddC8uuL3kwvsem8S/HLNVvFdu7mDjUVc3EAfgAIAAAABnVxAH4ACwAAACAnH0jqcIwZ43zMTbOz5l/SPBYA8I2G3ThJxyK3+CFqX3" +
+            "NxAH4ACAAAAAd1cQB+AAsAAAAgUesW9Krrb+DRkRfvw1GedWY2mkicW9gWysuxdpcwQpJzcQB+AAgAAAAIdXEAfgALAAAAILTstGe7" +
+            "7ZTz+Tu9hXo6W6Ceek8iqoMWR2LnlB4MlHDNc3EAfgAIAAAACXVxAH4ACwAAACBcak0jZQNXH/RqUaXXchab6lVlt0tFPwjDyjA6zj" +
+            "yigHhzcgARamF2YS51dGlsLlRyZWVNYXAMwfY+LSVq5gMAAUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHBw" +
+            "dwQAAAAAeHNxAH4AH3B3BAAAAAFzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW" +
+            "1iZXKGrJUdC5TgiwIAAHhwAAAACHNyABRqYXZhLnV0aWwuTGlua2VkTGlzdAwpU11KYIgiAwAAeHB3BAAAAAFzcQB+AAgAAAAIdXEA" +
+            "fgALAAAAINAd+MxJvrqmIxJYJvpW7TJZBtAw8xVVrWffg0v/FqNgeHhzcQB+AAgAAAAKdXEAfgALAAAAIOCommSN0UszkpJUMLzJxe" +
+            "856LQbH7hl73xPpFnCwVJtc3IAD2phdmEudXRpbC5TdGFjaxD+KsK7CYYdAgAAeHIAEGphdmEudXRpbC5WZWN0b3LZl31bgDuvAQMA" +
+            "A0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwALZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAA" +
+            "AAAAAAAHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAApwcHBwcHBwcHBweHNxAH4ABgAAAAh3BAAAAAhzcgAs" +
+            "b3JnLmJvdW5jeWNhc3RsZS5wcWMuY3J5cHRvLnhtc3MuQkRTVHJlZUhhc2gAAAAAAAAAAQIABloACGZpbmlzaGVkSQAGaGVpZ2h0SQ" +
+            "ANaW5pdGlhbEhlaWdodFoAC2luaXRpYWxpemVkSQAJbmV4dEluZGV4TAAIdGFpbE5vZGVxAH4AA3hwAQAAAAAAAAAAAAAAAABzcQB+" +
+            "AAgAAAAAdXEAfgALAAAAIJlIeq2/6feYEOIoFJ14wZsogn4eAI7kNj3Y4NZtAGY0c3EAfgAzAQAAAAEAAAABAAAAAABzcQB+AAgAAA" +
+            "ABdXEAfgALAAAAIO5nPo5M/pLgkDLgzkCTUy+VjaPEo3cgMm5Mrg11jKXoc3EAfgAzAQAAAAIAAAACAAAAAABzcQB+AAgAAAACdXEA" +
+            "fgALAAAAIKGPe8aqDKAN6p7i5wpnVgBr+wigNp8CRKtJI1FjDgnLc3EAfgAzAQAAAAMAAAADAAAAAABzcQB+AAgAAAADdXEAfgALAA" +
+            "AAIEFdO93VUT6Q6tt4ZJaVf+Uh3BJ7ez9megbGCGEvjD1Sc3EAfgAzAQAAAAQAAAAEAAAAAABzcQB+AAgAAAAEdXEAfgALAAAAIGkP" +
+            "gbAYQss69U6Ak7S2yciX1cnj+9C3KjFh5j5pILQoc3EAfgAzAQAAAAUAAAAFAAAAAABzcQB+AAgAAAAFdXEAfgALAAAAICZR+aZttx" +
+            "PqjHYIQlaFac2mK5WiEiSy8Je+XmItQ6Xac3EAfgAzAQAAAAYAAAAGAAAAAABzcQB+AAgAAAAGdXEAfgALAAAAINoMTeI/1jvR+IIh" +
+            "yA+vQ0xR9/8utcwXpV+hT/qkVNtCc3EAfgAzAQAAAAcAAAAHAAAAAABzcQB+AAgAAAAHdXEAfgALAAAAIFmxQ2yQ05Na9oL4WA2Qhp" +
+            "qICwl81rpce4LFUAtTdj95eA==");
+
+    private static final byte[] testPublicKey = Base64.decode(
+        "MIGxMCEGCisGAQQBgbAaAgIwEwIBAAIBCjALBglghkgBZQMEAgMDgYsAMIGHAgEABEDcKHL+5XfQ9jTGJptcqN71MmzT1qe/s42wwR" +
+            "6TkILd1jH6e5vP9Iwp+hANEWJdbxYX4gyyQQpudfOQ6+7xLJNaBEAmGsvLXJAJXu5NTICpC5LpKrWWxrz6tKRiLP10EBbxtLwM3wCW" +
+            "6+d4CehmSP7B0ffx6AzJtD6l6T+lxyO0EMXG");
+
+    public void setUp()
+    {
+        if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null)
+        {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+    }
+
+    public void testPrivateKeyRecovery()
+        throws Exception
+    {
+        KeyFactory kFact = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey privKey = (XMSSKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(testPrivKey));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(privKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        XMSSKey privKey2 = (XMSSKey)oIn.readObject();
+
+        assertEquals(privKey, privKey2);
+    }
+
+    public void testPublicKeyRecovery()
+        throws Exception
+    {
+        KeyFactory kFact = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey pubKey = (XMSSKey)kFact.generatePublic(new X509EncodedKeySpec(testPublicKey));
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(pubKey);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        XMSSKey pubKey2 = (XMSSKey)oIn.readObject();
+
+        assertEquals(pubKey, pubKey2);
+    }
+
+    public void testXMSSSha256Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSSha512Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA512), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA512withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSShake128Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(5, XMSSParameterSpec.SHAKE128), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHAKE128withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSShake256Signature()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(5, XMSSParameterSpec.SHAKE256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHAKE256withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testXMSSSha256SignatureMultiple()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        StateAwareSignature sig1 = (StateAwareSignature)Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        StateAwareSignature sig2 = (StateAwareSignature)Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        StateAwareSignature sig3 = (StateAwareSignature)Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        sig1.initSign(kp.getPrivate());
+
+        sig2.initSign(sig1.getUpdatedPrivateKey());
+
+        sig3.initSign(sig2.getUpdatedPrivateKey());
+
+        sig1.update(msg, 0, msg.length);
+
+        byte[] s1 = sig1.sign();
+
+        sig2.update(msg, 0, msg.length);
+
+        byte[] s2 = sig2.sign();
+
+        sig3.update(msg, 0, msg.length);
+
+        byte[] s3 = sig3.sign();
+
+        sig1.initVerify(kp.getPublic());
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s1));
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s2));
+
+        sig1.update(msg, 0, msg.length);
+
+        assertTrue(sig1.verify(s3));
+    }
+
+    public void testXMSSSha256KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey privKey = (XMSSKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(10, privKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHA256, privKey.getTreeDigest());
+
+        testSig("SHA256withXMSS", pubKey, (PrivateKey)privKey);
+    }
+
+    public void testXMSSSha512KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA512), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey privKey = (XMSSKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        XMSSKey pubKey = (XMSSKey)keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(10, privKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHA512, privKey.getTreeDigest());
+
+        assertEquals(10, pubKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHA512, pubKey.getTreeDigest());
+    }
+
+    public void testXMSSShake128KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHAKE128), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey privKey = (XMSSKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        XMSSKey pubKey = (XMSSKey)keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(10, privKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHAKE128, privKey.getTreeDigest());
+
+        assertEquals(10, pubKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHAKE128, pubKey.getTreeDigest());
+    }
+
+    public void testXMSSShake256KeyFactory()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHAKE256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        XMSSKey privKey = (XMSSKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()));
+
+        assertEquals(kp.getPrivate(), privKey);
+
+        XMSSKey pubKey = (XMSSKey)keyFactory.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded()));
+
+        assertEquals(kp.getPublic(), pubKey);
+
+        assertEquals(10, privKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHAKE256, privKey.getTreeDigest());
+
+        assertEquals(10, pubKey.getHeight());
+        assertEquals(XMSSParameterSpec.SHAKE256, pubKey.getTreeDigest());
+    }
+
+    private void testSig(String algorithm, PublicKey pubKey, PrivateKey privKey)
+        throws Exception
+    {
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        Signature s = Signature.getInstance(algorithm, "BCPQC");
+
+        s.initSign(privKey);
+
+        s.update(message, 0, message.length);
+
+        byte[] sig = s.sign();
+
+        s.initVerify(pubKey);
+
+        s.update(message, 0, message.length);
+
+        assertTrue(s.verify(sig));
+    }
+
+    public void testKeyExtraction()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        assertTrue(xmssSig.isSigningCapable());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] s = xmssSig.sign();
+
+        PrivateKey nKey = xmssSig.getUpdatedPrivateKey();
+
+        assertFalse(kp.getPrivate().equals(nKey));
+        assertFalse(xmssSig.isSigningCapable());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        try
+        {
+            xmssSig.sign();
+            fail("no exception after key extraction");
+        }
+        catch (SignatureException e)
+        {
+            assertEquals("signing key no longer usable", e.getMessage());
+        }
+
+        try
+        {
+            xmssSig.getUpdatedPrivateKey();
+            fail("no exception after key extraction");
+        }
+        catch (IllegalStateException e)
+        {
+            assertEquals("signature object not in a signing state", e.getMessage());
+        }
+
+        xmssSig.initSign(nKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        s = sig.sign();
+
+        xmssSig.initVerify(kp.getPublic());
+
+        xmssSig.update(msg, 0, msg.length);
+
+        assertTrue(xmssSig.verify(s));
+    }
+
+    public void testKeyRebuild()
+        throws Exception
+    {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance("SHA256withXMSS", "BCPQC");
+
+        assertTrue(sig instanceof StateAwareSignature);
+
+        StateAwareSignature xmssSig = (StateAwareSignature)sig;
+
+        xmssSig.initSign(kp.getPrivate());
+
+        for (int i = 0; i != 5; i++)
+        {
+            xmssSig.update(msg, 0, msg.length);
+
+            xmssSig.sign();
+        }
+
+        PrivateKey pKey = xmssSig.getUpdatedPrivateKey();
+
+        PrivateKeyInfo pKeyInfo = PrivateKeyInfo.getInstance(pKey.getEncoded());
+
+        KeyFactory keyFactory = KeyFactory.getInstance("XMSS", "BCPQC");
+
+        ASN1Sequence seq = ASN1Sequence.getInstance(pKeyInfo.parsePrivateKey());
+
+        // create a new PrivateKeyInfo containing a key with no BDS state.
+        pKeyInfo = new PrivateKeyInfo(pKeyInfo.getPrivateKeyAlgorithm(),
+            new DERSequence(new ASN1Encodable[]{seq.getObjectAt(0), seq.getObjectAt(1)}));
+
+        XMSSKey privKey = (XMSSKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pKeyInfo.getEncoded()));
+
+        xmssSig.initSign(pKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] sig1 = xmssSig.sign();
+
+        xmssSig.initSign((PrivateKey)privKey);
+
+        xmssSig.update(msg, 0, msg.length);
+
+        byte[] sig2 = xmssSig.sign();
+
+        // make sure we get the same signature as the two keys should now
+        // be in the same state.
+        assertTrue(Arrays.areEqual(sig1, sig2));
+    }
+
+    public void testPrehashWithWithout()
+        throws Exception
+    {
+        testPrehashAndWithoutPrehash("XMSS-SHA256", "SHA256", new SHA256Digest());
+        testPrehashAndWithoutPrehash("XMSS-SHAKE128", "SHAKE128", new SHAKEDigest(128));
+        testPrehashAndWithoutPrehash("XMSS-SHA512", "SHA512", new SHA512Digest());
+        testPrehashAndWithoutPrehash("XMSS-SHAKE256", "SHAKE256", new SHAKEDigest(256));
+
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_SHA256ph, BCObjectIdentifiers.xmss_SHA256, "SHA256", new SHA256Digest());
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_SHAKE128ph, BCObjectIdentifiers.xmss_SHAKE128, "SHAKE128", new SHAKEDigest(128));
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_SHA512ph, BCObjectIdentifiers.xmss_SHA512, "SHA512", new SHA512Digest());
+        testPrehashAndWithoutPrehash(BCObjectIdentifiers.xmss_SHAKE256ph, BCObjectIdentifiers.xmss_SHAKE256, "SHAKE256", new SHAKEDigest(256));
+    }
+
+    public void testExhaustion()
+        throws Exception
+    {
+        StateAwareSignature s1 = (StateAwareSignature)Signature.getInstance(BCObjectIdentifiers.xmss_SHA256.getId(), "BCPQC");
+        Signature s2 = Signature.getInstance(BCObjectIdentifiers.xmss_SHA256.getId(), "BCPQC");
+
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(2, "SHA256"), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        XMSSPrivateKey privKey = (XMSSPrivateKey)kp.getPrivate();
+
+        assertEquals(4, privKey.getUsagesRemaining());
+
+        s1.initSign(privKey);
+        
+        do
+        {
+            s1.update(message, 0, message.length);
+
+            byte[] sig = s1.sign();
+
+            s2.initVerify(kp.getPublic());
+
+            s2.update(message, 0, message.length);
+
+            assertTrue(s2.verify(sig));
+
+            privKey = (XMSSPrivateKey)s1.getUpdatedPrivateKey();
+
+            s1.initSign(privKey);
+        }
+        while (s1.isSigningCapable());
+
+        assertEquals(0, privKey.getUsagesRemaining());
+    }
+
+    private void testPrehashAndWithoutPrehash(String baseAlgorithm, String digestName, Digest digest)
+        throws Exception
+    {
+        Signature s1 = Signature.getInstance(digestName + "with" + baseAlgorithm, "BCPQC");
+        Signature s2 = Signature.getInstance(baseAlgorithm, "BCPQC");
+
+        doTestPrehashAndWithoutPrehash(digestName, digest, s1, s2);
+    }
+
+    private void testPrehashAndWithoutPrehash(ASN1ObjectIdentifier oid1, ASN1ObjectIdentifier oid2, String digestName, Digest digest)
+        throws Exception
+    {
+        Signature s1 = Signature.getInstance(oid1.getId(), "BCPQC");
+        Signature s2 = Signature.getInstance(oid2.getId(), "BCPQC");
+
+        doTestPrehashAndWithoutPrehash(digestName, digest, s1, s2);
+    }
+
+    private void doTestPrehashAndWithoutPrehash(String digestName, Digest digest, Signature s1, Signature s2)
+        throws Exception
+    {
+        byte[] message = Strings.toByteArray("hello, world!");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(2, digestName), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        s1.initSign(kp.getPrivate());
+
+        s1.update(message, 0, message.length);
+
+        byte[] sig = s1.sign();
+
+        s2.initVerify(kp.getPublic());
+
+        digest.update(message, 0, message.length);
+
+        byte[] dig = new byte[(digest instanceof Xof) ? digest.getDigestSize() * 2 : digest.getDigestSize()];
+
+        if (digest instanceof Xof)
+        {
+            ((Xof)digest).doFinal(dig, 0, dig.length);
+        }
+        else
+        {
+            digest.doFinal(dig, 0);
+        }
+        s2.update(dig);
+
+        assertTrue(s2.verify(sig));
+    }
+
+    public void testReserialization()
+        throws Exception
+    {
+        String digest = "SHA512";
+        String sigAlg = digest + "withXMSS";
+        byte[] payload = Strings.toByteArray("Hello, world!");
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+        kpg.initialize(new XMSSParameterSpec(4, digest));
+        KeyPair keyPair = kpg.generateKeyPair();
+
+        PrivateKey privateKey = keyPair.getPrivate();
+        PublicKey publicKey = keyPair.getPublic();
+
+        for (int i = 0; i != 10; i++)
+        {
+            StateAwareSignature signer = (StateAwareSignature)Signature.getInstance(sigAlg, "BCPQC");
+            signer.initSign(privateKey);
+            signer.update(payload);
+
+            byte[] signature = signer.sign();
+
+            // serialise private key
+            byte[] enc = signer.getUpdatedPrivateKey().getEncoded();
+            privateKey = KeyFactory.getInstance("XMSS").generatePrivate(new PKCS8EncodedKeySpec(enc));
+            Signature verifier = Signature.getInstance(sigAlg, "BCPQC");
+            verifier.initVerify(publicKey);
+            verifier.update(payload);
+            assertTrue(verifier.verify(signature));
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java
index 7eb2ac1..2995412 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java
@@ -12,6 +12,8 @@
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.ShortBufferException;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+
 
 /**
  * The AsymmetricBlockCipher class extends CipherSpiExt.
@@ -102,7 +104,7 @@
             return 0;
         }
 
-        return maxLen;
+        return opMode == ENCRYPT_MODE ? cipherTextSize : maxPlainTextSize;
     }
 
     /**
@@ -145,7 +147,7 @@
     {
         try
         {
-            initEncrypt(key, null, new SecureRandom());
+            initEncrypt(key, null, CryptoServicesRegistrar.getSecureRandom());
         }
         catch (InvalidAlgorithmParameterException e)
         {
@@ -202,7 +204,7 @@
     public final void initEncrypt(Key key, AlgorithmParameterSpec params)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
-        initEncrypt(key, params, new SecureRandom());
+        initEncrypt(key, params, CryptoServicesRegistrar.getSecureRandom());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java
index 84a5537..d8153c4 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java
@@ -10,6 +10,8 @@
 import javax.crypto.BadPaddingException;
 import javax.crypto.ShortBufferException;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+
 /**
  * The AsymmetricHybridCipher class extends CipherSpiExt.
  * NOTE: Some Ciphers are using Padding. OneAndZeroesPadding is used as default
@@ -122,7 +124,7 @@
     {
         try
         {
-            initEncrypt(key, null, new SecureRandom());
+            initEncrypt(key, null, CryptoServicesRegistrar.getSecureRandom());
         }
         catch (InvalidAlgorithmParameterException e)
         {
@@ -179,7 +181,7 @@
     public final void initEncrypt(Key key, AlgorithmParameterSpec params)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
-        initEncrypt(key, params, new SecureRandom());
+        initEncrypt(key, params, CryptoServicesRegistrar.getSecureRandom());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/CipherSpiExt.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/CipherSpiExt.java
index 9154212..05f37d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/CipherSpiExt.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/CipherSpiExt.java
@@ -53,7 +53,6 @@
      * </p><p>
      * Note: If the mode needs an initialization vector, a blank array is used
      * in this case.
-     * </p>
      * @param opMode the operation mode ({@link #ENCRYPT_MODE} or
      *               {@link #DECRYPT_MODE})
      * @param key    the key
@@ -379,7 +378,7 @@
     /**
      * Initialize this cipher with a key, a set of algorithm parameters, and a
      * source of randomness for encryption.
-     * </p><p>
+     * <p>
      * If this cipher requires any algorithm parameters and paramSpec is null,
      * the underlying cipher implementation is supposed to generate the required
      * parameters itself (using provider-specific default or random values) if
@@ -411,7 +410,7 @@
     /**
      * Initialize this cipher with a key, a set of algorithm parameters, and a
      * source of randomness for decryption.
-     * </p><p>
+     * <p>
      * If this cipher requires any algorithm parameters and paramSpec is null,
      * the underlying cipher implementation is supposed to generate the required
      * parameters itself (using provider-specific default or random values) if
@@ -455,7 +454,7 @@
      * Returns the length in bytes that an output buffer would need to be in
      * order to hold the result of the next update or doFinal operation, given
      * the input length inputLen (in bytes).
-     * </p><p>
+     * <p>
      * This call takes into account any unprocessed (buffered) data from a
      * previous update call, and padding.
      * </p><p>
@@ -479,7 +478,7 @@
 
     /**
      * Returns the parameters used with this cipher.
-     * </p><p>
+     * <p>
      * The returned parameters may be the same that were used to initialize this
      * cipher, or may contain the default set of parameters or a set of randomly
      * generated parameters used by the underlying cipher implementation
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPrivateKey.java
new file mode 100644
index 0000000..4bc1774
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPrivateKey.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PrivateKey;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSMTPrivateKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXMSSMTPrivateKey
+    implements PrivateKey, XMSSMTPrivateKey
+{
+    private static final long serialVersionUID = 7682140473044521395L;
+
+    private transient ASN1ObjectIdentifier treeDigest;
+    private transient XMSSMTPrivateKeyParameters keyParams;
+    private transient ASN1Set attributes;
+
+    public BCXMSSMTPrivateKey(
+        ASN1ObjectIdentifier treeDigest,
+        XMSSMTPrivateKeyParameters keyParams)
+    {
+        this.treeDigest = treeDigest;
+        this.keyParams = keyParams;
+    }
+
+    public BCXMSSMTPrivateKey(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.attributes = keyInfo.getAttributes();
+        XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+        this.treeDigest = keyParams.getTreeDigest().getAlgorithm();
+        this.keyParams = (XMSSMTPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo);
+    }
+
+    public long getUsagesRemaining()
+    {
+        return keyParams.getUsagesRemaining();
+    }
+
+    public String getAlgorithm()
+    {
+        return "XMSSMT";
+    }
+
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(keyParams, attributes);
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCXMSSMTPrivateKey)
+        {
+            BCXMSSMTPrivateKey otherKey = (BCXMSSMTPrivateKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(keyParams.toByteArray(), otherKey.keyParams.toByteArray());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return treeDigest.hashCode() + 37 * Arrays.hashCode(keyParams.toByteArray());
+    }
+
+    ASN1ObjectIdentifier getTreeDigestOID()
+    {
+        return treeDigest;
+    }
+
+    public int getHeight()
+    {
+        return keyParams.getParameters().getHeight();
+    }
+
+    public int getLayers()
+    {
+        return keyParams.getParameters().getLayers();
+    }
+
+    public String getTreeDigest()
+    {
+        return DigestUtil.getXMSSDigestName(treeDigest);
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPublicKey.java
new file mode 100644
index 0000000..7c943c1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSMTPublicKey.java
@@ -0,0 +1,134 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PublicKey;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
+import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
+import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSMTKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXMSSMTPublicKey
+    implements PublicKey, XMSSMTKey
+{
+    private static final long serialVersionUID = 3230324130542413475L;
+
+    private transient ASN1ObjectIdentifier treeDigest;
+    private transient XMSSMTPublicKeyParameters keyParams;
+
+    public BCXMSSMTPublicKey(ASN1ObjectIdentifier treeDigest, XMSSMTPublicKeyParameters keyParams)
+    {
+        this.treeDigest = treeDigest;
+        this.keyParams = keyParams;
+    }
+
+    public BCXMSSMTPublicKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());
+        this.treeDigest = keyParams.getTreeDigest().getAlgorithm();
+        this.keyParams = (XMSSMTPublicKeyParameters)PublicKeyFactory.createKey(keyInfo);
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCXMSSMTPublicKey)
+        {
+            BCXMSSMTPublicKey otherKey = (BCXMSSMTPublicKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(keyParams.toByteArray(), otherKey.keyParams.toByteArray());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return treeDigest.hashCode() + 37 * Arrays.hashCode(keyParams.toByteArray());
+    }
+
+    /**
+     * @return name of the algorithm - "XMSSMT"
+     */
+    public final String getAlgorithm()
+    {
+        return "XMSSMT";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParams);
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    public int getHeight()
+    {
+        return keyParams.getParameters().getHeight();
+    }
+
+    public int getLayers()
+    {
+        return keyParams.getParameters().getLayers();
+    }
+
+    public String getTreeDigest()
+    {
+        return DigestUtil.getXMSSDigestName(treeDigest);
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPrivateKey.java
new file mode 100644
index 0000000..651ce11
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPrivateKey.java
@@ -0,0 +1,141 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PrivateKey;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSPrivateKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXMSSPrivateKey
+    implements PrivateKey, XMSSPrivateKey
+{
+    private static final long serialVersionUID = 8568701712864512338L;
+
+    private transient XMSSPrivateKeyParameters keyParams;
+    private transient ASN1ObjectIdentifier treeDigest;
+    private transient ASN1Set attributes;
+
+    public BCXMSSPrivateKey(
+        ASN1ObjectIdentifier treeDigest,
+        XMSSPrivateKeyParameters keyParams)
+    {
+        this.treeDigest = treeDigest;
+        this.keyParams = keyParams;
+    }
+
+    public BCXMSSPrivateKey(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        this.attributes = keyInfo.getAttributes();
+        XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+        this.treeDigest = keyParams.getTreeDigest().getAlgorithm();
+        this.keyParams = (XMSSPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo);
+    }
+
+    public long getUsagesRemaining()
+    {
+        return keyParams.getUsagesRemaining();
+    }
+
+    public String getAlgorithm()
+    {
+        return "XMSS";
+    }
+
+    public String getFormat()
+    {
+        return "PKCS#8";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(keyParams, attributes);
+
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCXMSSPrivateKey)
+        {
+            BCXMSSPrivateKey otherKey = (BCXMSSPrivateKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(keyParams.toByteArray(), otherKey.keyParams.toByteArray());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return treeDigest.hashCode() + 37 * Arrays.hashCode(keyParams.toByteArray());
+    }
+
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    ASN1ObjectIdentifier getTreeDigestOID()
+    {
+        return treeDigest;
+    }
+
+    public int getHeight()
+    {
+        return keyParams.getParameters().getHeight();
+    }
+
+    public String getTreeDigest()
+    {
+        return DigestUtil.getXMSSDigestName(treeDigest);
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(PrivateKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPublicKey.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPublicKey.java
new file mode 100644
index 0000000..35e74c6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/BCXMSSPublicKey.java
@@ -0,0 +1,130 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.PublicKey;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.pqc.asn1.XMSSKeyParams;
+import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
+import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
+import org.bouncycastle.pqc.jcajce.interfaces.XMSSKey;
+import org.bouncycastle.util.Arrays;
+
+public class BCXMSSPublicKey
+    implements PublicKey, XMSSKey
+{
+    private static final long serialVersionUID = -5617456225328969766L;
+    
+    private transient XMSSPublicKeyParameters keyParams;
+    private transient ASN1ObjectIdentifier treeDigest;
+
+    public BCXMSSPublicKey(
+        ASN1ObjectIdentifier treeDigest,
+        XMSSPublicKeyParameters keyParams)
+    {
+        this.treeDigest = treeDigest;
+        this.keyParams = keyParams;
+    }
+
+    public BCXMSSPublicKey(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        init(keyInfo);
+    }
+
+    private void init(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());
+        this.treeDigest = keyParams.getTreeDigest().getAlgorithm();
+        this.keyParams = (XMSSPublicKeyParameters)PublicKeyFactory.createKey(keyInfo);
+    }
+
+    /**
+     * @return name of the algorithm - "XMSS"
+     */
+    public final String getAlgorithm()
+    {
+        return "XMSS";
+    }
+
+    public byte[] getEncoded()
+    {
+        try
+        {
+            SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParams);
+            return pki.getEncoded();
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    CipherParameters getKeyParams()
+    {
+        return keyParams;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof BCXMSSPublicKey)
+        {
+            BCXMSSPublicKey otherKey = (BCXMSSPublicKey)o;
+
+            return treeDigest.equals(otherKey.treeDigest) && Arrays.areEqual(keyParams.toByteArray(), otherKey.keyParams.toByteArray());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return treeDigest.hashCode() + 37 * Arrays.hashCode(keyParams.toByteArray());
+    }
+
+    public int getHeight()
+    {
+        return keyParams.getParameters().getHeight();
+    }
+
+    public String getTreeDigest()
+    {
+        return DigestUtil.getXMSSDigestName(treeDigest);
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        byte[] enc = (byte[])in.readObject();
+
+        init(SubjectPublicKeyInfo.getInstance(enc));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/DigestUtil.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/DigestUtil.java
new file mode 100644
index 0000000..95beb30
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/DigestUtil.java
@@ -0,0 +1,83 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Xof;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+
+class DigestUtil
+{
+    static Digest getDigest(ASN1ObjectIdentifier oid)
+    {
+        if (oid.equals(NISTObjectIdentifiers.id_sha256))
+        {
+            return new SHA256Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_sha512))
+        {
+            return new SHA512Digest();
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake128))
+        {
+            return new SHAKEDigest(128);
+        }
+        if (oid.equals(NISTObjectIdentifiers.id_shake256))
+        {
+            return new SHAKEDigest(256);
+        }
+
+        throw new IllegalArgumentException("unrecognized digest OID: " + oid);
+    }
+
+    public static byte[] getDigestResult(Digest digest)
+    {
+        byte[] hash = new byte[DigestUtil.getDigestSize(digest)];
+
+        if (digest instanceof Xof)
+        {
+            ((Xof)digest).doFinal(hash, 0, hash.length);
+        }
+        else
+        {
+            digest.doFinal(hash, 0);
+        }
+
+        return hash;
+    }
+
+    public static int getDigestSize(Digest digest)
+    {
+        if (digest instanceof Xof)
+        {
+            return digest.getDigestSize() * 2;
+        }
+
+        return digest.getDigestSize();
+    }
+
+    public static String getXMSSDigestName(ASN1ObjectIdentifier treeDigest)
+    {
+        if (treeDigest.equals(NISTObjectIdentifiers.id_sha256))
+        {
+            return XMSSParameterSpec.SHA256;
+        }
+        if (treeDigest.equals(NISTObjectIdentifiers.id_sha512))
+        {
+            return XMSSParameterSpec.SHA512;
+        }
+        if (treeDigest.equals(NISTObjectIdentifiers.id_shake128))
+        {
+            return XMSSParameterSpec.SHAKE128;
+        }
+        if (treeDigest.equals(NISTObjectIdentifiers.id_shake256))
+        {
+            return XMSSParameterSpec.SHAKE256;
+        }
+
+        throw new IllegalArgumentException("unrecognized digest OID: " + treeDigest);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyFactorySpi.java
new file mode 100644
index 0000000..dbf2762
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyFactorySpi.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+public class XMSSKeyFactorySpi
+    extends KeyFactorySpi
+    implements AsymmetricKeyInfoConverter
+{
+    public PrivateKey engineGeneratePrivate(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof PKCS8EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to PKCS#8 from the spec
+            byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
+
+            try
+            {
+                return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("unsupported key specification: "
+            + keySpec.getClass() + ".");
+    }
+
+    public PublicKey engineGeneratePublic(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof X509EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to X.509 from the spec
+            byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded();
+
+            // decode the SubjectPublicKeyInfo data structure to the pki object
+            try
+            {
+                return generatePublic(SubjectPublicKeyInfo.getInstance(encKey));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("unknown key specification: " + keySpec + ".");
+    }
+
+    public final KeySpec engineGetKeySpec(Key key, Class keySpec)
+        throws InvalidKeySpecException
+    {
+        if (key instanceof BCXMSSPrivateKey)
+        {
+            if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new PKCS8EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else if (key instanceof BCXMSSPublicKey)
+        {
+            if (X509EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new X509EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else
+        {
+            throw new InvalidKeySpecException("unsupported key type: "
+                + key.getClass() + ".");
+        }
+
+        throw new InvalidKeySpecException("unknown key specification: "
+            + keySpec + ".");
+    }
+
+    public final Key engineTranslateKey(Key key)
+        throws InvalidKeyException
+    {
+        if (key instanceof BCXMSSPrivateKey || key instanceof BCXMSSPublicKey)
+        {
+            return key;
+        }
+
+        throw new InvalidKeyException("unsupported key type");
+    }
+
+    public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCXMSSPrivateKey(keyInfo);
+    }
+
+    public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCXMSSPublicKey(keyInfo);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..6ea6fea
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSKeyPairGeneratorSpi.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.crypto.xmss.XMSSKeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+
+public class XMSSKeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    private XMSSKeyGenerationParameters param;
+    private ASN1ObjectIdentifier treeDigest;
+    private XMSSKeyPairGenerator engine = new XMSSKeyPairGenerator();
+
+    private SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
+    private boolean initialised = false;
+
+    public XMSSKeyPairGeneratorSpi()
+    {
+        super("XMSS");
+    }
+
+    public void initialize(
+        int strength,
+        SecureRandom random)
+    {
+        throw new IllegalArgumentException("use AlgorithmParameterSpec");
+    }
+
+    public void initialize(
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        if (!(params instanceof XMSSParameterSpec))
+        {
+            throw new InvalidAlgorithmParameterException("parameter object not a XMSSParameterSpec");
+        }
+
+        XMSSParameterSpec xmssParams = (XMSSParameterSpec)params;
+
+        if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHA256))
+        {
+            treeDigest = NISTObjectIdentifiers.id_sha256;
+            param = new XMSSKeyGenerationParameters(new XMSSParameters(xmssParams.getHeight(), new SHA256Digest()), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHA512))
+        {
+            treeDigest = NISTObjectIdentifiers.id_sha512;
+            param = new XMSSKeyGenerationParameters(new XMSSParameters(xmssParams.getHeight(), new SHA512Digest()), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHAKE128))
+        {
+            treeDigest = NISTObjectIdentifiers.id_shake128;
+            param = new XMSSKeyGenerationParameters(new XMSSParameters(xmssParams.getHeight(), new SHAKEDigest(128)), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHAKE256))
+        {
+            treeDigest = NISTObjectIdentifiers.id_shake256;
+            param = new XMSSKeyGenerationParameters(new XMSSParameters(xmssParams.getHeight(), new SHAKEDigest(256)), random);
+        }
+
+        engine.init(param);
+        initialised = true;
+    }
+
+    public KeyPair generateKeyPair()
+    {
+        if (!initialised)
+        {
+            param = new XMSSKeyGenerationParameters(new XMSSParameters(10, new SHA512Digest()), random);
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+        XMSSPublicKeyParameters pub = (XMSSPublicKeyParameters)pair.getPublic();
+        XMSSPrivateKeyParameters priv = (XMSSPrivateKeyParameters)pair.getPrivate();
+
+        return new KeyPair(new BCXMSSPublicKey(treeDigest, pub), new BCXMSSPrivateKey(treeDigest, priv));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyFactorySpi.java
new file mode 100644
index 0000000..abcb6e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyFactorySpi.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+public class XMSSMTKeyFactorySpi
+    extends KeyFactorySpi
+    implements AsymmetricKeyInfoConverter
+{
+    public PrivateKey engineGeneratePrivate(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof PKCS8EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to PKCS#8 from the spec
+            byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
+
+            try
+            {
+                return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("unsupported key specification: "
+            + keySpec.getClass() + ".");
+    }
+
+    public PublicKey engineGeneratePublic(KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof X509EncodedKeySpec)
+        {
+            // get the DER-encoded Key according to X.509 from the spec
+            byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded();
+
+            // decode the SubjectPublicKeyInfo data structure to the pki object
+            try
+            {
+                return generatePublic(SubjectPublicKeyInfo.getInstance(encKey));
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("unknown key specification: " + keySpec + ".");
+    }
+
+    public final KeySpec engineGetKeySpec(Key key, Class keySpec)
+        throws InvalidKeySpecException
+    {
+        if (key instanceof BCXMSSMTPrivateKey)
+        {
+            if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new PKCS8EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else if (key instanceof BCXMSSMTPublicKey)
+        {
+            if (X509EncodedKeySpec.class.isAssignableFrom(keySpec))
+            {
+                return new X509EncodedKeySpec(key.getEncoded());
+            }
+        }
+        else
+        {
+            throw new InvalidKeySpecException("unsupported key type: "
+                + key.getClass() + ".");
+        }
+
+        throw new InvalidKeySpecException("unknown key specification: "
+            + keySpec + ".");
+    }
+
+    public final Key engineTranslateKey(Key key)
+        throws InvalidKeyException
+    {
+        if (key instanceof BCXMSSMTPrivateKey || key instanceof BCXMSSMTPublicKey)
+        {
+            return key;
+        }
+
+        throw new InvalidKeyException("unsupported key type");
+    }
+
+    public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCXMSSMTPrivateKey(keyInfo);
+    }
+
+    public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+        throws IOException
+    {
+        return new BCXMSSMTPublicKey(keyInfo);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..bd4a9d7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTKeyPairGeneratorSpi.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTKeyGenerationParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTKeyPairGenerator;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
+import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+
+public class XMSSMTKeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    private XMSSMTKeyGenerationParameters param;
+    private XMSSMTKeyPairGenerator engine = new XMSSMTKeyPairGenerator();
+    private ASN1ObjectIdentifier treeDigest;
+
+    private SecureRandom random = CryptoServicesRegistrar.getSecureRandom();
+    private boolean initialised = false;
+
+    public XMSSMTKeyPairGeneratorSpi()
+    {
+        super("XMSSMT");
+    }
+
+    public void initialize(
+        int strength,
+        SecureRandom random)
+    {
+        throw new IllegalArgumentException("use AlgorithmParameterSpec");
+    }
+
+    public void initialize(
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidAlgorithmParameterException
+    {
+        if (!(params instanceof XMSSMTParameterSpec))
+        {
+            throw new InvalidAlgorithmParameterException("parameter object not a XMSSMTParameterSpec");
+        }
+
+        XMSSMTParameterSpec xmssParams = (XMSSMTParameterSpec)params;
+
+        if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHA256))
+        {
+            treeDigest = NISTObjectIdentifiers.id_sha256;
+            param = new XMSSMTKeyGenerationParameters(new XMSSMTParameters(xmssParams.getHeight(), xmssParams.getLayers(), new SHA256Digest()), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHA512))
+        {
+            treeDigest = NISTObjectIdentifiers.id_sha512;
+            param = new XMSSMTKeyGenerationParameters(new XMSSMTParameters(xmssParams.getHeight(), xmssParams.getLayers(), new SHA512Digest()), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHAKE128))
+        {
+            treeDigest = NISTObjectIdentifiers.id_shake128;
+            param = new XMSSMTKeyGenerationParameters(new XMSSMTParameters(xmssParams.getHeight(), xmssParams.getLayers(), new SHAKEDigest(128)), random);
+        }
+        else if (xmssParams.getTreeDigest().equals(XMSSParameterSpec.SHAKE256))
+        {
+            treeDigest = NISTObjectIdentifiers.id_shake256;
+            param = new XMSSMTKeyGenerationParameters(new XMSSMTParameters(xmssParams.getHeight(), xmssParams.getLayers(), new SHAKEDigest(256)), random);
+        }
+
+        engine.init(param);
+        initialised = true;
+    }
+
+    public KeyPair generateKeyPair()
+    {
+        if (!initialised)
+        {
+            param = new XMSSMTKeyGenerationParameters(new XMSSMTParameters(10, 20, new SHA512Digest()), random);
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+        XMSSMTPublicKeyParameters pub = (XMSSMTPublicKeyParameters)pair.getPublic();
+        XMSSMTPrivateKeyParameters priv = (XMSSMTPrivateKeyParameters)pair.getPrivate();
+
+        return new KeyPair(new BCXMSSMTPublicKey(treeDigest, pub), new BCXMSSMTPrivateKey(treeDigest, priv));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTSignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTSignatureSpi.java
new file mode 100644
index 0000000..a58b232
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSMTSignatureSpi.java
@@ -0,0 +1,243 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.NullDigest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSMTSigner;
+import org.bouncycastle.pqc.jcajce.interfaces.StateAwareSignature;
+
+public class XMSSMTSignatureSpi
+    extends Signature
+    implements StateAwareSignature
+{
+    protected XMSSMTSignatureSpi(String algorithm)
+    {
+        super(algorithm);
+    }
+
+    private Digest digest;
+    private XMSSMTSigner signer;
+    private ASN1ObjectIdentifier treeDigest;
+    private SecureRandom random;
+
+    protected XMSSMTSignatureSpi(String sigName, Digest digest, XMSSMTSigner signer)
+    {
+        super(sigName);
+
+        this.digest = digest;
+        this.signer = signer;
+    }
+
+    protected void engineInitVerify(PublicKey publicKey)
+        throws InvalidKeyException
+    {
+        if (publicKey instanceof BCXMSSMTPublicKey)
+        {
+            CipherParameters param = ((BCXMSSMTPublicKey)publicKey).getKeyParams();
+
+            treeDigest = null;
+            digest.reset();
+            signer.init(false, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown public key passed to XMSSMT");
+        }
+    }
+
+    protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+        throws InvalidKeyException
+    {
+        this.random = random;
+        engineInitSign(privateKey);
+    }
+
+    protected void engineInitSign(PrivateKey privateKey)
+        throws InvalidKeyException
+    {
+        if (privateKey instanceof BCXMSSMTPrivateKey)
+        {
+            CipherParameters param = ((BCXMSSMTPrivateKey)privateKey).getKeyParams();
+
+            treeDigest = ((BCXMSSMTPrivateKey)privateKey).getTreeDigestOID();
+            if (random != null)
+            {
+                param = new ParametersWithRandom(param, random);
+            }
+
+            digest.reset();
+            signer.init(true, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown private key passed to XMSSMT");
+        }
+    }
+
+    protected void engineUpdate(byte b)
+        throws SignatureException
+    {
+        digest.update(b);
+    }
+
+    protected void engineUpdate(byte[] b, int off, int len)
+        throws SignatureException
+    {
+        digest.update(b, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        byte[] hash = DigestUtil.getDigestResult(digest);
+
+        try
+        {
+            byte[] sig = signer.generateSignature(hash);
+
+            return sig;
+        }
+        catch (Exception e)
+        {
+            if (e instanceof IllegalStateException)
+            {
+                throw new SignatureException(e.getMessage());
+            }
+            throw new SignatureException(e.toString());
+        }
+    }
+
+    protected boolean engineVerify(byte[] sigBytes)
+        throws SignatureException
+    {
+        byte[] hash = DigestUtil.getDigestResult(digest);
+
+        return signer.verifySignature(hash, sigBytes);
+    }
+
+    protected void engineSetParameter(AlgorithmParameterSpec params)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
+     */
+    protected void engineSetParameter(String param, Object value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated
+     */
+    protected Object engineGetParameter(String param)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    public boolean isSigningCapable()
+    {
+        return treeDigest != null && signer.getUsagesRemaining() != 0;
+    }
+
+
+    public PrivateKey getUpdatedPrivateKey()
+    {
+        if (treeDigest == null)
+        {
+            throw new IllegalStateException("signature object not in a signing state");
+        }
+        PrivateKey rKey = new BCXMSSMTPrivateKey(treeDigest, (XMSSMTPrivateKeyParameters)signer.getUpdatedPrivateKey());
+
+        treeDigest = null;
+
+        return rKey;
+    }
+
+    static public class withSha256
+        extends XMSSMTSignatureSpi
+    {
+        public withSha256()
+        {
+            super("XMSSMT-SHA256", new NullDigest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withShake128
+        extends XMSSMTSignatureSpi
+    {
+        public withShake128()
+        {
+            super("XMSSMT-SHAKE128", new NullDigest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withSha512
+        extends XMSSMTSignatureSpi
+    {
+        public withSha512()
+        {
+            super("XMSSMT-SHA512", new NullDigest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withShake256
+        extends XMSSMTSignatureSpi
+    {
+        public withShake256()
+        {
+            super("XMSSMT-SHAKE256", new NullDigest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withSha256andPrehash
+        extends XMSSMTSignatureSpi
+    {
+        public withSha256andPrehash()
+        {
+            super("SHA256withXMSSMT-SHA256", new SHA256Digest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withShake128andPrehash
+        extends XMSSMTSignatureSpi
+    {
+        public withShake128andPrehash()
+        {
+            super("SHAKE128withXMSSMT-SHAKE128", new SHAKEDigest(128), new XMSSMTSigner());
+        }
+    }
+
+    static public class withSha512andPrehash
+        extends XMSSMTSignatureSpi
+    {
+        public withSha512andPrehash()
+        {
+            super("SHA512withXMSSMT-SHA512", new SHA512Digest(), new XMSSMTSigner());
+        }
+    }
+
+    static public class withShake256andPrehash
+        extends XMSSMTSignatureSpi
+    {
+        public withShake256andPrehash()
+        {
+            super("SHAKE256withXMSSMT-SHAKE256", new SHAKEDigest(256), new XMSSMTSigner());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSSignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSSignatureSpi.java
new file mode 100644
index 0000000..00a54a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/xmss/XMSSSignatureSpi.java
@@ -0,0 +1,243 @@
+package org.bouncycastle.pqc.jcajce.provider.xmss;
+
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.NullDigest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters;
+import org.bouncycastle.pqc.crypto.xmss.XMSSSigner;
+import org.bouncycastle.pqc.jcajce.interfaces.StateAwareSignature;
+
+public class XMSSSignatureSpi
+    extends Signature
+    implements StateAwareSignature
+{
+    protected XMSSSignatureSpi(String algorithm)
+    {
+        super(algorithm);
+    }
+
+    private Digest digest;
+    private XMSSSigner signer;
+    private SecureRandom random;
+    private ASN1ObjectIdentifier treeDigest;
+
+    protected XMSSSignatureSpi(String sigName, Digest digest, XMSSSigner signer)
+    {
+        super(sigName);
+
+        this.digest = digest;
+        this.signer = signer;
+    }
+
+    protected void engineInitVerify(PublicKey publicKey)
+        throws InvalidKeyException
+    {
+        if (publicKey instanceof BCXMSSPublicKey)
+        {
+            CipherParameters param = ((BCXMSSPublicKey)publicKey).getKeyParams();
+
+            treeDigest = null;
+            digest.reset();
+            signer.init(false, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown public key passed to XMSS");
+        }
+    }
+
+    protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+        throws InvalidKeyException
+    {
+        this.random = random;
+        engineInitSign(privateKey);
+    }
+
+    protected void engineInitSign(PrivateKey privateKey)
+        throws InvalidKeyException
+    {
+        if (privateKey instanceof BCXMSSPrivateKey)
+        {
+            CipherParameters param = ((BCXMSSPrivateKey)privateKey).getKeyParams();
+
+            treeDigest = ((BCXMSSPrivateKey)privateKey).getTreeDigestOID();
+            if (random != null)
+            {
+                param = new ParametersWithRandom(param, random);
+            }
+
+            digest.reset();
+            signer.init(true, param);
+        }
+        else
+        {
+            throw new InvalidKeyException("unknown private key passed to XMSS");
+        }
+    }
+
+    protected void engineUpdate(byte b)
+        throws SignatureException
+    {
+        digest.update(b);
+    }
+
+    protected void engineUpdate(byte[] b, int off, int len)
+        throws SignatureException
+    {
+        digest.update(b, off, len);
+    }
+
+    protected byte[] engineSign()
+        throws SignatureException
+    {
+        byte[] hash = DigestUtil.getDigestResult(digest);
+
+        try
+        {
+            byte[] sig = signer.generateSignature(hash);
+
+            return sig;
+        }
+        catch (Exception e)
+        {
+            if (e instanceof IllegalStateException)
+            {
+                throw new SignatureException(e.getMessage());
+            }
+            throw new SignatureException(e.toString());
+        }
+    }
+
+    protected boolean engineVerify(byte[] sigBytes)
+        throws SignatureException
+    {
+        byte[] hash = DigestUtil.getDigestResult(digest);
+
+        return signer.verifySignature(hash, sigBytes);
+    }
+
+    protected void engineSetParameter(AlgorithmParameterSpec params)
+    {
+        // TODO
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
+     */
+    protected void engineSetParameter(String param, Object value)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    /**
+     * @deprecated
+     */
+    protected Object engineGetParameter(String param)
+    {
+        throw new UnsupportedOperationException("engineSetParameter unsupported");
+    }
+
+    public boolean isSigningCapable()
+    {
+        return treeDigest != null && signer.getUsagesRemaining() != 0;
+    }
+
+    public PrivateKey getUpdatedPrivateKey()
+    {
+        if (treeDigest == null)
+        {
+            throw new IllegalStateException("signature object not in a signing state");
+        }
+        PrivateKey rKey = new BCXMSSPrivateKey(treeDigest, (XMSSPrivateKeyParameters)signer.getUpdatedPrivateKey());
+
+        treeDigest = null;
+
+        return rKey;
+    }
+
+    static public class withSha256
+        extends XMSSSignatureSpi
+    {
+        public withSha256()
+        {
+            super("XMSS-SHA256", new NullDigest(), new XMSSSigner());
+        }
+    }
+
+    static public class withShake128
+        extends XMSSSignatureSpi
+    {
+        public withShake128()
+        {
+            super("XMSS-SHAKE128", new NullDigest(), new XMSSSigner());
+        }
+    }
+
+    static public class withSha512
+        extends XMSSSignatureSpi
+    {
+        public withSha512()
+        {
+            super("XMSS-SHA512", new NullDigest(), new XMSSSigner());
+        }
+    }
+
+    static public class withShake256
+        extends XMSSSignatureSpi
+    {
+        public withShake256()
+        {
+            super("XMSS-SHAKE256", new NullDigest(), new XMSSSigner());
+        }
+    }
+
+    static public class withSha256andPrehash
+        extends XMSSSignatureSpi
+    {
+        public withSha256andPrehash()
+        {
+            super("SHA256withXMSS-SHA256", new SHA256Digest(), new XMSSSigner());
+        }
+    }
+
+    static public class withShake128andPrehash
+        extends XMSSSignatureSpi
+    {
+        public withShake128andPrehash()
+        {
+            super("SHAKE128withXMSSMT-SHAKE128", new SHAKEDigest(128), new XMSSSigner());
+        }
+    }
+
+    static public class withSha512andPrehash
+        extends XMSSSignatureSpi
+    {
+        public withSha512andPrehash()
+        {
+            super("SHA512withXMSS-SHA512", new SHA512Digest(), new XMSSSigner());
+        }
+    }
+
+    static public class withShake256andPrehash
+        extends XMSSSignatureSpi
+    {
+        public withShake256andPrehash()
+        {
+            super("SHAKE256withXMSS-SHAKE256", new SHAKEDigest(256), new XMSSSigner());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/QTESLAParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/QTESLAParameterSpec.java
new file mode 100644
index 0000000..f86980a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/QTESLAParameterSpec.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.pqc.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.pqc.crypto.qtesla.QTESLASecurityCategory;
+
+/**
+ * qTESLA parameter details. These are divided up on the basis of the security categories for each
+ * individual parameter set.
+ */
+public class QTESLAParameterSpec
+    implements AlgorithmParameterSpec
+{
+    /**
+     * Available security categories.
+     */
+    public static final String HEURISTIC_I = QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_I);
+    public static final String HEURISTIC_III_SIZE = QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SIZE);
+    public static final String HEURISTIC_III_SPEED = QTESLASecurityCategory.getName(QTESLASecurityCategory.HEURISTIC_III_SPEED);
+    public static final String PROVABLY_SECURE_I = QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_I);
+    public static final String PROVABLY_SECURE_III = QTESLASecurityCategory.getName(QTESLASecurityCategory.PROVABLY_SECURE_III);
+
+    private String securityCategory;
+
+    /**
+     * Base constructor.
+     *
+     * @param securityCategory the security category we want this parameterSpec to match.
+     */
+    public QTESLAParameterSpec(String securityCategory)
+    {
+        this.securityCategory = securityCategory;
+    }
+
+    /**
+     * Return the security category.
+     *
+     * @return the security category.
+     */
+    public String getSecurityCategory()
+    {
+        return securityCategory;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java
index 9da1e45..85a71e4 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java
@@ -57,18 +57,11 @@
     public RainbowParameterSpec(int[] vi)
     {
         this.vi = vi;
-        try
-        {
-            checkParams();
-        }
-        catch (Exception e)
-        {
-            e.printStackTrace();
-        }
+
+        checkParams();
     }
 
     private void checkParams()
-        throws Exception
     {
         if (vi == null)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSMTParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSMTParameterSpec.java
new file mode 100644
index 0000000..d905522
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSMTParameterSpec.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.pqc.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+public class XMSSMTParameterSpec
+    implements AlgorithmParameterSpec
+{
+    /**
+     * Use SHA-256 for the tree generation function.
+     */
+    public static final String SHA256 = "SHA256";
+
+    /**
+     * Use SHA512 for the tree generation function.
+     */
+    public static final String SHA512 = "SHA512";
+
+    /**
+     * Use SHAKE128 for the tree generation function.
+     */
+    public static final String SHAKE128 = "SHAKE128";
+
+    /**
+     * Use SHAKE256 for the tree generation function.
+     */
+    public static final String SHAKE256 = "SHAKE256";
+
+    private final int height;
+    private final int layers;
+    private final String treeDigest;
+
+    public XMSSMTParameterSpec(int height, int layers, String treeDigest)
+    {
+        this.height = height;
+        this.layers = layers;
+        this.treeDigest = treeDigest;
+    }
+
+    public String getTreeDigest()
+    {
+        return treeDigest;
+    }
+
+    public int getHeight()
+    {
+        return height;
+    }
+
+    public int getLayers()
+    {
+        return layers;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSParameterSpec.java
new file mode 100644
index 0000000..72cef09
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/spec/XMSSParameterSpec.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.pqc.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+public class XMSSParameterSpec
+    implements AlgorithmParameterSpec
+{
+    /**
+     * Use SHA-256 for the tree generation function.
+     */
+    public static final String SHA256 = "SHA256";
+
+    /**
+     * Use SHA512 for the tree generation function.
+     */
+    public static final String SHA512 = "SHA512";
+
+    /**
+     * Use SHAKE128 for the tree generation function.
+     */
+    public static final String SHAKE128 = "SHAKE128";
+
+    /**
+     * Use SHAKE256 for the tree generation function.
+     */
+    public static final String SHAKE256 = "SHAKE256";
+
+    private final int height;
+    private final String treeDigest;
+
+    public XMSSParameterSpec(int height, String treeDigest)
+    {
+        this.height = height;
+        this.treeDigest = treeDigest;
+    }
+
+    public String getTreeDigest()
+    {
+        return treeDigest;
+    }
+
+    public int getHeight()
+    {
+        return height;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java
index a61f950..2841d17 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java
@@ -2,6 +2,8 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * This class describes some operations with matrices over finite field GF(2)
  * and is used in ecc and MQ-PKC (also has some specific methods and
@@ -1230,7 +1232,7 @@
         int hash = (numRows * 31 + numColumns) * 31 + length;
         for (int i = 0; i < numRows; i++)
         {
-            hash = hash * 31 + matrix[i].hashCode();
+            hash = hash * 31 + Arrays.hashCode(matrix[i]);
         }
         return hash;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java
index 2dc52ed..f241ba3 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java
@@ -4,6 +4,8 @@
 import java.math.BigInteger;
 import java.util.Random;
 
+import org.bouncycastle.util.Arrays;
+
 
 /**
  * This class stores very long strings of bits and does some basic arithmetics.
@@ -572,7 +574,7 @@
      */
     public int hashCode()
     {
-        return len + value.hashCode();
+        return len + Arrays.hashCode(value);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java
index ec35b68..e972f6f 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java
@@ -2,6 +2,8 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * This class implements the abstract class <tt>Vector</tt> for the case of
  * vectors over the finite field GF(2). <br>
@@ -505,7 +507,7 @@
     public int hashCode()
     {
         int hash = length;
-        hash = hash * 31 + v.hashCode();
+        hash = hash * 31 + Arrays.hashCode(v);
         return hash;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java
index f5f7b64..75eb7ee 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java
@@ -2,6 +2,8 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+
 /**
  * This class describes operations with elements from the finite field F =
  * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with
@@ -233,7 +235,7 @@
      */
     public int getRandomNonZeroElement()
     {
-        return getRandomNonZeroElement(new SecureRandom());
+        return getRandomNonZeroElement(CryptoServicesRegistrar.getSecureRandom());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java
index f2527f6..17b92df 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java
@@ -1,6 +1,8 @@
 package org.bouncycastle.pqc.math.linearalgebra;
 
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * This class implements vectors over the finite field
  * <tt>GF(2<sup>m</sup>)</tt> for small <tt>m</tt> (i.e.,
@@ -222,7 +224,7 @@
     public int hashCode()
     {
         int hash = this.field.hashCode();
-        hash = hash * 31 + vector.hashCode();
+        hash = hash * 31 + Arrays.hashCode(vector);
         return hash;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java
index faa99dc..3c7c3a2 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java
@@ -105,10 +105,8 @@
      *
      * @param minuend the minuend
      * @return <tt>this - minuend</tt> (newly created)
-     * @throws DifferentFieldsException if the elements are of different fields.
      */
     public final GFElement subtract(GFElement minuend)
-        throws RuntimeException
     {
         return add(minuend);
     }
@@ -118,7 +116,6 @@
      * overwriting this element.
      *
      * @param minuend the minuend
-     * @throws DifferentFieldsException if the elements are of different fields.
      */
     public final void subtractFromThis(GFElement minuend)
     {
@@ -156,11 +153,8 @@
      *
      * @param basis the GF2nField representation to transform this element to
      * @return this element in the representation of <tt>basis</tt>
-     * @throws DifferentFieldsException if <tt>this</tt> cannot be converted according to
-     * <tt>basis</tt>.
      */
     public final GF2nElement convert(GF2nField basis)
-        throws RuntimeException
     {
         return mField.convert(this, basis);
     }
@@ -177,8 +171,6 @@
      * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z.
      *
      * @return z with z<sup>2</sup> + z = <tt>this</tt>
-     * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a
-     * solution
      */
     public abstract GF2nElement solveQuadraticEquation()
         throws RuntimeException;
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java
index 48bbecc..091a200 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java
@@ -155,16 +155,9 @@
         // initialize a as a copy of matrix and inv as E(inheitsmatrix)
         for (i = 0; i < mDegree; i++)
         {
-            try
-            {
-                a[i] = new GF2Polynomial(matrix[i]);
-                inv[i] = new GF2Polynomial(mDegree);
-                inv[i].setBit(mDegree - 1 - i);
-            }
-            catch (RuntimeException BDNEExc)
-            {
-                BDNEExc.printStackTrace();
-            }
+            a[i] = new GF2Polynomial(matrix[i]);
+            inv[i] = new GF2Polynomial(mDegree);
+            inv[i].setBit(mDegree - 1 - i);
         }
         // construct triangle matrix so that for each a[i] the first i bits are
         // zero
@@ -225,8 +218,6 @@
      * @param basis the basis to convert <tt>elem</tt> to
      * @return <tt>elem</tt> converted to a new element representation
      *         according to <tt>basis</tt>
-     * @throws DifferentFieldsException if <tt>elem</tt> cannot be converted according to
-     * <tt>basis</tt>.
      * @see GF2nField#computeCOBMatrix
      * @see GF2nField#getRandomRoot
      * @see GF2nPolynomial
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java
index 95236e7..d5d9c05 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java
@@ -4,6 +4,8 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * This class implements an element of the finite field <i>GF(2<sup>n </sup>)</i>.
  * It is represented in an optimal normal basis representation and holds the
@@ -403,7 +405,7 @@
      */
     public int hashCode()
     {
-        return mPol.hashCode();
+        return Arrays.hashCode(mPol);
     }
 
     // /////////////////////////////////////////////////////////////////////
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java
index f122be0..29d69d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java
@@ -287,11 +287,8 @@
      * @param b -
      *          the <tt>PolynomialGF2n</tt> to add
      * @return <tt>this + b</tt>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial add(GF2nPolynomial b)
-        throws RuntimeException
     {
         GF2nPolynomial result;
         if (size() >= b.size())
@@ -329,11 +326,8 @@
      *
      * @param s the scalar to multiply
      * @return <i>this</i> x <i>s</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>s</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial scalarMultiply(GF2nElement s)
-        throws RuntimeException
     {
         GF2nPolynomial result = new GF2nPolynomial(size());
         int i;
@@ -352,11 +346,8 @@
      *
      * @param b the PolynomialGF2n to multiply
      * @return <i>this</i> * <i>b</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial multiply(GF2nPolynomial b)
-        throws RuntimeException
     {
         int i, j;
         int aDegree = size();
@@ -394,13 +385,9 @@
      * @param b the PolynomialGF2n to multiply
      * @param g the modul
      * @return <i>this</i> * <i>b</i> mod <i>g</i>
-     * @throws DifferentFieldsException if <tt>this</tt>, <tt>b</tt> and <tt>g</tt> are
-     * not all defined over the same field.
      */
     public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b,
                                                   GF2nPolynomial g)
-        throws RuntimeException,
-        ArithmeticException
     {
         return multiply(b).reduce(g);
     }
@@ -412,8 +399,6 @@
      * @param g -
      *          the modulus
      * @return <i>this</i> % <i>g</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>g</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial reduce(GF2nPolynomial g)
         throws RuntimeException, ArithmeticException
@@ -478,11 +463,8 @@
      *
      * @param b the divisor
      * @return the quotient and remainder of <i>this</i> / <i>b</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial[] divide(GF2nPolynomial b)
-        throws RuntimeException, ArithmeticException
     {
         GF2nPolynomial[] result = new GF2nPolynomial[2];
         GF2nPolynomial a = new GF2nPolynomial(this);
@@ -524,8 +506,6 @@
      *
      * @param b the divisor
      * @return the remainder <i>this</i> % <i>b</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial remainder(GF2nPolynomial b)
         throws RuntimeException, ArithmeticException
@@ -541,8 +521,6 @@
      *
      * @param b the divisor
      * @return the quotient <i>this</i> / <i>b</i>
-     * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over
-     * the same field.
      */
     public final GF2nPolynomial quotient(GF2nPolynomial b)
         throws RuntimeException, ArithmeticException
@@ -559,12 +537,8 @@
      * @param g -
      *          a GF2nPolynomial
      * @return gcd(<i>this</i>, <i>g</i>)
-     * @throws DifferentFieldsException if the coefficients of <i>this</i> and <i>g</i> use
-     * different fields
-     * @throws ArithmeticException if coefficients are zero.
      */
     public final GF2nPolynomial gcd(GF2nPolynomial g)
-        throws RuntimeException, ArithmeticException
     {
         GF2nPolynomial a = new GF2nPolynomial(this);
         GF2nPolynomial b = new GF2nPolynomial(g);
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java
index f6d6008..e004bdc 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java
@@ -3,6 +3,9 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.util.BigIntegers;
+
 /**
  * Class of number-theory related functions for use with integers represented as
  * <tt>int</tt>'s or <tt>BigInteger</tt> objects.
@@ -887,7 +890,7 @@
             n -= 2;
         }
 
-        while (n > 3 & !isPrime(n))
+        while (n > 3 && !isPrime(n))
         {
             n -= 2;
         }
@@ -1051,7 +1054,7 @@
     {
         if (sr == null)
         {
-            sr = new SecureRandom();
+            sr = CryptoServicesRegistrar.getSecureRandom();
         }
         return randomize(upperBound, sr);
     }
@@ -1064,12 +1067,12 @@
 
         if (prng == null)
         {
-            prng = sr != null ? sr : new SecureRandom();
+            prng = sr != null ? sr : CryptoServicesRegistrar.getSecureRandom();
         }
 
         for (int i = 0; i < 20; i++)
         {
-            randomNum = new BigInteger(blen, prng);
+            randomNum = BigIntegers.createRandomBigInteger(blen, prng);
             if (randomNum.compareTo(upperBound) < 0)
             {
                 return randomNum;
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java
index 28b58d3..e0a07b8 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java
@@ -2,6 +2,8 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * This class implements permutations of the set {0,1,...,n-1} for some given n
  * &gt; 0, i.e., ordered sequences containing each number <tt>m</tt> (<tt>0 &lt;=
@@ -217,7 +219,7 @@
      */
     public int hashCode()
     {
-        return perm.hashCode();
+        return Arrays.hashCode(perm);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java b/bcprov/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
index 03f9a19..604d7b4 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/BigIntPolynomial.java
@@ -2,11 +2,11 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.util.Arrays;
 
 /**
@@ -83,7 +83,7 @@
         {
             coeffs.add(Constants.BIGINT_ZERO);
         }
-        Collections.shuffle(coeffs, new SecureRandom());
+        Collections.shuffle(coeffs, CryptoServicesRegistrar.getSecureRandom());
 
         BigIntPolynomial poly = new BigIntPolynomial(N);
         for (int i = 0; i < coeffs.size(); i++)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
index 6873117..c48ae75 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
@@ -8,11 +8,21 @@
  */
 public final class Arrays
 {
-    private Arrays() 
+    private Arrays()
     {
         // static class, hide constructor
     }
 
+    public static boolean areAllZeroes(byte[] buf, int off, int len)
+    {
+        int bits = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            bits |= buf[off + i];
+        }
+        return bits == 0;
+    }
+
     public static boolean areEqual(
         boolean[]  a,
         boolean[]  b)
@@ -135,36 +145,37 @@
 
     /**
      * A constant time equals comparison - does not terminate early if
-     * test will fail.
+     * test will fail. For best results always pass the expected value
+     * as the first parameter.
      *
-     * @param a first array
-     * @param b second array
+     * @param expected first array
+     * @param supplied second array
      * @return true if arrays equal, false otherwise.
      */
     public static boolean constantTimeAreEqual(
-        byte[]  a,
-        byte[]  b)
+        byte[]  expected,
+        byte[]  supplied)
     {
-        if (a == b)
+        if (expected == supplied)
         {
             return true;
         }
 
-        if (a == null || b == null)
+        if (expected == null || supplied == null)
         {
             return false;
         }
 
-        if (a.length != b.length)
+        if (expected.length != supplied.length)
         {
-            return false;
+            return !Arrays.constantTimeAreEqual(expected, expected);
         }
 
         int nonEqual = 0;
 
-        for (int i = 0; i != a.length; i++)
+        for (int i = 0; i != expected.length; i++)
         {
-            nonEqual |= (a[i] ^ b[i]);
+            nonEqual |= (expected[i] ^ supplied[i]);
         }
 
         return nonEqual == 0;
@@ -335,6 +346,18 @@
     }
 
     public static void fill(
+        byte[] array,
+        int start,
+        int finish,
+        byte value)
+    {
+        for (int i = start; i < finish; i++)
+        {
+            array[i] = value;
+        }
+    }
+
+    public static void fill(
         char[] array,
         char value)
     {
@@ -355,7 +378,7 @@
     }
 
     public static void fill(
-        short[] array, 
+        short[] array,
         short value)
     {
         for (int i = 0; i < array.length; i++)
@@ -373,7 +396,63 @@
             array[i] = value;
         }
     }
-    
+
+    public static void fill(
+        byte[] array,
+        int out,
+        byte value)
+    {
+        if(out < array.length)
+        {
+            for (int i = out; i < array.length; i++)
+            {
+                array[i] = value;
+            }
+        }
+    }
+
+    public static void fill(
+        int[] array,
+        int out,
+        int value)
+    {
+        if(out < array.length)
+        {
+            for (int i = out; i < array.length; i++)
+            {
+                array[i] = value;
+            }
+        }
+    }
+
+    public static void fill(
+        short[] array,
+        int out,
+        short value)
+    {
+        if(out < array.length)
+        {
+            for (int i = out; i < array.length; i++)
+            {
+                array[i] = value;
+            }
+        }
+    }
+
+    public static void fill(
+        long[] array,
+        int out,
+        long value)
+    {
+        if(out < array.length)
+        {
+            for (int i = out; i < array.length; i++)
+            {
+                array[i] = value;
+            }
+        }
+    }
+
     public static int hashCode(byte[] data)
     {
         if (data == null)
@@ -681,9 +760,9 @@
             return null;
         }
         long[] copy = new long[data.length];
-        
+
         System.arraycopy(data, 0, copy, 0, data.length);
-        
+
         return copy;
     }
 
@@ -1126,7 +1205,7 @@
 
         int p1 = 0, p2 = a.length;
         byte[] result = new byte[p2];
-        
+
         while (--p2 >= 0)
         {
             result[p2] = a[p1++];
@@ -1196,4 +1275,20 @@
             throw new UnsupportedOperationException("Cannot remove element from an Array.");
         }
     }
+
+    /**
+     * Fill input array by zeros
+     *
+     * @param array input array
+     */
+    public static void clear(byte[] array)
+    {
+        if (array != null)
+        {
+            for (int i = 0; i < array.length; i++)
+            {
+                array[i] = 0;
+            }
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
index f7f7e68..a118ba5 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
@@ -8,8 +8,13 @@
  */
 public final class BigIntegers
 {
+    public static final BigInteger ZERO = BigInteger.valueOf(0);
+    public static final BigInteger ONE = BigInteger.valueOf(1);
+
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+    private static final BigInteger THREE = BigInteger.valueOf(3);
+
     private static final int MAX_ITERATIONS = 1000;
-    private static final BigInteger ZERO = BigInteger.valueOf(0);
 
     /**
      * Return the passed in value as an unsigned byte array.
@@ -92,7 +97,7 @@
 
         for (int i = 0; i < MAX_ITERATIONS; ++i)
         {
-            BigInteger x = new BigInteger(max.bitLength(), random);
+            BigInteger x = createRandomBigInteger(max.bitLength(), random);
             if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0)
             {
                 return x;
@@ -100,7 +105,7 @@
         }
 
         // fall back to a faster (restricted) method
-        return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
+        return createRandomBigInteger(max.subtract(min).bitLength() - 1, random).add(min);
     }
 
     public static BigInteger fromUnsignedByteArray(byte[] buf)
@@ -118,4 +123,81 @@
         }
         return new BigInteger(1, mag);
     }
+
+    public static int getUnsignedByteLength(BigInteger n)
+    {
+        return (n.bitLength() + 7) / 8;
+    }
+
+    /**
+     * Return a positive BigInteger in the range of 0 to 2**bitLength - 1.
+     *
+     * @param bitLength maximum bit length for the generated BigInteger.
+     * @param random a source of randomness.
+     * @return a positive BigInteger
+     */
+    public static BigInteger createRandomBigInteger(int bitLength, SecureRandom random)
+    {
+        return new BigInteger(1, createRandom(bitLength, random));
+    }
+
+    /**
+     * Return a prime number candidate of the specified bit length.
+     *
+     * @param bitLength bit length for the generated BigInteger.
+     * @param random a source of randomness.
+     * @return a positive BigInteger of numBits length
+     */
+    public static BigInteger createRandomPrime(int bitLength, int certainty, SecureRandom random)
+    {
+        if (bitLength < 2)
+        {
+            throw new IllegalArgumentException("bitLength < 2");
+        }
+
+        BigInteger rv;
+
+        if (bitLength == 2)
+        {
+            return (random.nextInt() < 0) ? TWO : THREE;
+        }
+
+        do
+        {
+            byte[] base = createRandom(bitLength, random);
+
+            int xBits = 8 * base.length - bitLength;
+            byte lead = (byte)(1 << (7 - xBits));
+
+            // ensure top and bottom bit set
+            base[0] |= lead;
+            base[base.length - 1] |= 0x01;
+
+            rv = new BigInteger(1, base);
+        }
+        while (!rv.isProbablePrime(certainty));
+
+        return rv;
+    }
+
+    private static byte[] createRandom(int bitLength, SecureRandom random)
+        throws IllegalArgumentException
+    {
+        if (bitLength < 1)
+        {
+            throw new IllegalArgumentException("bitLength must be at least 1");
+        }
+
+        int nBytes = (bitLength + 7) / 8;
+
+        byte[] rv = new byte[nBytes];
+
+        random.nextBytes(rv);
+
+        // strip off any excess bits in the MSB
+        int xBits = 8 * nBytes - bitLength;
+        rv[0] &= (byte)(255 >>> xBits);
+
+        return rv;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Fingerprint.java b/bcprov/src/main/java/org/bouncycastle/util/Fingerprint.java
new file mode 100644
index 0000000..7027d49
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Fingerprint.java
@@ -0,0 +1,158 @@
+package org.bouncycastle.util;
+
+import org.bouncycastle.crypto.digests.SHA512tDigest;
+import org.bouncycastle.crypto.digests.SHAKEDigest;
+
+/**
+ * Basic 20 byte finger print class.
+ */
+public class Fingerprint
+{
+    private static char[] encodingTable =
+    {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    private final byte[] fingerprint;
+
+    /**
+     * Base constructor - use SHAKE-256 (160 bits). This is the recommended one as it is also
+     * produced by the FIPS API.
+     *
+     * @param source original data to calculate the fingerprint from.
+     */
+    public Fingerprint(byte[] source)
+    {
+        this(source, 160);
+    }
+
+    /**
+     * Constructor with length - use SHAKE-256 (bitLength bits). This is the recommended one as it is also
+     * produced by the FIPS API.
+     *
+     * @param source original data to calculate the fingerprint from.
+     */
+    public Fingerprint(byte[] source, int bitLength)
+    {
+        this.fingerprint = calculateFingerprint(source, bitLength);
+    }
+
+    /**
+     * Base constructor - for backwards compatibility.
+     *
+     * @param source original data to calculate the fingerprint from.
+     * @param useSHA512t use the old SHA512/160 calculation.
+     * @deprecated use the SHAKE only version.
+     */
+    public Fingerprint(byte[] source, boolean useSHA512t)
+    {
+        if (useSHA512t)
+        {
+            this.fingerprint = calculateFingerprintSHA512_160(source);
+        }
+        else
+        {
+            this.fingerprint = calculateFingerprint(source);
+        }
+    }
+
+    public byte[] getFingerprint()
+    {
+        return Arrays.clone(fingerprint);
+    }
+
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i != fingerprint.length; i++)
+        {
+            if (i > 0)
+            {
+                sb.append(":");
+            }
+            sb.append(encodingTable[(fingerprint[i] >>> 4) & 0xf]);
+            sb.append(encodingTable[fingerprint[i] & 0x0f]);
+        }
+
+        return sb.toString();
+    }
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+        if (o instanceof Fingerprint)
+        {
+            return Arrays.areEqual(((Fingerprint)o).fingerprint, fingerprint);
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return Arrays.hashCode(fingerprint);
+    }
+
+    /**
+     * Return a byte array containing a calculated fingerprint for the passed in input data.
+     * This calculation is compatible with the BC FIPS API.
+     *
+     * @param input data to base the fingerprint on.
+     * @return a byte array containing a 160 bit fingerprint.
+     */
+    public static byte[] calculateFingerprint(byte[] input)
+    {
+        return calculateFingerprint(input, 160);
+    }
+
+    /**
+     * Return a byte array containing a calculated fingerprint for the passed in input data.
+     * This calculation is compatible with the BC FIPS API.
+     *
+     * @param input data to base the fingerprint on.
+     * @param bitLength bit length of finger print to be produced.
+     * @return a byte array containing a 20 byte fingerprint.
+     */
+    public static byte[] calculateFingerprint(byte[] input, int bitLength)
+    {
+        if (bitLength % 8 != 0)
+        {
+            throw new IllegalArgumentException("bitLength must be a multiple of 8");
+        }
+
+        SHAKEDigest digest = new SHAKEDigest(256);
+
+        digest.update(input, 0, input.length);
+
+        byte[] rv = new byte[bitLength / 8];
+
+        digest.doFinal(rv, 0, bitLength / 8);
+
+        return rv;
+    }
+
+    /**
+     * Return a byte array containing a calculated fingerprint for the passed in input data.
+     * The fingerprint is based on SHA512/160.
+     *
+     * @param input data to base the fingerprint on.
+     * @return a byte array containing a 20 byte fingerprint.
+     * @deprecated use the SHAKE based version.
+     */
+    public static byte[] calculateFingerprintSHA512_160(byte[] input)
+    {
+        SHA512tDigest digest = new SHA512tDigest(160);
+
+        digest.update(input, 0, input.length);
+
+        byte[] rv = new byte[digest.getDigestSize()];
+
+        digest.doFinal(rv, 0);
+
+        return rv;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java b/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
index 8af1709..8f57263 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
@@ -118,7 +118,7 @@
      *
      * @param address the IP address as a String.
      *
-     * @return true if a valid IPv4 address, false otherwise
+     * @return true if a valid IPv6 address, false otherwise
      */
     public static boolean isValidIPv6(
         String address)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Pack.java b/bcprov/src/main/java/org/bouncycastle/util/Pack.java
index 82b02ea..44b0b27 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Pack.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Pack.java
@@ -211,6 +211,15 @@
         }
     }
 
+    public static void littleEndianToLong(byte[] bs, int bsOff, long[] ns, int nsOff, int nsLen)
+    {
+        for (int i = 0; i < nsLen; ++i)
+        {
+            ns[nsOff + i] = littleEndianToLong(bs, bsOff);
+            bsOff += 8;
+        }
+    }
+
     public static byte[] longToLittleEndian(long n)
     {
         byte[] bs = new byte[8];
@@ -239,4 +248,13 @@
             off += 8;
         }
     }
+
+    public static void longToLittleEndian(long[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
+    {
+        for (int i = 0; i < nsLen; ++i)
+        {
+            longToLittleEndian(ns[nsOff + i], bs, bsOff);
+            bsOff += 8;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Properties.java b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
index e533b58..c472d18 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Properties.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
@@ -1,10 +1,13 @@
 package org.bouncycastle.util;
 
+import java.math.BigInteger;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 
@@ -13,24 +16,31 @@
  */
 public class Properties
 {
-    public static boolean isOverrideSet(final String propertyName)
+    private Properties()
+    {
+
+    }
+
+    private static final ThreadLocal threadProperties = new ThreadLocal();
+                          
+    /**
+     * Return whether a particular override has been set to true.
+     *
+     * @param propertyName the property name for the override.
+     * @return true if the property is set to "true", false otherwise.
+     */
+    public static boolean isOverrideSet(String propertyName)
     {
         try
         {
-            return "true".equals(AccessController.doPrivileged(new PrivilegedAction()
-            {
-                // JDK 1.4 compatibility
-                public Object run()
-                {
-                    String value = System.getProperty(propertyName);
-                    if (value == null)
-                    {
-                        return null;
-                    }
+            String p = fetchProperty(propertyName);
 
-                    return Strings.toLowerCase(value);
-                }
-            }));
+            if (p != null)
+            {
+                return "true".equals(Strings.toLowerCase(p));
+            }
+
+            return false;
         }
         catch (AccessControlException e)
         {
@@ -38,10 +48,78 @@
         }
     }
 
-    public static Set<String> asKeySet(final String propertyName)
+    /**
+     * Enable the specified override property for the current thread only.
+     *
+     * @param propertyName the property name for the override.
+     * @param enable true if the override should be enabled, false if it should be disabled.
+     * @return true if the override was already set, false otherwise.
+     */
+    public static boolean setThreadOverride(String propertyName, boolean enable)
+    {
+        boolean isSet = isOverrideSet(propertyName);
+
+        Map localProps = (Map)threadProperties.get();
+        if (localProps == null)
+        {
+            localProps = new HashMap();
+        }
+
+        localProps.put(propertyName, enable ? "true" : "false");
+
+        threadProperties.set(localProps);
+
+        return isSet;
+    }
+
+    /**
+     * Enable the specified override property in the current thread only.
+     *
+     * @param propertyName the property name for the override.
+     * @return true if the override set true in thread local, false otherwise.
+     */
+    public static boolean removeThreadOverride(String propertyName)
+    {
+        boolean isSet = isOverrideSet(propertyName);
+
+        Map localProps = (Map)threadProperties.get();
+        if (localProps == null)
+        {
+            return false;
+        }
+
+        localProps.remove(propertyName);
+
+        if (localProps.isEmpty())
+        {
+            threadProperties.remove();
+        }
+        else
+        {
+            threadProperties.set(localProps);
+        }
+
+        return isSet;
+    }
+
+    public static BigInteger asBigInteger(String propertyName)
+    {
+        String p = fetchProperty(propertyName);
+
+        if (p != null)
+        {
+            return new BigInteger(p);
+        }
+
+        return null;
+    }
+
+    public static Set<String> asKeySet(String propertyName)
     {
         Set<String> set = new HashSet<String>();
-        String p = System.getProperty(propertyName);
+
+        String p = fetchProperty(propertyName);
+
         if (p != null)
         {
             StringTokenizer sTok = new StringTokenizer(p, ",");
@@ -50,6 +128,24 @@
                 set.add(Strings.toLowerCase(sTok.nextToken()).trim());
             }
         }
+
         return Collections.unmodifiableSet(set);
     }
+
+    private static String fetchProperty(final String propertyName)
+    {
+        return (String)AccessController.doPrivileged(new PrivilegedAction()
+        {
+            public Object run()
+            {
+                Map localProps = (Map)threadProperties.get();
+                if (localProps != null)
+                {
+                    return localProps.get(propertyName);
+                }
+
+                return System.getProperty(propertyName);
+            }
+        });
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Strings.java b/bcprov/src/main/java/org/bouncycastle/util/Strings.java
index a42830b..bda9d9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Strings.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Strings.java
@@ -8,6 +8,8 @@
 import java.util.ArrayList;
 import java.util.Vector;
 
+import org.bouncycastle.util.encoders.UTF8;
+
 /**
  * String utilities.
  */
@@ -17,105 +19,41 @@
 
     static
     {
-       try
-       {
-           LINE_SEPARATOR = AccessController.doPrivileged(new PrivilegedAction<String>()
-           {
-               public String run()
-               {
-                   // the easy way
-                   return System.getProperty("line.separator");
-               }
-           });
+        try
+        {
+            LINE_SEPARATOR = AccessController.doPrivileged(new PrivilegedAction<String>()
+            {
+                public String run()
+                {
+                    // the easy way
+                    return System.getProperty("line.separator");
+                }
+            });
 
-       }
-       catch (Exception e)
-       {
-           try
-           {
-               // the harder way
-               LINE_SEPARATOR = String.format("%n");
-           }
-           catch (Exception ef)
-           {
-               LINE_SEPARATOR = "\n";   // we're desperate use this...
-           }
-       }
+        }
+        catch (Exception e)
+        {
+            try
+            {
+                // the harder way
+                LINE_SEPARATOR = String.format("%n");
+            }
+            catch (Exception ef)
+            {
+                LINE_SEPARATOR = "\n";   // we're desperate use this...
+            }
+        }
     }
 
     public static String fromUTF8ByteArray(byte[] bytes)
     {
-        int i = 0;
-        int length = 0;
-
-        while (i < bytes.length)
+        char[] chars = new char[bytes.length];
+        int len = UTF8.transcodeToUTF16(bytes, chars);
+        if (len < 0)
         {
-            length++;
-            if ((bytes[i] & 0xf0) == 0xf0)
-            {
-                // surrogate pair
-                length++;
-                i += 4;
-            }
-            else if ((bytes[i] & 0xe0) == 0xe0)
-            {
-                i += 3;
-            }
-            else if ((bytes[i] & 0xc0) == 0xc0)
-            {
-                i += 2;
-            }
-            else
-            {
-                i += 1;
-            }
+            throw new IllegalArgumentException("Invalid UTF-8 input");
         }
-
-        char[] cs = new char[length];
-
-        i = 0;
-        length = 0;
-
-        while (i < bytes.length)
-        {
-            char ch;
-
-            if ((bytes[i] & 0xf0) == 0xf0)
-            {
-                int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i + 1] & 0x3F) << 12) | ((bytes[i + 2] & 0x3F) << 6) | (bytes[i + 3] & 0x3F);
-                int U = codePoint - 0x10000;
-                char W1 = (char)(0xD800 | (U >> 10));
-                char W2 = (char)(0xDC00 | (U & 0x3FF));
-                cs[length++] = W1;
-                ch = W2;
-                i += 4;
-            }
-            else if ((bytes[i] & 0xe0) == 0xe0)
-            {
-                ch = (char)(((bytes[i] & 0x0f) << 12)
-                    | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
-                i += 3;
-            }
-            else if ((bytes[i] & 0xd0) == 0xd0)
-            {
-                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
-                i += 2;
-            }
-            else if ((bytes[i] & 0xc0) == 0xc0)
-            {
-                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
-                i += 2;
-            }
-            else
-            {
-                ch = (char)(bytes[i] & 0xff);
-                i += 1;
-            }
-
-            cs[length++] = ch;
-        }
-
-        return new String(cs);
+        return new String(chars, 0, len);
     }
 
     public static byte[] toUTF8ByteArray(String string)
@@ -263,6 +201,7 @@
         return bytes;
     }
 
+
     public static byte[] toByteArray(String string)
     {
         byte[] bytes = new byte[string.length()];
@@ -401,4 +340,6 @@
             return strs;
         }
     }
+
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
index 4216674..645b151 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -149,11 +149,27 @@
             
             end--;
         }
+
+        // empty data!
+        if (end == 0)
+        {
+            return 0;
+        }
         
-        int  i = off;
-        int  finish = end - 4;
-        
-        i = nextI(data, i, finish);
+        int  i = 0;
+        int  finish = end;
+
+        while (finish > off && i != 4)
+        {
+            if (!ignore((char)data[finish - 1]))
+            {
+                i++;
+            }
+
+            finish--;
+        }
+
+        i = nextI(data, off, finish);
 
         while (i < finish)
         {
@@ -185,8 +201,13 @@
             i = nextI(data, i, finish);
         }
 
-        outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]);
-        
+        int e0 = nextI(data, i, end);
+        int e1 = nextI(data, e0 + 1, end);
+        int e2 = nextI(data, e1 + 1, end);
+        int e3 = nextI(data, e2 + 1, end);
+
+        outLen += decodeLastBlock(out, (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3]);
+
         return outLen;
     }
 
@@ -224,11 +245,27 @@
             
             end--;
         }
+
+        // empty data!
+        if (end == 0)
+        {
+            return 0;
+        }
         
         int  i = 0;
-        int  finish = end - 4;
+        int  finish = end;
+
+        while (finish > 0 && i != 4)
+        {
+            if (!ignore(data.charAt(finish - 1)))
+            {
+                i++;
+            }
+
+            finish--;
+        }
         
-        i = nextI(data, i, finish);
+        i = nextI(data, 0, finish);
         
         while (i < finish)
         {
@@ -260,8 +297,13 @@
             i = nextI(data, i, finish);
         }
 
-        length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1));
+        int e0 = nextI(data, i, end);
+        int e1 = nextI(data, e0 + 1, end);
+        int e2 = nextI(data, e1 + 1, end);
+        int e3 = nextI(data, e2 + 1, end);
 
+        length += decodeLastBlock(out, data.charAt(e0), data.charAt(e1), data.charAt(e2), data.charAt(e3));
+        
         return length;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java
new file mode 100644
index 0000000..e64e443
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java
@@ -0,0 +1,156 @@
+package org.bouncycastle.util.encoders;
+
+/**
+ * Utilities for working with UTF-8 encodings.
+ * 
+ * Decoding of UTF-8 is based on a presentation by Bob Steagall at CppCon2018 (see
+ * https://github.com/BobSteagall/CppCon2018). It uses a Deterministic Finite Automaton (DFA) to
+ * recognize and decode multi-byte code points.
+ */
+public class UTF8
+{
+    // Constants for the categorization of code units
+    private static final byte C_ILL = 0;            //- C0..C1, F5..FF  ILLEGAL octets that should never appear in a UTF-8 sequence
+    private static final byte C_CR1 = 1;            //- 80..8F          Continuation range 1
+    private static final byte C_CR2 = 2;            //- 90..9F          Continuation range 2
+    private static final byte C_CR3 = 3;            //- A0..BF          Continuation range 3
+    private static final byte C_L2A = 4;            //- C2..DF          Leading byte range A / 2-byte sequence
+    private static final byte C_L3A = 5;            //- E0              Leading byte range A / 3-byte sequence
+    private static final byte C_L3B = 6;            //- E1..EC, EE..EF  Leading byte range B / 3-byte sequence
+    private static final byte C_L3C = 7;            //- ED              Leading byte range C / 3-byte sequence
+    private static final byte C_L4A = 8;            //- F0              Leading byte range A / 4-byte sequence
+    private static final byte C_L4B = 9;            //- F1..F3          Leading byte range B / 4-byte sequence
+    private static final byte C_L4C = 10;           //- F4              Leading byte range C / 4-byte sequence
+//  private static final byte C_ASC = 11;           //- 00..7F          ASCII leading byte range
+
+    // Constants for the states of a DFA
+    private static final byte S_ERR = -2;           //- Error state
+    private static final byte S_END = -1;           //- End (or Accept) state
+    private static final byte S_CS1 = 0x00;         //- Continuation state 1
+    private static final byte S_CS2 = 0x10;         //- Continuation state 2
+    private static final byte S_CS3 = 0x20;         //- Continuation state 3
+    private static final byte S_P3A = 0x30;         //- Partial 3-byte sequence state A
+    private static final byte S_P3B = 0x40;         //- Partial 3-byte sequence state B
+    private static final byte S_P4A = 0x50;         //- Partial 4-byte sequence state A
+    private static final byte S_P4B = 0x60;         //- Partial 4-byte sequence state B
+
+    private static final short[] firstUnitTable = new short[128];
+    private static final byte[] transitionTable = new byte[S_P4B + 16];
+
+    private static void fill(byte[] table, int first, int last, byte b)
+    {
+        for (int i = first; i <= last; ++i)
+        {
+            table[i] = b;
+        }
+    }
+
+    static
+    {
+        byte[] categories = new byte[128];
+        fill(categories, 0x00, 0x0F, C_CR1);
+        fill(categories, 0x10, 0x1F, C_CR2);
+        fill(categories, 0x20, 0x3F, C_CR3);
+        fill(categories, 0x40, 0x41, C_ILL);
+        fill(categories, 0x42, 0x5F, C_L2A);
+        fill(categories, 0x60, 0x60, C_L3A);
+        fill(categories, 0x61, 0x6C, C_L3B);
+        fill(categories, 0x6D, 0x6D, C_L3C);
+        fill(categories, 0x6E, 0x6F, C_L3B);
+        fill(categories, 0x70, 0x70, C_L4A);
+        fill(categories, 0x71, 0x73, C_L4B);
+        fill(categories, 0x74, 0x74, C_L4C);
+        fill(categories, 0x75, 0x7F, C_ILL);
+
+        fill(transitionTable, 0, transitionTable.length - 1, S_ERR);
+        fill(transitionTable, S_CS1 + 0x8, S_CS1 + 0xB, S_END);
+        fill(transitionTable, S_CS2 + 0x8, S_CS2 + 0xB, S_CS1);
+        fill(transitionTable, S_CS3 + 0x8, S_CS3 + 0xB, S_CS2);
+        fill(transitionTable, S_P3A + 0xA, S_P3A + 0xB, S_CS1);
+        fill(transitionTable, S_P3B + 0x8, S_P3B + 0x9, S_CS1);
+        fill(transitionTable, S_P4A + 0x9, S_P4A + 0xB, S_CS2);
+        fill(transitionTable, S_P4B + 0x8, S_P4B + 0x8, S_CS2);
+
+        byte[] firstUnitMasks = { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07 };
+        byte[] firstUnitTransitions = { S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B };
+
+        for (int i = 0x00; i < 0x80; ++i)
+        {
+            byte category = categories[i];
+
+            int codePoint = i & firstUnitMasks[category];
+            byte state = firstUnitTransitions[category];
+
+            firstUnitTable[i] = (short)((codePoint << 8) | state);
+        }
+    }
+
+    /**
+     * Transcode a UTF-8 encoding into a UTF-16 representation. In the general case the output
+     * {@code utf16} array should be at least as long as the input {@code utf8} one to handle
+     * arbitrary inputs. The number of output UTF-16 code units is returned, or -1 if any errors are
+     * encountered (in which case an arbitrary amount of data may have been written into the output
+     * array). Errors that will be detected are malformed UTF-8, including incomplete, truncated or
+     * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
+     * be produced. An error will also result if {@code utf16} is found to be too small to store the
+     * complete output.
+     * 
+     * @param utf8
+     *            A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf16
+     *            A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *            the output will fit.
+     * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
+     *         else -1 if the input was either malformed or encoded any unmappable characters, or if
+     *         the {@code utf16} is too small.
+     */
+    public static int transcodeToUTF16(byte[] utf8, char[] utf16)
+    {
+        int i = 0, j = 0;
+
+        while (i < utf8.length)
+        {
+            byte codeUnit = utf8[i++];
+            if (codeUnit >= 0)
+            {
+                if (j >= utf16.length) { return -1; }
+
+                utf16[j++] = (char)codeUnit;
+                continue;
+            }
+
+            short first = firstUnitTable[codeUnit & 0x7F];
+            int codePoint = first >>> 8;
+            byte state = (byte)first;
+
+            while (state >= 0)
+            {
+                if (i >= utf8.length) { return -1; }
+
+                codeUnit = utf8[i++];
+                codePoint = (codePoint << 6) | (codeUnit & 0x3F);
+                state = transitionTable[state + ((codeUnit & 0xFF) >>> 4)];
+            }
+
+            if (state == S_ERR) { return -1; }
+
+            if (codePoint <= 0xFFFF)
+            {
+                if (j >= utf16.length) { return -1; }
+
+                // Code points from U+D800 to U+DFFF are caught by the DFA
+                utf16[j++] = (char)codePoint;
+            }
+            else
+            {
+                if (j >= utf16.length - 1) { return -1; }
+
+                // Code points above U+10FFFF are caught by the DFA
+                utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10));
+                utf16[j++] = (char)(0xDC00 | (codePoint & 0x3FF));
+            }
+        }
+
+        return j;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java b/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
index 3045b4d..be9090d 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
@@ -22,6 +22,12 @@
         super(reader);
     }
 
+    /**
+     * Read the next PEM object as a blob of raw data with header information.
+     *
+     * @return the next object in the stream, null if no objects left.
+     * @throws IOException in case of a parse error.
+     */
     public PemObject readPemObject()
         throws IOException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/util/test/SimpleTest.java b/bcprov/src/main/java/org/bouncycastle/util/test/SimpleTest.java
index 9e70a3f..146fb12 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/test/SimpleTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/test/SimpleTest.java
@@ -60,6 +60,16 @@
     }
 
     protected void isEquals(
+        long a,
+        long b)
+    {
+        if (a != b)
+        {
+            throw new TestFailedException(SimpleTestResult.failed(this, "no message"));
+        }
+    }
+
+    protected void isEquals(
         String message,
         boolean a,
         boolean b)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java b/bcprov/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java
index 89073d7..d20a28f 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/test/UncloseableOutputStream.java
@@ -4,6 +4,11 @@
 import java.io.IOException;
 import java.io.OutputStream;
 
+/**
+ * This is a testing utility class to check the property that an {@link OutputStream} is never
+ * closed in some particular context - typically when wrapped by another {@link OutputStream} that
+ * should not be forwarding its {@link OutputStream#close()} calls. Not needed in production code.
+ */
 public class UncloseableOutputStream extends FilterOutputStream
 {
     public UncloseableOutputStream(OutputStream s)
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/CertPathValidatorUtilities.java b/bcprov/src/main/java/org/bouncycastle/x509/CertPathValidatorUtilities.java
index ca8769c..7d788cb 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/CertPathValidatorUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/CertPathValidatorUtilities.java
@@ -8,25 +8,19 @@
 import java.security.KeyFactory;
 import java.security.PublicKey;
 import java.security.cert.CRLException;
-import java.security.cert.CertPath;
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertStore;
 import java.security.cert.CertStoreException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
 import java.security.cert.PKIXParameters;
 import java.security.cert.PolicyQualifierInfo;
-import java.security.cert.TrustAnchor;
 import java.security.cert.X509CRL;
 import java.security.cert.X509CRLEntry;
-import java.security.cert.X509CRLSelector;
-import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.DSAParams;
 import java.security.interfaces.DSAPublicKey;
 import java.security.spec.DSAPublicKeySpec;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -41,37 +35,26 @@
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Enumerated;
-import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1OutputStream;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.CRLDistPoint;
 import org.bouncycastle.asn1.x509.CRLReason;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
 import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import org.bouncycastle.asn1.x509.PolicyInformation;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509Extension;
 import org.bouncycastle.jcajce.PKIXCertStoreSelector;
-import org.bouncycastle.jce.X509LDAPCertStoreParameters;
 import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
 import org.bouncycastle.jce.provider.AnnotatedException;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.provider.PKIXPolicyNode;
 import org.bouncycastle.util.Encodable;
-import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Selector;
 import org.bouncycastle.util.Store;
 import org.bouncycastle.util.StoreException;
@@ -117,148 +100,8 @@
         "privilegeWithdrawn",
         "aACompromise"};
 
-    /**
-     * Search the given Set of TrustAnchor's for one that is the
-     * issuer of the given X509 certificate. Uses the default provider
-     * for signature verification.
-     *
-     * @param cert         the X509 certificate
-     * @param trustAnchors a Set of TrustAnchor's
-     * @return the <code>TrustAnchor</code> object if found or
-     *         <code>null</code> if not.
-     * @throws AnnotatedException if a TrustAnchor was found but the signature verification
-     * on the given certificate has thrown an exception.
-     */
-    protected static TrustAnchor findTrustAnchor(
-        X509Certificate cert,
-        Set trustAnchors)
-        throws AnnotatedException
-    {
-        return findTrustAnchor(cert, trustAnchors, null);
-    }
 
-    /**
-     * Search the given Set of TrustAnchor's for one that is the
-     * issuer of the given X509 certificate. Uses the specified
-     * provider for signature verification, or the default provider
-     * if null.
-     *
-     * @param cert         the X509 certificate
-     * @param trustAnchors a Set of TrustAnchor's
-     * @param sigProvider  the provider to use for signature verification
-     * @return the <code>TrustAnchor</code> object if found or
-     *         <code>null</code> if not.
-     * @throws AnnotatedException if a TrustAnchor was found but the signature verification
-     * on the given certificate has thrown an exception.
-     */
-    protected static TrustAnchor findTrustAnchor(
-        X509Certificate cert,
-        Set trustAnchors,
-        String sigProvider)
-        throws AnnotatedException
-    {
-        TrustAnchor trust = null;
-        PublicKey trustPublicKey = null;
-        Exception invalidKeyEx = null;
 
-        X509CertSelector certSelectX509 = new X509CertSelector();
-        X500Principal certIssuer = getEncodedIssuerPrincipal(cert);
-
-        try
-        {
-            certSelectX509.setSubject(certIssuer.getEncoded());
-        }
-        catch (IOException ex)
-        {
-            throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex);
-        }
-
-        Iterator iter = trustAnchors.iterator();
-        while (iter.hasNext() && trust == null)
-        {
-            trust = (TrustAnchor)iter.next();
-            if (trust.getTrustedCert() != null)
-            {
-                if (certSelectX509.match(trust.getTrustedCert()))
-                {
-                    trustPublicKey = trust.getTrustedCert().getPublicKey();
-                }
-                else
-                {
-                    trust = null;
-                }
-            }
-            else if (trust.getCAName() != null
-                && trust.getCAPublicKey() != null)
-            {
-                try
-                {
-                    X500Principal caName = new X500Principal(trust.getCAName());
-                    if (certIssuer.equals(caName))
-                    {
-                        trustPublicKey = trust.getCAPublicKey();
-                    }
-                    else
-                    {
-                        trust = null;
-                    }
-                }
-                catch (IllegalArgumentException ex)
-                {
-                    trust = null;
-                }
-            }
-            else
-            {
-                trust = null;
-            }
-
-            if (trustPublicKey != null)
-            {
-                try
-                {
-                    verifyX509Certificate(cert, trustPublicKey, sigProvider);
-                }
-                catch (Exception ex)
-                {
-                    invalidKeyEx = ex;
-                    trust = null;
-                    trustPublicKey = null;
-                }
-            }
-        }
-
-        if (trust == null && invalidKeyEx != null)
-        {
-            throw new AnnotatedException("TrustAnchor found but certificate validation failed.", invalidKeyEx);
-        }
-
-        return trust;
-    }
-
-    protected static void addAdditionalStoresFromAltNames(
-        X509Certificate cert,
-        ExtendedPKIXParameters pkixParams)
-        throws CertificateParsingException
-    {
-        // if in the IssuerAltName extension an URI
-        // is given, add an additional X.509 store
-        if (cert.getIssuerAlternativeNames() != null)
-        {
-            Iterator it = cert.getIssuerAlternativeNames().iterator();
-            while (it.hasNext())
-            {
-                // look for URI
-                List list = (List)it.next();
-                if (list.get(0).equals(Integers.valueOf(GeneralName.uniformResourceIdentifier)))
-                {
-                    // found
-                    String temp = (String)list.get(1);
-                    CertPathValidatorUtilities.addAdditionalStoreFromLocation(temp, pkixParams);
-                }
-            }
-        }
-    }
 
     /**
      * Returns the issuer of an attribute certificate or certificate.
@@ -651,54 +494,6 @@
         return policySet == null || policySet.contains(ANY_POLICY) || policySet.isEmpty();
     }
 
-    protected static void addAdditionalStoreFromLocation(String location,
-                                                         ExtendedPKIXParameters pkixParams)
-    {
-        if (pkixParams.isAdditionalLocationsEnabled())
-        {
-            try
-            {
-                if (location.startsWith("ldap://"))
-                {
-                    // ldap://directory.d-trust.net/CN=D-TRUST
-                    // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
-                    // skip "ldap://"
-                    location = location.substring(7);
-                    // after first / baseDN starts
-                    String base = null;
-                    String url = null;
-                    if (location.indexOf("/") != -1)
-                    {
-                        base = location.substring(location.indexOf("/"));
-                        // URL
-                        url = "ldap://"
-                            + location.substring(0, location.indexOf("/"));
-                    }
-                    else
-                    {
-                        url = "ldap://" + location;
-                    }
-                    // use all purpose parameters
-                    X509LDAPCertStoreParameters params = new X509LDAPCertStoreParameters.Builder(
-                        url, base).build();
-                    pkixParams.addAdditionalStore(X509Store.getInstance(
-                        "CERTIFICATE/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
-                    pkixParams.addAdditionalStore(X509Store.getInstance(
-                        "CRL/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
-                    pkixParams.addAdditionalStore(X509Store.getInstance(
-                        "ATTRIBUTECERTIFICATE/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
-                    pkixParams.addAdditionalStore(X509Store.getInstance(
-                        "CERTIFICATEPAIR/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
-                }
-            }
-            catch (Exception e)
-            {
-                // cannot happen
-                throw new RuntimeException("Exception adding X.509 stores.");
-            }
-        }
-    }
-
     /**
      * Return a Collection of all certificates or attribute certificates found
      * in the X509Store's that are matching the certSelect criteriums.
@@ -853,176 +648,6 @@
         return certs;
     }
 
-    protected static void addAdditionalStoresFromCRLDistributionPoint(
-        CRLDistPoint crldp, ExtendedPKIXParameters pkixParams)
-        throws AnnotatedException
-    {
-        if (crldp != null)
-        {
-            DistributionPoint dps[] = null;
-            try
-            {
-                dps = crldp.getDistributionPoints();
-            }
-            catch (Exception e)
-            {
-                throw new AnnotatedException(
-                    "Distribution points could not be read.", e);
-            }
-            for (int i = 0; i < dps.length; i++)
-            {
-                DistributionPointName dpn = dps[i].getDistributionPoint();
-                // look for URIs in fullName
-                if (dpn != null)
-                {
-                    if (dpn.getType() == DistributionPointName.FULL_NAME)
-                    {
-                        GeneralName[] genNames = GeneralNames.getInstance(
-                            dpn.getName()).getNames();
-                        // look for an URI
-                        for (int j = 0; j < genNames.length; j++)
-                        {
-                            if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier)
-                            {
-                                String location = DERIA5String.getInstance(
-                                    genNames[j].getName()).getString();
-                                CertPathValidatorUtilities
-                                    .addAdditionalStoreFromLocation(location,
-                                        pkixParams);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Add the CRL issuers from the cRLIssuer field of the distribution point or
-     * from the certificate if not given to the issuer criterion of the
-     * <code>selector</code>.
-     * <p>
-     * The <code>issuerPrincipals</code> are a collection with a single
-     * <code>X500Principal</code> for <code>X509Certificate</code>s. For
-     * {@link X509AttributeCertificate}s the issuer may contain more than one
-     * <code>X500Principal</code>.
-     * </p>
-     * @param dp               The distribution point.
-     * @param issuerPrincipals The issuers of the certificate or attribute
-     *                         certificate which contains the distribution point.
-     * @param selector         The CRL selector.
-     * @param pkixParams       The PKIX parameters containing the cert stores.
-     * @throws AnnotatedException if an exception occurs while processing.
-     * @throws ClassCastException if <code>issuerPrincipals</code> does not
-     * contain only <code>X500Principal</code>s.
-     */
-    protected static void getCRLIssuersFromDistributionPoint(
-        DistributionPoint dp,
-        Collection issuerPrincipals,
-        X509CRLSelector selector,
-        ExtendedPKIXParameters pkixParams)
-        throws AnnotatedException
-    {
-        List issuers = new ArrayList();
-        // indirect CRL
-        if (dp.getCRLIssuer() != null)
-        {
-            GeneralName genNames[] = dp.getCRLIssuer().getNames();
-            // look for a DN
-            for (int j = 0; j < genNames.length; j++)
-            {
-                if (genNames[j].getTagNo() == GeneralName.directoryName)
-                {
-                    try
-                    {
-                        issuers.add(new X500Principal(genNames[j].getName()
-                            .toASN1Primitive().getEncoded()));
-                    }
-                    catch (IOException e)
-                    {
-                        throw new AnnotatedException(
-                            "CRL issuer information from distribution point cannot be decoded.",
-                            e);
-                    }
-                }
-            }
-        }
-        else
-        {
-            /*
-             * certificate issuer is CRL issuer, distributionPoint field MUST be
-             * present.
-             */
-            if (dp.getDistributionPoint() == null)
-            {
-                throw new AnnotatedException(
-                    "CRL issuer is omitted from distribution point but no distributionPoint field present.");
-            }
-            // add and check issuer principals
-            for (Iterator it = issuerPrincipals.iterator(); it.hasNext(); )
-            {
-                issuers.add((X500Principal)it.next());
-            }
-        }
-        // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
-        // distributionPoint
-//        if (dp.getDistributionPoint() != null)
-//        {
-//            // look for nameRelativeToCRLIssuer
-//            if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
-//            {
-//                // append fragment to issuer, only one
-//                // issuer can be there, if this is given
-//                if (issuers.size() != 1)
-//                {
-//                    throw new AnnotatedException(
-//                        "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
-//                }
-//                ASN1Encodable relName = dp.getDistributionPoint().getName();
-//                Iterator it = issuers.iterator();
-//                List issuersTemp = new ArrayList(issuers.size());
-//                while (it.hasNext())
-//                {
-//                    Enumeration e = null;
-//                    try
-//                    {
-//                        e = ASN1Sequence.getInstance(
-//                            new ASN1InputStream(((X500Principal) it.next())
-//                                .getEncoded()).readObject()).getObjects();
-//                    }
-//                    catch (IOException ex)
-//                    {
-//                        throw new AnnotatedException(
-//                            "Cannot decode CRL issuer information.", ex);
-//                    }
-//                    ASN1EncodableVector v = new ASN1EncodableVector();
-//                    while (e.hasMoreElements())
-//                    {
-//                        v.add((ASN1Encodable) e.nextElement());
-//                    }
-//                    v.add(relName);
-//                    issuersTemp.add(new X500Principal(new DERSequence(v)
-//                        .getDEREncoded()));
-//                }
-//                issuers.clear();
-//                issuers.addAll(issuersTemp);
-//            }
-//        }
-        Iterator it = issuers.iterator();
-        while (it.hasNext())
-        {
-            try
-            {
-                selector.addIssuerName(((X500Principal)it.next()).getEncoded());
-            }
-            catch (IOException ex)
-            {
-                throw new AnnotatedException(
-                    "Cannot decode CRL issuer information.", ex);
-            }
-        }
-    }
-
     private static BigInteger getSerialNumber(
         Object cert)
     {
@@ -1133,238 +758,6 @@
     }
 
     /**
-     * Fetches delta CRLs according to RFC 3280 section 5.2.4.
-     *
-     * @param currentDate The date for which the delta CRLs must be valid.
-     * @param paramsPKIX  The extended PKIX parameters.
-     * @param completeCRL The complete CRL the delta CRL is for.
-     * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
-     * @throws AnnotatedException if an exception occurs while picking the delta
-     * CRLs.
-     */
-    protected static Set getDeltaCRLs(Date currentDate,
-                                      ExtendedPKIXParameters paramsPKIX, X509CRL completeCRL)
-        throws AnnotatedException
-    {
-
-        X509CRLStoreSelector deltaSelect = new X509CRLStoreSelector();
-
-        // 5.2.4 (a)
-        try
-        {
-            deltaSelect.addIssuerName(CertPathValidatorUtilities
-                .getIssuerPrincipal(completeCRL).getEncoded());
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException("Cannot extract issuer from CRL.", e);
-        }
-
-        BigInteger completeCRLNumber = null;
-        try
-        {
-            ASN1Primitive derObject = CertPathValidatorUtilities.getExtensionValue(completeCRL,
-                CRL_NUMBER);
-            if (derObject != null)
-            {
-                completeCRLNumber = ASN1Integer.getInstance(derObject).getPositiveValue();
-            }
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException(
-                "CRL number extension could not be extracted from CRL.", e);
-        }
-
-        // 5.2.4 (b)
-        byte[] idp = null;
-        try
-        {
-            idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException(
-                "Issuing distribution point extension value could not be read.",
-                e);
-        }
-
-        // 5.2.4 (d)
-
-        deltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
-            .add(BigInteger.valueOf(1)));
-
-        deltaSelect.setIssuingDistributionPoint(idp);
-        deltaSelect.setIssuingDistributionPointEnabled(true);
-
-        // 5.2.4 (c)
-        deltaSelect.setMaxBaseCRLNumber(completeCRLNumber);
-
-        // find delta CRLs
-        Set temp = CRL_UTIL.findCRLs(deltaSelect, paramsPKIX, currentDate);
-
-        Set result = new HashSet();
-
-        for (Iterator it = temp.iterator(); it.hasNext(); )
-        {
-            X509CRL crl = (X509CRL)it.next();
-
-            if (isDeltaCRL(crl))
-            {
-                result.add(crl);
-            }
-        }
-
-        return result;
-    }
-
-    private static boolean isDeltaCRL(X509CRL crl)
-    {
-        Set critical = crl.getCriticalExtensionOIDs();
-
-        if (critical == null)
-        {
-            return false;
-        }
-
-        return critical.contains(Extension.deltaCRLIndicator.getId());
-    }
-
-    /**
-     * Fetches complete CRLs according to RFC 3280.
-     *
-     * @param dp          The distribution point for which the complete CRL
-     * @param cert        The <code>X509Certificate</code> or
-     *                    {@link org.bouncycastle.x509.X509AttributeCertificate} for
-     *                    which the CRL should be searched.
-     * @param currentDate The date for which the delta CRLs must be valid.
-     * @param paramsPKIX  The extended PKIX parameters.
-     * @return A <code>Set</code> of <code>X509CRL</code>s with complete
-     *         CRLs.
-     * @throws AnnotatedException if an exception occurs while picking the CRLs
-     * or no CRLs are found.
-     */
-    protected static Set getCompleteCRLs(DistributionPoint dp, Object cert,
-                                         Date currentDate, ExtendedPKIXParameters paramsPKIX)
-        throws AnnotatedException
-    {
-        X509CRLStoreSelector crlselect = new X509CRLStoreSelector();
-        try
-        {
-            Set issuers = new HashSet();
-            if (cert instanceof X509AttributeCertificate)
-            {
-                issuers.add(((X509AttributeCertificate)cert)
-                    .getIssuer().getPrincipals()[0]);
-            }
-            else
-            {
-                issuers.add(getEncodedIssuerPrincipal(cert));
-            }
-            CertPathValidatorUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, crlselect, paramsPKIX);
-        }
-        catch (AnnotatedException e)
-        {
-            throw new AnnotatedException(
-                "Could not get issuer information from distribution point.", e);
-        }
-        if (cert instanceof X509Certificate)
-        {
-            crlselect.setCertificateChecking((X509Certificate)cert);
-        }
-        else if (cert instanceof X509AttributeCertificate)
-        {
-            crlselect.setAttrCertificateChecking((X509AttributeCertificate)cert);
-        }
-
-
-        crlselect.setCompleteCRLEnabled(true);
-
-        Set crls = CRL_UTIL.findCRLs(crlselect, paramsPKIX, currentDate);
-
-        if (crls.isEmpty())
-        {
-            if (cert instanceof X509AttributeCertificate)
-            {
-                X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
-
-                throw new AnnotatedException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
-            }
-            else
-            {
-                X509Certificate xCert = (X509Certificate)cert;
-
-                throw new AnnotatedException("No CRLs found for issuer \"" + xCert.getIssuerX500Principal() + "\"");
-            }
-        }
-        return crls;
-    }
-
-    protected static Date getValidCertDateFromValidityModel(
-        ExtendedPKIXParameters paramsPKIX, CertPath certPath, int index)
-        throws AnnotatedException
-    {
-        if (paramsPKIX.getValidityModel() == ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL)
-        {
-            // if end cert use given signing/encryption/... time
-            if (index <= 0)
-            {
-                return CertPathValidatorUtilities.getValidDate(paramsPKIX);
-                // else use time when previous cert was created
-            }
-            else
-            {
-                if (index - 1 == 0)
-                {
-                    ASN1GeneralizedTime dateOfCertgen = null;
-                    try
-                    {
-                        byte[] extBytes = ((X509Certificate)certPath.getCertificates().get(index - 1)).getExtensionValue(ISISMTTObjectIdentifiers.id_isismtt_at_dateOfCertGen.getId());
-                        if (extBytes != null)
-                        {
-                            dateOfCertgen = ASN1GeneralizedTime.getInstance(ASN1Primitive.fromByteArray(extBytes));
-                        }
-                    }
-                    catch (IOException e)
-                    {
-                        throw new AnnotatedException(
-                            "Date of cert gen extension could not be read.");
-                    }
-                    catch (IllegalArgumentException e)
-                    {
-                        throw new AnnotatedException(
-                            "Date of cert gen extension could not be read.");
-                    }
-                    if (dateOfCertgen != null)
-                    {
-                        try
-                        {
-                            return dateOfCertgen.getDate();
-                        }
-                        catch (ParseException e)
-                        {
-                            throw new AnnotatedException(
-                                "Date from date of cert gen extension could not be parsed.",
-                                e);
-                        }
-                    }
-                    return ((X509Certificate)certPath.getCertificates().get(
-                        index - 1)).getNotBefore();
-                }
-                else
-                {
-                    return ((X509Certificate)certPath.getCertificates().get(
-                        index - 1)).getNotBefore();
-                }
-            }
-        }
-        else
-        {
-            return getValidDate(paramsPKIX);
-        }
-    }
-
-    /**
      * Return the next working key inheriting DSA parameters if necessary.
      * <p>
      * This methods inherits DSA parameters from the indexed certificate or
@@ -1383,7 +776,7 @@
      *              which should be extended with DSA parameters.
      * @return The public key of the certificate in list position
      *         <code>index</code> extended with DSA parameters if applicable.
-     * @throws AnnotatedException if DSA parameters cannot be inherited.
+     * @throws CertPathValidatorException if DSA parameters cannot be inherited.
      */
     protected static PublicKey getNextWorkingKey(List certs, int index)
         throws CertPathValidatorException
@@ -1429,62 +822,6 @@
         throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
     }
 
-    /**
-     * Find the issuer certificates of a given certificate.
-     *
-     * @param cert       The certificate for which an issuer should be found.
-     * @return A <code>Collection</code> object containing the issuer
-     *         <code>X509Certificate</code>s. Never <code>null</code>.
-     * @throws AnnotatedException if an error occurs.
-     */
-    static Collection findIssuerCerts(
-        X509Certificate cert,
-        List            certStores,
-        List            pkixCertStores)
-        throws AnnotatedException
-    {
-        X509CertSelector selector = new X509CertSelector();
-
-        try
-        {
-            selector.setSubject(cert.getIssuerX500Principal().getEncoded());
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException(
-                           "Subject criteria for certificate selector to find issuer certificate could not be set.", e);
-        }
-
-        PKIXCertStoreSelector certSelect = new PKIXCertStoreSelector.Builder(selector).build();
-        Set certs = new HashSet();
-
-        Iterator iter;
-
-        try
-        {
-            List matches = new ArrayList();
-
-            matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, certStores));
-            matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixCertStores));
-
-            iter = matches.iterator();
-        }
-        catch (AnnotatedException e)
-        {
-            throw new AnnotatedException("Issuer certificate cannot be searched.", e);
-        }
-
-        X509Certificate issuer = null;
-        while (iter.hasNext())
-        {
-            issuer = (X509Certificate)iter.next();
-            // issuer cannot be verified because possible DSA inheritance
-            // parameters are missing
-            certs.add(issuer);
-        }
-        return certs;
-    }
-
     protected static void verifyX509Certificate(X509Certificate cert, PublicKey publicKey,
                                                 String sigProvider)
         throws GeneralSecurityException
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java b/bcprov/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java
index 952f775..50a292b 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.x509;
 
-import org.bouncycastle.util.Selector;
-import org.bouncycastle.util.Store;
-
 import java.security.InvalidAlgorithmParameterException;
 import java.security.cert.CertSelector;
 import java.security.cert.CertStore;
@@ -16,6 +13,9 @@
 import java.util.List;
 import java.util.Set;
 
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
 /**
  * This class extends the PKIXParameters with a validity model parameter.
  *
@@ -286,7 +286,7 @@
      * 
      * @param store The store to add.
      * @see #getStores()
-     * @deprectaed use addStore().
+     * @deprecated use addStore().
      */
     public void addAdditionalStore(Store store)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java b/bcprov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java
index 535952f..33bc83d 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java
@@ -55,13 +55,13 @@
 import org.bouncycastle.asn1.x509.CRLDistPoint;
 import org.bouncycastle.asn1.x509.DistributionPoint;
 import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.GeneralSubtree;
 import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import org.bouncycastle.asn1.x509.NameConstraints;
 import org.bouncycastle.asn1.x509.PolicyInformation;
-import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.asn1.x509.qualified.Iso4217CurrencyCode;
 import org.bouncycastle.asn1.x509.qualified.MonetaryValue;
 import org.bouncycastle.asn1.x509.qualified.QCStatement;
@@ -75,7 +75,6 @@
 import org.bouncycastle.jce.provider.PKIXNameConstraintValidatorException;
 import org.bouncycastle.jce.provider.PKIXPolicyNode;
 import org.bouncycastle.util.Integers;
-import org.bouncycastle.x509.extension.X509ExtensionUtil;
 
 /**
  * PKIXCertPathReviewer<br>
@@ -84,9 +83,9 @@
 public class PKIXCertPathReviewer extends CertPathValidatorUtilities
 {
     
-    private static final String QC_STATEMENT = X509Extensions.QCStatements.getId();
-    private static final String CRL_DIST_POINTS = X509Extensions.CRLDistributionPoints.getId();
-    private static final String AUTH_INFO_ACCESS = X509Extensions.AuthorityInfoAccess.getId();
+    private static final String QC_STATEMENT = Extension.qCStatements.getId();
+    private static final String CRL_DIST_POINTS = Extension.cRLDistributionPoints.getId();
+    private static final String AUTH_INFO_ACCESS = Extension.authorityInfoAccess.getId();
     
     private static final String RESOURCE_NAME = "org.bouncycastle.x509.CertPathReviewerMessages";
     
@@ -894,30 +893,23 @@
             {
                 ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.NoIssuerPublicKey");
                 // if there is an authority key extension add the serial and issuer of the missing certificate
-                byte[] akiBytes = cert.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
+                byte[] akiBytes = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId());
                 if (akiBytes != null)
                 {
-                    try
+                    AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(
+                        DEROctetString.getInstance(akiBytes).getOctets());
+                    GeneralNames issuerNames = aki.getAuthorityCertIssuer();
+                    if (issuerNames != null)
                     {
-                        AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(
-                            X509ExtensionUtil.fromExtensionValue(akiBytes));
-                        GeneralNames issuerNames = aki.getAuthorityCertIssuer();
-                        if (issuerNames != null)
+                        GeneralName name = issuerNames.getNames()[0];
+                        BigInteger serial = aki.getAuthorityCertSerialNumber();
+                        if (serial != null)
                         {
-                            GeneralName name = issuerNames.getNames()[0];
-                            BigInteger serial = aki.getAuthorityCertSerialNumber(); 
-                            if (serial != null)
-                            {
-                                Object[] extraArgs = {new LocaleString(RESOURCE_NAME, "missingIssuer"), " \"", name , 
-                                        "\" ", new LocaleString(RESOURCE_NAME, "missingSerial") , " ", serial};
-                                msg.setExtraArguments(extraArgs);
-                            }
+                            Object[] extraArgs = {new LocaleString(RESOURCE_NAME, "missingIssuer"), " \"", name ,
+                                    "\" ", new LocaleString(RESOURCE_NAME, "missingSerial") , " ", serial};
+                            msg.setExtraArguments(extraArgs);
                         }
                     }
-                    catch (IOException e)
-                    {
-                        // ignore
-                    }
                 }
                 addError(msg,index);
             }
@@ -2208,7 +2200,7 @@
                     ASN1Enumerated reasonCode;
                     try
                     {
-                        reasonCode = ASN1Enumerated.getInstance(getExtensionValue(crl_entry, X509Extensions.ReasonCode.getId()));
+                        reasonCode = ASN1Enumerated.getInstance(getExtensionValue(crl_entry, Extension.reasonCode.getId()));
                     }
                     catch (AnnotatedException ae)
                     {
@@ -2497,7 +2489,7 @@
         try
         {
             certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded());
-            byte[] ext = cert.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
+            byte[] ext = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId());
 
             if (ext != null)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/X509AttributeCertStoreSelector.java b/bcprov/src/main/java/org/bouncycastle/x509/X509AttributeCertStoreSelector.java
index bd474fd..437bde7 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/X509AttributeCertStoreSelector.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/X509AttributeCertStoreSelector.java
@@ -14,11 +14,11 @@
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.Target;
 import org.bouncycastle.asn1.x509.TargetInformation;
 import org.bouncycastle.asn1.x509.Targets;
-import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.util.Selector;
 
 /**
@@ -27,7 +27,7 @@
  * 
  * @see org.bouncycastle.x509.X509AttributeCertificate
  * @see org.bouncycastle.x509.X509Store
- *  @deprecated use org.bouncycastle.cert.X509AttributeCertificateSelector and org.bouncycastle.cert.X509AttributeCertificateSelectorBuilder.
+ * @deprecated use org.bouncycastle.cert.X509AttributeCertificateSelector and org.bouncycastle.cert.X509AttributeCertificateSelectorBuilder.
  */
 public class X509AttributeCertStoreSelector
     implements Selector
@@ -118,7 +118,7 @@
         {
 
             byte[] targetInfoExt = attrCert
-                .getExtensionValue(X509Extensions.TargetInformation.getId());
+                .getExtensionValue(Extension.targetInformation.getId());
             if (targetInfoExt != null)
             {
                 TargetInformation targetinfo;
@@ -431,7 +431,7 @@
      * Adds a collection with target groups criteria. If <code>null</code> is
      * given any will do.
      * <p>
-     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code representing DER
+     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code> representing DER
      * encoded GeneralNames.
      * 
      * @param names A collection of target groups.
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java b/bcprov/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java
index 2486d20..63ed8ab 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java
@@ -7,7 +7,7 @@
 import java.security.cert.X509CRLSelector;
 
 import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Selector;
 import org.bouncycastle.x509.extension.X509ExtensionUtil;
@@ -100,7 +100,7 @@
         try
         {
             byte[] bytes = crl
-                .getExtensionValue(X509Extensions.DeltaCRLIndicator.getId());
+                .getExtensionValue(Extension.deltaCRLIndicator.getId());
             if (bytes != null)
             {
                 dci = ASN1Integer.getInstance(X509ExtensionUtil
@@ -139,7 +139,7 @@
         if (issuingDistributionPointEnabled)
         {
             byte[] idp = crl
-                .getExtensionValue(X509Extensions.IssuingDistributionPoint
+                .getExtensionValue(Extension.issuingDistributionPoint
                     .getId());
             if (issuingDistributionPoint == null)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java b/bcprov/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
index 2e4d14d..0779d1a 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
@@ -16,13 +16,19 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.X509Extension;
 import org.bouncycastle.util.Integers;
 
 
+/**
+ * @deprecated use org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils
+ */
 public class X509ExtensionUtil
 {
+    /**
+     * @deprecated use org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils.parseExtensionValue()
+     */
     public static ASN1Primitive fromExtensionValue(
         byte[]  encodedValue) 
         throws IOException
@@ -32,18 +38,24 @@
         return ASN1Primitive.fromByteArray(octs.getOctets());
     }
 
+    /**
+     * @deprecated use org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils.getIssuerAlternativeNames()
+     */
     public static Collection getIssuerAlternativeNames(X509Certificate cert)
             throws CertificateParsingException
     {
-        byte[] extVal = cert.getExtensionValue(X509Extension.issuerAlternativeName.getId());
+        byte[] extVal = cert.getExtensionValue(Extension.issuerAlternativeName.getId());
 
         return getAlternativeNames(extVal);
     }
 
+    /**
+     * @deprecated use org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils.getSubjectAlternativeNames()
+     */
     public static Collection getSubjectAlternativeNames(X509Certificate cert)
             throws CertificateParsingException
     {        
-        byte[] extVal = cert.getExtensionValue(X509Extension.subjectAlternativeName.getId());
+        byte[] extVal = cert.getExtensionValue(Extension.subjectAlternativeName.getId());
 
         return getAlternativeNames(extVal);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/util/LDAPStoreHelper.java b/bcprov/src/main/java/org/bouncycastle/x509/util/LDAPStoreHelper.java
index 128c1c8..68e450e 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/util/LDAPStoreHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/util/LDAPStoreHelper.java
@@ -61,7 +61,6 @@
  * href="http://www3.ietf.org/proceedings/01mar/I-D/pkix-ldap-schema-01.txt">Internet
  * X.509 Public Key Infrastructure Additional LDAP Schema for PKIs and PMIs</a>
  * </ul>
- * </p>
  */
 public class LDAPStoreHelper
 {
@@ -814,7 +813,7 @@
 
     /**
      * Returns end certificates.
-     * <p/>
+     * <p>
      * The attributeDescriptorCertificate is self signed by a source of
      * authority and holds a description of the privilege and its delegation
      * rules.
diff --git a/bouncycastle.version b/bouncycastle.version
index e258c89..24734c8 100644
--- a/bouncycastle.version
+++ b/bouncycastle.version
@@ -1,2 +1,2 @@
 BOUNCYCASTLE_JDK=15on
-BOUNCYCASTLE_VERSION=157
+BOUNCYCASTLE_VERSION=161